Codewhale is the public product from Shannon Labs. The
codewhale
command, npm package, and release-asset names remain lowercase technical
identifiers. The legacy npm packagedeepseek-tuiis deprecated and
receives no further releases. Users coming from v0.8.x legacydeepseek/
deepseek-tuinames should migrate withdocs/REBRAND.md.
Install
Recommended — official GitHub release
New macOS/Linux install (checksummed binaries from this release):
curl -fsSL https://codewhale.net/install.sh | CODEWHALE_VERSION="v0.10.0" sh
"$HOME/.local/bin/codewhale" --versionFor Windows, use the matching installer or archive below. For an existing
direct install, run codewhale update; it prints the executable path and
keeps newer builds. If the install directory is occupied by a different build,
use the fresh-directory migration in the installation guide.
Secondary packaging — npm and Cargo
npm install -g codewhale
# or build from source
cargo install codewhale-cli --lockedThe wrapper downloads the matched codewhale and codew command assets
from this Release. Both contain the same compiled runtime.
Docker / GHCR
docker run --rm -it \
-e DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" \
-v codewhale-home:/home/codewhale/.codewhale \
ghcr.io/hmbown/codewhale:v0.10.0The image exposes the same runtime as both codewhale and codew. The
latest tag is also updated on release.
Cargo (Linux / macOS)
cargo install codewhale-cli --lockedThe Cargo package installs codewhale. Cargo cannot create a second command
alias from one binary target; users who want the shorter spelling can add a
codew symlink to that installed executable. The npm, Homebrew, archive,
shell-installer, and container channels install both command names directly.
Manual download — platform archives (recommended)
Each archive below contains the same runtime under the codewhale and
codew command names, plus an install script:
| Platform | Archive | Install script |
|---|---|---|
| Linux x64 | codewhale-linux-x64.tar.gz
| install.sh
|
| Linux ARM64 | codewhale-linux-arm64.tar.gz
| install.sh
|
| Android ARM64 (Termux) | codewhale-android-arm64.tar.gz
| install.sh
|
| macOS x64 | codewhale-macos-x64.tar.gz
| install.sh
|
| macOS ARM | codewhale-macos-arm64.tar.gz
| install.sh
|
| Windows x64 (installer) | CodeWhaleSetup.exe
| NSIS setup |
| Windows x64 | codewhale-windows-x64.zip
| install.bat
|
| Windows x64 (portable) | codewhale-windows-x64-portable.zip
| — |
| Windows ARM64 | codewhale-windows-arm64.zip
| install.bat
|
| Windows ARM64 (portable) | codewhale-windows-arm64-portable.zip
| — |
Unix (Linux / macOS):
tar xzf codewhale-<platform>.tar.gz
cd codewhale-<platform>
./install.shWindows:
- For the installer path, run
CodeWhaleSetup.exe; it installs
codewhale.exe,codew.exe, andcodewhale.batunder
%LOCALAPPDATA%\Programs\CodeWhale\bin, adds that directory to the
current-user PATH, and creates a Start Menu shortcut that prefers
Windows Terminal (wt.exe) when it is installed. - Extract the archive for your machine:
codewhale-windows-x64.zipor
codewhale-windows-arm64.zip - Double-click
codewhale.bat(not the raw.exe) to launch - Run
install.batto copy the binaries and launcher to%USERPROFILE%\bin - Add
%USERPROFILE%\binto your PATH
The portable Windows archive skips the install script — extract and run codewhale.bat from any directory. The NSIS installer is currently unsigned and may trigger Windows SmartScreen until a signing certificate is wired into the release pipeline.
Each platform also has bare, unarchived codewhale-<platform> and
codew-<platform> assets. The seven codewhale-tui-<platform> filenames
attached to v0.9.5 are byte-identical compatibility copies used only to let
already-installed v0.9.4 clients discover and cross this single-binary
transition; current installers do not expose a third runtime. The legacy npm
package deepseek-tui is deprecated and is not republished. For migration
from v0.8.x legacy binary names, see docs/REBRAND.md.
Verify (recommended)
Download the checksum manifests from this Release and verify:
# Linux — archive bundles
sha256sum -c codewhale-bundles-sha256.txt --ignore-missing
# Linux — individual binaries
sha256sum -c codewhale-artifacts-sha256.txt --ignore-missing
# macOS
shasum -a 256 -c codewhale-bundles-sha256.txt --ignore-missing
shasum -a 256 -c codewhale-artifacts-sha256.txt --ignore-missingWhat's in v0.10.0
Codewhale v0.10.0 brings a redesigned terminal workbench, clearer settings, and
more reliable session and runtime behavior. It supersedes the unpublished
v0.9.14 candidate.
Security
-
Children never inherit desktop or computer-control tools. Desktop control is
the most machine-wide capability in the catalog, and a verifier child
inherited it by default: on 2026-09-17 one opened the host Terminal and typed
a blocked shell command into the user's live session. A family classifier now
removes those tools when a child's registry is built, so they are neither
eager nor searchable, andexecute_fullrefuses the family at dispatch for
every child role — visibility is a grant, and hiding is not the only defense
(#6296). -
Runtimes can be held to an organization's plugin allowlist. A managed policy
document (managed-policy.jsonbesidestate.json, or
CODEWHALE_MANAGED_POLICY_PATH) lists the plugin ids a Runtime may run,
with anallow_unlistedflag and a schema version checked exactly like
PluginStateFile. Enforcement sits insideapply_state, so a plugin
enabled before the policy arrived — or hand-edited toenabled: true—
never comes back enabled, andenable()re-reads the document so a policy
landing after discovery refuses with a reason that names it. A malformed
document fails closed rather than degrading to unenforced. With no policy
present, behaviour is bit-for-bit what it was. This is the enforcement
primitive only; the authority that decides the allowlist is still local,
so it binds a cooperating Runtime, not a hostile one. -
Approving an
apply_patch"for the session" is now scoped to the file you
approved. The grouping key that scopes a session grant was built by a second,
weaker patch parser that read only+++ b/headers and thereplacearray:
it saw no target at all for the documentedapply_patch{path, patch}
override, for--no-prefixdiffs, or for delete-only diffs, and collapsed
every one of them to a single shared key. One approval therefore pre-approved
every later patch of that shape, to any file, with no card and no notice. The
key now comes from the same resolver the executor and the permission path
already use, and an input that cannot be resolved gets its own key rather
than a shared one (#6247).
Added
-
The statusline's performance readings survive compact mode and are separately
configurable. Measured TTFT and average output rate are kept when space
allows, shedding help text and secondary counts first; the existing metrics
and statusline settings gain individual toggles with a migration that
preserves a legacy single setting, and the picker takes mouse selection and
scrolling. -
Tasks can be given their own run name.
NewTaskRequest,TaskRecordand
TaskSummarycarry an optionalname, stored as given and omitted when
absent, so queues still fall back to prompt-derived titles; a run started by
an automation inherits the automation's name. Thetaskstool's schema
extends backward-compatibly and legacy records decode through serde defaults
(APPS-153). -
The offline catalog seeds Xiaomi's MiMo 2.6 family —
xiaomi/mimo-v2.6-pro
andxiaomi/mimo-v2.6-flash— so a first boot without network no longer shows
the 2.5 generation.DEFAULT_XIAOMI_MIMO_MODELstays onmimo-v2.5-pro, and
the rows carry no price because the published rates cover onlysk-keys. -
Native memory is a reviewed store, not a model-writable file.
codewhale-memory
backs the TUI with SQLite as the authority instead of Markdown, and the
remembertool now only proposes candidates — a model can no longer write
an active memory./memoryand the Runtime API commit through
remember_reviewed, and a Context Lens surface (/v1/memory/lens,
/lens/actions,/events) shows what was kept and why. -
Code mode (Experimental, default off):
execute_toolsruns a JavaScript
program against a QuickJS host surface so a model can express several tool
calls as one program. Nested calls must be read-only and auto-approved; the
tool is hidden in Plan and refused under worker authority. Enable with
[features] code_mode. -
Two new built-in providers: ZenMux (
ZENMUX_API_KEY) and CSDN 星图
(CSDN_API_KEY, Coding Plan quota billing), each with its own key slot,
bootstrap model and catalog rows. -
The Runtime API gained the surface a native client actually needs: jobs with
stdin, kill and cursor reads; context, secrets, git, diagnostics, targets,
LSP and voice routes;GET /v1/commandsfor the slash-command catalog;
GET /v1/workspace/instructions; account-wideGET /v1/approvals; plan and
to-do inventory;/v1/settings/schema, withPOST /v1/confignow persisting
every declaredsettings.tomlkey rather than a curated allowlist; PTY byte
replay, resize and exit; and tool images as session artifacts. -
Codewhale holds the host's idle-sleep assertion while a turn is in flight
(caffeinate -ion macOS,systemd-inhibiton Linux), so an unattended
machine no longer sleeps mid-run. You will see one child process per turn. -
Skills are reachable in one call. The pinned
## Skillsindex told the model
to callload_skill, but the tool was deferred behindtool_search, so it
was never in the tool array that instruction was printed beside: using a
skill cost a discovery hop, aname="list"round trip, and a
change:tool_surfacere-pin of the whole cached prefix.load_skillis now
eager for the parent and for children — +244B of pinned catalog, measured,
against a re-prefill avoided every time a skill is used — and it takes a
querythat searches names and descriptions, so a truncated index no longer
forces pulling the whole catalogue to find one skill. The usage line now says
to load the matching skill by name before starting the work. -
A fast lane the router cannot serve now says so.
provider_router_candidates
answerscheap: Nonefor any pair its tables do not know, and a
Faster/Autochild on such a pair used to run at the parent's model and
price with no receipt and no way to tell "single tier by design" from
misconfiguration. The fallback stays — the router must not invent a model —
but the spawn receipt now carries afallback_notenaming the unserved lane,
the same field the pinned-provider fallback already uses.Inheritand
served lanes stay quiet: this reports a fallback, it does not nag. -
The runtime API tells a replayed submission apart from a new admission:
POST /v1/threads/{id}/turnsanswers200withidempotent_replay: true
when the operation key was already used, and201for a fresh admission, so a
client that retries after a dropped response can no longer create a second
turn. The flag is absent on a fresh admission, so responses existing clients
already parse are unchanged (#76). -
Event streams resume where they left off: journal frames carry their durable
seqas the SSE eventid:,Last-Event-IDis honoured as the cursor when
no explicitsince_seqis asked for, andRuntimeCapabilitiesadvertises
event_stream_resumeso a client can gate its reconnect controls on the
capability instead of discovering it from a missing id (#76). -
TTFT and average output rate stay visible in the compact working footer when
space allows./statuslinenow controls them separately; existing
session_metricssettings still work. -
StepFun’s current coding catalog includes Step 5 Preview, Step 3.7 Flash,
Step 3.5 Flash and its 2603 version, with published reasoning controls and
PAYG rates. Existing Step Plan routing remains subscription-based. -
readresponses now always report the file's byte size, line count, and
whether output was truncated, and truncation footers name the total size
alongside the continuation offset — so paging through a large file is
deliberate instead of a surprise (#6283). -
File edits are parse-gated before the write lands: Rust goes through
syn::parse_filefor a grammar-exactline:column, and.toml/.json
through the parsers already vendored. An edit is refused only when the file
parsed before and would not parse after — repairing an already-broken
file is the commonest reason to edit source at all, so pre-existing breakage
and new files fail open. The check precedes the write, so a rejection leaves
the file untouched andapply_patchcannot half-apply (#6204, #6206, #6151). -
Rust files that were already
rustfmt-clean are re-normalized after an edit,
so the next patch's anchors still match. Hand-formatted files are never
rewritten, and every failure path skips and lets the edit land (#6205, #6151). -
Native clients can finish provider setup without dropping to the CLI:
DELETE /v1/providers/{id}/keyclears a Codewhale-owned credential through
the same shared owner ascodewhale auth clear, andGET /v1/providers
now carriescredentialSource/credentialWritable(plus a reason) so a
client disables its control with a truthful explanation instead of letting
a write fail late. A credential Codewhale does not own — a literal key in a
config file, or an active external consent — refuses both verbs with409
rather than appearing to succeed against a source that still wins at
request time (#6179). -
The interactive approval card can be bounded:
[approval] timeout_seconds
resolves an unanswered card to deny when the window elapses — the same
fail-closed decision the external approval path takes — and the transcript
says the bound denied the call, not the operator. Omitted or0keeps
today's unbounded wait, so nothing changes unless you opt in (#6101). -
Transcript drag selection copies Markdown source by default: every cell the
selection touches serializes through the same canonical pathCtrl-Yand
/copyuse, joined with blank lines, and the toast names the copied cell
count. A selection that does not cover every touched cell end to end copies
the exact rendered fragment instead: rounding a pinpoint selection out to
whole cells meant copying an entire model message (#6228).
tui.selection_copy_markdown = falsekeeps the rendered-text payload
(#6156). -
The Runtime API serves the workspace files a native client browses and edits:
GET /v1/workspace/fileslists one directory,GET /v1/workspace/files/read
returns a bounded byte window with a whole-file SHA-256 revision, and
PUT /v1/workspace/fileswrites atomically through the confined opener with
revision-checked overwrites (409 on drift)..gitis never served and
symlinks are never followed. A saved session's oversized tool outputs are
served as artifacts atGET /v1/sessions/{id}/artifactsand
GET /v1/sessions/{id}/artifacts/{artifact_id}. (#6163) -
A session that ended mid-turn is no longer invisible to the model. The newest
workspace-scoped session still holding a crash-recovery checkpoint is
surfaced as a one-line## Prior Sessionnotice in the session-pinned prompt
prefix — metadata reads only, excluding the live session and any session this
process instance created. Clean sessions get no block, so their prefix bytes
are unchanged. Two bounded read-only tools,session_searchand
session_get, give the model workspace-scoped recall over the same store
(one-line summaries and an 8-message tail, labeled untrusted user data, read
on the blocking pool). Resuming stays the user's decision: the hint tells the
model to offer a continuation, never to silently take one (#5715). -
codewhale exec --hooksopts a headless run into the sameHookExecutorthe
TUI builds — global config, reviewed plugin snapshots, and trusted project
hooks.toml. Headless runs previously fired no hooks at all.tool_call_before
can still deny andshell_envstill applies; a hookaskresolves
fail-closed without a terminal. Fleet worker subprocesses never opt in, and
permissions.tomltyped rules are unchanged (#6099). -
codewhale doctorflags fleet and profile model pins that the provider's own
roster no longer offers. A pin is reported only when a fresh cached live
roster for that exact route exists and omits it — stale, failed, or absent
rosters prove nothing and are counted asunverifiablerather than raising a
false warning. Each row names the route and every owner of the pin; the pin
is surfaced, never rewritten (#6035). -
Background-capable clients can enumerate owned work and watch TUI-visible
conditions:GET /v1/threads/runninglists threads with queued or
in-progress turns in one call (one turns scan grouped by thread), and
GET /v1/threads/{id}/noticesserves activesubagent-terminal,
elevation-needed, andmodel-notifynotices with thread/turn identity,
cleared by ack or — for elevation — when the tool call completes (#6180,
#3757). -
Sub-agent launches adapt to provider throttling: a
DynamicGatereplaces
the fixed semaphore so launch capacity adjusts at runtime, and a
RateLimitGovernorhalves capacity on 429 pressure, pauses admissions
past the threshold, and recovers additively; 429 retries honor
Retry-Afterwith jittered backoff, and quota exhaustion keeps the
failure path (#6055). -
Turns record the mode they ran in, so mixed-mode sessions stay legible
after the fact (#6321). -
Shell spawning refuses NUL bytes in command and cwd before spawn, and
sub-agent runs fall back loudly past credentialless profile provider
pins instead of misrouting silently (#5529, #6318, #6320). -
Children land past a per-step context bound instead of burning
quadratically, and status rows surface live declared-vs-observed writes
(#6189, #6194). -
Queued Agent Mail can be cancelled before delivery, and the TUI
suspend/resume handshake restores on stop and rebuilds on continue
(#6176, #6169). -
MCP connections are supervised: dead servers are probed and reconnected
with transitions reported, and a failed reconnect keeps the last-good
catalog instead of dropping tools (#6187, #6142). -
Web search autodetects Tavily from
TAVILY_API_KEY(Firecrawl stays the
default), fleet refusals name the alternative, and verification runs on
a bounded Git fetch plus amerge_treeverify surface (#6298, #6296). -
Fleet authority projects through one
ChildGrant, and ModelScope joins
the built-in providers (#5633, #6299). -
Child tool results are capped at capture time, and run
tests/verifiers accept a bounded cwd (#6282, #6294, #6296).
Changed
-
SearXNG results rank by the score the instance returns rather than by arrival
order. Integers, floats and numeric strings are accepted; anything else,
including NaN and infinity, becomes 0.0, and rows are stable-sorted descending
before the result cap, so equal scores keep instance order. The docs now spell
out the self-hosting requirement: the separate process must expose
search.formats: [json]— an HTML-only instance answers 403 — and bind to
loopback or a policy-allowed host, with no default instance discovered. -
The bundled skills move to a new generation. social-media and health leave the
shipped pack (phone-export workflows rather than everyday skills), feedback
moves todocs/skills/beside contributor onboarding, and the exact earlier
body is retained so only unmodified shipped skills upgrade — a skill you have
edited is left in place. Google OAuth scopes and client setup, Photos exports,
forgetting limits, Spotify playback and plugin reload guidance were corrected
in the same pass. -
autois a declared default, not a guess about your wording. Reasoning
effort no longer maps request vocabulary to tiers (debug/error to Max, search
to Low) and Auto routing no longer infers cheap-versus-big from phrasing:
both resolve the configured default, with[auto] cost_savingas the
explicit opt-in. The same prompt now costs the same thing twice. -
A workflow's shared token budget is opt-in.
[workflow] default_token_budget
applied a silent 120,000-token cap across a run and all of its children, and
a fan-out died at the limit with no hand-back; the default is now 0, meaning
no shared cap. -
The shipped
deepseek-flashroute speaks the Responses endpoint, andxhigh
effort maps tohighper the vendor's own table. -
Streamed text is paced at a steady rate rather than inheriting the provider's
SSE chunking, so output reveals at a readable beat instead of in bursts. -
Broadening shell access for a conversation now requires an idle conversation
and rejects a stale-workspace check before it commits. Engines advertise a
thread_shell_consentcapability, so an older Engine reads as unsupported to
a native consent client instead of silently accepting. -
One base prompt now serves every host.
HEADLESS_BASE_PROMPTwas a second
hand-maintained rendering of the same constitution — the drift pattern this
repo forbids by convention — so headless runs compose the sameBASE_PROMPT
plus language and output layers that interactive runs do, skipping only host
chrome (execution profile, authority recap). Headless and interactive can no
longer disagree about what the agent is. -
BASE_PROMPTgains a Bearing article, which changes how the agent talks
to you: the user is a peer who gets honesty rather than deference, a blocking
gate is named plainly instead of dressed up as refusal, bad code is called
bad, a crude request is carried out without a lecture, and an apology appears
only when there is something to apologize for — not as punctuation. It also
states that the request is the whole mandate, so the scope law opens with
what is yours to do before it reaches the edge. Every pinned kernel invariant
survives verbatim. -
The ocean reads as animals rather than a mechanism. The school used to
translate as one rigid body — bob phase and tail pose were staggered per
fish, but horizontal position was locked to an exact wedge offset — so each
fish now eases a dot fore and aft of its slot on its own slow period, and the
formation breathes while it travels. Bubble emission and the per-animal
periods are hash-jittered instead of sharing one clock. Amplitude stays an
order of magnitude under the crossing speed, so no fish travels against the
school andfacing == velocitystill holds by construction. The species list
is unchanged. -
Extensions keeps the exact-content plugin review on the panel: confirming
a bundle's digest re-reads the inventory, so the row you just reviewed
reports its new trust state and offers Enable instead of leaving you in
the transcript with a stale "not reviewed" row. -
Underwater motion ticks at the cadence the frame limiter actually draws
(the atmosphere interval while only the water moves, the authored 80 ms
ocean cadence inside the interactive cap while a turn streams), and the
event loop wakes exactly for the next tick instead of on the next idle
poll. Idle water no longer requests frames it cannot draw or quantizes its
cadence to the poll interval; reduced motion, Ghostty, tmux and the
six-second idle settle are unchanged. -
The launcher keeps the Codewhale mark while balancing its layout above the
composer. A single cursor identifies the selected action; MCP faults retain
their warning color even in compact terminals. Recent-session counts now
read correctly for a single message. -
Model and provider settings use quieter selection surfaces, one focused
cursor, clearer missing-key warnings, compact output limits, and less
repetitive credential detail. Plugin actions use plain labels. -
The terminal opens on Shoreline, the same palette the GPUI client already
uses: warm charcoal field#211F23, a raised plate for panels and the
composer, one blue for action and selection#90B9FF, and the whale's ivory
#F2ECE5for body text, with 4.5:1 floors on every muted step. The old
saturated navy gradient is not gone — it is a namedunderwatertheme, and
after review it is once again what a fresh 0.10.0 install opens on
(DEFAULT_TUI_THEME); Shoreline is one/themeaway. Existing installs keep
whatever theme they have saved (#6222). -
Menu navigation is starting to mean the same thing everywhere.
menu_style
already single-sourced how a selected row looks; what a key does was still
reinvented per surface —h/lin the provider picker againstLeft/Right
in the model picker one screen later,Home/Endin one of seven pickers, and
no paging at all in Fleet's detail view.list_nav, which already owned the
wrap arithmetic, now owns the vocabulary too: one vertical axis, one
horizontal axis, and two entry points so a picker with a live filter never has
a letter stolen out of its query. Fleet's detail view adopts it first and
gains PageUp/PageDown and Home/End in both its lists (#6290). -
Reviewed plugin bundles are no longer re-hashed four times per MCP dispatch.
verify_plugin_authoritywalks and hashes both the reviewed source and the
runtime snapshot, and four separate authority checks ran pertools/call—
eight tree walks. Three of them sat one statement after a
validate_before_useon the same source, sois_readyre-verified what had
just been verified; readiness and authority are now separate, and only the
callers with no preceding check still pay for both. The tool catalog is built
once per turn instead of twice, which also removes a case where the two
assemblies could disagree if authority drifted between them. The digest is
deliberately not cached on(path, mtime, len): the reviewed tree is
user-writable andutimensat(2)lets a same-uid process restore an mtime
after an equal-length rewrite, so a stat-keyed cache would serve a pre-tamper
digest (#6209). -
A sub-agent's completion is read from the manager once instead of polled. The
workflow pump re-read it up to fifty times, sleeping 20ms between attempts,
waiting for a terminal status that was already committed — every publisher
commits the status inside the same&mut selfcall that wakes the pump, so
the write guard spans both and the first read always sees it. A child the
manager had no record of cost a full second of head-of-line blocking before
failing; it now fails immediately, and says what actually happened instead of
claiming the child "did not report a terminal status within 1s" (#6211). -
MCP protocol negotiation: every surface advertised the original 2024-11-05
revision and the stdio client required an exact match, so newer servers
could not connect. The server and both clients now advertise 2025-06-18
and negotiate over the supported set (2025-06-18, 2025-03-26, 2024-11-05)
— the server echoes the client's revision when it is supported and answers
with the latest otherwise, the stdio client accepts any supported revision,
and streamable HTTP sends the requiredMCP-Protocol-Versionheader on
every post-initialize request (#6280, first half). -
Configured MCP servers now connect lazily instead of all at session boot. The
pool owns aconnectingset marked at spawn and cleared on resolution or
abort, so "connecting" is no longer inferred as enabled-minus-connected. The
boot pass scopes to the eager set —requiredservers plus those covered by
tools.always_load/allowed_tools— and a turn naming an unstarted server
spawns its connects alongside, under the existing five-second deadline. A
configured-but-unstarted server now reads as configured on every surface
(session-boot rows, Extensions tab, launch card), never as connecting.
docs/MCP.mddocuments the lifecycle (#6033). -
The launch card's MCP problems row runs its own remedy. It already printed
/mcp login <name>or/mcp; it now joins the shared paint/click/keyboard
ordering, so Up/Down lands on it and Enter or a click types the printed
command into the composer for you to send. Typing beats copying: no clipboard
dependency over SSH, and you see the command before a second Enter runs it
(#6085). -
Computer Use is the only computer-use product in Extensions and
/mcp recommendations. Cua is no longer suggested as a parallel
desktop-control MCP; enable the first-partycomputer-useplugin
instead. The bundled plugin is 0.11.2 — macOS beta, with the Windows and
Linux backends experimental and source-only. It carries the per-app consent
ledger, background-by-default control, and a focus lease that refuses rather
than stealing the foreground, alongsideget_app_state,focus/get_value
andstop_computer_control. Shared-desktop pointer gestures stay gated, and
arbitrary background dragging remains unavailable. -
The bundled first-party catalog pins marketplace revision
d8640b17f27542e7122c76368724196f92a0af61, so installing Computer Use from
the Extensions listing fetches the same 0.11.2 source the binary already
carries. -
Materializing is not enabling: the built-in bundle is written under
$CODEWHALE_HOME/builtin-pluginson first run but staysNeverReviewed,
so Computer Use is never switched on without an explicit capability review.
Removed
- The host no longer parses prose into goals. Ten phrasings and a clause
allow-list ("make it your /goal to …") were turned into durable goals before
the provider call; prose now reaches the model, which callscreate_goalwhen
a goal is useful. The deterministic path — a leading/goals <objective>— is
unchanged.
Fixed
-
A thinking fold is an absolute choice again. The stored bit was relative to the
display preference (folded ^ !(verbose || thinking_default_expanded)), so
every recorded choice flipped meaning the moment a preference changed: turning
thinking_default_expandedon closed a block the reader had explicitly
expanded (#5847). An explicit tri-state intent now records Expanded or
Collapsed outright, and the absence of an entry means untouched, so the
preference baseline decides that cell's default. Space still toggles, and an
untouched cell still followsverbose || thinking_default_expanded. -
A terminal byte-stream cursor past the head is clamped instead of echoed back.
read_sincereturned a future cursor asnext_cursor, so a client that
continued from it skipped every byte the stream produced before reaching that
position — permanently. The start position now clamps tototal: a future
cursor reads nothing, is not a gap, and hands back the head. -
Language-server startup is bounded and a failed transport now terminates. The
client waits for successful initialization before sending notifications,
drains stderr without buffering it, bounds request queueing and replies, caps
protocol frames, and fails pending requests when the child transport dies. -
Input no longer freezes for the rest of a turn when the engine's 32-slot op
mailbox is full. The remaining input-path sends no longer await: droppable ops
whose rejection is reported and retryable usetry_send(CancelSubAgent,
PreviewOutboundRequest, bang shell input, PurgeContext, and the single-op
settings updates), ops that must land once the UI changed reserve first, and
ChangeModepublishes its live authority even on a full channel. Must-deliver
ordered transitions still await, and say why atEngineHandle::send(#6150). -
The pet's whale is one body again. The same authored point set is checked in
three copies plus four fixtures, and the Rust and TypeScript cores disagreed
on particle positions from frame 0 while agreeing on every channel and
constant — so the v1 fixtures had never matched what Rust produced and the
conformance job had never passed. The bodies and the fixtures are reconciled
and that check now runs green. -
The context meter and the auto-compact gate share one honest estimator. The
status bar inflatedctx %by about half and disagreed with the gate, so
"ctx 82%" could sit beside a/compactthat refused to run; displayed
percentages now read materially lower because they are correct (#6297). -
A steer the engine never accepted is queued for the next turn instead of
being shown as held and then silently dropped with no turn and no answer
(#6297). -
Every
reqwestclient routes throughcodewhale_release::tls. A bare
Client::builder()panics under rustls with no installed provider; 17 call
sites were swept. -
The bundled OpenAI-compatible hosts have their
/providerrows back.
Retiring theProviderSetupTemplatelayer moved SenseNova, Baseten, Groq,
Cerebras, DashScope and Command Code intoprovider_descriptors.jsonand
then wired that file to nothing, so six vendors silently lost their picker
rows; AICraft never had one. Each descriptor is a row again, built through
the same named-custom-provider builder a configured host uses, so endpoint,
bootstrap model and "missing<ENV>" reporting all come from one place. A
descriptor whose id or alias already names a[providers.*]entry is
dropped, so a configured row stays the only one. These rows are an
invitation, not a route: they are notis_configured, and only the form's
submit writes anything (#6289). -
A StepFun Step Plan subscription reaches its own catalog. A subscriber's
base_urlishttps://api.stepfun.ai/step_plan/v1, but only the
pay-as-you-go/v1host was recognised as official, so the Step Plan host
read as a custom endpoint, the catalog was withheld, and the picker showed
0 bundledand a guessed context window for a route whose console
advertisesstep-5-previewat 1M context. The model was in the seed the
whole time. All four StepFun hosts — global (.ai) and China (.com), each
with a/v1and a/step_plan/v1surface — are now recognised; a host
StepFun does not own stays custom and keyless, since this predicate also
scopes credentials. -
The send cue stopped strobing while you type.
[↵]flickered between dim
[·]and bold blue once per character at an ordinary typing pace. The cue
was not lying — Enter really does insert a newline during the ~120 ms
paste-safety window, which every keystroke re-armed — but that window only
exists for terminals that deliver a paste as a burst of ordinary keystrokes.
Ghostty, iTerm2, WezTerm, Windows Terminal and Terminal.app now skip the
heuristic from the first keystroke instead of waiting for proof by paste. -
Shift+Tab sets the permission posture in Plan. Tab cycles the mode and
Shift+Tab cycles Ask/Auto-Review/Full Access, but Plan refused the second
one outright, so the key silently did nothing there and the two axes read as
welded together. Plan's read-only guarantee comes from the mode, not the
posture —authoritymaps(Plan, _, Bypass)toSandboxPolicy::ReadOnly
andtool_cataloggates every write tool onmode != Plan— so the cycle
now moves the durable Act/Operate baseline while Plan's live policy stays
Suggest, and a toast says the posture lands when the mode leaves Plan. -
A steer delivered mid-turn now clears its pending card. The transcript
showed the message and the model answered it, while the "sending into this
turn" card still listed it as pending:turn_loopcommits a steer trimmed,
but the UI handed the engine the untrimmed text and kept that same copy for
matching, so a composer newline made the two differ by whitespace alone and
the steer was never promoted to a transcript cell. The text is trimmed once
at the source that feeds both, so they are the same string by construction;
the match stays an exact==. -
A Xiaomi MiMo key no longer fails verification with another vendor's 401.
The legacy top-levelbase_urlis a DeepSeek field, and a route without an
endpoint of its own inherited it — so a config carrying
base_url = "https://api.deepseek.com"made that host the MiMo route's
endpoint. The custom-endpoint guard correctly withheld the MiMo credential
from a host the route does not own, and the setup wizard's probe, which
bypassed the guard, sent the key asapi-key:— a header DeepSeek ignores.
The result wasHTTP 401 Unauthorized: Authentication Fails (governor),
DeepSeek's unauthenticated 401, shown to someone who believed they were
testing their own key. Every layer that reads the root now inherits it only
when the URL belongs to that provider's own official endpoint family;
DeepSeek, which owns the field, is unchanged, and any other host is still
expressible on[providers.xiaomi_mimo] base_url. Key verification
failures now also name the endpoint that was probed, so a wrong endpoint
reads as a wrong endpoint instead of a bad key. -
The Extensions panel can act on the rows it shows. Every plugin now carries
its on/off switch — Space, ore— including one that has never been
reviewed:/plugin enablealready routes an unreviewed bundle into the
exact-capability review and only flips the switch once you confirm the
digest, so withholding the control bought no safety and left the shipped
computer-usebundle readingdisabledwith nothing that could enable it. -
The panel stopped saying "untrusted". A bundle that is waiting on its
capability review now readsneeds review—off · needs reviewwhen the
switch is off too — and its detail line says what Codewhale is withholding
and which key ends the wait, instead of restating an internal pair of
booleans. Enter's verb on that row isreview, notinspect. -
MCP servers say where they live, and mutations write there. Each row is
labelledglobal,project, orplugin: <name>. Enabling, disabling or
removing a workspace-scoped server previously edited the user's global file
and failed with "MCP server '' not found" on a row the panel had just
offered; the write now lands in the file that declares the server, and a
plugin-contributed server says it is switched off by disabling its plugin. -
The permanent "Plugins · Problems: N" footer chip is gone. It could not be
acted on from the footer, and its count and the Plugins tab disagreed by
construction — the chip counted bundles waiting on a review while the tab's
Problems group lists only registry-level diagnostics, so the rows behind the
number were never the rows you found. A bundle waiting on a review is a row
with an action, and/pluginsstates it per bundle next to the key that
resolves it. -
The Extensions marketplace no longer advertises what it cannot install.
Browser Use and the sandbox runtime were pinned rows whose only possible
action wasunavailable; they are removed, along with the browser-driver
and sandbox-runtime component vocabulary that existed solely to describe
them. -
Configuration parsing keeps the parsed base config boxed, so loading a
profile no longer carries the fullConfigby value through the
deserializer and overflows a default 2 MiB test-thread stack; the
runtime-store binding test that also overflowed is split into phases and
pinned to that budget so CI's larger stack cannot mask a regression
(#6362). -
Stopping a turn revokes its pending approvals. A late approval cannot resume
the cancelled action or save an automatic approval for later turns. -
Expanding and collapsing selected reasoning now matches its rendered state
when verbose mode and the default-expansion preference are both enabled. -
Branch navigation preserves sibling histories, stable entry IDs and timestamps
through autosave, resume and forks, and synchronizes the selected branch into
the live engine. Thanks to @7jrxt42BxFZo4iAnN4CX for the report (#6367). -
Compaction now writes local diagnostic records for completed passes and
automatic refusals.codewhale metricsreports them with summarizer token
usage; text-mode exec retains its session after attempting compaction.
Historical artifacts are not backfilled. Thanks to @7jrxt42BxFZo4iAnN4CX
for identifying the missing producer (#6368). -
The opening workbench retains the canonical Codewhale mark in a responsive
header while keeping recent-session titles full-width and actions clickable. -
serve --acpno longer ignores the approval posture. The ACP adapter
hardcodedApprovalMode::Suggest, so--yolo(and any configured
approval_policy) never reached admission: every mutating tool parked
behind a permission request no unattended client answers, and sessions
executed zero tools. Posture now derives from server config —--yolo
pre-approves prompts and flows into the sandbox policy — while hard blocks
(safety floor, repo law, reviewer consult) and the Plan read-only guardrail
stay intact (#6337). -
Plain agent spawns could not resolve.
built_in_members()seeded both
generalandworker, and the role parse boundary migratesworkerto
general, so both canonicalized to the same role — androle:general, the
selector the roster advertises for the default posture, matched two members
and raisedAmbiguousevery time, permanently. The duplicate built-in is
gone. The legacy name still resolves:general,member:general,
role:generalanddefaultall land on theworkerposture through the
identity selector rather than through a second member, which is what allowed
the duplicate to be removed (#6244). -
Clicking a
path:linein tool output no longer spawns$EDITORdetached
while the TUI still owns the terminal, and no longer spawns one editor per
matching line. The launch goes through the single terminal-handoff path, and
a click is one request to open one file (#6235). -
A write-scope contention refusal now names a remedy that works. The
agent
tool's description claimedreleasewas "the remediation a write-scope
contention refusal names"; the refusal did not name it, and pointing back at
it would have been worse, becausereleaseonly clears claims whose owner is
no longer running while a contention refusal names a live one. The refusal
itself now says to wait for that owner to settle or cancel it (#6272). -
The session picker no longer refuses a saved session whose Runtime store
exists but holds nothing. A force-quit leaves the store on disk, ownerless
and empty, and the switch path refused it because recovery only covered a
missing store. A switch now also adopts a store that is provably empty
(every work directory, plus the event sequence that remembers pruned
appends) and provably unheld (the process-owner lock, which a live
manager holds from open to close), with no automation pinned to its
execution scope — and the save gate treats that shape as abandonable too,
so the repaired binding persists. The first fix was reverted on a race
(emptiness without liveness); this reland checks the lock first (#6207). -
Double-tap Enter now sends every queued follow-up into the running turn,
oldest first. The second Enter used to steer only the most recent message
and leave older ones queued; a failed steer restores the failed message
plus everything unattempted in original order, so nothing is lost or
reordered. -
Only the most recently sent prompt carries the elevated-surface background
now; every older prompt renders on the bare ground. The fill used to sit
behind every user row (striping), then behind none; newest-only keeps the
eye on the turn in play. Sending a new prompt moves the highlight and
un-highlights its predecessor. -
Diff rows tint whole: added/deleted line numbers now share the row's green
/ red background instead of sitting bare next to a painted body. Context
rows stay on the bare ground. -
MCP connections are supervised now: a background task notices a dead
server within one sweep, reconnects on the existing backoff ladder, and
reports each transition, so Extensions rows flip with liveness instead
of parking on stale-ready or a silent [reconnect]. Five consecutive
failures park the server with a notice naming/mcp retry; an explicit
retry or a fresh connection resumes watching. Tool calls also retry
once across a dead pipe/socket (not just stale sessions), and a
reconnect that fails reports both errors instead of swallowing the
original (#6187;list_changedcatalog refresh stays open). -
A steer the engine never delivered is no longer reported as sent. The runtime
API persisted the steer item as already-Completedand emitted
turn.steered+item.completedthe moment the text entered the engine's
mailbox — before the engine decided anything. The engine discards a steer
whose turn has moved on, and an interrupted or failed turn drops whatever it
had queued, so a GUI could show "Guidance sent", clear the composer, and lose
the user's words. The engine now returns a verdict for every steer on every
exit path, the item settlescompletedorcanceledto match, a dropped
steer emitsturn.steer_droppedand answers409so a client can resend,
andsteer_countcounts steers the model actually received (#6276). -
<recommended_plugins>suggestions stop nagging: a plugin id is now
injected at most once per engine lifetime, and a plugin whose name a
loaded skill already covers is never suggested — the local skill owns
the domain, so the nudge was noise. Dismissals still apply, and the
fragment stays append-only on the user turn (#6274). -
A canceled automation run now settles with a transcript receipt that names
the cancellation (by request, cancel timeout, or shutdown) instead of
vanishing from the live band silently. The receipt wears attention ink and
never lights the failure demand; the run record keeps the cancellation
reason as its error detail. (#6162) -
A failed workflow run no longer settles silently: its terminal failure
raises a sticky error toast naming the cause (dispatch, schema, or script
errors), alongside the existing panel state (#5528). -
MCP OAuth re-login now forces the provider's consent screen: logout only
clears the local token, so without a prompt the provider silently
re-granted the same account/workspace and a re-login could never change
it./mcp logoutandcodewhale mcp logoutalso say plainly that they
clear local credentials only (#6040). -
Session retention no longer deletes transcripts once the store reaches the
cap: the oldest active session is archived — still openable from the
picker's archived view — instead of being unlinked, and archived records
sit outside the cap until they are pruned (#6136). -
Empty auto-created "New Session" stubs are capped separately (the ten
newest are kept) and can no longer occupy a real transcript's slot in the
session cap (#6137). -
A failed resume or session load is now a durable transcript error instead
of a status line the next footer update replaces, so a resume that cannot
restore its target no longer looks like a silent new session (#6138). -
Compaction no longer retains a tool result whose tool call was summarized
away: an older turn that mixes text with a tool result keeps its text and
drops the orphaned result blocks, which providers reject outright (#6119). -
Automation runs that need a tool approval no longer die as silent
idle-timeout cancels: a pending approval suspends the idle watchdog for its
decision window, and an unanswered window settles the run Failed with the
recorded reason instead of a silent Canceled (#6118). -
/mcpno longer freezes the console while a turn is running: the panel
opens immediately from the last known MCP snapshot with a receipt naming
the wait, and live-pool mutations say their refresh is deferred instead of
parking the UI event loop behind the running turn (#6159). -
MCP OAuth login no longer fails with "Authorization server response missing
required issuer" against servers that implement RFC 9207, such as
Cloudflare'smcp.cloudflare.com. The local callback listener now keeps the
issparameter from the redirect and hands it to the token exchange so the
callback binds to the discovered issuer; servers that do not sendiss
keep working unchanged. (#6157)
Contributors
- @AdityaVG13 — fixed composer wrapping, tab/caret placement, pasted and editor-returned draft history, painted-column transcript copying, explicit terminal foregrounds, headless user-input tool availability, and engine synchronization after importing foreign sessions (#6369, #6363, #6365).
- @aboimpinto — moved the TUI session-export slice onto shared command contracts (FEAT-025): a session-export contract facet with one shared sanitizer,
/exportrouted through the facet, pinned with baseline-captured goldens and gates (#6096). - @BX166 — contributed the AICraft provider template and its documentation (#6171). It was closed unmerged, but it is what surfaced the decision to stop special-casing named OpenAI-compatible hosts (#6289).
- @7jrxt42BxFZo4iAnN4CX — reported the session-retention defects behind archive-past-the-cap and empty-session cap occupancy (#6136, #6137), the resume-failure design behind durable transcript errors (#6138), and the gaps behind the opt-in approval timeout (#6101),
codewhale exec --hooks(#6099), Markdown drag-copy (#6156), and the browsable, current-aware session picker (#6014); the goal token-budget hard stop (#6013) and the fleet no-progress guard shared with child workers (#6015) landed as first slices of two larger proposals, and the runtime-store session refusal (#6207). - @Lstarsky0 — reported TUI tests reading machine state instead of hermetic fixtures; the
lock_test_envremedy from that report shaped two more hermetic fixes, for the shared UI fixtures and the compaction budget test (#5359). - @Lujc0523 — reported
/hooks editsplitting keystrokes between the editor and the composer, fixed by pausing the TUI input pump inside the editor handoff (#6165). - @Statter — reported the Gemini
/modelsfailure that now surfaces the provider's reason instead of an empty error (#6173). - @sequico — reported the ACP
session/newids thatsession/loadcould not resolve, fixed by minting resolvable session ids (#6174). - @bevis-wong — reported the mid-run engine freeze behind the bounded turn-end foreground-child join, and the resume path that re-ran identical tool-call repair on every load instead of persisting it (#6184, #6185).
- @gaord — recorded the mode each turn ran in (#6321, harvested), stated the approval posture a task thread starts on (#6386), and rebuilt the runtime-API thread summary in one store pass (#6376).
- @zhuowp — preserved chat roles across compaction, protected user turns on recompaction, and kept the operate contract intact (#6286).
- @h3c-hexin — rate-limit-adaptive subagent launch scheduling: the
DynamicGatethat replaces fixed spawn pacing under provider throttling (#6055, harvested). - @asto18089 — co-authored the rate-limit-adaptive launch scheduling work (#6055).
- @yrk111222 — ModelScope provider support, landed through the shared
ChildGrantfleet authority (#6299, harvested). - @xiechimon — capped child tool results at capture time rather than at render (#6294, harvested).
- @VincentCorleone — defined and implemented the weixin-bridge Quick Start so a first run works from the README alone (#6170, harvested).
- @Serendo — fixed the
/configtheme editor exiting the panel on an arrow key (#6336). - @yetuge — dropped the retired
token_budgetfield from the subagent documentation (#6335). - @Water-Run — ingested namespaced model-only catalog entries so models present only in the canonical
modelsmap reach the offering list (#6400); translated the local browser client guide into Simplified Chinese (#6401); and retired the blanket dead-code allowance with its unused feature stages, tightening the budget to match (#6402).
See CHANGELOG.md for full notes and docs/CHANGELOG_ARCHIVE.md for older releases.