Repository navigation
Releases
v0.4.0
Multi-model runtime
- Native Kimi and GLM profiles share provider routing, model budgets and scoped
credentials across the CLI and app-server clients. Unknown prices are shown
as unavailable. See multi-model configuration. - Claude Agent SDK 0.3.310 is an explicit experimental engine, with controlled
DeepCode tools, approvals, snapshots and isolated sessions. Native remains the
default. To roll back, selectengine: "native"and start a new session;
existing SDK records are retained. - Truncated responses and exhausted turn budgets report incomplete execution.
Summaries inherit the selected model and cancellation, credentials follow the
documented precedence, and user prompts can be cancelled. - File mutations recheck permissions after approval; review restore respects
file restrictions, and SDK rollback records point to the pre-edit snapshot.
Hook processes and SDK resources are cleaned up when work stops. Cancellation
during the final path check or file preparation no longer starts a later write.
Additional writable directories work with the default sandbox mode; SDK
session forks are rejected before copying incomplete history. - Real SDK executable tests run on Ubuntu and macOS against local fixtures;
Linux bwrap/netns, desktop protocol journeys and release gates also pass.
Real Kimi/GLM endpoints and long-task quality still need live acceptance. - The VSIX uses native by default and does not bundle the optional SDK
executable. Signed macOS installers and npm publication require the release
credentials described in Releasing DeepCode.
The following changes accumulated on main since v0.3.0 are also included.
⚠️ Breaking
deepcode mcp servenow applies your permission settings. It executed
Read/Write/Edit/Bash for any connected MCP peer with no mode, no permission
rules, no file contract and noPreToolUsehooks — the same shape as the
runAgentbypass fixed in #181, in a different entry point. Every call now
goes through the central gate, a call that would need approval is refused
(nobody is attached to that pipe to approve it), and a permissive
permissions.defaultModeis clamped todefaultexactly as a scheduled job's
is. A peer can now do whatpermissions.allowsays it can and nothing else,
so anyone relying on the old behaviour must add rules — or start the server
with an explicit--mode.--sandboxalso applies now; it did not before.
✨ Added
-
Grep and Glob results in the desktop are now clickable locations. Both
tools attach the places they found as structured data — resolved from their
own parsed rows, where a path containing:is still unambiguous — and the
desktop renders a search's card as a list of openable paths (with line and
matched text in content mode) instead of a grey text blob. Clicking one opens
it in the file panel. This is thelocationsrender intent the DSH round
specified and deferred: re-deriving entries by parsing the formatted result
text was the rejected design, and attaching them at the source is what makes
the entries exact. A session restored from its log has only the text the
model saw, so replayed cards degrade to today's plain-text body. -
/planenters plan mode directly (/plan offleaves it) — the same
switch/mode planalready threw, one word shorter and matching what Claude
Code users type. A trailing prompt (/plan do X) is refused with a pointer
rather than silently dropped, because half-obeying a command teaches the
wrong lesson about what it did. -
Persistent shells now last a whole CLI session, and
/shellsshows them.
The registry landed in #273 owned by a singlerunAgentcall, which meant a
shell opened in one turn was gone by the next — a slowerBashwith extra
steps. The REPL now owns one registry for the session and threads it through
every turn, socd,export, and an activated virtualenv survive from one
message to the next./shellslists what is open and where each started;/shells close <id>
closes one and whatever is still running in it. Worth having because the
change is what makes these processes outlive a turn: without a view of them,
the user has no way to see or stop something the agent left running.Everything closes when the session ends, including when it ends by throwing —
these are real processes in their own process group, so they do not die with
the CLI. Background tasks and sub-agents deliberately do not share the
session's shells: two agents interleaving commands in one shell would each be
wrong about its state. -
A shell that survives between tool calls (#273) —
ShellOpen/ShellRun/
ShellClose/ShellList. EveryBashcall is a fresh process, socd,
export, andsource venv/bin/activatewere forgotten the moment they
returned; the workaround was re-pasting the whole prefix into every command,
which still could not hold a running dev server.Not a PTY. That would mean
node-pty, a native dependency needing a build for
every platform the desktop ships to, bought for full-screen programs that are a
small share of the value. This runs over pipes with a per-session random
sentinel, and says plainly in its description thatvimandtopdo not work.
Commands run with stdin closed socatcannot swallow the sentinel, and stderr
is merged at the shell so the streams interleave in true order.Interrupting an overrunning command signals the shell's children, not its
process group — signalling the group kills a non-interactive bash, which would
lose the session every time. Lifetime is guaranteed rather than remembered: a
run that opens its own registry closes every shell before returning, including
when the loop throws. Idle shells close themselves and there is a cap on how
many can be open. -
The agent can search its own past sessions (#272) —
SessionSearchfinds
text across previous sessions andSessionReadfollows a hit into the
surrounding conversation. Every session was already on disk as JSONL and
nothing could read it back, so "how did we fix this last month" was
unanswerable while the answer sat in a file the agent wrote.Scoped to the current workspace by default, matched on a resolved path boundary
so/a/projectcannot capture/a/project-two. Neither tool takes a scope
argument: widening the search is a privacy decision, and a parameter would let
the model consent to reading another project's history on the user's behalf.
SessionReadenforces the same rule, so knowing an id is not authorisation. No
index — a scan is fast at this volume, and an index is a second copy of the
truth that can disagree with it. -
Tools declare how they should be drawn (#271). Every tool call rendered as
the same grey blob: anEditshowed its result sentence but never the change,
aBashshowed output with no sign of which command produced it. A tool now
declares a render intent beside its schema —Bashisterminal,
Edit/Write/NotebookEditarediff— and the desktop draws real coloured
+/-lines and a shell transcript. The mapping lives in core so the CLI and
the extension read one answer instead of each hardcoding tool names.Presentation is a pure function of the call's arguments, never the result or
the filesystem, so a replayed session renders as it did live. That is why
Writerenders as wholly added: its arguments genuinely do not say what the
file held before. -
A backstop deadline on every tool call (#270). Only
Bashbounded itself;
aGrepagainst a stalled mount could hang the turn forever with nothing on
screen but a blinking cursor. Deliberately generous (10 min default) so each
tool's own limit fires first and produces the more specific error — this only
covers the case where the inner limit never fires. A caller-requestedtimeout
always wins when longer. On a side-effecting tool the message says the effect is
unknown rather than implying nothing happened. -
The loop notices when the model is repeating itself (#269). The most
expensive agent failure is the loop that neither errors nor terminates: same
tool, same arguments, same answer, quietly spending the budget. At 3, 5, and 8
consecutive identical calls an escalating reminder is injected. It has no veto,
which is what makes false positives affordable. Excluded tools are transparent
to the chain rather than resets, so a bookkeeping call interleaved into a loop
cannot launder it, and calls the gate refused still count. -
thread/delete— the protocol could list, fork and archive threads but not
delete one, so the desktop deleted session files through Tauri instead. The
app-server is the single owner of thread storage; a client removing files
behind it can pull the ground out from under an open writer. Served under the
existingthreadManagementcapability, with the local writer kept as the
fallback for a sidecar too old to know the method. -
Change ledger records provenance. Each entry now carries
derivedFrom—
the files the turn read before making that change — answering the question
after "what changed" and "how do I undo it": what was it derived from. That
is what you ask when a generated file is wrong and you need to know which
input to fix. Shown bydeepcode ledger show. Built on the existing ledger
rather than a second store.It is observed, not declared: only
Readcounts (Grep/Globtake a search
root and return many paths — calling the root an input claims a derivation the
turn did not make), a failed read is not an input, and the file being written
is excluded so anEditdoes not look self-derived. Absent rather tha...
v0.3.0
[0.3.0] — 2026-08-08
A workspace-governance layer: what the agent may touch, what it changed, and how
to undo it. Derived from a first-hand study of Floatboat's open Selfware
protocol — see docs/research/floatboat.md for
the research (with evidence grading) and
docs/FLOATBOAT_ADOPTION_PLAN.md for what was
adopted, what was rejected, and where the implementation diverged from the plan.
⚠️ Breaking
- The CLI is published as
@deepcode/cli, notdeepcode-cli. Install with
npm i -g @deepcode/cli. The unscoped name on npm belongs to an unrelated
project, so it was never ours to publish to. The binary is stilldeepcode
and nothing about the tool's behaviour changes. (#249) - Unattended runs no longer inherit a permissive permission mode. A
permissions.defaultModeofbypassPermissionsoracceptEdits— chosen for
interactive convenience — is clamped todefaultfor scheduled jobs, which
run with nobody present to approve anything. Set a job'sprofile.mode
explicitly to opt back in. The clamp names itself and the fix in the job log
on the first run after upgrading. (#244)
🔒 Security
.envcould be read, and there was no way to say otherwise. Permission
rules match on the tool; their only path-aware match is a prefix compare
against an argument that is usually an absolute path, soRead(.env*)matched
nothing. The new file contract adds the missing axis —
glob × read/write/execute × allow/ask/deny — composed with the existing rules
by most-restrictive-wins. It can only tighten. (#238, #239)- A contract
denycannot be waived bybypassPermissions. It states
something standing about a path rather than prompting about one call, so the
mode that exists to skip prompts has no business clearing it. (#239) - Plugin subprocesses are gated by the same path rules; the capability bridge
previously called the dispatcher without a contract. (#239) /combodrafts exclude paths the contract denies reading and redact
credential-shaped values. A rule that stops at the tool call but not at the
export is not much of a rule. (#243)
✨ Added
- File contract —
deepcode contract <show|init|check>. Optional; with no
contract file, behaviour is unchanged. (#238, #239) - Change ledger —
deepcode ledger <list|show|export|rollback>. An
append-only record pairing each mutation with the request that motivated it
and the checkpoint that reverses it, on two timelines (changes,
governance). Stored outside the repository sogit statusstays clean.
(#240, #241) - No Silent Apply — explain, preview, accept/reject/defer, rollback point
first.confirmis a required argument, so a caller that cannot ask a human
cannot apply. (#241) runtime/capabilities— a protocol method answering what the runtime may
write and which actions always stop for a human, distinct frominitialize's
protocol-feature flags. The CLI and app-server build it through one function,
with a test asserting they agree field-for-field. (#242)/combo— distil a finished thread into aSKILL.mddraft, with
allowed-toolsderived from the tools actually called. (#243)- Trigger profiles — per-job
mode,permissions, andsandboxfor
scheduled work. Permissions and sandbox can only tighten. (#244) onApprovalRequired: 'abort'for scheduled jobs, plus exit code6. A job
whose first write is refused otherwise grinds on and reports a confidently
wrong result. (#237)deepcode doctorprints the runtime capability declaration and the
file-contract warnings. (#242)
🐛 Fixed
- Aborting mid-batch left
tool_useblocks unanswered, which a provider rejects
on resume. Remaining calls now get an explicit "never ran" result. (#237) docs/cli-flags.md's exit-code table contradicted the implementation (it
listed3as "tool denied" and5as "API key invalid"). Corrected against
apps/cli/src/headless.ts, which owns the contract. (#237)
📄 Documentation
- New:
docs/file-contract.md,
docs/change-ledger.md,
docs/combo.md,
docs/research/floatboat.md,
docs/FLOATBOAT_ADOPTION_PLAN.md. docs/security-model.mdgains threats #8–#10 and a residual-risk section
stating plainly that the file contract is policy, not a boundary: it does not
constrain Bash, and only the sandbox does.
🚫 Deliberately not adopted
.self self-executing distribution (a supply-chain surface for a coding agent),
Floatboat's passive habit observation across files and browser tabs (a privacy
line, and unnecessary — /combo gets the value from an explicit invocation),
cross-organisation agent networks, and a second loopback HTTP runtime alongside
the app-server. Reasoning in the adoption plan §3.
No macOS build in this release. Apple signing credentials were not configured when it was cut; see
docs/RELEASING.md.
Not published to npm.
NPM_TOKENwas not configured when this release was cut; install the VSIX below or build from source.
Full commit list: v0.3.0 compare view