All notable changes to Codeep CLI are documented here. Format: Keep a Changelog · Versioning: Semantic Versioning.
For releases before v1.3.35, see GitHub Releases.
Authoring convention: put a one-line
> TL;DRunder each## [version]heading. It's auto-extracted bycodeep.dev/releases/rss.xmlas the social-share summary (IFTTT → X/Bluesky), capped at 220 chars. If omitted, the feed falls back to the first paragraph.
TL;DR — a long answer from a phone-started run now arrives whole, across up to three messages, instead of being cut at 3000 characters.
-
A long answer to a phone-started run arrives whole. Telegram refuses a message over 4096 characters outright, so 3.1.0 cut the answer at 3000 and said it had. Honest, but lossy in the place it hurts: an answer that lists files or walks through a change passes 3000 easily, and the conclusion is at the end. It is split now — on a paragraph break where there is one nearby, else a line break, else a space, so a part never ends mid-word. Three messages is the cap; a long answer is worth several, an enormous one is not worth a phone buzzing eleven times. Only what runs past that is cut, and it still says so. The summary head shares the first message, so a continuation is never a wall of text with no idea which run it belongs to.
Matches the Mac app, which shipped the same rules in its 3.1.0 today. Two implementations of "what does the phone get" that can disagree is worse than either.
Send Codeep an instruction from Telegram and it runs, with the answer coming back to the same chat — plus Claude Fable 5.1, a Sonnet 5 price that was 50% too high, and cache reads on Kimi and Qwen that were billed wrong in opposite directions.
-
Start a task from your phone. V3 let Telegram answer a question the agent chose to ask. This lets it ask one: send the bot an instruction in plain words and it runs, shown in the terminal like any other, with the agent's answer coming back to the same chat.
Its own switch, off by default —
/settings→ Start tasks from Telegram. A chat that can only say Run, Skip or Cancel is bounded by what the agent already decided to do; a chat that can send a prompt is a keyboard attached to your machine. Both are gated on the same chat ID, but they are not the same permission, so turning one on does not turn on the other.An instruction sent while a run is in flight waits, and the bot says so. One waits at a time and a newer one replaces it: two corrections typed during one long run mean the second, not both in order.
Four refusals, each with something to say back rather than silence. Non-text, slash commands, anything over 2000 characters, and anything older than five minutes — Telegram holds undelivered messages for 24 hours, so without that last rule an instruction typed to a machine that was switched off would run the moment the CLI next started, hours later, with nobody watching. A message from any other chat is ignored with no reply at all: answering confirms the bot is live and attached to something worth probing.
A task started from the phone is not exempt from confirmation. It stops at a dangerous tool exactly as it would have for someone at the keyboard, and that question arrives on the same phone.
-
Telegram says when a run is over. Past a minute of run time, so a phone is not buzzed about work you watched finish. It carries the task, the duration and the token or cost line. The agent's answer travels only for a run the phone started — at the terminal it is already on the screen you are sitting at, and sending it would put file contents or a command line into a chat that syncs to Telegram's servers for nothing.
-
Claude Fable 5.1 (
claude-fable-5-1). Prices like Fable 5, but reads a cached token at 0.025× the input rate where every other Anthropic model charges 0.1×, so the cache rate is resolved per model before per provider now. Fable 5 stays listed: dropping an id does not leave a pinned config on the older model, it drops the lookup and lands you on another provider's default.
-
Claude Sonnet 5 was priced 50% high. The $2/$10 launch rate was billed as introductory through 2026-08-31 and the scheduled rise to $3/$15 was cancelled. Nothing failed when the table still said $3 — the number was simply wrong, with no test to notice.
-
Cache reads on Kimi and Qwen were billed wrong, in opposite directions. Two errors, and the first hid the second. Cache hits were read only from
prompt_tokens_details.cached_tokens; Kimi reports them at the top level ofusage, so every Kimi cache hit came back as zero and the whole prompt billed at the cache-miss rate. And where a cache read was seen it billed at 0.1× — Anthropic's multiplier, hardcoded for everyone — while Kimi lists $0.19 against $0.95 and Alibaba prices Qwen's implicit cache at 20% of input. Only the metered keys were affected; the subscription tiers quote no dollar figure at all. -
npm run devhad been dead for two weeks. Two interfaces were re-exported with value syntax.tscerases the types and compiles happily, so the build,dist/and the published CLI were all fine and CI had nothing to report — buttsxtranspiles file by file, leaves the re-export in, and the loader then fails to find an export that never existed at runtime.isolatedModulesis on now, which named all eleven sites of that shape in one pass. -
A run reported itself by session id, not by its task. The dashboard, the stats report and the Telegram notice all read a name that was copied by value before it was set, so
session-2026-09-02-ddc1f13cwent out in place of the task. And the notice used the session's name, which is the first task's and stays put — every task after the first announced itself under the first one's name. -
The agent refused commands it is allowed to run.
execute_commandwas described as "Use for npm, git, build tools, tests, etc.", and a few examples read as an exhaustive list: it declinedsleep, which is on the allowlist besideecho,pwd,date,ls,catandcurl, explaining that its tool was limited to package managers and version control. It believed the description over its own capability. -
The "N new · PgDn" badge offered a key that does nothing mid-run. While the agent runs, the timeline owns the whole screen and returns before the transcript is drawn, so scrolling moves an offset nothing reads. The badge also replaced the status bar's runtime and token counts to say it.
-
A failing Telegram poll looked like an idle bot. Errors were caught and turned into null with nothing said, so a webhook left configured (409) and a revoked token (401) were indistinguishable from a phone nobody had messaged. It now says so once when polling starts failing and once when it recovers.
- One Telegram poll, one cursor.
getUpdatesconfirms every update older than itsoffsetregardless ofallowed_updates— that parameter only filters the response. A second poller would have acknowledged approval taps it never delivered, so approvals would have begun failing intermittently with nothing to say why. The loop is shared now, asks for both kinds, and dispatches locally where losing one is impossible.
Approval on your phone, on every platform — and the version numbers across the CLI, the Mac app and the VS Code extension now line up.
Codeep V3 is one release across three surfaces, so the three carry one number.
The CLI moves 2.25.0 → 3.0.0, the VS Code extension 2.9.0 → 3.0.0, and the Mac
app 1.14.0 → 3.0.0. Nothing here is a breaking change — the major is the
alignment, said out loud rather than left to look like an accident. The GitHub
Action keeps its own v1.x line: it is a separate product with its own
consumers and its own supply-chain pinning.
-
Answer a confirmation from your phone, over Telegram. When a run stops on a dangerous tool it waits — and if you have walked away, it waits for as long as you are gone. Connect a bot with
/telegramand the same question arrives on your phone with the same three answers. Whichever you use first decides; answering in the terminal takes the message down rather than leaving buttons that no longer do anything, and answering on the phone closes the terminal dialog without running either of its callbacks.This is where Telegram earns its keep. The Mac app can carry approvals through the user's own iCloud; Linux and Windows have nothing of the sort. The bot API is polled outbound, so there is no server to host and no inbound port to open, and the same code works everywhere the CLI does.
Only the chat ID you configure can answer. A bot's username is discoverable, and without that check anyone who found yours could approve a destructive command on your machine. A late tap cannot reopen a settled question either — each message carries a token matched against the question actually in flight.
Interactive runs only. A headless run —
codeep review --fix, ACP, CI — has nobody to ask, and blocking one on an answer that cannot come would hang a pipeline. Those paths are untouched and still decide fromagentConfirmation.The token goes in your OS keychain; the chat ID is ordinary config. Telegram necessarily sees the command, because you cannot judge what you are approving otherwise — which is why this is off by default.
-
The confirmation showed the binary, not the command. It read
parameters.command, which isgitwhere the truth isgit status— asking someone to approve a command they had not been shown, which is the one thing this gate must not do. The same truncated string went to the phone. The audit record already joined the binary with its arguments; that is now what both use. -
A Telegram send that failed said nothing. A wrong chat ID was indistinguishable from a phone nobody picked up. Telegram's own words are now surfaced, and the failure people actually hit while setting this up is translated into what to do about it.
-
The Telegram toggle wrote a value its own getter could not read. Its options were the strings
'true'and'false'while the getter returned a boolean, so the setting reported "On" and read false immediately afterwards, forever. Every select in the settings list is now checked against the value it reads back.
All three were found by using it, not by testing it.
GLM-5.3-Flash: the same million-token window and tool calling as the flagship, at a twentieth of the price.
- GLM-5.3-Flash on both international Z.AI providers. Same 1M context, same tool calling, and it takes the same graded thinking control — Flash also refuses a disabled thinking block, so it goes through the same machinery as 5.3 rather than the on/off path. Listed at $0.15 in / $0.50 out per 1M; the 50% launch promotion is deliberately not what the cost tracker records, since a promotional rate understates every session the day it ends. China gateways keep 5.2 for the same reason 5.3 is not offered there.
kimi-k2.7-code-highspeednow has a rate. It was exempted because Moonshot published no distinct high-speed price; they now do — double the standard model across every token category. The exemption is removed rather than left as a dead suppression that would hide the next gap.
A rejected API key made an agent run look like it had simply run out of steps: every request was retried, the budget drained, and the report blamed the iteration limit.
-
A 4xx from the provider was retried until the iteration budget ran out.
agentChatthrew a bareErrorfor any failed HTTP response, so the retry loop's "never retry a 4xx" branch — which testserr instanceof ApiError && err.status— never applied. An expired key was retried once per iteration and the run then reportedExceeded maximum of N iterations: the one explanation with nothing to do with the cause. A run that used to burn twenty-five requests over six minutes now stops on the first, saying401. -
review --fixnever loaded the API keys.getApiKeyis synchronous and reads a cache that onlyloadAllApiKeysfills; it does not consult the environment itself. The fix agent started without that call, so every request went out with an empty bearer token. In CI this looked exactly like a model that would not do the work, with a perfectly valid key sitting in the environment the whole time. -
A fix that changed nothing said nothing about why. The summary reported what did not happen and never what did. It now counts the agent's tool calls, separates failures from successes, and quotes the first failure — usually a refusal, and usually the entire explanation.
src/utils/agentChat.tsis a text file again. It contained a literal NUL byte — a deliberate separator written as a raw byte rather than\u0000— which madegrep,fileand diff viewers treat the whole file as binary and silently skip it. Same behaviour, same separator, now searchable.
Security rules had never once looked at a
.mjsfile,forEach+awaitonly counted when it wasn't an arrow function, and the CI fix agent ran out of steps before it could finish.
-
.mjsand.cjswere invisible to thirteen rules. Every rule that names.jsin itsextensionslist — which is all four security rules, plus the performance and best-practice ones — skipped these files entirely.eval(),innerHTMLand hardcoded credentials went unreported in any ES-module or CommonJS-suffixed file. Found while reviewingcodeep-action, whose own scripts are.mjs: its self-review had never applied a single security rule to itself.The extension is now normalised once, rather than widening thirteen arrays that would go stale on the next rule added. The directory walk picks these files up too, so a whole-project review no longer steps over them.
-
foreach-awaitnever matched an arrow function. The pattern required theawaitto follow forEach's closing paren, which only lines up forfunction (x) {. Every modern form went unreported, and the rule also only fired whenawaitwas the very first token in the body — so{ out.push(await f(id)) }was invisible. Both bounds stay explicit and both classes negated, so matching is linear. -
A CI fix run gave up after 12 steps.
codeep review --fixcapped the agent at 12 iterations, under half the product default. Fixing oneinnerHTMLcall and running the test suite exhausted it, and an agent stopped mid-edit leaves a worse diff than one that never started. Now 25, the same as everywhere else; the plan size and the caller's wall-clock are the real bounds.
Every run now records what it touched, including what the boundary refused — and four bugs that only a real session could surface.
-
/audit— what an agent actually did. Each run appends to.codeep/audit/in the project: one JSON line per event, one file per day. It records reads and refusals, whichhistory.tsdoes not, because that journal exists to undo writes rather than to say what happened. The most useful line is the one that never existed — a tool call the boundary refused. A capability limit nothing records is one you have to take on faith.File contents are deliberately absent. Undo already keeps them; a record you might hand to someone else should not carry your source with it. Command lines are kept, so treat the directory like shell history.
/audit offstops recording without deleting anything already written. -
codeep review --fix. After reporting, hand the findings to an agent and let it edit the working tree. It runs under a files-and-tests boundary — no shell, no network, no git — enforced by the same gate as any custom bot, not suggested in a prompt. Suggestions are never eligible: acting on opinion produces churn and buries the findings that matter. It never commits, branches or pushes, and it never changes the exit code, because a fix that turned a red check green would hide the finding rather than resolve it.
- Stop now stops during a retry. Three waits in the agent loop used a plain timer that ignored the abort signal, so pressing Esc or Ctrl-C during "retrying in 10s" did nothing until the wait expired — and then the loop retried the request anyway. Found by trying to cancel one.
- A run that failed is recorded as failed. Several failure paths — a user abort, a 4xx from the provider — return without throwing, and the audit record read only the exception. Three runs that died at the provider were all logged as successful. A record that says a failed run passed is worse than no record.
- A key with an invisible character now says so. A non-Latin-1 character in
an API key produced "Cannot convert argument to a ByteString because the
character at index 10…", counted across
Bearer <key>so it pointed seven characters left of the real one, never mentioned the key, and retried twice more. It now names the position in the key and fails immediately.
noUnusedLocalsearns its keep. Turning it on in 2.21.0 caught a helper written for this release and never wired — the same shape asreportTurnStatsshipping dead in 2.18.1, found this time within the hour.
Agents you delete on the dashboard now disappear from your machines, sync says when it failed instead of going quiet, and a dependency advisory is closed.
- Deleting an agent on codeep.dev now reaches your machines. It never did:
the pull loop only iterated what the server returned, so a bot removed on the
dashboard stayed on every client forever. The server now sends an explicit
tombstone list and the CLI applies it — backing each file up to
~/.codeep/backups/personalities/before removing it. Absence from the payload still means nothing at all: an expired session, the wrong account or a truncated response all produce an empty payload, and deleting on absence would wipe every local agent. Project-scoped agents in.codeep/personalities/are not cloud-owned and are never touched.
- Sync failures no longer look like success. Every failure path returned
nulland the caller printed only when something changed, so a dead network, an expired token and "nothing new" were all the same silence. Pulls and pushes now report which of the four happened — not linked, unreachable, rejected, or a response this version can't read — and say "already up to date" when that is the truth.
- A high-severity advisory in
js-yaml. Patch-level bump, one call site. The CLI's production dependencies now report zero advisories.
- A dead full-screen permission picker, superseded by the in-app overlay and
unreachable since. Its component and tests went with it — 142 lines that
looked alive and had passing tests.
noUnusedLocalsis now on: it is the check that would have caughtreportTurnStatsshipping dead in 2.18.1, and it caught an unwired function inside an hour of being enabled.
GLM-5.3 reached Z.AI's pay-per-use API and now carries the rate they publish, instead of no price at all.
- GLM-5.3 on the Z.AI pay-per-use API. It shipped to GLM Coding Plan
subscribers first; the standalone model API accepts it as of 2026-08-19, so
it now appears on that roster too and is its default. The China gateway
(
z.ai-cn,z.ai-cn-api) is a separate listing that Z.AI's page does not cover, so it keeps the GLM-5.2 roster — offering an id a gateway rejects would be a guaranteed 4xx.
- GLM-5.3 is priced at $1.40 / $4.40 per 1M tokens. It was carried unpriced
by design: no per-token rate existed while the model was Coding-Plan only, and
borrowing GLM-5.2's would have been an invented number. Z.AI now publishes one,
and it happens to equal GLM-5.2's — a fact about today's price list, not a rule
linking the two.
/costand the dashboard stop showing pay-per-use GLM-5.3 traffic as free. Coding Plan usage is flat-fee and still reads "Included in plan".
Custom bots you can actually trust: pin a model, grant only the capabilities you choose, and scope a bot to the projects it belongs to — enforced at runtime across CLI, Mac, VS Code and the dashboard.
- Portable, enforceable custom bots.
custom-bot/v1personalities can pin an exactprovider/model, expose only selected Files/Terminal/Tests/Git/Web/ MCP capabilities, and limit availability to all projects, selected project names, or personal mode. Legacy prompt-only personalities remain compatible and unrestricted. CLI, ACP, VS Code, macOS, and Dashboard Agent Studio share the same Markdown contract. Versioned files fail closed when tool metadata is missing or malformed, and invalid model/scope metadata cannot activate. - Native ACP custom-bot controls. Clients can list, activate, and sync bots
through
session/list_personalities,session/set_personality, andsession/sync_personalitieswithout scraping chat output.
-
Capabilities now say what they actually grant. The builder promised that unselected tools are "removed from this agent's runtime", which reads as a guarantee that a Git-only bot cannot see file contents. It can:
git show HEAD:fileis functionallycat file, and history inspection (log -p,diff,blame) cannot be separated from the content it inspects. Nor is Git read-only:git rm,git commitandgit pushare on the allowlist, so a Git-only bot can rewrite the repo and publish the result. Every capability now carries a description, and choosing Git without Files spells out both halves — in the builder, in/personality, in the Mac persona sheet and in the VS Code picker. The enforcement is unchanged; the promise is now true. -
Dashboard personality edits now reach local runtimes safely. A manual cloud pull atomically applies changed personality bodies and backs up every divergent local copy under
~/.codeep/backups/personalities/. Custom command pulls remain additive. -
App.tsrefactor begins: HunkPicker extracted. The interactive/apply --interactivepicker (state + key handling + rendering) moved from the 3.3k-lineApp.tsmonolith intocomponents/HunkPicker.ts, following the same{ State, handleKey, render }convention as Settings/Export/ Search. App.ts now owns a single state field and wires it in. The picker logic is now unit-tested in isolation (12 tests pinning the y/n/a/q/↑/↓ semantics and the fires-exactly-onceonCompletecontract) — previously untestable inline. First of several planned extractions (mention picker, paste dialog, autocomplete) to bring App.ts down to a manageable size. -
PasteDialog extracted from App.ts. The large-paste confirmation ("Paste Detected" with Add/Send/Cancel) moved to
components/PasteDialog.tsin the same shape. The key handler returns{ state, action }— a discriminated action union (add-to-input/send-directly/cancel/none) — so App keeps the side effects (editor insert, message submit, notification) while the decision logic is pure and unit-tested (7 tests). -
MentionPicker extracted from App.ts. The mid-sentence
@fileautocomplete (5 state fields) moved tocomponents/MentionPicker.ts. The load-bearing@-sigil buffer math (re-adding the@after slicing, without which a completed path silently stops being a mention and the file never gets attached) now lives in a pureapplyMentionToBuffer()covered by tests, including cursor positioning and mid-buffer replacement (9 tests). -
CommandAutocomplete extracted from App.ts. The
/commandpicker (3 state fields) moved tocomponents/CommandAutocomplete.ts— same shape as MentionPicker (pure key handler +commandToBuffer()buffer math, 9 tests). With this, all four picker-style widgets live outside App.ts as testable components. -
Shared command core established (
commands/core/)./telemetryand/keysyncare the first commands whose semantics (env-var hard-off checks, config toggling, status facts) live in one place used by BOTH the TUI and ACP dispatch — previously two hand-maintained copies that could (and did) drift in wording and behavior. Surfaces now only render theCommandResult. The env-var invariants (CODEEP_NO_TELEMETRY, CODEEP_NO_KEY_SYNC overriding any config flag) and the server-readable-keys disclosure are pinned by 11 unit tests. Remaining ~38 shared commands migrate incrementally, same pattern. -
Default rate limits lowered from effectively-unlimited. New configs get
rateLimitApi: 240/min andrateLimitCommands: 120/min — generous for a full 50-iteration agent run, but a runaway loop now stops instead of burning quota. Existing configs are untouched; tune via/settings.
-
Stray characters no longer survive in the TUI. The screen paints differentially — a cell whose value already matches the shadow copy is skipped — which meant a BLANK cell was never emitted at all. Column 0 of the header is blank (the wordmark starts at x = 1), so whatever the terminal happened to show there before Codeep started stayed for the whole session; the session picker and confirm prompt kept it on screen, and only resizing the window cleared it. Both overlays now invalidate the shadow on the way in and out, and the invalidation fills it with a sentinel no real cell can hold, so blanks repaint too.
-
An aborted or failed turn reports its tokens again. 2.18.1 shipped a
reportTurnStatshelper that was defined but never called: the success path kept an inline duplicate, so cloud stats kept working there, while the catch path reported nothing at all. Tokens burned by a turn you stopped with Esc, or that errored, reached no one — andgracefulShutdownno longer sends the cumulative catch-all that used to sweep them up. Both paths now go through the one helper, and the inline copy is gone.tscstayed silent about the dead function becausenoUnusedLocalsis off. -
SSRF guard now covers
curl/wget/http/httpsinexecute_command. Thefetch_urltool already blocked private/loopback/metadata IPs (169.254.169.254), but the same model-controlled URL could simply be passed tocurlinstead and sail through. URL arguments (including scheme-less host forms likecurl 169.254.169.254/latestand hostnames that resolve privately) now go through the identicalassertFetchUrlAllowedcheck. The guard moved to a sharedutils/ssrfGuard.tsmodule sofetch_urland the shell path can't drift apart. -
envremoved from the agent command whitelist. A singleenvcall dumpedprocess.envinto the model's context — including every provider API key riding in environment variables. Runenvyourself outside the agent if you need environment info. -
Exec-escape flags blocked on whitelisted utilities.
find -exec,-execdir,-ok,-okdirandtar --to-commandspawn arbitrary commands as arguments, silently bypassing the command whitelist (find . -exec rm -rf / \;). Plainfind/tarusage is unaffected. -
Rate limiting is now enforced everywhere. Previously
checkApiRateLimitwas called only on the TUI's manual-chat path andcheckCommandRateLimithad no production call sites at all — an autonomous agent run (up to 50 iterations, each with its own API call and shell commands) was completely unthrottled. The guards now live at the transport layer:chat()inapi/index.tsandagentChat()/agentChatFallback()inutils/agentChat.ts(covering TUI, ACP sessions, sub-agents and session titles), plusexecute_commandinutils/toolExecution.ts. Local no-key providers (Ollama) bypass the API limiter — there's no quota to protect on localhost. The duplicate check in the TUI submit path was removed so a request isn't counted twice. Source-level regression tests inrateLimitWiring.test.tskeep the guards from being silently dropped.
- Nothing removed — the earlier note in this section was wrong: on
case-insensitive filesystems
readme.mdandREADME.mdare the same file, so "the duplicate" never existed and deleting it would have deleted the README itself. (Caught before release; restored from git.)
The last place
/coststill priced plan usage: the prompt-caching savings line quoted dollars for subscription providers that bill a flat fee.
npm run export:cataloguewrites the shipped model catalogue toCodeep-web/src/data/catalogue.jsonstraight fromPROVIDERSand the context/pricing tables, so codeep.dev renders what the client actually offers instead of a hand-kept list that had drifted to 4 models against a catalogue of 72.
- Prompt-caching "savings" no longer invents money on a plan.
/costpriced cached tokens at the provider's pay-per-use input rate even on Z.AI's Coding Plan, MiniMax, Kimi and Qwen — where caching saves latency, not money, because nothing is billed per token. The cache read/write counts stay (they are measured); the dollar figure now covers pay-per-use models only and says so when a session mixes both, and a plan-only session reads "caching saves latency, not money" instead. Thebilled at 0.1× / 1.25× input ratenotes are likewise dropped where no per-token billing applies.
Subscription plans stop inventing dollar amounts — GLM Coding Plan, MiniMax, Kimi and Qwen now read "included in plan". Adds Grok 4.6, Gemini 3.7 Flash and GLM-5.3, and halves a Gemini rate stored at double.
- Grok 4.6 (
grok-4.6) — xAI's flagship reasoning model, 500K context, recommended for code. The xAI default deliberately stays ongrok-build-0.1: 4.6 bills 2× input and 3× output, so it is opt-in via/modelrather than a silent upgrade for everyone. - Gemini 3.7 Flash (
gemini-3.7-flash) — 1M context, Google's current workhorse for coding and agents. - GLM-5.3 (
glm-5.3) — 1M context, on the GLM Coding Plan. Z.AI publishes no per-token rate and its standalone API is still "coming soon", so the model carries no price at all rather than borrowing GLM-5.2's. - Gemini gains its medium thinking tier.
/thinkingnow offers low · medium · high on Gemini instead of collapsing medium into high. Medium is Gemini 3.7 Flash's own default and the level Google recommends for agentic coding; it used to be dropped because Gemini 3 Preview rejected it, which has since been fixed.
- Subscription providers no longer quote a price. Z.AI's GLM Coding Plan,
MiniMax, Kimi's Coding Plan, Qwen's plans and the free ModelScope tier bill a
flat fee or nothing, yet their tokens were priced at pay-per-use rates.
/cost,/statsand the status bar now read "included in plan"; a session mixing both shows the metered figure plus a note. Token counts, context and the energy/water estimates are unchanged — those are measured.
- Gemini estimates were double.
gemini-3.6-flashwas stored at the post-promotional 1.50/7.50 rather than the 0.75/3.75 actually charged through 2026-12-31, so every Gemini cost read 2× high. Both tiers step back up on 2027-01-01; the table names the date. - Gemini 3.7 Flash was sent
temperature. That generation removed the sampling parameters, and the rule that omits them was only consulted on the Anthropic request path — Google is served over the OpenAI-compatible one, so the guard never ran. The same fix closes a related gap where namespaced OpenRouter ids (google/gemini-3.7-flash,anthropic/claude-opus-4.8) slipped past it entirely.
A redesigned agent view: a live PLAN → READ → EDIT → VERIFY timeline with changed files and checks in a side rail, a persistent header, and energy/water estimates beside cost. Plus a self-migrating model catalogue.
- Agent timeline TUI. While the agent runs, the screen is now a staged
timeline —
PLAN → READ → EDIT → VERIFY → SUMMARY— with the active stage highlighted, a per-stage one-line summary, the current tool target, and a progress bar against the iteration budget. On terminals ≥132 columns a context rail on the right lists the files touched (A/M/D), the verification commands and their pass/fail state, and the project + branch. Terminals below that width get the same timeline without the rail. - Persistent header. Version, session, model, provider, project and git
branch now sit on a fixed top line instead of only being reachable through
/status. Segments drop out progressively as the terminal narrows, so the line never wraps. - Resource-impact estimates.
/cost,/statsand the wide status bar now show an estimated energy and water range for the session's token count (src/utils/resourceImpact.ts). These are explicitly labelled ranges derived from published inference benchmarks — 0.3–1.5 J/token and 0.27–1.08 L/kWh — never presented as provider measurements. - Live ModelScope catalogue in
/model. Selecting the ModelScope provider fetches its free-tier catalogue for your token instead of showing a single hardcoded id, falling back to the built-in entry if the fetch fails. - Graded reasoning effort on Kimi K3.
/thinkingnow offerslow · high · maxon K3; the globalmediumtier collapses tohigh, and the status chip shows the level the model will actually run.
- Model catalogue refresh.
- New Qwen Token Plan provider (
qwen-token-plan) forsk-sp-…subscription keys, defaulting toqwen3.8-max-preview. - Qwen's hosted line moves to 3.5–3.8:
qwen3.8-max-preview,qwen3.7-max,qwen3.7-plus,qwen3.6-plus,qwen3.6-flash,qwen3.5-plus— replacing theqwen3-coder-*aliases. - Kimi K3 consolidates to a single
kimi-k3id on the pay-per-use endpoints (the-code,-code-highspeedand-thinkingsplits are gone); the Kimi Code subscription keepsk3/k3-256k. - Gemini 3.5 / 3.6 Flash (plus 3.5 Flash Lite) replace the 3.1 Flash entries.
- OpenRouter now defaults to
openrouter/auto. - Context-window and pricing tables were refreshed in lockstep, and
providers.test.tsnow enforces that every curated model has both a context entry and a pricing row (or a documented exemption).
- New Qwen Token Plan provider (
- Retired model ids migrate automatically. GPT-5.4 / 5.4-mini / 5.5, the
Grok
grok-code-fast-1andgrok-4-fast-reasoningaliases, the Kimi K3 variants, GLM-5 / 5.1 and theqwen3-coder-*ids are rewritten to their supported replacements on first launch — and now also when you load a saved profile with/profile load. Dynamic OpenRouter, Ollama and custom ids are never rewritten.
- Cloud stats double-counted tokens. Every prompt reported the cumulative session totals to the append-only dashboard endpoint, so a three-prompt session was stored as 1× + 2× + 3× its real usage; shutdown then reported the whole session once more. Each turn now reports only its own delta, and the shutdown re-report is gone. Dashboard token counts and costs are accurate again.
- The status bar dropped the
Esc to stophint on wide terminals, because the resource-impact estimate and the hint shared one slot and the estimate always won. The hint now owns the right edge and the estimate uses whatever gap is left. The reasoning-effort chip is shown in the wide footer too. - The header's
branch:segment rendered nothing at all whenever the branch name was long enough to need truncating — the truncation budget didn't account for the separator and label, so the segment always failed its own fit check. - Long CJK / emoji text in the agent timeline overflowed its pane by up to 2×
and broke the divider: width budgeting measured
String.lengthwhile the screen advances by display columns. --version,--helpandaccountno longer spawn git subprocesses at startup; the header branch is resolved on first use and re-read after an agent run, so it no longer goes stale when the agent switches branches.
@mentionsinline file context — type@src/file.tsanywhere in your message and the file's contents are attached to that message. No more/add+/dropdance for one-off file references.
-
Kimi K3 (Moonshot) joins the pay-per-use rosters —
kimi-k3-code(the new default),kimi-k3-code-highspeed, andkimi-k3-thinking, on the international and China endpoints. K3 carries a 1M context window (vs 256K on K2.x) and the thinking variant returns explicit reasoning traces; both K3 code models are in the sampling-params guard since they fix temperature internally. ThetokenTrackercontext and pricing tables list all three, so the context gauge and cost readout are right — without the entries they fell back to a 128K window, which would have shown the gauge as nearly full at ~1/8 of the real budget and triggered compaction far too early. Pricing follows the Kimi family rate ($0.60/$2.50 per MTok); adjust if Moonshot publishes a different K3 rate. -
Claude Opus 5 replaces Claude Opus 4.8 in the Anthropic model list and becomes the default. Same $5/$25 per-MTok pricing and 1M context, with the full
low→maxeffort ladder. The reasoning-effort gate, the sampling-parameter guard, and thetokenTrackercontext/pricing tables were all updated in lockstep, so/thinkingand the cost readout are correct for it. (Opus 4.8 still routes correctly if a saved session pins it.) -
@mentionsinline file context (CLI + Mac). Type@path/to/fileanywhere in your message and its contents are loaded as an[Attached files]block for that message only — no need to/addand remember to/dropafterwards. Works alongside the existing/add(persistent) and pinned-files (Mac) mechanisms.- Forms:
@src/file.ts(relative to project root),@./local.ts(relative to cwd),@/abs/path(absolute),@~/path(home),@"file with space.ts"/@'...'(quoted). - GitHub handles (
@octocat) and emails (user@host) are left untouched — only paths containing/,.,_, or-are treated as mentions. - Files over 100 KB are skipped with a warning (same cap as
/add); binaries (NUL-byte detection) are rejected. - Failures surface inline (
@missing.ts: file not found) without aborting the message — the agent still sees the rest. - CLI:
src/utils/mentions.ts(+36 tests) — pure parser, integrated intohandleSubmitinmain.ts. - Mac:
CodeepCore/Agent/MentionExpander.swift(+14 tests) — byte-for-byte parity with the CLI, integrated intoChatView.send(agent:).
- Forms:
-
@mentionautocomplete picker (CLI). Typing@mid-sentence now opens a file picker below the input — arrow keys navigate, Tab inserts the selected path. Skipsnode_modules,.git, binaries. Cached per-root (5 s TTL) so keystrokes stay snappy on large repos.detectMentionQueryincomponents/Autocomplete.ts(+11 tests) tracks the cursor inside an in-progress mention.suggestMentionsinutils/mentions.tspowers the picker; the/commandand@mentionpickers never compete (one opens at a time depending on cursor position).
-
@web <url>inline web context (CLI + Mac). Type@web https://example.com/docs(or@web example.com/docs) and the page is fetched, converted to readable text, and attached to the message — same[Web pages]block shape as file mentions.- Forms: full URLs (
@web https://…,@web http://…) and bare hosts (@web docs.example.com/api, auto-https://). - HTML is stripped to text (scripts/styles removed, links kept as
text (url)); plain-text and JSON responses pass through as-is. - Output capped at 32 KB; 12 s fetch timeout; binary/PDF content types rejected with a clear reason.
- CLI:
src/utils/webFetch.ts(+33 tests), integrated intohandleSubmitinmain.ts. - Mac:
CodeepCore/Agent/WebMentionExpander.swift(+14 tests), integrated intoChatView.send(agent:)(nowasync).
- Forms: full URLs (
-
@folder <path>inline directory context (CLI + Mac). Type@folder src/components(or@dir …) and every source file under the directory is loaded recursively into a single[Attached files]block, merged with@filementions. Same ignore rules as the autocomplete scanner (node_modules,.git, binary extensions, dotfiles). 200 KB total cap per mention, 6-level depth limit, deduplicated across overlapping folders. Quoted paths supported:@folder "my dir".- CLI:
expandFolderMentions+expandFileAndFolderMentions(merged single-block output) inutils/mentions.ts(+21 tests);main.tsnow calls the combined expander. - Mac:
CodeepCore/Agent/FolderMentionExpander.swift(+13 tests), wired intoChatView.sendahead of@file/@web.
- CLI:
-
Selective per-hunk apply (CLI + Mac). The diff pipeline now supports applying a subset of a file diff's hunks, leaving rejected hunks' original lines intact (
git add -p-style granularity).- CLI:
applyHunks/applyHunksToFiles/countChangeHunksinutils/diffPreview.ts(+17 tests)./applynow shows per-file hunk counts and accepts--only file.ts:0,1 other.ts:2to apply only the chosen hunks. Without--onlythe original all-or-nothing behavior is unchanged. - Mac:
CodeepCore/Agent/HunkApplier.swift(+11 tests) — same algorithm, Swift-nativeFileDiff/DiffHunk/DiffLinetypes. (UI integration — SwiftUI hunk-picker modal — to follow.)
- CLI:
-
@git <ref>inline context (CLI + Mac). New@gitmention injects git diffs, commit patches, and file-at-ref content into the prompt as a[Git ref]block — same UX family as@folder/@file/@web. Supported forms:@git diff,@git diff --staged(orstaged),@git HEAD/@,@git <sha>,@git main:src/x.ts(file at ref),@git diff a..b(range), or any quoted ref (@git "diff HEAD~3"). Capped at 64 KB/mention (truncated with a marker); unknown refs surface a friendly failure note.- CLI:
getGitContentinutils/git.ts(+12 tests),expandGitMentions/extractGitMentionsinutils/mentions.ts(+11 tests), wired intomain.tsbetween@file/@folderand@web. - Mac:
CodeepCore/Agent/GitMentionExpander.swift(+10 tests), wired intoChatView.sendin the same order.
- CLI:
-
Interactive hunk picker (
/apply --interactive). Agit add -p-style review modal walks you through each change hunk one at a time. Keys:y/Enter accept,nskip,aaccept all remaining,q/Esc quit,↑/↓ navigate. The chosen hunks are applied via the existing per-hunk apply pipeline. Reuses the modal/state plumbing of the inlineshowConfirmdialog. CLI only (Mac UI modal to follow). -
Session web cache (
@web). Successful@webfetches are now cached for 30 minutes (per session, max 50 entries, LRU eviction) so a second@webfor the same URL is instant and free. Failures are never cached. New/web-cachecommand (alias/webcache) shows stats and/web-cache clearresets. URL keys are normalized (lowercased host, trailing slash stripped, fragment dropped) soexample.com/pathandhttps://example.com/path/share an entry. -
ACP mention parity (VS Code / Zed). The ACP path (
src/acp/session.ts) now expands@file/@folder/@git/@webmentions the same way the TUI does — previously ACP sent the raw prompt with no inline-context resolution. Failures surface as thoughts in the editor. (+2 smoke tests insession.test.ts.) -
Command alias resolution.
handleCommandnow resolves aliases via the registry'sresolveCommandbefore dispatching, so e.g./webcachereaches theweb-cachehandler without a duplicate case label. -
Mac: SwiftUI hunk picker sheet.
HunkPickerSheet(CodeepCore) is the Mac counterpart to the CLI's/apply --interactive: walks the user through each change hunk, toggling accept/skip, with keyboard shortcuts (y accept, a accept all, arrows navigate). Built on the existingHunkApplier.FileDiff. (+6 unit tests for the pureitems(from:)builder + header formatting.) -
Mac: session web cache (
@web).WebMentionExpandernow caches successful fetches for 30 min (per session, max 50 entries, LRU) — full parity with the CLI'swebCache. Failures are not cached. URL keys are normalized (lowercased host, trailing slash stripped, fragment dropped). New public API:clearCache(),cacheStats(). (+5 Swift tests.) -
Mac: clipboard image paste. The composer now accepts Cmd+V with an image on the pasteboard (alongside the existing file picker attach). Images are downscaled + JPEG-compressed through the same
ImageResizepipeline, gated bysupportsVision. -
Mac: hunk picker wired into ConfirmationSheet.
edit_file/write_fileconfirmation prompts now offer a "Review hunks" button that opensHunkPickerSheetover the call's diff. NewHunkApplier.diffFromEditCall/diffFromWriteCallbuilders derive aFileDifffrom tool-call arguments. (+5 Swift tests.) -
Mac:
/web-cacheslash command. Parity with the CLI —/web-cacheshows session cache stats,/web-cache clearwipes it. Aliases:/webcache,reset,flush. (+6 Swift tests.) -
Mac: drag & drop image onto composer. Dropping an image file onto the input bar attaches it (same resize/JPEG pipeline as paste and the file picker), with a brand-tinted drop affordance ring.
-
Mac:
/usageslash command. Pops the toolbar's token + cost breakdown popover from the composer — parity with the CLI's/usage. Aliases:/stats,/cost. (+2 Swift tests.) -
Mac: dock icon unread badge. When the app is in the background and an agent finishes a turn, the conversation's unread counter bumps and the dock icon shows the total. Clicking the app to bring it frontmost, or selecting the conversation, clears its badge.
Conversation.unreadCountis session-only (never persisted — no phantom badges across launches). (+3 Swift tests.) -
Mac: slash-command autocomplete popover. Typing
/in the composer opens a filtered list of commands; ↑/↓ navigate, Tab/Return accept, Escape closes. NewSlashCommandHandler.catalog+ puresuggestions(for:)filter (boundary-aware — ignores/inside URLs, suppresses after a space when the user is in the argument). (+10 Swift tests.) -
Mac: Conversation menu bar group. Mirrors the slash commands in the menu bar (Scan, Plan Mode, Show Usage, Show/Clear Web Cache, New Chat) with shortcuts. Wired through a new
.codeepRunSlashCommandnotification so menu items reuse the composer's dispatch path. -
Mac: App Intents for Plan / Usage / Web Cache. Spotlight + Shortcuts + Siri phrases for "Codeep Plan Mode", "Show Usage", "Clear Web Cache". Reuses a new
.slashCommand(raw:)case onPendingIntentActionso intents route through the same slash dispatch as typing the command. (+3 CodeepCore tests.) -
Mac: Settings refactored into tabs. The 1450-line monolithic Form is now four focused tabs — General (appearance, accent, response language, system prompt, About Me), Model & Agent (confirmation, self-verify, model parameters, limits, hooks), Providers (API keys, endpoints, Ollama, OpenRouter), and Tools & Privacy (privacy/cloud, tool permissions, MCP, skills). Active tab persists across sheet re-opens.
-
Mac: Keyboard Shortcuts cheat sheet.
Help → Keyboard Shortcuts…(⌘?) opens a catalog of every shortcut in the app, grouped by surface (App, Conversations, Composer, Chat & tools, Slash commands). StaticShortcutCategorycatalog is the single source of truth. (+5 Swift tests.) -
Mac: custom accent colors. Settings → General → Accent lets you pick from 9 preset colors (Codeep Red default + Orange, Amber, Green, Teal, Blue, Indigo, Purple, Pink). Applies live to
.tint()across the whole app via a reactive@AppStorage. NewAccentChoiceenum. (+7 Swift tests.) -
Mac: Quick Agent pinned prompts. The ⌥Space panel now shows a horizontally-scrolling strip of pinned one-click starters (seeded with sensible defaults on first launch). Right-click to remove; a "Pin" button appears when there's draft text to save. New
QuickPromptStore(UserDefaults-backed,@Observable) + pin sheet. (+14 Swift tests.)
@git <ref>no longer executes shell commands. The ref was interpolated into a string run through/bin/sh, so prompt text containing@git HEAD; <command>ran that command — reachable from anything pasted into a prompt (an issue body, a log, model output routed over ACP). git is now spawned via argv with no shell, and refs are validated;@git diffflags are allow-listed so a ref can't be smuggled in as--output=…either.- Mentions no longer auto-inline secret files.
@.env,@~/.aws/credentials,*.pem,id_rsaand friends are refused with a pointer to/add, so a pasted mention can't ship credentials to the provider. @webwon't follow a redirect into your private network. A public URL that redirects to loopback/RFC1918/link-local (including the cloud-metadata endpoint) is refused. Directly typing@web http://localhost:3000still works — that's the documented dev-server case. The fetch timeout now covers the response body (a slow-drip reply could hang the chat forever), the body is read with a hard byte cap instead of being buffered whole, and the timer can no longer leak on failure.
-
The
@-mention picker no longer draws over the chat. It was missing from the layout snapshot entirely, so the bottom panel measured zero rows while the picker still painted up to eleven — straight over the end of the transcript. -
The suggestion scan is bounded. It ran on the first
@keystroke with no file cap and astatSyncper entry, so opening the picker in a large monorepo blocked the render loop for seconds. Capped at 20k entries and switched toreaddirSync(withFileTypes), which drops a syscall per file. -
@~nameresolves correctly. Only~/is treated as a home reference; a bare@~foo/bar.tsused to become$HOME/oo/bar.tsand report a confusing "file not found". -
/applyno longer corrupts files whose changes are close together. Unified-diff context overlaps between adjacent hunks; the applier replayed each hunk in sequence, so shared context lines were written twice — and with a small gap a later hunk's deletion landed inside an earlier hunk's context and was silently dropped. Rebuilt as a per-line union, with a regression test sweeping the hunk gap. -
/apply --onlywith a malformed spec applied everything. The selective branch fell through to the apply-all path, doing the opposite of what was asked. It now reports the bad spec and applies nothing. -
Tab-completing an
@-mention dropped the@, so the completed path was no longer a mention and the file was never attached — the picker broke the feature it exists to serve. The picker's boundary rule is now shared with the expander so the two can't drift apart again. -
Every
@-mention attached its file twice, doubling the token cost of each mention: the merged file+folder path stripped its own attachment block back out with a lazy regex that removed only the header. -
/applyon a very large file no longer crashes the CLI — a spread-basedMath.minover the hunk's line numbers blew the argument limit and surfaced as an unhandled rejection. The apply paths also report failures as a toast instead of dying.
Cross-tool rules + MCP config parity: Codeep now reads
AGENTS.md(Claude Code / Cursor / Kilo Code standard) as a third project-rules source, and.mcp.jsonat the workspace root as a fourth MCP config source — so users coming from those tools don't have to duplicate config.
-
New provider models. Refreshed the catalog for the current release: OpenAI's GPT-5.6 family —
gpt-5.6-sol(new default, $5/$30 per MTok, 1.05M context),gpt-5.6-terra($2.50/$15), andgpt-5.6-luna($1/$6); xAIgrok-4.5(flagship reasoning, $2/$6, 500K context); Googlegemini-3.1-flash-lite(low-latency workhorse, $0.25/$1.50, ~1.05M context); andqwen3.7-max(replacesqwen3-max, $2.50/$7.50, 1M context) across all four Qwen variants. Model lists, the OpenRouter seed, reasoning-effort gates, and thetokenTrackercontext + pricing tables (src/config/providers.ts,src/utils/tokenTracker.ts) were updated in lockstep, so cost and context-window estimates are correct for each id. -
Cross-device session resume (
/cloud). The CLI can now pull sessions synced from other devices (Mac app, VS Code, another CLI machine) and resume them locally.src/utils/codeepCloud.tsgainedlistCloudSessions(projectId?)andpullCloudSession(sessionId)— thinx-sync-token-authenticated wrappers over the existingGET /api/sessionsendpoint (which already served the web dashboard). The new/cloudslash command lists remote sessions scoped to the current project (or all if none is open), and on selection fetches the full message array, persists it into the local.codeep/sessions/store viasaveSession, and loads it — so the resumed session is first-class: it shows up in/sessions, autosaves on the next change, and re-syncs on the next turn.Why: the push path (
syncSession→POST /api/sessions) has existed since 2.0, and the Mac app and web dashboard already read from the cloud store. The CLI was the only surface that could push but not pull, so cross-device resume — start on the desktop, continue on the laptop — was impossible from the terminal. Now it isn't.telemetryis deliberately NOT consulted on the pull path: reading your own previously-pushed data back is not telemetry, and the user is explicitly asking for it. The original push was already gated. -
AGENTS.mdproject-rules support (closes #3).loadProjectRules(src/utils/agentChat.ts) now falls back toAGENTS.mdwhen neither.codeep/rules.mdnorCODEEP.mdexists. Lookup precedence (highest first):.codeep/rules.md→CODEEP.md→AGENTS.md. First non-empty file wins; they are not concatenated, so a user can keep a trimmedAGENTS.mdfor the other tools and a richer Codeep-specific rules file.Why:
AGENTS.mdis becoming the de-facto cross-tool project-rules standard. Users coming from Claude Code, Cursor, or Kilo Code can now point Codeep at their existing file without duplicating rules into a Codeep-specific file. -
.mcp.jsonMCP config source (closes #5).loadMcpServerConfig(src/utils/mcpConfig.ts) now reads.mcp.jsonat the workspace root in addition to.codeep/mcp_servers.json. Same JSON shape, samemcpServersmap/array form. Precedence chain (highest first):.codeep/mcp_servers.json(project) →.mcp.json(project, cross-tool) →~/.codeep/mcp_servers.json(global). Non-colliding server names from all sources merge; on collisions, higher-precedence sources win.Why: Claude Code, Cursor, and Kilo Code already read
.mcp.jsonat the repo root. Users can now keep one MCP config file for their whole toolset. -
Web dashboard: cache-savings insight card. The main dashboard (
Codeep-web/src/app/dashboard/page.tsx) now shows a green insight banner when the user has any cache reads: "Prompt caching saved you ~$X" with the total cache-read tokens and a circular cache-hit-ratio gauge. Cost-by-model rows gained a ⚡ badge with per-model cache-read counts. Brings the dashboard to parity with the macOS app's existing "Saved $X via caching" display.The savings estimate assumes $3/1M input rate (a common flagship rate) and a 90% discount for cache reads. It's a ballpark — the actual billed cost is already shown via
estimated_cost. The point is the "you avoided spending ~$X" framing, which is what users actually want to see.
- Workspace MCP servers now require a one-time trust approval. MCP
servers defined by files that travel WITH a repo —
.codeep/mcp_servers.jsonand the new.mcp.json— no longer auto-spawn at startup: cloning a repository containing one of these files would otherwise execute repo-author-chosen commands on your machine before you typed anything. The TUI now shows the server list and asks once per workspace ("Trust & start"); ACP sessions (Zed, VS Code) skip untrusted workspace servers with a notice. Manage with/mcp trust//mcp untrust(mirrors the existing per-workspace hooks trust). Global~/.codeepservers and ACP-provided servers are your own config and start without a prompt. /cloudhardening. Cloud API responses are now shape-validated before use (malformed payloads degrade to the normal "unavailable" path instead of throwing mid-picker), and the pulled session id is whitelisted before being used as a local filename — a hostile or corrupted server response could otherwise write outside.codeep/sessions/.- Project rules are capped at 64KB when injected into the system prompt
(applies to
.codeep/rules.md,CODEEP.md, and the newAGENTS.md), so an oversized rules file can't bloat every request or overflow small-context models.
/cloudresume left session identity half-updated. The pulled session now also updatesconfig.currentSessionId(autosave and agent-mode sync previously kept writing under the PREVIOUS session's id) and the session display name (the next sync would have renamed the cloud record to the old session's title). If a local copy of the pulled session is newer than the cloud record, the local copy is loaded instead of being overwritten./cloudcross-device listing actually finds your sessions now. Scope is a hash of the local absolute project path, so the same repo cloned at a different path on another machine (the normal cross-device case) produced an empty list. When the project-scoped list is empty,/cloudnow falls back to listing all sessions; when it's non-empty it appends a "Show all cloud sessions…" entry so a differently-pathed session is still reachable..mcp.jsonnow expands${VAR}/${VAR:-default}env references (all MCP config files do), matching Claude Code semantics — previously the literal"${GITHUB_TOKEN}"string was passed through and even shadowed a real environment variable of the same name at spawn time.- Truncated action targets show the
...marker again — the path shortener lost it for very long paths during theActionFormattingextraction. - Lifecycle hooks degrade gracefully on Windows without a POSIX shell
(closes #4).
.shhooks can't be spawned directly on Windows; previously apre_tool_call/pre_commithook would fail to spawn and — because those events are blocking — could wedge every tool call. Codeep now detects the platform's shell: it runs.shhooks through Git Bash'sshwhen installed, and when no POSIX shell is found reports an explicit "cannot run on this system" state in/hooksand the welcome banner and skips them (never blocking). A new README "Windows notes" section documents which features need a shell (hooks) and which are Node-native (rules, commands, skills, MCP config).
- Stored API keys need a one-time re-login after the keytar →
@napi-rs/keyringmigration (closes #6). The two libraries use different credential-store naming on Linux (Secret Service attributes) and Windows (Credential Vault target names), so keys saved by Codeep ≤ 2.14 are not visible to the new backend there. The CLI detects this and prints a one-time hint; re-add keys with/login <provider> <key>. macOS is unaffected (both use the same Keychain items). Keychain access now also runs off the event loop (AsyncEntry), so the TUI no longer freezes while macOS shows a keychain-authorization dialog.
-
CLI:
App.tsmessage-formatting extracted intoMessageFormatter.ts. The 3.2k-lineApp.tshad five pure (or near-pure) formatting methods inlined asprivate—wordWrap,applyInlineMarkdown,formatTextLines,formatCodeBlock,formatMessage— totalling ~600 LoC. Pulled them out intosrc/renderer/components/MessageFormatter.ts(443 LoC) as module-level functions, following the samecomponents/pattern already used bySettings,Export,Search, etc.The only non-trivial change:
codeBlockCounter(previously aprivate numberonApp) is now aBlockCounterobject ({ current: number }) threaded throughformatMessageby reference, so the counter still increments across consecutive calls during a single render pass — block numbers for/copy [n]stay stable.Also removed a duplicate
wordWrap: the chat-flavoured variant inApp.ts(hard-breaks over-width words like long file paths) is now the one inMessageFormatter.ts; the simplerwordWrapinansi.ts(lets over-width words overflow) stays for non-chat use cases.Why:
App.tswas the largest TypeScript file in the project. The formatting logic was untestable in isolation (it lived on a class that requires a screen, input loop, and 30+ state fields to instantiate). Extracted as pure functions, it now has 26 unit tests covering inline markdown, word-wrap, headings, lists, blockquotes, code blocks, and counter threading — the first tests ever for this code. App.ts shrank from 3189 → 2860 LoC (-10%). -
ACP registry entry synced to 2.14.0.
acp-registry/codeep/agent.jsonhad fallen behind to 2.4.2 — ten minor releases out of date — so ACP clients (Zed, Cursor, etc. via the registry) were advertising a stale binary. Bumped theversionfield and all four platform archive URLs (darwin-aarch64, darwin-x86_64, linux-x86_64, linux-aarch64) to thev2.14.0GitHub release. -
Mac: ProviderChoice + Conversation computed-property test coverage; +199 tests total. Added two pure test files (no
AppStateinstance needed):ProviderChoiceTests.swift(17 tests) — exhaustive coverage of theProviderChoiceenum's pure API:displayName,isKeyless,from(providerID:)round-trip for every case (including thez.ai/minimax-api/qwen-cn-apihyphenated-id cases that a naiverawValuematch would miss),keychainKeyIDuniqueness,makeProvider().id↔keychainKeyIDconsistency, andCaseIterablecompleteness.ConversationDisplayTests.swift(13 tests) — coverage for the sidebar's two computed invariants:title(custom-wins, derived from first user message, 40-char trim, whitespace-only fallback to "New chat") andupdatedAt(last message timestamp, createdAt fallback), plussnapshot()round-trip assertions (customSystem vs resolved system separation, identity preservation, message inclusion). Total Mac tests: 169 → 199 (+30 this round).
-
Web: key validation + provider metadata extraction; +228 tests total. Extracted
validateKeyand thePROVIDERSlist fromapp/dashboard/KeysSection.tsx(287→239 LoC) into a newsrc/lib/keyValidation.ts(63 LoC, 18 tests). The component also now uses the sharedmaskKeyfromlib/format.tsinstead of a localmask()duplicate. Tests cover all five prefix rules (openai, anthropic, google, openrouter, deepseek), the empty/whitespace/length guards, and the PROVIDERS array integrity. -
Web: review-analytics + project-page stats extraction; +210 tests total. Extracted two more pure data-transform layers:
src/lib/reviewStats.ts(206 LoC, 25 tests) fromapp/dashboard/reviews/page.tsx(404→366 LoC):readJson,buildHotspotsAndCategories,shapeTrendRows,shapeRepoRows,shapeRecentRows,shapeTotals.src/lib/projectStats.ts(50 LoC, 16 tests) fromapp/dashboard/projects/[name]/page.tsx(317→309 LoC):buildProjectCostByModel,sumTokens,parseCost. Also removed a duplicateCOLORS/colorForpalette that was shadowing the one inlib/format.ts.
-
Web: dashboard stats pipeline extraction; +169 tests total. Extracted the pure data-transform layer from
app/dashboard/page.tsx(933→883 LoC) into a newsrc/lib/stats.ts(209 LoC, 26 tests):buildModelStats— merge live model rows with the snapshot table, sort/cap/colour. 8 tests.buildProviderStats— shape provider GROUP BY rows with pct + colour. 4 tests.buildCostByModel— merge live cost-by-model with snapshot, handling the snapshot's missing cache columns. 5 tests.computeCacheStats— the cache-savings ($3/1M × 0.9) and hit-ratio roll-up. 4 tests.buildDailyCost— parse MySQL string costs to numbers. 5 tests. The page now imports these instead of inlining ~60 lines of merge logic.
-
CLI: commands.ts refactoring round 4 — insights, cloud, me, skills extraction; +2239 tests total. Extracted 8 more helpers covering the remaining mid-sized cases:
parseInsightsDays— the--days/--days=flag parser from/insights. 10 tests.formatCloudSessionLabel— the/cloudpicker row formatter (title · date · N msg · [project]). 6 tests.formatMeSyncReport— the/me syncpush/pull result list. 5 tests.formatMeLearnResult— the/me learnupdated/no-changes renderer. 2 tests.formatMeInitResult— the/me initcreated/exists renderer. 3 tests.formatSkillsShow— the/skills showdetail view. 1 test.formatSkillsBrowseEmpty— the/skills browseempty-state message. 2 tests.formatSkillsPublishResult— the/skills publishsuccess message. 3 tests.commands.tsis now down to 2350 LoC (−138 from the original 2488).
-
CLI: commands.ts refactoring round 3 — full /mcp subcommand extraction; +2207 tests total. Decomposed the entire
/mcpcase (the last big monolith incommands.ts, originally ~200 lines) by extracting 9 helpers intorenderer/commands/helpers.ts(now 514 LoC, 146 tests):parsePromptArgs— thekey=valuetoken parser from/mcp prompt. 8 tests.pluralTools/groupToolsByServer— shared pluraliser and tool-by-server grouper used across/mcp,/mcp reload, and install reports. 7 tests.formatMcpServerList— the default/mcpserver/tool listing (with empty-state and failed-servers sections). 5 tests.formatMcpReloadReport— the/mcp reloadsummary. 4 tests.formatMcpResourcesList— the/mcp resourceslisting. 3 tests.formatMcpResourceRead— the/mcp readcontent renderer (text fences, json/markdown detection, blob notes). 6 tests.formatMcpPromptsList— the/mcp promptslisting with arg annotations. 4 tests.formatMcpPromptResult— the/mcp promptmaterialised-output renderer. 4 tests. All eight/mcpsubcommand branches now delegate to these helpers, shrinking each by 5–34 lines.commands.tsis down to 2388 LoC (−100 from the original 2488).
-
CLI: commands.ts refactoring round 2 — stats, copy, apply extraction; +2166 tests total. Continued decomposing
commands.tsby extracting three more groups of pure helpers intorenderer/commands/helpers.ts(now 346 LoC, 105 tests):formatStatsReport+formatModelCost— the full/statsMarkdown report (per-model breakdown, totals, prompt-cache summary, pricing table). Token formatter is injected so the module stays pure. 15 tests.formatProfileList/formatMemoryList— list renderers for/profile listand/memory list. 8 tests.extractCodeBlocks/resolveBlockIndex— the code-block extractor and 1-based index validator from/copy. 14 tests.extractFileChanges/formatApplyDiffLine/shortPathForDisplay— the fence/comment-pattern file-change extractor and diff-line formatter from/apply. 21 tests. The/stats,/copy, and/applycases now delegate to these helpers, shrinking each by 40 / 5 / 22 lines respectively.
-
CLI: commands.ts refactoring — extracted helpers module, 2 bugs fixed, +2082 tests total. Decomposed
commands.ts(2488 → 2448 LoC, −40) by extracting pure helpers from the giant switch/case into a newrenderer/commands/helpers.tsmodule (140 LoC, 49 tests):buildSearchSnippets— the asymmetric 30/50-char window snippet extractor from/search(kept separate fromutils/search.ts’s 50/50 because the inline panel is narrower). 10 tests.parseKeepRecent— the/compact <n>arg parser. Fixed a real bug: the oldparseInt(arg) || fallbacktreated0as falsy and returned the default (4) instead of clamping to 2. Switched toNumber.isNaNcheck. 7 tests.joinSessionName— the hyphen-joiner from/rename. 6 tests.parseTaskAddArgs— the--bug/--feature/--descflag parser from/tasks add. 13 tests.formatTaskList— the Markdown list renderer from/tasks. 13 tests. Thecommands.tscases now delegate to these helpers; the parser and renderer have direct coverage for edge cases (empty args, flag ordering, type overrides, null descriptions).
-
CLI: App.ts refactoring round 3 — progress bar, paste detection, notification truncation extracted; 1 bug fixed. Continued decomposing
App.ts(2622 → 2613 LoC, −9 more) by pulling three more pure concerns intorenderer/layout.ts:agentProgressBar— the gradient ░▒▓█ bar fromrenderInlineAgentProgress. Fixed a real bug: whenmaxIterationswas 0, the old code divided by zero (yielding Infinity) and rendered a full bar instead of an empty one. Added amaxIterations > 0guard. 6 new tests, including the zero-budget edge case.truncateNotification— the ellipsis truncation fromrenderStatusBar. 7 new tests.shouldShowPasteDialog/buildPasteInfo— the paste-size threshold check and preview struct builder extracted fromhandlePaste. 14 new tests covering the exact 100-char boundary, the>3lines rule, and the 200-char preview truncation. The layout module is now 123 tests strong and has uncovered one bug.
-
CLI: App.ts refactoring round 2 — input display + scroll window extracted, +2006 tests total. Continued decomposing
App.ts(2662 → 2632 LoC, −30 more) by pulling two more pure concerns intorenderer/layout.ts:computeInputDisplay/inputPromptSymbol/inputViewport— the input-row geometry: prompt-symbol scaling for multi-line content, horizontal viewport scrolling for long lines (cursor anchored at 70% of the available width), placeholder selection. 21 new tests.scrollWindow— the visible-window math fromgetVisibleMessages(maxScroll clamping, startIndex/endIndex derivation). 7 new tests.renderInputandgetVisibleMessagesnow delegate to these helpers; the layout module is up to 96 tests covering every extracted helper.
-
CLI: App.ts refactoring — extracted pure layout/input modules, +1978 tests total. Decomposed
App.ts(2727 → 2662 LoC, −65) by pulling three previously inlined, untestable concerns into pure, unit-tested modules:- New module
renderer/layout.ts(190 LoC, 68 tests) — the layout math that was buried insiderenderChat,scrollToMessage,renderStatusBar, andhandleChatKey:bottomPanelHeight(snapshot)— the 15-branch if/else that reserves space for whichever inline panel is open (paste / agent / permission / session picker / confirm / status / help / search / export / logout / login / menu / settings / autocomplete), with caps and priority.chatLayout(height, panel)— derives the y-coordinates for the messages / separator / input / status rows.messageLineCount,messageOffsets,scrollOffsetForTarget— the word-wrap line-counting and scroll-centring logic that used to live inscrollToMessage.formatTokenCount— the "1.2K tok" compaction.statusBarRightHint— context-sensitive right-edge hint (idle / streaming / "new messages below" badge).activePanel(state)— the focus-precedence decision the keystroke handler walked as a 13-step if/else chain on every keypress.
- New module
renderer/inputParsing.ts(14 tests) —parseCommandInputextracts the/command arg argparser fromhandleCommand, with whitespace collapsing and case normalisation.
App.tsnow delegates to these modules via small snapshot objects; the big methods shrank by 65 lines and their core logic is now covered by 82 dedicated tests instead of being unreachable from the test suite. - New module
-
CLI: deeper coverage round 5 — 5 more test files, +50 tests. Closed the remaining small-module gaps:
utils/search.test.ts(15 tests) —searchMessages: case-insensitive match (both directions), messageIndex tracking, one-result-per-message, 50-char context window, leading/trailing ellipsis boundary, original-case preservation, multi-word terms, regex-char-as-literal safety.utils/commandIndex.test.ts(8 tests) —COMMAND_INDEXstructural invariants (non-empty, no dupes, slash-prefixed) andformatCommandIndexMarkdown rendering (bullet list, backtick wrapping, em-dash separator, one-bullet-per-command).utils/taskContext.test.ts(11 tests) —getTaskContextPromptempty-state, task title/description/project/badge rendering, missing-field fallbacks, multi-task bullet list, header + footer.utils/ollamaCatalog.test.ts(10 tests) —OLLAMA_CODING_MODELScatalog invariants (fields populated, pull-tag format, no dupes, size variety) andcatalogAgentHint(7B threshold, consistency with the catalog).renderer/components/uiConstants.test.ts(6 tests) —PRIMARY_COLORANSI escape,SPINNER_FRAMES(8 distinct single chars),LOGO_LINES/LOGO_HEIGHTconsistency.
-
CLI: deeper coverage round 4 — 6 more test files, +83 tests, 1 duplikat helpera istaknut.
renderer/main.test.ts(10 tests) —deriveSessionName(whitespace collapse, 5-word cap, 48-char truncation with ellipsis).utils/export.test.ts(25 tests) — Markdown / JSON / plain-text exporters: header structure, role labels, message preservation, session name defaulting, message-count field, ISO timestamp, format dispatch + unknown-format error, empty-list safety.renderer/components/Box.test.ts(20 tests) —boxCharscompleteness,createBox(line count, y-placement, corner chars per style, middle-row pattern, x-padding, title embed/alignment/ truncation/narrow-box suppression),centerBox(centring, floor, negative when overflow).renderer/components/Search.test.ts(7 tests) —handleSearchKey(escape/close, up/down clamping, enter-selects-messageIndex, empty-results guard, ignore-unknown, position vs index).renderer/components/Intro.test.ts(12 tests) —GLITCH_CHARS,generateNoiseLine(length, space preservation, glitch membership),getDecryptedLine(length, space preservation, full reveal at progress > 0.95, probabilistic reveal at 0, empty + long input).renderer/components/Logout.test.ts(9 tests) —handleLogoutKey(escape close, up/down navigation capped at providers + all + cancel, enter on provider / "all" / "cancel" slot, no-callback safety, unknown-key ignore, empty-providers cap).- Also surfaced a duplicate
truncatePathhelper (identical implementation inPermission.tsandStatus.ts) — exported both for testability; a future refactor should consolidate into a shared util.
-
CLI: deeper coverage round 3 — 4 more test files, +79 tests. Continued closing the test gap:
utils/terminal.test.ts(31 tests) — terminal control helpers:supportsSynchronizedOutput(env-driven detection of ghostty / iterm / kitty / wezterm / vscode / alacritty + xterm/256color fallback),hideCursor/showCursorescape sequences,clearLinesAbovemulti-line clear-and-restore,moveCursor(up/down),getTerminalSizefallback, andcreateSyncWriter(sync-frame wrapping, idempotent startSync, unsupported-terminal pass-through).utils/skillBundles.test.ts(32 tests) — the skill-bundle engine:parseFrontmatter(BOM/CRLF normalisation, inline + block lists, quote stripping, kebab-case keys, body preservation, no-frontmatter fallback),stripQuotes,asStringArray,formatBundlesForSysprompt(per-line 200-char cap, 3-trigger hint, total 4000-char budget with "omitted" note),formatBundleList(project/global grouping, version badge, empty hint).utils/skillBundlesCloud.test.ts(14 tests) —serialiseSkillMd(YAML frontmatter round-trip for all optional fields and list sections),readRawSkillMd(missing-file null),uninstallLocalBundle(delete + idempotent second call).renderer/components/Permission.test.ts(9 tests) —getPermissionOptions,truncatePath(basename fallback, .../ prefix, boundary behaviour).renderer/components/SelectScreen.test.ts(12 tests) —handleSelectKeydispatch for all navigation keys, mutation-free state, and the defensive empty-list case.
-
CLI: deeper coverage round 2 — 4 more test files, +117 tests, 1 crash bug fixed. Continued closing the test gap on large untested modules:
utils/codeepCloud.test.ts(23 tests) —generateProjectId(deterministic sha256-16, trailing-slash + case normalisation),_readFileBundleForTest/_writeFileBundleForTest(the personality/command bundle sync): case-folding of filenames, name-regex validation, 64-char name cap, 64 KB size cap (boundary included), additive-merge (no clobber of local edits), directory auto-creation, empty/invalid-body skip.renderer/Input.test.ts(35 tests) —LineEditor, the readline-style input editor: cursor movement, word-boundary navigation (wordLeft/wordRightover spaces, path separators and dots),deleteWordBackward(path-aware),deleteToEnd, history navigation (up/down walk, draft restoration, empty-entry skip, 100-entry cap), andhandleKeydispatch (backspace/delete/left/ right/home/end/ctrl-left/ctrl-right/alt+b/alt+f, regular char insert, ctrl-suppression).renderer/Screen.test.ts(16 tests) — the terminal screen buffer: out-of-bounds rejection, newline-truncation inwrite,writeLineoverwrite,writeWrappedline counting,horizontalLine, cursor API, and render/fullRender safety.renderer/agentExecution.test.ts(43 tests) —getActionType(tool-name → action classification) andisDangerousTool(name + command heuristics). Found and fixed a real crash bug: when the LLM returnedparameters.commandas a number (123) instead of a string,isDangerousToolthrewTypeError: command.toLowerCase is not a function— taking the whole agent loop down with it. The fix guards withtypeof rawCommand === 'string'.
-
CLI: deep test coverage — 6 new test files, +126 tests. Added tests for six previously-untested modules across the CLI:
config/index.test.ts(26 tests) — project detection (isProjectDirectory,hasStandardProjectMarkers,initializeAsProject,isManuallyInitializedProject): all 9 standard markers, the.codeep/project.jsonmanual marker, idempotent re-initialisation, non-writable-path failure, and theisProjectDirectoryprecedence (manual marker wins over standard markers).utils/taskPlanner.test.ts(19 tests) — the dependency-aware task scheduler (canStartTask,getNextTask,formatTaskPlan): blocked / unblocked dependency chains, failed-dependency handling, multi-level chains, in-progress skip, and the status-icon / dependency rendering.utils/logger.test.ts(9 tests) —formatLogEntry: level uppercasing, JSON-serialised data suffix, falsy-data suppression (0/false/''), multi-line messages, trailing newline.utils/interactive.test.ts(28 tests) — the prompt-clarification flow (analyzeForClarification,formatQuestions,parseAnswers,enhancePromptWithAnswers): all ambiguity triggers (auth, database, api, deploy, test, styling, state-management, refactor, form), letter-answer parsing (1a/1b), option-name matching, and the "proceed" escape hatch. Documents a real false-positive bug incheckForDetails(substring match: "authentication" contains "auth" from the "Basic auth" option label).utils/mcpMarketplace.test.ts(14 tests) —findMarketplaceEntry,formatMarketplaceList,formatMarketplaceEntry: case-insensitive lookup, the Markdown table rendering, one-row-per-entry invariant, arg-hint / env-note / docs-link sections.renderer/handlers.test.ts(30 tests) — the inline modal key handlers (handleInlineStatusKey,handleInlineHelpKey,handleInlinePermissionKey,handleInlineSessionPickerKey,handleInlineConfirmKey): close-on-escape, cursor clamping, scroll step sizes, the yes/no/extra cycle, delete-mode toggling, and empty-list cleanup.
-
CLI:
App.ts+commands.tsdecomposition — +61 tests, 2 more helpers extracted. Continued theApp.ts/commands.tscleanup:components/Autocomplete.ts—filterCommands(value, commands), the/-command prefix matcher. Rules: only triggers on input starting with/and containing no space, case-insensitive prefix match, 8-item cap. 10 tests cover the accept/reject paths, the cap, and the empty-query special case.ollamaHint.ts—ollamaModelHint(modelId), the parameter- count hint (✓ agent mode/⚠ chat only (< 7B)) shown beside Ollama models. 20 tests cover the size-threshold boundary (7B), namespaced ids (qwen3:14b), hyphenated ids (mistral-7b-instruct), and the no-size-detected fallback.utils/toolParsing.test.ts(31 tests) — the LLM tool-call parser had no test coverage despite being critical path (it's how the agent extractsread_file/write_file/execute_commandcalls from model output). Added test seams (_forTestexports) for the three file-private helpers (extractPartialToolParams,tryExtractParams,tryParseToolCall) and covered: name normalisation (camelCase → snake_case), OpenAI / Anthropic / text response formats, the truncation-recovery path (partial JSON → recovered params), required-field validation (write_file missing path, edit_file missing old_text/new_text), and the trailing-comma tolerance intryParseToolCall.
-
VS Code:
extension.tsdecomposed — 654 → 627 LoC, +50 tests (81 → 131).extension.ts(the activation entry point) had no test coverage and kept its status-bar presentation rules and input-box validators inline. Two pure-function modules extracted:statusBarRenderer.ts—renderStatusBar(state)returns the text / tooltip / command / background-colour per connection state (connecting / connected / reconnecting / disconnected / failed). Theactivate()body now has a 6-lineapplyStatusBarwrapper that applies those fields to the liveStatusBarItem. 17 tests cover each state's icon, label, click command, tooltip content (model name in connected, attempt counter in reconnecting), and background tint (warning vs. error).validators.ts—validateMcpServerName,validateSkillBundleName,validateApiKey,validateModelId,validateRequired. Five regex / length rules that were inline asInputBox.validateInputlambdas, now reusable and unit-tested. 33 tests cover accept paths, reject paths, boundary lengths, leading-character rules, whitespace trimming, and the embedded-whitespace API-key check.
-
Web: dashboard formatting helpers de-duplicated across 8 files + 24 tests. Six pure helpers (
COLORS,colorFor,maskKey,timeAgo,fmt,fmtCost,fmtTokens) were copy-pasted — sometimes with subtly different bodies — across eight dashboard pages and components:File Had app/dashboard/page.tsxall six app/dashboard/KeysSection.tsxtimeAgoapp/dashboard/ProjectRow.tsxtimeAgoapp/dashboard/CliConnectionSection.tsxtimeAgoapp/dashboard/projects/[name]/page.tsxtimeAgo,fmtCost,fmtTokensapp/dashboard/sessions/page.tsxtimeAgoapp/dashboard/sessions/[id]/page.tsxfmtTokensapp/dashboard/reviews/page.tsxtimeAgo,fmtapp/dashboard/reviews/CiTokensSection.tsxtimeAgoConsolidated into
src/lib/format.ts(78 LoC, single source of truth), withfmtCost'sNumber(n) || 0NaN-guard propagated everywhere (the project-page copy lacked it). All eight call sites now import from@/lib/format.New
src/lib/format.test.ts(24 tests) pins the rules — palette wrapping, key masking,timeAgotier boundaries (mocked clock), compact number/cost/token formats including the adaptive cost precision that sub-cent cached-token bills depend on. -
CLI:
App.tsdecomposed — 2860 → 2732 LoC, +22 tests, 3 constants de-duplicated.App.ts(the terminal renderer's main file) had drifted back to 2860 LoC after the earlierMessageFormatterextraction. Three more pure-function / constant clusters extracted intocomponents/:uiConstants.ts—PRIMARY_COLOR,SPINNER_FRAMES,LOGO_LINES,LOGO_HEIGHT. De-duplication win:PRIMARY_COLORwas redeclared inStatus.tsandIntro.ts, andLOGO_LINESwas copy-pasted intoIntro.ts(asLOGO). Both now import from the single source, so the palette / logo can't drift between files.ActionFormatting.ts—getActionColor,formatActionTarget,getActionLabel(the agent-progress-panel colour/label/path helpers). Pure functions, now unit-tested (11 tests).WelcomeFormatter.ts—formatWelcomeMessage(the welcome- banner DSL renderer: version header, Project/Access/Mode labels, ⚠ warnings, /help hints). Pure function, now unit-tested (11 tests).
Writing the
ActionFormattingtests surfaced a real truncation bug informatActionTarget: when a 3+ segment path was already shortened to.../parent/fileand still exceededmaxLen, the function prepended another...and sliced from the end — producing.../b/c→../b/c(dropping a dot) instead of a clean left-trim. Now slices the short form directly so the file extension always survives. -
Mac App: unit-test coverage expanded (+64 tests, 114 → 178) + 3 parser bugs fixed. Four new test files cover the pure parsers and helpers behind the
MessageContent/views (the ones reinstated when the split was fixed in Cycle #29):MarkdownTableParserTests.swift(13 tests) — GFM table splitter: header + delimiter detection, alignment colons, pipe-less tables, row padding, prose-before/after, single-dash delimiters.MarkdownBlocksTests.swift(19 tests) — block-level markdown: paragraphs, ATX headings (levels 1–6 + 7-hash fallback), bullet / numbered / task lists (with[ ]/[x]/[X]markers), blockquotes, horizontal rules, mixed content.ParseEditDiffTests.swift(8 tests) —parseEditDiff: the edit-file tool-output parser. Nil for non-edit output, prefix-only guard, mixed added/removed/context ordering, leading-blank skip.CodeHighlightingTests.swift(13 tests) — the SwiftUI bridge fromSyntaxHighlightertokens toColors. Pins the colour per kind and verifiesattributed(_:)round-trips source verbatim.SegmentsTests.swift(11 tests) —segments(from:): the fenced-code-block splitter drivingMessageContentView. Covers language tags, unclosed fences (streaming), inline-backtick non-detection, tables-inside-prose promotion.
Writing the tests surfaced three real bugs in the parsers we'd reinstated from memory in Cycle #29, now fixed:
MarkdownBlocks.parseTaskItemread the checkbox marker at the wrong string index (off-by-one) —- [ ]and- [x]were never recognized and fell through toparseBullet, so task items rendered as plain bullets with the[ ]/[x]visible in the text. Index arithmetic corrected.MarkdownTableParser.isDelimiterrequired 3+ dashes per delimiter cell. GFM/CommonMark only require one dash, so valid tables with short delimiters (|-|-|,|:--|:-:|) were rejected and rendered as prose. Regex relaxed to:?-+:?.MarkdownTableParser.flushBufferdidn't trim trailing blank lines, so an empty string or a table followed by a blank produced a spurious empty.prosechunk. Now trims trailing blanks before emitting.
-
Mac App:
ChatView.swiftdecomposed — 2554 → 1075 LoC (−58%). The 2.5k-lineChatView.swiftwas the second-largest Swift file in the project (afterAppState.swift). It bundled the main chat surface, the privateChatDetailViewhelper, and eight independent sheet / popover / banner sub-views behind a single file. The sub-views had no shared state with the detail view beyond what they already received as parameters or read from@Environment(AppState.self), so they were pure extraction candidates.Split into eight focused files under
Views/, each carrying a header comment documenting its origin:File LoC Contents ConfirmationSheet.swift259 tool-approval modal (Run/Skip/Cancel) TasksSheet.swift285 codeep.dev tasks panel + TaskRowUsageStatsView.swift193 token/cost popover + ModelCostRowProjectContextSheet.swift243 .codeep/context viewer +ProjectNotesSheetInsightsView.swift224 codeep.dev insights panel CheckpointsSheet.swift126 checkpoint history + rewind UI UserPromptsEditor.swift138 "My Prompts" CRUD editor ErrorBanner.swift83 error banner What's left in
ChatView.swift(1075 LoC): the top-levelChatView(NavigationSplitView shell + sheet wiring) andChatDetailView(the actual message list + composer + toolbar). These two are tightly coupled and share a lot of state — further splitting would just shuffle parameters around.private struct→struct(module-internal) on every moved type so it stays visible toChatView.swiftacross files;private extensioncompanions moved with their owner. -
VS Code Extension: unit-test coverage expanded (+32 tests, 49 → 81). The VS Code extension had 4 test files covering 49 tests, all on the vscode-free modules (
acpClient,mcpConfigFile,diffPreview,webview/markdown).chatPanel.ts(1008 LoC) — the largest source file — had zero tests; same forcodeActions.ts.Three new test files + one expanded one close that gap by testing the pure functions those modules already contained, exposed for tests via
_…ForTestnamed exports (same pattern asdiffPreview.test.ts):chatPanel.test.ts(6 tests) —friendlyError, the user-facing message formatter for CLI failure modes (timeouts, crashes, missing binary). Covers each known failure string + passthrough for unknown.codeActions.test.ts(12 tests) —fence(code-fence builder) andbuildPrompt(lightbulb prompt templates for explain / improve / tests / docs / fix). Verifies each action kind produces the right instruction and embeds the fenced block; fix-without-diagnostics omits the problems section.webview/markdown.test.ts(+14 tests) —escapeHtmlandinline, the two security-sensitive internal helpers behindrenderMarkdown. Adds direct coverage for HTML-entity escaping (including the"href-breakout regression) and the URL-scheme allowlist (http(s),mailto:,vscode:pass;javascript:,data:are stripped to label-only) independently of the full-pipeline tests.
Mocking:
chatPanel.test.tsandcodeActions.test.tsusevi.mock('vscode', …)with a minimal stub (same approach the existingdiffPreview.test.tspioneered) so the modules load under Node without the Electron extension host. -
Mac App:
AppState+MCP.swiftextracted + MessageContent build fix. The MCP (Model Context Protocol) server lifecycle —reloadMCPServers,trustPendingMCP,dismissPendingMCP,revokeMCPTrust,handleMCPToolsChanged,addCatalogServer,makeMCPSampler,reattachToolsToAllConversations— moved out of the 2.8k-lineAppState.swiftinto a dedicatedAppState+MCP.swiftextension (170 LoC), following the same pattern as the existing+Cloud/+Git/+Permissions/+RecentProjectssplits.The MCP-related stored properties (
mcpManager,mcpTools,mcpResults,isReloadingMCP,didWireMCPToolsHandler,trustedMCPProjectPaths,persistTrustedMCPProjects) were widened fromprivate/private(set)tointernal/internal(set)so the cross-file extension can reach them — Swift'sprivateis file-scoped. Each carries a comment explaining why.Also fixed a build regression left over from the Ciklus #29
MessageBubbledecomposition:MarkdownTableParserandMarkdownBlockswere referenced by the extracted sub-views (MessageContentView,ProseView,MarkdownTableView) but never defined — the parsers had been dropped during the split. Added them as standalone files (MarkdownTableParser.swift115 LoC,MarkdownBlocks.swift190 LoC) and refactored the twoForEach { switch … }blocks inMessageContentViewandProseViewinto dedicated@ViewBuilderhelper functions to satisfy Swift's result-builder type inference (the inline switch was producing the misleadingAccessibilityRotorContentconformance error).Why:
AppState.swiftwas the largest Swift file in the project. Pulling MCP out cuts it to 2684 LoC (-4.5%) and groups every MCP-related entry-point under one navigable file. The parser fix was blocking the build outright — Xcode was failing onMessageContentViewbefore any of the new MCP extension could compile. -
Mac App:
MessageBubble.swiftdecomposed into focused sub-components. The 924-line view was a God Object holding the bubble shell, the markdown segment dispatcher + parser, code-block rendering + syntax highlighting, prose/list/heading/quote rendering, GFM table rendering, and the three tool-execution card views (ToolCallCard,ToolResultCard,DiffBody) plus their diff parser. Split into:Views/MessageBubble.swift(315 LoC) — the bubble shell: role icon, content switch, hover-revealed action row, inline edit mode.Views/MessageContent/MessageContentView.swift— prose/code/table dispatcher + the fenced-code parser.Views/MessageContent/CodeBlockView.swift— code panel + the sharedCodeHighlightingpalette.Views/MessageContent/ProseView.swift— block + inline markdown.Views/MessageContent/MarkdownTableView.swift— GFM table grid.Views/MessageContent/ToolCards.swift—ToolCallCard,ToolResultCard,EditDiff/DiffLine/DiffBody,parseEditDiff.
Pure relocation — no behaviour change. The
privatetypes becameinternalso the bubble (and the existingQuickAgentView/ChatViewcallers) can still reach them. Xcode 16 file-system- synchronized groups pick up the new folder automatically, so no.pbxprojedit was needed. The iOS target keeps its own copy for now (it has UIKit-specific rendering); the same split can be applied there as a follow-up.Why: the monolith was the largest Swift file in the project and the one most likely to grow further (tool-card variants, snapshot tests). Smaller files mean smaller review diffs, Xcode Previews that target one piece at a time, and a natural seam for the snapshot-test suite that's still missing.
-
README: replaced the stale "Upgrading from 1.x to 2.0" section. The top-of-README upgrade note still described the 2.0.0 breaking changes as if they were the latest migration, 14 minor releases later. Replaced with a short "Upgrading" paragraph that points at CHANGELOG for per-release breaking changes and mentions 2.0.0 only as the last breaking bump. The old details (MCP
clientInfoversion, optionalMcpServershape) remain in the 2.0.0 changelog entry. -
Native keychain backend swapped from
keytarto@napi-rs/keyring.src/utils/keychain.tsnow loads@napi-rs/keyring(a Rust binary with prebuilt per-platform artifacts shipped as optionalDependencies) instead of the deprecatedkeytaraddon. The publicSecureStoragesurface is unchanged — only the internal adapter was rewritten to bridge the new syncEntryAPI to the async methods the rest of the code expects.Why:
keytarpullsprebuild-install, which frequently fails to fetch or compile on new macOS releases and ARM Linux, breakingnpm installoutright for affected users.@napi-rs/keyringhas no build step and installs a precompiled binary for the target platform automatically, so installs are reliable across macOS / Linux / Windows / FreeBSD on both x64 and arm64. -
Docs:
/docs/rulesand/docs/mcprewritten to list the new sources and the full precedence chains. README updated in lockstep. The old docs claimed both Codeep-native rules files were loaded and concatenated — that was never true (first non-empty file wins), so the rewrite also fixes that inaccuracy.
Claude Sonnet 5 replaces Sonnet 4.6 in the Anthropic picker (1M context, the full low→max reasoning-effort range), plus correctness fixes from a full-project audit: the ACP edit-approval diff, per-session naming, Grok effort gating, OpenAI-protocol cache accounting, and a
/copyedge case.
- Claude Sonnet 5 (
claude-sonnet-5) replaces Claude Sonnet 4.6 in the Anthropic model list (and the OpenRouter seed). Same $3/$15 pricing and 1M context window; graded/thinkingeffort (low–max). Sonnet 5 rejects non-default sampling params, so a custom/temperatureis now omitted for it (it would otherwise 400) — matching the Fable 5 / Opus 4.7+ handling. - Claude Fable 5 (
claude-fable-5) is back in the Anthropic model picker (and the OpenRouter seed) now that it's available again — Anthropic's most capable model, for the hardest reasoning and long-horizon agentic work. 1M context, fulllow–max/thinkingeffort, $10/$50 pricing in/cost. The sampling-param and effort handling were already wired up, so this just re-lists it in the picker.
- ACP edit approvals showed no diff. The permission dialog shown to Zed /
VS Code branched on
old_string/new_string, but theedit_filetool emitsold_text/new_text— so in Manual mode you approved an edit seeing only the file path, not the change. Now reads the real parameter names. /newreported the new session under the previous session's name. The derived display name wasn't cleared on/new, sosyncSession/reportStatsattributed the fresh, unrelated session to the old name on the dashboard.reasoning_effortsent to non-reasoning Grok coders.grok-build(the default) andgrok-code-fastare coders, not reasoning models — the param 400s, and a 400 there silently dropped the turn into the weaker text-tool fallback. They're now excluded from the effort gate.- OpenAI-protocol cache tokens over-billed in
/cost.extractOpenAIUsageignoredprompt_tokens_details.cached_tokens, so DeepSeek/OpenAI cache hits were estimated at the full input rate instead of the ~0.1× cache-read rate. /copy <non-number>(e.g./copy abc) reported a bogus success and cleared the clipboard instead of showing "Invalid block number"; now guarded.- Streamed agent turns recorded zero tokens on most non-OpenAI providers.
stream_options.include_usagewas only requested for the literalopenaiprovider, so DeepSeek/Kimi/Grok/Qwen/GLM/… streamed with no usage block and the whole turn logged 0 tokens / $0.00 in/costand the dashboard. Now requested for every OpenAI-compatible provider (as the plain-chat path already does). - An agent run (or ACP prompt) wiped the session's running
/costtotal. Token tracking was destructively reset at the start of each run to compute the cloud-telemetry delta, so the status bar and/costlost all prior session usage (and in the ACP server,/costafter a 2nd prompt showed only the last one — or nothing). Now uses a non-destructive marker: cloud stats still get only the run's delta while the session-cumulative total survives. - Concurrent ACP sessions mixed each other's token usage. The tracker kept one process-wide record buffer, so two sessions on one process (e.g. VS Code "New chat" while another turn streams) clobbered and cross-reported totals. Each ACP session now accumulates into its own buffer (via AsyncLocalStorage), isolating usage while keeping the single-session TUI path unchanged.
Cloud stats: the CLI now reports Anthropic prompt-caching breakdown (cache-creation and cache-read token counts) alongside the existing input/output/cost totals, so the dashboard can show "saved $X with caching" for CLI and VS Code (ACP) sessions.
-
Cache token reporting in cloud stats.
StatsPayload(src/utils/codeepCloud.ts) now carries optionalcacheCreationTokens/cacheReadTokens. The threereportStatscall sites —src/renderer/main.ts(sync and async paths) andsrc/renderer/agentExecution.ts— pass through the per-model buckets fromgetCostBreakdown(), andgetSessionStats()exposes session-totals via newtotalCacheCreationTokens/totalCacheReadTokensfields. The ACP server (src/acp/server.ts) — the path VS Code uses — was updated in lockstep so both clients report the same shape.Why:
estimatedCostalready folded cache multipliers into the dollar total, but the raw cache counts were dropped on the floor, so the dashboard couldn't break out "this session read 500k tokens from cache." Now it can. -
ProviderCostBreakdownandSessionTokenStatsextended. Both interfaces gainedcacheCreationTokens/cacheReadTokens(required on the breakdown, since every provider reports something — 0 for non-caching).getCostBreakdown()accumulates them per provider/model group;getSessionStats()sums across all records. Two new tests cover the accumulation paths.
Repo hygiene: dropped dead React-hooks dir, locked the package manager to npm, made
npm run buildrebuilddist/from scratch, added a command registry as the single source of truth for autocomplete +/help+ dispatch validation, lifted ACP test coverage from 0% to ~45% on the testable surface, removed allas anycasts from the settings screen, upgradedconfto v13, fixed the tsconfig sotsc --noEmitis now genuinely clean (was silently emitting 445+ errors), patched the high-severityviteadvisory, switched CI from bun to npm, addedCONTRIBUTING.md+SECURITY.md, extracted the synchronous ACP handlers + pure helpers out of thestartAcpServer()closure into a testableserverHandlers.tsmodule (89.7% line coverage on the extracted surface; 0 → 25.7% onserver.ts), migratedrelease-binaries.ymlfrom bun to npm (keeping bun only for thebun build --compilebinary step), removed the deprecated + vulnerablepkgdevDependency (0 high/critical advisories remaining), and hardened the build withnoEmitOnError: true.
src/hooks/deleted. This directory (index.ts+useAgent.ts) was a leftover from the old Ink-based TUI: it importedreact(which is not inpackage.json) and was kept out oftscvia anexcludeintsconfig.json, so the project type-checked only because the dead code was hidden. Nobody imported it (grep "from.*hooks/" src/returned zero hits), and it referenced arunAgentsignature that no longer exists. Thesrc/hooks/**/*exclude entry is removed fromtsconfig.json—tsc --noEmitis now clean with nothing hidden.
- Single package manager: npm.
bun.lockis removed andpackage-lock.jsonis now the canonical lockfile (previously both were git-ignored and drifted)..gitignoreno longer ignorespackage-lock.json; other lockfiles (yarn.lock,pnpm-lock.yaml,bun.lock*) stay ignored. /autocomplete trimmed. Five rarely-tab-completed utility/legacy commands were dropped from the/dropdown —/clear,/exit,/context-save,/context-load,/context-clear— to cut clutter. They remain fully dispatchable and listed in/help. (/sessionswas kept in the dropdown as a primary navigation command.)npm run buildnow wipesdist/first (rm -rf dist && tsc …). Thebun build --compilebinary step reads fromdist/, so a staledist/(e.g. leftover output from the now-deletedsrc/hooks/dir, or a renamed module) could ship outdated JS. A clean rebuild on every build closes that hole.src/acp/**/*.tsadded to the Vitest coverage scope so the editor- integration layer is measured alongsideutils/,api/, andconfig/.confupgraded 12 → 13 (^13.1.0). BringsrootSchema/ajvOptionsoptions, a.delete()dot-notation typing fix, and updated transitive deps (dot-prop8→9,ajvpatch bumps). Held below v14 pending a separate engine/compat review — the v13 bump was all this release needed.tsconfig.jsonnow declares"types": ["node"]. Without it,tsc --noEmitwas silently emitting 445+TS2591: Cannot find name 'fs'/'path'/ 'process'/…errors across 86 files —npm run buildappeared to succeed only because the config doesn't setnoEmitOnError, so the broken JS was written todist/alongside the errors. Withtypes: ["node"]the type check is genuinely clean (0 errors). This was a long-standing latent issue masked by the emit-on-error behaviour.npm audit fixpatched the high-severityviteadvisory (GHSA-v6wh-96g9-6wx3 / GHSA-fx2h-pf6j-xcff —launch-editorUNC path +server.fs.denybypass on Windows) by bumping the transitivevitebrought in byvitest. One advisory remains: a single low inesbuild(GHSA-g7r4-m6w7-qqqr — dev-server-only arbitrary file read on Windows), pulled in transitively byvitest/tsx, with no fix short of a major bump of those dev tools. Removingpkg(below) cleared the prior moderate advisory, sonpm auditnow reports 0 high/critical (1 low total).
- Command registry — single source of truth for slash-command metadata
(
src/renderer/commands/registry.ts). Previously three places carried the same data and drifted: theCOMMAND_DESCRIPTIONSrecord inApp.ts(123 hand-typed rows driving/autocomplete), thehelpCategoriesarray incomponents/Help.ts(hand-typed/helprows), and thecaselabels inrenderer/commands.ts+acp/commands.ts. NowApp.tsandHelp.tsboth derive from the registry, and two new tests (registry.test.ts) enforce the invariant at build time: every top-levelcaselabel in either dispatcher must exist in the registry — adding acase 'foo':without a registry entry now fails CI. The registry also exposesresolveCommand(),ALL_COMMAND_NAMES, andALL_ALIASESso the dispatchers themselves can move to registry lookups incrementally. - ACP layer test coverage. Two new test files lift the previously
untested ACP adapter from 0% to meaningful coverage:
src/acp/session.test.ts(35 tests) coversbuildProjectContext,toolCallMeta(every tool → ACP kind mapping), andbuildRawOutput(diff formatting for edits, stdout surfacing for commands, error paths).toolCallMetaandbuildRawOutputwere promoted from module-private to exported specifically so they can be tested in isolation.src/acp/commands.test.ts(10 tests) coversinitWorkspace— the filesystem bootstrap that runs on every ACPsession/new:.codeep/creation, project initialization, the read+write permission grant, and the informed-consent banners for custom slash commands and lifecycle hooks. Uses an isolated tmpdir per test (same pattern ascheckpoints.test.ts).- Together they catch regressions in the data-shaping surface and the
onboarding flow without mocking the agent loop, which is exercised
end-to-end by
toolExecution.test.ts.
- Settings screen: all 7
as anycasts removed. Previously everyconfig.set/config.getcall inSettings.tsusedsetting.key as anybecauseSettingItem.keywas typed asstringrather thankeyof ConfigSchema. Now:ConfigSchemais exported fromconfig/index.tsso consumers can reference its keys.SettingItem.keyis typed askeyof ConfigSchema— a typo'd or unknown setting key is now a compile error, not a silent runtime no-op.- All writes go through a single typed
writeSetting(setting, value)helper. The unavoidableas ConfigSchema[K]cast lives in exactly one audited spot instead of seven call sites, andconfig.get(setting.key)no longer needs any cast. - 13 new tests (
Settings.test.ts) pin the write path: number editing + clamping, select cycling (forward/backward/wrap), escape-abort, and theupdateRateLimits()side effect for the rate-limit settings.
CONTRIBUTING.md+SECURITY.mdadded. Previously the repo had neither — the README's "Contributing" section was one sentence pointing at GitHub issues. The new files cover:CONTRIBUTING.md: the npm-based setup, the test/build/type-check loop (including "what gets a test" guidance), code style, an architecture reading order for new contributors, the provider-integration flow, and the release pipeline.SECURITY.md: how to report a vulnerability (GitHub Security Advisories preferred), what's in/out of scope for an agent that can edit files and run shell commands, and a list of the hardening features already in place (project permissions, confirmation modes, hook trust gate, keychain storage, telemetry opt-out) so contributors don't regress them.- The README's "Contributing" section now links to both.
- CI workflow switched from bun to npm (
.github/workflows/ci.yml). The workflow usedbun install --frozen-lockfile, butbun.lockwas removed and.gitignoredwhen npm became canonical (#2), so the gate would fail on a clean checkout. Now usesactions/setup-node@v5with Node 20 +npm ci+npx tsc --noEmit+npm test, consistent withCONTRIBUTING.md.release-binaries.ymlstill uses bun (itsbun build --compilestep produces the cross-platform binaries — that's tracked as a separate migration, not safe to flip blindly). server.tsrefactor: extracted handlers + pure helpers to module scope.startAcpServer()was a ~1300-line closure whose 12+ request handlers (session/set_mode,session/set_config_option,session/list,session/delete, …) were unreachable by unit tests because they captured the live stdio transport. The refactor lifts them into two testable surfaces:server.tsmodule-scope exports — the pure helpers (formatToolInputForPermission,resolveLocalPath,collectEmbeddedContext,providerHasKey,buildConfigOptions,AGENT_MODES) now carryexportand are covered byserver.test.ts.- New
serverHandlers.tsmodule — the four synchronous session handlers (handleSetMode,handleSetConfigOption,handleSessionList,handleSessionDelete) plus a pureapplyConfigOptionhelper, each taking an explicit(msg, deps)pair wheredeps = { transport, sessions }. The transport is stubbed in tests via a recorded-call array, which is what makes the assertions readable. startAcpServer()now constructs onehandlerDepsobject and delegates to the extracted functions, keeping the dispatch loop intact.- The async handlers (
handleSessionPrompt,handleSessionNew,handleSessionLoad,handleSessionResume, image/vision, agent loop) stay in the closure for now — they reach into MCP spawning, the keychain, andrunAgentSession, each of which needs its own extraction pass. Tracked as a follow-up. - Coverage:
serverHandlers.tsat 89.7% lines / 87% statements;server.tsup from 0% to 25.7%. 56 new tests acrossserver.test.ts(34) andserverHandlers.test.ts(22).
release-binaries.ymlmigrated from bun to npm (build + publish-npm jobs).bun install→npm ci,bun run→npm run,setup-bun→setup-node@v5(Node 20 build, Node 24 publish). Only thebun build --compilestep keeps bun — it produces the distributed standalone binaries and has no npm-side equivalent (the deprecatedpkgwas never wired into CI). This makes CI + release consistent with the canonical npm toolchain (#8 fixed the same drift inci.yml).- Removed deprecated
pkgdevDependency.pkgwas unmaintained since 2023 (archived by Vercel), carried a moderate-severity advisory, and itsbuild:binaryscript was never invoked by any workflow — the release pipeline usesbun build --compileinstead. Removedpkg, thebuild:binaryscript, and the top-level"pkg"config block frompackage.json.npm auditnow reports 0 high/critical (was 1 high).@yao-pkg/pkg(the active fork) stays available if we ever need an npm-side path to standalone binaries. - Added
noEmitOnError: truetotsconfig.json. Now thattsc --noEmitis clean (0 errors), this flag hardens the build:tscwill refuse to emitdist/if a type error slips in, so a broken build can no longer be masked by a staledist/. Prevents regressions of the kind that #7 fixed (445+ errors silently emitted for months). - Lifted
handleListProviders+buildProviderListintoserverHandlers.ts. The provider-catalog handler was the simplest remaining async-safe handler — pure shape mapping overPROVIDERS+ a singletransport.respond. Extracted asbuildProviderList()(pure, testable shape) +handleListProviders(msg, deps)(thin wrapper). 5 new tests pin the shape includingdynamicModelsflagging for open-ended providers (OpenRouter, Ollama).serverHandlers.tscoverage now at 89.7% lines.
Three new providers — Kimi (Moonshot), Grok (xAI), and Qwen (Alibaba) — covering the major coding models. Kimi and Qwen include their flat-fee coding-plan subscriptions alongside pay-per-use; Grok adds graded
/thinkingeffort.
- Kimi (Moonshot AI).
kimidrives the Kimi Code subscription (api.kimi.com/coding, model aliaskimi-for-coding);kimi-apiis pay-per-use (api.moonshot.ai, defaultkimi-k2.7-code);kimi-cnfor mainland China. Keys:KIMI_CODE_API_KEY/MOONSHOT_API_KEY. - Qwen (Alibaba Model Studio).
qwendrives the Coding Plan subscription (coding-intl.dashscope…,sk-sp-key);qwen-apiis pay-per-use (DashScope, defaultqwen3-coder-plus); plusqwen-cn/qwen-cn-apiand a free ModelScope tier (modelscope). Keys:BAILIAN_CODING_PLAN_API_KEY/DASHSCOPE_API_KEY/MODELSCOPE_API_KEY. - Grok (xAI).
grok— pay-per-use (api.x.ai), defaultgrok-build-0.1plusgrok-4.3and the fast/reasoning variants. Key:XAI_API_KEY.
/thinkingnow covers Grok (reasoning_effort— low/medium/high). Kimi and the Qwen coder models have no graded knob, so they stay out of the picker.- Qwen tool turns are sent non-streamed. DashScope rejects
toolswithstream:true, so agent turns that carry tools buffer the reply (handled transparently); other providers keep streaming. - Kimi K2.x code models fix temperature internally — Codeep withholds the sampling params so they don't 400.
New
/thinking(alias/effort) reasoning-effort control —auto · low · medium · high · max, shown beside the model in the status bar and clamped per provider+model so it never sends a value the API rejects. Plus a Codeep agent identity and iOS-testing MCP servers.
/thinking(alias/effort) — thinking / reasoning-effort tiers. A single control with five tiers (auto · low · medium · high · max) for how hard the model reasons.auto(default) sends nothing — each model's own default. The other tiers are clamped to the nearest level the active provider+model actually accepts, so an unsupported value is never sent: Anthropic Opus/Sonnet →output_config.effort, OpenAI GPT‑5.x →reasoning_effort(Max→xhigh), Google Gemini 3 → low/high, DeepSeek V4 & Z.AI GLM‑5.2 → high/max, OpenRouter → unifiedreasoning.effort. The active tier shows next to the model in the status bar; models without a graded knob (Haiku, GLM‑Turbo, Ollama, custom) hide it.- About‑Codeep persona. The agent system prompt now states what Codeep is and points you at the right slash‑command, backed by a curated command index.
- MCP marketplace: iOS‑testing servers. Added iOS Simulator and Mobile (iOS + Android) servers for device/UI automation.
- MCP browser server is now Playwright (supersedes Puppeteer) — the de‑facto browser‑automation MCP.
Trimmed the model pickers (Claude Fable 5 is de-listed — unavailable under the US export ban — and a few older variants drop off), and editor clients (VS Code, Zed) now see API retry/backoff instead of an endless "Thinking…" spinner.
- Model picker cleanup across every provider. Claude Fable 5 is removed
from the Anthropic picker (unavailable under the US export ban; Opus 4.8 stays
the default). Z.AI drops
glm-5.1andglm-5(keepsglm-5.2+glm-5-turbo); OpenAI dropsgpt-5.4-nano(keeps 5.5 / 5.4 / 5.4-mini). DeepSeek, Google, and MiniMax are unchanged. All ids remain valid if set by hand — they're just no longer offered. Context/cost tables updated to match.
- ACP retry visibility. When a request hit a transient API error and the agent retried with backoff, the ACP path dropped the notice (only the bare iteration counter was suppressed, but the retry message went with it) — so editor clients showed an indefinite "Thinking…" while the CLI was actually retrying. Retry/backoff notices ("API 429 … retrying in 10s (1/3)") and context warnings (⚠) are now forwarded as agent thoughts; the plain iteration counter stays internal.
Hotfix: the Z.AI default was
glm-5.2[1m], but the API rejects that id ("Unknown Model", code 1211) — so a fresh Z.AI session failed on its first request. The default is now plainglm-5.2(which works), and the non-working[1m]variant is removed from the picker.
- Z.AI default model
glm-5.2[1m]returned "Unknown Model". The 1M-context[1m]suffix from the devpack docs isn't accepted by the Z.AI chat API endpoints Codeep uses, so it 400'd on every request. The default (and cold-start default) is nowglm-5.2, andglm-5.2[1m]is dropped from all four Z.AI providers' model lists. If your config still points atglm-5.2[1m], switch with/model glm-5.2.
New default model GLM-5.2 (1M-context
glm-5.2[1m]) across every Z.AI provider, plus TUI polish: ↑ recalls history, diffs render green/red, full/autocomplete, and/settingsvalues now stick.
- GLM-5.2 — the new default Z.AI model. Added across all four Z.AI
providers (international + China, subscription + pay-per-use):
glm-5.2[1m](1M context — the[1m]suffix selects the million-token window) is now the default, with plainglm-5.2also offered; GLM-5.1, GLM-5 Turbo, and GLM-5 stay available. Context-window and cost tables include both new ids. (GLM-5.2 per-token pricing isn't published yet, so/costmirrors GLM-5.1 for now — and on the GLM Coding Plan billing is a flat subscription anyway.) Editor clients pick this up automatically over ACP.
/settingsvalues stick now. A block of startup "migrations" ran on every launch and silently forced user-chosen values back up —maxTokensbelow 32768,agentMaxDurationbelow 480 min, API timeout and rate limits — so the affected settings were effectively lies. They now run exactly once per config (recorded viamigrationVersion); after that, what you set is what you get.↑recalls prompt history on an empty input. The status bar has always advertised "↑↓ history", but a scroll handler intercepted the arrows first, so history recall was unreachable. Arrows now do history (like every shell); scrolling lives on PgUp/PgDn and the mouse wheel.- New messages no longer yank you to the bottom. While you're scrolled up reading, incoming messages (every agent action, mid-run) used to reset the view to the bottom. The view now stays put and the status bar shows a "↓ N new · PgDn" badge until you return.
- Diff blocks render as diffs. ```diff fences — which the agent emits on every edit confirmation — now highlight +added lines green, -removed red, and @@hunks cyan. Previously they fell through to JS keyword colors.
- Every command in
/autocomplete has a description. 48 of 123 rows were blank (the whole scaffold/git/devops family — /component, /pr, /docker, …) and 9 bare single-letter aliases cluttered the list. The dropdown is now derived from a single command registry, so a command can't ship without a description again; the single-letter shortcuts (/c,/t, …) still work, they're just not listed. - ~800 lines of dead UI code removed (an unused parallel chat renderer and two unreachable fullscreen screens) — no behavior change, but edits can no longer land in the wrong renderer by mistake.
- ACP
session/newnow returns the prior transcript on resume. When an editor client reconnects withfresh: false(e.g. a VS Code window reload), the response carries the workspace session'shistory(user/assistant only, mirroringsession/load) so the client can repaint the chat instead of showing blank while the agent still holds the context. Empty on a fresh session; older clients ignore the extra field. Powers Codeep VS Code 2.6.0's reload restore.
/tasks addnow matches the dashboard: tag a task as a bug or feature and give it a description inline (--bug/--feature/--desc), and the list tags each row with its project when global.
- Task types in
/tasks add. Append--bugor--feature(or--task, the default) to file the task under the right type on the codeep.dev dashboard — e.g./tasks add login button misaligned --bug. The flag can sit anywhere in the arguments and is stripped from the title; the dashboard and the macOS app already render the type with its own icon and color, so this brings all three surfaces to parity (the dashboard and macOS both let you pick a type; the CLI previously hardcodedtask). - Task descriptions in
/tasks add.--desc(or--description) captures the following words — up to the next flag — as the task's description, e.g./tasks add Fix login --bug --desc NPE when the email is empty. It's the same field the dashboard and macOS app set; the/taskslist already prints it and it's injected into the agent's task-context prompt, so a CLI-set description immediately enriches what the agent sees. Omitted from the request when absent.
/taskslist tags each row with its project when listed globally. Running/tasksoutside a project lists pending tasks across all projects; each row now shows its project name (matching the macOS and dashboard task rows) so a mixed list is legible. Inside a project the header already names it, so rows stay uncluttered./tasksautocomplete description now reflects the full command — it covered only "show pending tasks" and hid theadd/done/deletesubcommands and the type flags from/autocomplete.
/statsnow shows the prompt-caching summary, and a dead duplicate cost case is gone. The session-cost view had two switch branches sharing acase 'cost':/costalways rendered the fullformatCostReport()(the cross-surface report the editor clients use, with the prompt-caching section), while the second branch — the detailed/statsview — was unreachable for/costyet was the only one missing that caching section./statsnow reports cache reads/writes and estimated savings too (parity with/costand the 2.0.2 caching work), and the deadcostlabel was removed so the dispatch is honest. What/costdisplays is unchanged./keysyncnow appears in/autocomplete. The command shipped in 2.8.0 with a description and an ACP entry, but was missing from the TUI command list, so terminal users never saw it offered. (It always worked when typed.)
Claude Fable 5 — Anthropic's most powerful model, a new tier above Opus — is now in the model picker ($10/$50 per MTok, 1M context). Opus 4.7 and 4.6 leave the picker (Opus 4.8 stays the default). Plus a real compatibility fix: temperature is no longer sent to models that reject it (Fable 5 / Opus 4.7+), which previously surfaced as an opaque 400.
- Claude Fable 5 (
claude-fable-5) in the Anthropic provider — the most powerful Claude model, a new tier above Opus. $10 input / $50 output per MTok, 1M context window. Pick it with/model claude-fable-5.
- Opus 4.7 and 4.6 removed from the model picker now that Opus 4.8 and Fable 5 cover both tiers. The ids remain valid — if your config still points at one, it keeps working; it just isn't offered for new selection.
temperatureis no longer sent to Anthropic models that reject it. Fable 5 and Opus 4.7+ return HTTP 400 when the request includestemperature— and that 400 was previously masked by the tools-fallback retry, surfacing as a generic "API error". A model-aware guard (modelRejectsSamplingParams) now omits the parameter on those models across all three Anthropic request paths (agent, fallback, plain chat); omission means the API default, so behavior on other models is unchanged.
API keys are now keychain-first and stay local by default — syncing them to codeep.dev is an explicit opt-in (
/keysync on), andcodeep account purge-keyswipes any keys already on the server.
/keysync on|off|status— opt in (or out) of syncing API keys to codeep.dev. OFF by default: your keys live only in the OS keychain unless you enable this. When on,codeep account push/syncupload/download keys; the command warns that synced keys are stored server-readable. Also available in/settings, and forced off by theCODEEP_NO_KEY_SYNCenv var (org policy).codeep account purge-keys— delete every API key stored on codeep.dev in one shot (cloud-only; your local OS keychain is untouched). A clean exit if you synced keys before and want them off the server.
codeep account push/account syncno longer move API keys unless cloud key sync is enabled (/keysync on). They still push/pull personalities, custom commands, and your profile as before — only the secret half is gated. Existing users who relied on key sync just run/keysync ononce.
A batch of review tooling: YAML review config, a
codeep hook installpre-commit reviewer,codeep review --rulesto list rule ids, and an opt-incodeep review --aisecond opinion. Plus fixes: compiled binaries report the real version (no more "vunknown"), ACP editor sessions no longer mutate the global confirmation setting, and keychain-fallback keys get swept into the keychain once it's available.
- YAML review config.
.codeep/review.yml/.codeep/review.yamlare now supported alongside.codeep/review.json(YAML preferred when present). Single-quoted YAML keeps regex backslashes literal (pattern: '\bfoo\('), avoiding JSON's double-escaping. Same schema; format is auto-detected. codeep hook install— installs a git pre-commit (or--pre-push) hook that runscodeep review --fail-on <level>on your changes, blocking the commit when issues at/above the threshold are found (honors.codeep/review.*, no API key).codeep hook uninstallremoves it; Codeep never overwrites a hook it didn't create.codeep review --rules— lists the built-in rule ids (the values you can put indisablein.codeep/review.*) and exits.codeep review --ai— opt-in: after the offline pass, asks your configured provider for a contextual second opinion, merged into the report as a clearly tagged advisory section. Needs an API key (degrades to deterministic-only without one) and never affects the exit code — the deterministic review stays authoritative, so CI (the GitHub Action) is unchanged.
-
Keychain fallback sweep. If the OS keychain was unavailable on a prior run, API keys fell back to plaintext config. They're now swept into the keychain automatically once it becomes available (completes the 2.5.2 key-storage work).
-
Compiled binary version. The standalone binaries printed "Codeep vunknown" because they read the version from
package.json, which isn't on disk in a compiled binary. The version is now baked in at build time, so--versionis correct everywhere (npm, Homebrew, and the standalone binaries). -
ACP confirmation setting no longer leaks/races. Manual-mode editor sessions used to flip the global
agentConfirmWriteFileconfig and restore it non-atomically around each prompt — which could leak the session's mode into the terminal app and race when prompts overlapped. Write/edit confirmation is now scoped to the run via a per-call option, with no global config mutation.
New: configurable code-review rules. Drop a
.codeep/review.jsoninto a repo to add your own deterministic review rules, disable built-in ones, and scope which files are reviewed — enforced the same way bycodeep review(CLI) and the Codeep GitHub Action, with zero LLM cost.
.codeep/review.json— review rules as config. The deterministic reviewer (codeep review,/review --static, and the GitHub Action) now reads a per-project config:rules— your own checks:id,pattern(regex),message(required) plus optionalflags,category,severity,suggestion,extensions.disable— turn off built-in rules by id (each built-in now has a stable id, e.g.eval-usage,todo-comment,any-type,long-file).include/exclude— glob scoping (**,*,?). A missing, malformed, or partially-invalid config never breaks a review — bad entries are skipped with a warning and valid ones still apply.
- Hardened the reviewer against untrusted custom rules. Since a PR's
.codeep/review.jsonruns in CI via the Action, custom regexes are screened at load (length cap + a catastrophic-backtracking/ReDoS heuristic), the match loop guards zero-width patterns (no infinite loop) and caps matches per rule, and the GitHub Action bounds each review's wall-clock at 180s.
Security: provider API keys are now stored in your OS keychain instead of plaintext in the config file, and there's a first-class telemetry opt-out (
CODEEP_NO_TELEMETRY/DO_NOT_TRACK/telemetry: false). Existing plaintext keys migrate to the keychain automatically on first run.
- API keys moved to the OS keychain. Keys were written in plaintext to
~/.codeep/config.json. They now persist in the system keychain (macOS Keychain / Linux Secret Service / Windows Credential Vault) via the secure storage layer; a synchronous in-memory cache keeps key lookups fast. On first run, any existing plaintext keys (and the legacy single-key field) are migrated into the keychain and the plaintext is wiped — a key is only removed from plaintext after its keychain write is confirmed, so an interrupted migration never loses a key (it retries next start). When no keychain is available (e.g. headless Linux without libsecret) Codeep falls back to config storage and warns. - Telemetry opt-out. Once linked to codeep.dev, Codeep uploads usage stats,
session transcripts,
progress.md, and project memory notes to power the dashboard. SetCODEEP_NO_TELEMETRY=1(or the cross-toolDO_NOT_TRACK=1, or"telemetry": falsein config) to disable all automatic uploads. Explicitcodeep account push/account syncare user-initiated and never gated. /telemetrycommand. New slash command (TUI + ACP) to show telemetry status and toggle it:/telemetry,/telemetry on,/telemetry off. It reports when an env var is forcing it off (the config flag can't override env).- Confirmation gate fails closed. The agent's permission gate now allows a
dangerous tool only on an explicit allow outcome — a malformed/unknown
permission response from an editor client now denies instead of letting the
tool run. The ACP mode switch no longer writes the global
agentConfirmationsetting, so switching an editor session to auto-approve can't silently disarm the confirmation gate in your terminal sessions.
/telemetry— show or toggle automatic cloud telemetry from the CLI or any ACP editor.
- The keychain migration is one-way: after upgrading, plaintext keys are
removed from the config file. If you downgrade to an older Codeep that doesn't
read the keychain, re-enter your keys or run
codeep account sync. Your keys remain in the keychain and are picked up again when you re-upgrade.
Fix: chat could crash with "Cannot read properties of undefined (reading 'indentation')" when a project's
.codeep/intelligence.jsonwas missing sections (from an interrupted or older scan). The file is now backfilled on load, so partial intelligence can never crash a prompt — most visible in the VS Code/Zed (ACP) chat.
- Partial
.codeep/intelligence.jsoncrash — a project intelligence file that was missing whole sections (e.g.conventions) — written by an interrupted scan or an older CLI — causedgenerateContextFromIntelligenceto throwCannot read properties of undefined (reading 'indentation')on the next prompt. This surfaced in editor clients (VS Code / Zed via ACP) as a chat error on every message.loadProjectIntelligencenow merges loaded data over a complete default skeleton (per-section), and the context formatter defensively normalizes its input, so a partial or older-schema file is backfilled instead of dereferenced blindly. No re-scan required.
New:
codeep review(offline, CI-friendly code review) and Continue (a paused-at-the-limit run resumes when you say "continue" instead of dead-ending). Plus a fix where file edits or skill params containing a$could be written corrupted.
codeep review— a headless, deterministic code review you can drop into CI (no API key, no TUI). Reviews the files you pass (or your unstaged git changes, falling back to asrc/scan), prints a markdown report or--json, and exits non-zero when an issue at or above--fail-on <error|warning|info|none>is found (defaulterror). Pairs with a GitHub Action to gate PRs.- Continue after a safety limit. When the agent reaches its step or time
limit it no longer dead-ends — the run pauses with a clear, resumable notice
(
⏸ Paused … say **continue** to pick up where it left off) instead of looking like a failure, and saying continue resumes it with full context. Works in the TUI and in ACP clients (Zed, the VS Code extension).
- Edits containing
$are written literally.edit_file(and the diff preview) applied replacements withString.replace(text, newText), which interprets$&,$$,$1etc. in the replacement — so any edit whose new text contained$(template literals, shell variables, regex) was silently corrupted on write. Replacements are now inserted verbatim. - Skill parameter expansion is
$-safe and literal.${param}substitution had the same$-interpretation bug in the value, and interpolated the param name into a regex unescaped (so a.over-matched and a(could throw). Both are fixed.
Stability: an unexpected error no longer crashes Codeep to a garbled terminal — it's logged, your conversation is saved, and recoverable background errors keep the session alive. Also fixes
codeep accountoccasionally linking without storing the sync token.
- Crash resilience — added global
uncaughtException/unhandledRejectionhandlers. A stray throw deep in the agent loop used to kill the process with the terminal still in raw mode + alternate screen (leaving your shell garbled), or vanish silently. Now an uncaught exception restores the terminal, logs the cause, and best-effort saves the conversation before exiting; an unhandled promise rejection (e.g. a failed background sync) surfaces as a warning and keeps the TUI running instead of tearing it down. codeep accountsync token — account linking now waits for the sync token before completing. The server could briefly report the login authorized before the token was issued, so the CLI linked the account but stored no token, leavingcodeep account syncfailing with "Not linked to codeep.dev". Pairs with the matching codeep.dev fix.
MiniMax M3: the new MiniMax flagship replaces M2.7 across all three MiniMax providers (subscription, pay-per-use, China), with updated pricing and context window so cost tracking stays accurate.
- MiniMax M3 (
MiniMax-M3) replacesMiniMax-M2.7as the model + default for theminimax,minimax-api, andminimax-cnproviders. Pricing updated to the standard rate $0.60 / $2.40 per 1M tokens (input / output) and context window to 512K, so/costand the dashboard bill it correctly. The native macOS / iOS apps get the same update via the shared CodeepCore catalog. - README provider list is now generic (model families, not pinned versions) — it no longer needs editing every time a provider ships a new model.
New models (Claude Opus 4.8, Gemini 3.5 Flash) plus a better local-model experience: browse a curated catalog of coding models, remove models, and see on-disk sizes — all from
/model.
- Claude Opus 4.8 — added to the Anthropic provider and set as the new default model. Pricing: $5 / $25 per 1M tokens (input / output), 1M context.
- Gemini 3.5 Flash (
gemini-3.5-flash) — replaces the preview Flash in the Google provider list. Pricing: $1.50 / $9.00 per 1M tokens, 1M context. /model browse(Ollama) — a curated catalog of recommended local coding models (Qwen2.5 Coder, DeepSeek Coder V2, Llama 3.1, DeepSeek R1, …) with parameter sizes, rough VRAM, and an agent-mode suitability hint. Pick one to pull it. Mirrors the MCP/skills catalog pattern./model rm <name>(Ollama) — remove a locally-installed model to reclaim disk, without leaving Codeep. Remote-server guard like/model pull.- On-disk size in
/modelpicker — the Ollama model list now shows each model's size on disk alongside the agent-mode hint. - Native Ollama API (beta, opt-in) — set Ollama Native API (beta) → On
in
/settingsto route Ollama through its native/api/chatendpoint instead of the OpenAI-compatible/v1shim. Honorsnum_ctx(the model uses its full context window instead of Ollama's small default) andkeep_alive(keeps the model resident, avoiding reload latency every turn). Tunable viaollamaKeepAlive(default30m) andollamaNumCtx(0= auto-detect via/api/show). Off by default — existing transport unchanged unless you opt in. Verified against Ollama 0.24 (chat, streaming, usage, native tool calls); marked beta while it gets coverage across more models and longer sessions. Please report issues at https://github.com/VladoIvankovic/Codeep/issues — feedback decides when it becomes the default.
- Pricing tables (
/cost, dashboard) updated so the new models bill at the right rates. Previous models (Opus 4.7 / 4.6, Flash preview) stay listed for back-compat. VS Code/Zed inherit the new catalog automatically over ACP; the native macOS / iOS apps get the same update via the shared CodeepCore catalog. /model browseand/model rmshell out to the localollamabinary, so they only run when Ollama is local (remote servers get an SSH hint instead).
Profile sync everywhere:
codeep account sync/pushnow carry your user profile too, and a new/me syncpushes it from any surface.
codeep account sync/account pushnow include your user profile. They synced keys + personalities + commands but skipped~/.codeep/profile.md; now the profile rides along (additive pull — never clobbers a local profile).
/me sync— push your profile to the dashboard (and additive-pull) right from the profile command, reachable on every surface (TUI, ACP/Zed, and the VS Code chat).
Codeep gets personal and gains a team: a user profile (
/me) makes it adapt to you across every surface, and multi-agent delegation lets it hand self-contained sub-tasks to specialist sub-agents that run in their own context.
- User profile (
/me). A durable, human-readable description of you, injected into the agent's system prompt on every run so it adapts to how you work without you repeating yourself. Two scopes: global~/.codeep/profile.md(reply language, response style, default stack, universal "always / never") and project.codeep/profile.md(your role, goals, constraints for this repo). Manage with/me,/me init [project], and/me on//me off. Flows to every surface because they share the same files. - Opt-in profile auto-learn.
/me learn onlets Codeep quietly extract your durable preferences from sessions — one cheap, throttled LLM pass at session save — and merge them into a separateprofile.learned.md(global + project), kept apart from your hand-written file so it's never clobbered./me learnruns it once on demand,/me learn projectscopes to this repo,/me forgetclears it. Off by default; gated byautoLearnProfile. - Profile sync.
codeep account syncpushes your globalprofile.mdto the codeep.dev dashboard (where it's editable) and pulls it to new machines. Pull is additive — a web edit never overwrites an existing local profile. /mein ACP. Zed, VS Code, and any ACP client can view and manage the profile, not just the terminal.
- Sub-agents + the
delegatetool. The agent can delegate a self-contained sub-task to a specialist that runs in its OWN fresh context window and returns only a summary — so the main context stays small and each sub-task runs with a tuned persona and a scoped toolset. Four built-ins:planner(read-only planning),researcher(read-only explorer),reviewer(read-only senior review),tester(writes + runs tests). Run/agentsto list them. - Custom sub-agents. Define your own with a frontmatter
.mdin.codeep/agents/<name>.md(project) or~/.codeep/agents/(global): name, description, atoolsallowlist, optionalmodeloverride,personalitypreset, andmaxIterationsbudget. Mirrors the personalities/skills pattern. - Auto-review pipeline. Enable Agent Auto-Review (
agentAutoReview, off by default) and after any run that changes files, Codeep automatically delegates to thereviewerand appends its findings — a review stage that always happens, without relying on the model to self-delegate one. /agentssurfaced in the TUI and ACP (Zed / VS Code).
- Profile is local-first and opt-in: injection is gated by
userProfile(default on), auto-learn byautoLearnProfile(default off). Nothing reaches the dashboard unless you runcodeep account sync. - Sub-agent tool scoping is enforced at dispatch — a
researchercan't write files even if it tries. Sub-agents inherit your profile, project rules, and permission prompts, and their file changes are covered by/undo(they record into the parent's session). Delegation depth is capped at 1; model overrides are sequential-safe.
Long agent runs no longer silently forget how they started — when prior chat history overflows the context budget, the dropped older messages are summarized instead of just truncated. Plus a command-whitelist hardening.
- Inline code execution is blocked in agent mode. The command whitelist
allowed interpreters like
node/python/php, but their eval flags (node -e,python -c,php -r,deno eval, …) turned a whitelisted runtime into arbitrary code execution. Those flags are now rejected (including combined short clusters like-pe). Running a file (node app.js,python script.py) is unaffected. Defense-in-depth — the manual-mode permission prompt is still the primary gate.
- Auto-summarized history. When the prior conversation exceeds the agent's
context budget, Codeep now condenses the dropped (oldest) messages into a
short recap — preserving early decisions, constraints, and unfinished threads
— and injects it before the recent verbatim history. Previously those older
messages were silently truncated. The recap is one cheap LLM call, made only
on overflow and cached per session. Opt out with
autoSummarizeHistory: false(falls back to plain truncation, no extra call).
Security hardening: project hooks now require trust before they run, the web-fetch tool blocks internal/metadata addresses, and usage stats are sent with your sync token.
- Hooks now require trust-on-first-use. Project-local
.codeep/hooks/*run arbitrary shell, so a freshly-cloned repo could previously execute its scripts on your first tool call. Hooks in an unapproved workspace are now skipped until you run/hooks trust(revoke with/hooks untrust)./hooksand the welcome banner show the trust state. Your own already-set-up projects just need a one-time/hooks trust. - SSRF guard on the
fetch_urlweb tool. The agent can no longer be steered (e.g. via prompt injection) into fetchinglocalhost, private/RFC1918, or link-local addresses — including the cloud metadata endpoint169.254.169.254. Onlyhttp/httpsare allowed, on the initial request and redirects. Your configured provider endpoints (Ollama, custom vLLM/Tailscale) are unaffected — they don't go through this tool.
- Stats reporting now sends the
x-sync-tokenheader. The dashboard derives your GitHub id from the token instead of trusting thegithubIdin the request body, closing a spoofing gap where anyone could forge usage events (or unarchive projects) for another user. Stats keep working on older CLIs — they're just recorded anonymously until you upgrade. No behavior change for you locally.
ACP server enhancements that power the new Codeep VS Code 2.2 features — editor clients can now list models per provider and pin a provider, model, or custom endpoint over the protocol.
session/list_providersnow returns model metadata — each provider carries itsmodels(id + name),defaultModel, and adynamicModelsflag. Lets ACP clients (the VS Code model picker, Zed) build a provider → model selector without hardcoding a catalog. Backward-compatible: older clients ignore the extra fields.- New
session/set_config_optionids:providerandcustomBaseUrl.providerswitches the active provider (and picks its default model + protocol);customBaseUrlsets the base URL for thecustom(OpenAI-compatible) provider. These let editor settings drive provider / model / endpoint without hand-editing~/.codeep/config.json.
- Pure additive ACP surface — no behavior change for the TUI or existing clients. The Codeep VS Code extension 2.2.0 builds on these.
Codeep now works with any OpenAI-compatible endpoint — vLLM, LiteLLM, LM Studio, text-generation-webui. New "Custom (OpenAI-compatible)" provider with a configurable base URL, plus support for the standard OPENAI_BASE_URL env var. Fixes #1.
- Custom (OpenAI-compatible) provider. Point Codeep at any self-hosted
or proxied OpenAI-compatible server (vLLM, LiteLLM, LM Studio,
text-generation-webui). Pick Custom (OpenAI-compatible) in the welcome
flow or
/provider, set the endpoint under/settings→ Custom Base URL (config keycustomBaseUrl, e.g.http://host:8000/v1), then choose your model with/model(fetched live from the server's/modelsendpoint). No API key required; set one only if your endpoint enforces it. OPENAI_BASE_URLenv var. Theopenaiprovider now honorsOPENAI_BASE_URL(OpenAI-SDK convention), so an OpenAI-compatible proxy servinggpt-*model names works with zero config changes.
- Custom base URLs were silently ignored for every provider except Ollama —
requests always went to
api.openai.com, and an unknown model fell back to the default. Base-URL resolution is now centralized (resolveBaseUrl) and applied consistently across every path — chat, agent (TUI + ACP/editor),/plantask planning, and API-key validation. (#1) - Welcome flow no longer forces an API-key prompt for keyless providers (Ollama, Custom) — selecting one proceeds straight into the app.
- Test isolation:
customCommandstests now run against an isolated HOME so a developer's global~/.codeep/commandscan't make the suite non-deterministic.
Session memory:
/recall <query>searches across all your saved sessions,--resumejumps straight back into the best match,--summarizeasks the LLM what you accomplished, and sessions now get readable AI-generated titles instead of truncated first messages.
/recall <query>scans every saved session in the active scope (project.codeep/sessions/when in a project, else global~/.codeep/sessions/), matches with AND semantics (every query term must appear), and ranks results by term-hit count plus a recency boost. Each result shows a context snippet and the session name./recall <query> --resumeloads the top-matching session directly into the current conversation — skips the list +/sessionspicker dance. (TUI only; ACP shows results since it can't swap the client's conversation in place.)/recall <query> --summarizereads the matching sessions and returns a short LLM recap of what you actually accomplished across them — "ask your history a question". Works in TUI + ACP.- No new dependency: in-memory JSON scan, fast for the realistic tens-to-hundreds-of-sessions case.
- Personalities and custom commands now sync across your machines
via
codeep account sync(pull) andcodeep account push. Global ones (~/.codeep/personalities/*.md,~/.codeep/commands/*.md) travel with your account alongside API keys and profiles — set up asenior-reviewerpersonality or a/deploycommand once, get it everywhere. New endpoints/api/personalities+/api/commands, new DB tablesuser_personalities+user_commands. - Additive merge, never destructive: pull only writes files that don't already exist locally, so a sync can't clobber edits you haven't pushed. Last-write-wins on the server via upsert.
- Dashboard sections to view + delete synced personalities and commands at codeep.dev/dashboard (read + prune; editing stays in the CLI).
- Deliberately not synced: lifecycle hooks (arbitrary shell — syncing + auto-running on another machine is a security risk) and MCP server configs (contain tokens). Those stay local by design.
- Sessions now get a concise LLM-generated title ("OAuth2 migration
for auth module") instead of the first user message truncated to 60
chars ("help me with the…"). Generated once per session in the
background after it has ≥3 messages — fire-and-forget on autosave,
never blocks a save, never regenerates once set. Makes both
/sessionsand/recalldramatically more readable. - Title priority: AI title > stored title > first-message fallback >
session name. Stored under
aiTitlein the session JSON. - Opt-out:
autoSessionTitlesetting (default on). This is the only feature that makes a background API call you didn't explicitly request, so it's toggleable in/settingsfor privacy/cost-conscious users. Off → sessions keep the first-message title, zero background calls.
/searchdescription clarified to "search the current session" (vs/recallfor cross-session) — the two were easy to confuse when both said "search history".
/sessionspicker showed raw session ids (session-2026-05-20-757cbda5) instead of readable titles. Now shows the title (AI-generated > storedfirst-message) with a short date + message count, so the list is scannable.
- Models hallucinating their identity in chat mode. Asked "which
model are you", GLM (and others) would claim to be Claude because the
chat system prompt never stated the actual identity. Both the chat
and agent system prompts now inject the real
model+providerfrom config, so the answer is truthful. (Agent mode already said "never call yourself Claude" but didn't state the real model; now it does.)
Discoverability patch: new
/docs <command>jumps from any slash command to its full guide on codeep.dev, the/helpfooter now points at the same place, and/personalityand/insightshave proper docs pages instead of one-liners.
/docs <command>— opens the per-command guide for any 2.0 feature in your default browser. Knows 17 commands directly (personality,insights,plan,go,mcp,skills,checkpoint,rewind,hooks,commands,openrouter,memory,profile,compact,cost, …); falls back to a marketplace search on/docs/commands?q=<cmd>for unknown ones. Plain/docsopens the docs index. Closes the gap between brief slash-command autocomplete and the actual reference material./helpfooter hint. Below the scroll line:Full guides → codeep.dev/docs · /docs <command>. Users skimming the inline help now know there's a deeper layer one keystroke away.
/personalityguide went from a 2-row table to a full reference: 3-column "when to use / what it changes" table for all 6 presets, basic-flow terminal demo, end-to-end "combo with plan mode" example (security-paranoid OAuth callback), custom personalities section with full Acme Corp template, scope override rules, where-it-works matrix, and a warning callout about chat() path behaviour./insightsguide got similar treatment: source-of-truth JSON shape from~/.codeep/history/<id>.json, what-you-see breakdown per section, flags table, realistic terminal demo, and a tip pointing at the dashboard for historical cost.
- No agent behaviour or API changes — this is purely discoverability and documentation. Safe to skip if you already know the surface area, worthwhile if you've been wondering "what else is in here".
Two Hermes-inspired additions:
/personality <name>switches agent tone mid-conversation (concise, security-paranoid, senior-reviewer, junior-mentor, ship-it, verbose, or your own from.codeep/personalities/*.md), and/insights [--days N]summarises what you've been working on — runs, files, tools, projects.
- Six built-in personalities that swap the agent's tone and
priorities by appending a system-prompt addendum:
concise— no preamble, no filler, bullet-heavyverbose— explains rationale + alternatives + caveatssecurity— treats every input as hostile, enumerates attack surfacesenior-reviewer— pushes back on shortcuts, names things welljunior-mentor— explains as it goes, links to canonical docsship-it— picks first reasonable approach, defers cleanup
- Custom personalities via
.codeep/personalities/<name>.md(project) or~/.codeep/personalities/<name>.md(global). First# Personality: Nameline becomes the display name; rest of the Markdown body is the prompt addendum. Capped at 64 KB per file. - Persistence: active personality lives in
config.activePersonalityso it survives session restarts. Clear with/personality off. - Usable from CLI TUI, Zed, and the VS Code extension via ACP.
- Activity summary over a configurable window (default 7 days,
capped at 365). Reads
~/.codeep/history/<id>.jsonfiles written by every agent run, so output reflects actual tool actions rather than chat-message proxies. - Headline metrics: total runs, total tool actions, total active time, active-days density, average actions per run.
- By-project breakdown sorted by active time — see which repo soaked up your week.
- Top tools (read_file × 340, write_file × 80, …) and
most-touched files (with
~prefix for readability). - Recent runs list — 10 most recent with project, duration, and the user prompt that started them.
- Per-session cost still lives in
/cost;/insightsis a deliberately history-only view (the in-memory token tracker doesn't survive a restart, so historical cost would be misleading).
- Both commands appear in
/help,/autocomplete,Codeep-web/docs/commands, VS Code Settings → Commands chips, and ACPavailableCommands. Spot-check parity: typing/peror/insiin any client autocompletes to the right command.
Two big quality-of-life additions: Anthropic prompt caching is on by default (60–90% cheaper on cache-eligible input), and
/planlets you preview an agent's full plan before any file gets touched. Run/goto execute, or/plan <revised task>to refine.
- Two cache breakpoints per request: the system prompt (and embedded
skills catalog / project intelligence) and the tools array. Cache hits
bill at 0.1× the input rate; cache writes at 1.25×. Net win after the
second same-shape request, which is every iteration in an agent loop.
Below 1024 input tokens Anthropic silently skips caching — no error
path. Applies to the agent chat path, the agent fallback path, and
the chat() path used by
/agentand inline replies. Also propagates through OpenRouter → Anthropic routes (caching headers honoured upstream). TokenUsage.cacheCreationTokens+cacheReadTokensfields surfaced on every record.getCacheStats()aggregates per-session cache hits, misses, and estimated USD savings vs running without caching./cost(and/stats) renders a new "Prompt caching" section when at least one cached call landed.
/plan <task>— generates a numbered plan for the task (no tool calls, no file changes), surfaces it as a Markdown message so you can review what the agent would do, which files it would touch, what commands it would run, and the risk level it self-assesses. Holds the (task, plan) pair as the pending plan, scoped to the current process. Re-running/plan <revised task>replaces the pending plan with a new one (you pay one extra LLM call but get readable revision history in the chat)./go— executes the pending plan: hands the task + approved plan as a single prompt to the regular agent loop, so all MCP tools, lifecycle hooks, verification, permissions, and skill bundles apply unchanged. Includes an explicit anti-improvisation clause in the injected prompt — if any step turns out to be wrong mid-execution the agent must stop and report rather than silently rewriting the plan.- Available in both the TUI and ACP clients (Zed, VS Code). ACP
/planstreams the plan back viasession/update; ACP/goruns the agent inline and streams iterations through onChunk. - Surfaced in
/help("Agent Mode" section) and/autocomplete.
- Anthropic streaming usage extraction missed cache fields. Both
the agent stream handler (
utils/agentStream.ts) and the chat stream handler (api/index.ts) now pick upcache_creation_input_tokensandcache_read_input_tokensfrom themessage_startevent, so cached requests no longer undercount prompt tokens or display $0 savings.
- OpenAI-format providers (OpenAI direct, Z.AI, DeepSeek, MiniMax,
Ollama) don't expose explicit cache markers — those providers
generally apply automatic prefix caching server-side. No code change
on our end needed; cost reports stay accurate via standard
prompt_tokensaccounting.
Patch:
/mcpnow works in the CLI TUI (was only wired into the ACP path in 2.0.0, so Zed and VS Code worked butcodeepdirect didn't). Full subcommand parity — browse, install, add, remove, reload, resources, read, prompts, prompt.
/mcpslash command in CLI TUI returnedUnknown command: /mcp. The 2.0.0 implementation lived only insrc/acp/commands.ts, so it worked for ACP clients (Zed, VS Code extension) but TUI users hit the unknown-command path. Ported the full handler tosrc/renderer/commands.tswith TUI-appropriate output (uses the fixedcodeep-tuisession id andctx.projectPathas workspace root). Subcommands now usable from the TUI:/mcp,/mcp browse [id],/mcp install <id> [args...],/mcp add <name> <command> [args...],/mcp remove <name>,/mcp reload,/mcp resources,/mcp read <uri>,/mcp prompts,/mcp prompt <server> <name> [k=v].- Eight 2.0 commands missing from
/autocomplete and/help—/mcp,/compact,/checkpoint,/checkpoints,/rewind,/hooks,/openrouter,/commandswere all implemented but invisible to discovery. Added toApp.COMMANDS+COMMAND_DESCRIPTIONSso they appear when the user types/, and added two new/helpsections ("Checkpoints (2.0)", "Extensions & MCP (2.0)") plus/compactunder Sessions and/openrouterunder Settings. /skills publishrejected global bundles. The helper requiredbundle.scope === 'project', blocking a common case: user writes a cross-project skill once in~/.codeep/skills/<name>/and tries to share it. The--publicflag is the user's explicit consent gate, so an extra scope check is redundant gatekeeping. Now publishes project OR global bundles; project wins on slug collision (mirrorsloadSkillBundles). Error message also clarifies both lookup paths when the slug isn't found anywhere.
Codeep 2.0 is here. Full MCP support (stdio + HTTP), skill bundles with a public marketplace, OpenRouter with accurate per-call cost, checkpoints, custom commands, lifecycle hooks. 921 tests green.
Big release. Major version bump because the on-disk mcp_servers.json
shape now accepts url (HTTP transport) alongside command (stdio),
because the agent now actively reads from MCP servers' resources,
prompts, and (optionally) hosts sampling for them — clients that
relied on Codeep behaving as a tools-only client will see new traffic
— and because skill bundles are a new top-level concept the agent
auto-discovers and invokes.
openrouterprovider wired through the existing OpenAI-compatible flow. Top 12 popular models hardcoded for the picker; the full catalogue (100+) is fetched on demand via/model, with live pricing per 1M tokens and context-window size shown per row.- Authoritative cost from
usage.cost. OpenRouter returns the per-call USD figure in its response — we use that instead of our local pricing table, so your dashboard //costnumbers match the OpenRouter invoice exactly with zero local maintenance. - Branding headers (
HTTP-Referer: https://codeep.dev,X-Title: Codeep) sent on every OpenRouter request — surfaces Codeep traffic in their dashboard for attribution. /openrouterslash command for routing preferences:prefer <p1>,<p2>(provider order),ignore <p1>(block list),fallbacks on|off,privacy strict|allow(setsdata_collection),clear. Stored per-machine in conf.openrouter/autosupport — set the model id toopenrouter/autoand OpenRouter picks the best upstream for each task. Combine with/openrouter preferto bias the auto-router without locking it down.
- Structured skill bundles under
.codeep/skills/<name>/SKILL.md(project) and~/.codeep/skills/<name>/SKILL.md(global). The SKILL.md format is a superset of Claude Code skills — paste an existing skill verbatim and it works. Codeep-specific extensions (codeep-min-version,codeep-requires-mcp) are valid YAML, so Claude Code parsers tolerate them. - Agent auto-discovery. Every agent run injects the bundle catalog
into the system prompt and registers a virtual
invoke_skilltool. The model picks a skill when the user's intent matches; we return the SKILL.md body for it to follow step by step. - Slash commands for managing bundles:
/skills bundles— list installed/skills create-bundle <name>— scaffold a project skill/skills show <name>— print the SKILL.md/skills browse [query]— search the public marketplace/skills install <owner>/<slug>— pull from marketplace/skills publish <slug> [--public]— share to codeep.dev/skills unpublish <owner>/<slug>— remove your published skill
- Public marketplace at codeep.dev/skills.
Owners manage their published skills at
/dashboard/skills— toggle visibility, unpublish, see install counts. - VS Code commands for the bundle workflow:
Codeep: Browse Skill Bundles…,Codeep: Create Skill Bundle…,Codeep: Open Skills Folder. - Welcome banner warning when a workspace ships project-scoped skill bundles — informed consent before the agent starts invoking unfamiliar capabilities.
- Streamable HTTP transport. MCP servers configured with
url(and optionalheaders) are reached over the spec's HTTP+SSE flow instead of stdio. POST for requests, GET-side SSE for server-pushed notifications and server-initiated requests. Mutually exclusive withcommand— pick one per server. - Sampling capability. When a server opts into
sampling, it can ask Codeep to generate a completion on its behalf; we bridge to the active provider viachat(). Server gets just the assistant text; no tool use is forwarded. - Resources & prompts auto-injected into the agent's tool catalog.
Each server that exposes resources or prompts gets four virtual tools
the model can call natively:
<server>__resource_list,<server>__resource_read,<server>__prompt_list,<server>__prompt_get. No more "user types/mcp read <uri>manually". Servers that don't expose either get nothing extra. - Mid-run tool catalog refresh. A
tools/list_changednotification (or a successful auto-restart) flips a dirty bit; the agent re-fetches the catalog at the start of the next iteration so the model sees new tools without a session restart. - MCP marketplace.
/mcp browseshows a curated catalog of popular servers (filesystem, github, postgres, slack, brave-search, …);/mcp install <id> [extra args]writes the config + spawns. Each entry surfaces env-var and arg hints so the user knows what to set. roots+roots/listcapability negotiation. Codeep advertisesroots: { listChanged: true }ininitializeand handlesroots/listrequests by returning the current workspace folder — filesystem-shaped servers can scope reads accordingly.
- Type-to-filter in every menu picker.
/model,/provider,/login,/lang, sessions, export, logout — start typing and the list narrows by key / label / description. Backspace edits, first Esc clears the filter, second Esc closes. Critical for the OpenRouter 100+ model catalogue but useful everywhere. - First-run provider picker reordered. Anthropic, OpenAI, OpenRouter, Z.AI sit at the top instead of being buried under regional / parameter-variant entries. Each row now shows the short provider description ("Unified access to 100+ models via one API key") so the value prop is visible at a glance.
/cost,/compact [keepN],/commands,/checkpoint [name],/checkpoints,/rewind <id>,/hooks,/mcpslash commands.- Custom slash commands.
.codeep/commands/<name>.mdMarkdown templates with{{args}}/$ARGUMENTS/{{argN}}placeholders. Project files shadow global. Warning banner on first session. - Lifecycle hooks.
.codeep/hooks/<event>.shshell scripts run onpre_tool_call,post_edit,on_error,pre_commit. Apply uniformly to built-in and MCP tools. /memoryand/profilenow work in ACP (Zed / VS Code), not just the TUI.- ACP
fs/read_text_fileandfs/write_text_filedelegation — agent tool calls route through the client when capability is advertised, with a 100 KB size cap on delegated reads. - ACP
authMethods— singleCodeep CLIagent-type entry for acp-registry compliance +authenticateno-op handler. - Auto-reconnect on MCP server crash (3× in 60s with exponential
backoff). Persistent failures surface in
/mcpinstead of being silently dropped. - VS Code 0.2.0:
- Native
vscode.diffviewer for proposed edits + Accept/Reject CodeLens (closes diff tab → implicit reject). Cmd+Shift+AAttach Active File.@symbolmentions alongside@file.- MCP server management from the command palette (Add / Remove / Open Config).
- Auto-loads
~/.codeep/mcp_servers.jsonand project equivalent. - Permission labels honest about scope ("Allow for this session").
- Native
/providerwas not inAVAILABLE_COMMANDS— invisible to Zed / VS Code/autocomplete./apikeyand/loginwarn that inline keys leak into shell history.write_filedouble-recorded itself in the action log when client-side delegation failed and we fell through to disk.- Delegated
fs/read_text_filehad no size cap; a misbehaving client could return a multi-GB blob and OOM the agent. compactHistory()had no timeout — a hung provider would wedge the session. Now caps at 60 s with an externalabortSignalhonoured.- Diff editor occasionally stayed orphaned in VS Code if the user responded faster than the open completed.
- MCP tool name normalization stripped hyphens, so servers named with a
-couldn't route their tool calls (my-fs__read_file≠my_fs__read_file).
- 19 obsolete model entries in
tokenTracker.ts(gpt-4.1*, o3, o4-mini, gpt-4o, claude-mythos-preview, claude-sonnet-4-5-20250929, gemini-2.5-, gemini-3.1-flash-lite-preview, MiniMax-M2.5, MiniMax-M2.1*, MiniMax-M2) — continuation of the 1.3.42 cleanup.
- MCP
sampling/createMessagenow rate-limited and budget-capped per server (≥1 s spacing, 100 requests / process). Each accepted request is logged to stderr with the originating server name. Closes the path by which a misbehaving or malicious MCP server could drain a user's paid-provider credits. npm audit fixresolvedfast-uri(path traversal / host confusion) andpicomatch(ReDoS / method injection) high-severity CVEs in transitive dependencies.
- npm tarball reduced from 164.8 MB → 340 kB (unpacked 436 MB → 1.4 MB)
by excluding
dist/zed/*andbin/codeep-*pkg-built standalone binaries from thefilesfield. Those binaries continue to ship via GitHub releases and the Zed extension distribution.
McpServerin the protocol now hascommand?andargs?(was required), plus newurl?andheaders?. ACP clients that produced the old shape still work — fields are optional, parser accepts both.- MCP client protocol version bumped from
1.4.0to2.0.0ininitialize'sclientInfo. Servers that key off the version string may need an allowlist update.
- Default model
glm-4.7did not exist in the Z.AI catalogue.src/config/index.tsshipped withmodel: 'glm-4.7'as the cold-start default for thez.aiprovider, butsrc/config/providers.tsonly advertisesglm-5.1,glm-5-turbo, andglm-5. Fresh installs therefore booted with a model id that wasn't in any picker — the first send had to be preceded by a manual model switch, and any client (Mac / iOS / web dashboard) reading the config saw an unknown model. Default is nowglm-5.1, matching whatproviders.tslists as the Z.AI default. config.test.tsupdated to the new default so the validation test stays in sync.
- Obsolete Z.AI model entries in
tokenTracker.ts. Pricing and context-window rows forglm-4.7-flashandglm-4.5-airwere never reachable through the provider catalogue (providers.tsretired both during the GLM-5 rebrand). They only produced phantom cost estimates if a user typed those ids in by hand. Dropping them keeps the tracker's tables in lockstep with the canonical provider list.
- Slash commands (
/) still showed "Available commands: none" in Zed after v1.3.40. The earlier patch (removing the spec-extensionsession_info_updatenotification and re-emitting commands on every prompt) was insufficient — the actual root cause is a race in Zed: it processesAvailableCommandsUpdatedevents synchronously and silently drops them if the session'sthread_viewisn't registered yet. Codeep was sending the notification ~1 ms after thesession/newresponse — well inside Zed's setup window — so the agent's command list never reached the slash autocomplete and/helpwas rejected as unsupported. Confirmed by reading Zed source (crates/agent_ui+crates/agent) and reproduced via the newCODEEP_ACP_DEBUGlog. Codeep now sendsavailable_commands_updatewith a configurable delay (200 ms by default, override viaCODEEP_ACP_COMMANDS_DELAY_MS) onsession/new,session/load, andsession/resume— well outside the race window.
- First attempt at fixing slash commands in Zed. Removed a
session_info_updatenotification that was emitted between thesession/newresponse andavailable_commands_update, on the suspicion that it was poisoning Zed's deserializer. Turned out the variant was actually spec-valid (SessionInfoUpdateexists inagent-client-protocolv0.11+) and the real fix needed was in v1.3.41. Also added the resilience pass:available_commands_updateis re-emitted at the start of everysession/promptturn.
CODEEP_ACP_DEBUGenv var for ACP debugging — when set, every inbound and outbound JSON-RPC frame is appended to~/.cache/codeep/acp-debug.log(override path viaCODEEP_ACP_DEBUG_FILE). Writes go to a file rather than stderr because most ACP clients (Zed included) don't pipe agent stderr anywhere readable. This is what let us confirm the v1.3.41 fix.
- CLI sessions surfaced in Zed welcome message — when you open a chat in Zed, Codeep now lists the most recent saved sessions for that workspace with one-line load instructions. Solves the discoverability gap where users couldn't find their CLI sessions in Zed's sidebar (Zed only lists sessions it created itself; CLI ones live in the hidden "Import Threads" modal).
- README "Loading CLI sessions in Zed" section documenting both the
in-chat (
/sessions,/session load <name>) and the import-modal paths.
available_commands_updatenot sent onsession/loadandsession/resume— the slash command popup (/) stayed empty after a panel reload because Zed registers commands only when the agent emits the notification. Codeep was emitting it only onsession/new. Now also sent on load and resume.
- Added
npm run releasewrapper script — single command bumps version, verifies CHANGELOG section, runs tests, commits, tags, and pushes. Seescripts/release.js.
promptCapabilities.embeddedContext— dragging a file into the Zed chat (or pinning a code selection) now actually injects the file content into the prompt. Previously theresource_link/resourceblocks were silently dropped. 200 KB cap per resource with a visible truncation marker.session/resumeACP method — lightweight reconnect on panel reload. The client keeps history locally and only re-wires the in-memory session (modes + config), avoiding a full history replay. Advertised viasessionCapabilities.resume.- Dashboard sync after every manual chat in the CLI — previously only
the agent-mode and graceful-shutdown paths reported to
codeep.dev, so Agent Mode: OFF looked like it never synced.
- ACP boolean dropdown labels in the Zed agent settings panel now include
the action prefix (
Confirm delete: ON,Confirm exec: ON,Confirm write: ON) so the three toggles are distinguishable. Previously Zed rendered all three identically asON.
terminal/wait_for_exit— was calling the camelCaseterminal/waitForExitvariant; corrected to the spec snake_case name. Also dropped the non-standardtimeoutMsparameter.- Removed
terminal: truefromagentCapabilities—terminalis a client capability per the ACP spec, not an agent capability. Codeep now readsclientCapabilities.terminalfrom the initialize params and routesexecute_commandthrough the editor's terminal only when the client supports it (falling back to local execution otherwise).
- Maintenance release. Republished with the same code as v1.3.36 to align the npm version, GitHub binaries, and Homebrew formula across all distribution channels. If you're on v1.3.36, no need to upgrade.
session/list_providersACP method — returns the canonical provider catalog (id,name,groupLabel,hint,requiresKey,subscribeUrl) so ACP clients no longer need to hard-code provider lists. Prerequisite for Codeep VS Code extension v0.1.23+, which fetches the catalog dynamically.groupLabelandhintfields inProviderConfig(src/config/providers.ts) for richer client-side rendering of the provider picker.
- Diff previews on permission prompts — manual-mode
session/request_permissionpayloads now include the actual change (old_string/new_stringforedit_file, full content forwrite_file, command + cwd forexecute_command). Truncated at ~4 KB per field, 200 lines per file. Other ACP clients see the extra fields as harmless additional keys. - Reasoning stream support —
agent_thought_chunknotifications flow through to clients that subscribe to them, so models with extended thinking (Claude, GPT-5 reasoning, DeepSeek R1, etc.) can surface their reasoning UI.
- Input formatting tweaks for clearer user-side verification.