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 — npm (one command, both entrypoints)
npm install -g codewhaleThe 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.9.11The 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.9.11
Codewhale v0.9.11 tightens the long-running agent loop, makes workflow
failures visible instead of successful-looking, adds an experimental
vision-capable DeepSeek route, and prepares reproducible Codewhale-versus-Pi
evaluation without publishing a result before a real run. The complete
item-level change record is retained below the categorized release highlights.
Added
- Added first-party
deepseek-v4-flash-vision-expdiscovery and selection for
DeepSeek, including theflash-visionalias, bundled offline metadata,
registry and picker entries, and image-input capability on the chat route.
Context and output limits inherit from V4 Flash until DeepSeek publishes
distinct values; pricing remains unknown rather than guessed. - Added a provider-controlled Codewhale-versus-Pi parity harness with three
hermetic coding tasks, route and reasoning-effort receipts, doctor/dry-run
modes, and bounded result artifacts. The repository ships the harness, not a
benchmark verdict; comparable real runs remain an acceptance gate. - Added portable, secret-free config export/import with a reviewable plan,
explicit headless consent, backup and rollback, and idempotent re-import. - Added bounded multi-file diagnostics through the existing model-facing
lsp
tool without increasing the tool-catalog count. Thanks to Isabel Wu
(. - Added portable presentation, media-attachment, and operation-digest facets to
the command contract, then moved all seven utility handlers onto the
contract-backed dispatch path. Thanks to Paulo Aboim Pinto
(.
Changed
- Sub-agent, Fleet-worker, workflow-task, and thread-runtime model turns no
longer inherit a hidden role-based step ceiling. An omitted or zero
max_stepsis unbounded; a positive user/config value remains an explicit
cap and is still clamped to the runtime safety ceiling. Wall-clock, provider,
heartbeat, cancellation, and admission safeguards are unchanged. /rcnow mirrors one shared session rather than transferring terminal
ownership: local and web prompts remain available while idle, approvals use
first-decision-wins semantics, and transport/integrity failures remain
fail-closed.- The terminal status rows around the composer are now two stable bands:
provider · model · thinking levelis the persistent identity row below
the composer in every phase, and a separate activity row above the
composer carries the live phase, notices, and cost/metrics. Sending a
prompt no longer relocates the route identity above the composer, and
neither row ever duplicates it. - The embedded local Web client now uses the current CWC Ocean hierarchy and
readable control sizing, follows the shared Enter/Shift+Enter composer
grammar, and chooses a provider plus model per new thread without mutating
Runtime defaults. Exact image-input capability is labelled honestly; a
vision-capable route does not imply that browser attachments exist. - The runtime now has one authoritative model-turn loop. The placeholder
crates/coreengine tree is gone, while the active TUI loop and its extracted
tool-call stages retain existing policy, hook, cancellation, and budget
behavior. Thanks to Sun Zhenyuan
(.
Fixed
- Chat Completions streams now require terminal proof from
[DONE]or a
non-emptyfinish_reason. Protocol-only frames no longer count as answer
content or time-to-first-token, and a provider continuation that ends after
tool results with no answer or tool call fails durably instead of producing
a falseCompletedreceipt. - A selected v2 Fleet now drives one bounded, deterministic Agent roster across
terminal and runtime surfaces. Fleet operator/member/explicit-route
precedence, resolved member identity, and exactvisionrequirement
admission now fail visibly instead of silently falling back, first-matching,
or rerouting. - A workflow whose
task()dispatch was rejected no longer loses that failure
inside aparallel()null slot or presents a successful-looking run. Rejected
dispatches now fail the run, persist as typed bounded receipts with an exact
count, and appear in transcript, activity detail, and workflow-panel views. - Provider readiness, credential-source explanations, focused-agent scrolling,
compact/statusand/helprendering, shell/web output bounds, MCP
lifecycle reporting, and narrow-terminal onboarding received the detailed
fixes recorded below. - Portable config import/export now preserves typed tables, arrays, numbers,
booleans, and datetimes without stringifying them, while refusing
machine-bound trust overlays, credential readers, automatically executable
hooks/LSP definitions, local-path authority, machine-local network proxy
routes, cookies, redaction placeholders, and nested or camel/dotted
credential keys. Project and global bundle operations now load and validate
the document for their actual scope in both directions, including a
workspace whose document still lives under the legacy app directory. codewhale loginnow means Codewhale account sign-in (the same browser
device flow ascodewhale account login, with--no-openand
--timeout-seconds); provider API keys are configured exclusively through
codewhale auth set --provider <provider>, and the hidden legacy
--api-key/--providerflags redirect loudly instead of silently writing
a key.- Account sessions prefer the OS credential manager and now fall back
automatically to the private0600Codewhale secrets file on headless
hosts, SSH boxes, and containers; theCODEWHALE_CLOUD_ALLOW_FILE_SESSION_STORE
opt-in is deprecated and ignored. /updategained aCtrl+Shift+Uinstall chord (catalogued in
docs/KEYBINDINGS.md, localized in all 15 packs) and a startup hint that
names the previous and current version on the first launch of a newer
build, pointing at/change.- Fleet product-model copy pass: the TUI, docs, and locale packs now use
Fleet / Member / Role / Model / Access / Saved consistently so a user can
say "scout", "DeepSeek V4 Flash", or the member name and mean the same
thing. - A reasoning model that returns only hidden reasoning and a clean stop (no
answer, no tool call) is now re-requested automatically up to twice before
the turn fails, instead of dead-ending with "the provider response was
incomplete." The retry reuses the cached prefix so it is cheap; an
output-length stop (length/max_tokens) is never retried, and a
persistently answerless model still fails honestly after the bound. - Model-bound tool results now use a credential-shaped redaction policy:
only values that look like secrets (known prefixes, JWTs, bearer tokens,
PEM private-key blocks, long opaque strings) are masked before areador
shell result reaches the model, so code such aspassword: credentials?.passwordor"password-validator": "^5.3.0"stays byte-exact
for edits and read-back. Exact configured credential values are still always
replaced, and logs/previews/exports keep the broad key-based scrubber. () - Terminal input shutdown no longer waits forever on a wedged TTY read,
Windows launch receipts can atomically replace an existing record, and the
complete/statusreport now follows the active locale without rewriting
exact custom-provider identities that contain brace-like text. - Localized READMEs again match the English install and third-party-notice
surface, including shell-completion guidance in all 18 translations.
Security
- Unified OAuth device-code polling now validates verification URLs before
opening them, redacts token-bearing types, honors server slowdown intervals,
and keeps credential save/logout mutations serialized. - Project instructions, rules-directory traversal, secret-shaped config data,
URL fingerprints, and shell network authority now retain the explicit bounds
and fail-closed behavior described in the detailed record.
Detailed change record
The notes below are preserved in full so the categorized highlights do not
erase behavior, migration, security, compatibility, or verification details.
Provider completion is now evidence-based. A Chat Completions stream reaches
MessageStoponly after[DONE]or a non-emptyfinish_reason; raw EOF
without either is a typed failure. Message-start, ping, usage/terminal
deltas, block-stop, and message-stop frames do not count as productive
content or mint time-to-first-token. After tool results, a terminal provider
step with no answer or tool call now emits a durable failed turn and never
fabricates an empty assistant message.
A selected v2 Fleet is the single effective Agent roster across terminal,
Runtime threads, direct Workflow, Fleet execution, doctor, and
setup/readiness; legacy profile layers are consulted only when no Fleet is
selected, and invalid selections fail visibly with bounded, redacted errors.
Member references resolve exact id first and otherwise require a unique
display name, role, pinned model, offline model name, or provider/model route;
agent action=rosterexposes that same bounded roster. The Fleet operator
supplies fresh-root and inherited-member routing unless an explicit launch
route or member pin wins, the resolved member is shown separately from the
requested alias, andrequires = ["vision"]is admitted only on an exact
route with verified offlineimage_inputsupport—never by silent rerouting
or custom-proxy inference. Fleet selection remains an explicit user/folder
contract independent of legacy project-profile loading.
Breaking (app-server):
/prompt,prompt/requestandprompt/runnow
execute a real model turn instead of reporting success for work they never
did.Runtime::handle_promptcalled no model: it resolved config, ran a
localModelRegistrylookup, emitted three canned hook events
(ResponseDeltawas literally the stringmodel-selected), and returned
HTTP 200 withoutputset to a stringified JSON echo of the caller's own
routing metadata — the prompt included. Worse, when athread_idwas
supplied it appended a real user row, flipped the thread toRunning, and
then wrote that echo into durable history as an assistant message plus a
prompt_responsecheckpoint. Nothing marked the row synthetic and nothing
ever moved the thread out ofRunning. All three endpoints now route
through the sameRuntimeBridgethat stdiothread/messagehas always
used, sooutputis the model's streamed text,modelis what the runtime
reports for the thread that ran the turn, andeventsare the real
streaming frames.Runtime::handle_promptand its synthetic history write
are gone.
Breaking (app-server): a failed prompt is now a typed failure rather
than a success-shaped body.POST /promptreturns
{"error":{"code":...,"message":...}}with400(invalid request),404
(thread not found),503(runtime_unavailable) or500, instead of HTTP
500 carrying aPromptResponsewith the error text stuffed intooutput
where model text belongs. The stdio surface gained JSON-RPC-32005
runtime_unavailablefor "the turn engine could not be reached, so nothing
ran" — distinct from-32603, and retryable. There is no configuration in
which a prompt silently echoes instead of running.
Breaking (app-server):
POST /threadwith aMessagebody runs the
turn. It previously repliedstatus: "accepted"with a
ResponseDelta("queued")frame while starting no worker and calling no
bridge — the stdio path for the same request has always done real work, so
the two transports disagreed about whatacceptedmeant. HTTP now replies
status: "completed"once the turn reaches a terminal state, with the
streamed frames ineventsand the turn id indata.Runtime::handle_thread
no longer acceptsThreadRequest::Messageat all: it owns thread
bookkeeping, not the turn engine, and returns an error naming
POST /v1/threads/{id}/turnsrather than a canned acceptance.
Breaking (app-server):
AppRequest::SubmitUserInputnow refuses
explicitly (ok: false,error: "user_input_reply_unsupported") instead of
returningresolved: trueand filing the answers in a map that had no
reader anywhere in the crate — every answer submitted was silently
discarded. It cannot be made to work on this transport: while a turn
streams, the stdio loop executes onlythread/interruptand queues
everything else, so an answer sent there would wait on the very turn
waiting for it. The refusal names the surface that does accept it,
POST /v1/user-input/{thread_id}/{request_id}on the runtime API. The
client-visible refusal is whitespace-clean, and the/toolpath that mints
theUserInputRequestis unchanged and still genuine.
Split the coordination ledger out of
tools/subagent/coord.rsinto
tools/subagent/coord/ledger.rs. The file held two unrelated things: the
model-facingagents/*tool wrappers, and the durable decision/claim/
contention records those wrappers happen to write — records whose consumers
are mostly not in the tool layer (tui::coordination_detail,
tui::work_surface,tui::ui::tests,core::engine::testsall name these
types). At 3.8k lines, reading either one started by scrolling past the
other. A pure move with a glob re-export fromcoord, so every
crate::tools::subagent::coord::{…}path still resolves and no consumer file
was edited; the only content change the move required is one constant going
from private topub(super)because its caller stayed behind.coord.rsis
now 2.3k lines andledger.rs1.6k.
agentis now the only sub-agent tool the model can see.AGENTS.mdhas
said "the model-facing sub-agent surface isagentonly" since the lifecycle
tools were removed, but six more were reachable:agents/list,
agents/message,agents/followup,agents/interrupt,agents/coordinate,
andagents/waitall defaulted to model-visible, so they shipped in the
catalog andtool_searchcould load any of them — and theagent
description told the model they existed. They now declare
model_visible() -> false, the same shaperlmandexec_shelluse: still
registered, still executable by name so a persisted transcript replays
against the same implementation, never advertised and never returned by
eithertool_searchmatcher.
Five of the six were already duplicates of an
agentaction. The sixth was
not:agents/coordinate action=claimwas the only way to widen a write
claim, and write enforcement fails closed, so hiding it would have left a
refusal ("expand it first with…") pointing at a tool the model could no
longer call.agentgains one action,claim, taking the write scope
vocabularyaction=startalready uses (write_roots, plus parse-accepted
exact_filesandcoordination_contracts). It keepsagents/coordinate's
Autoapproval — gating it deadlocks autonomous fan-in — and it can only
widen the caller's own scope; peer contention still fails. A scopeless claim
is refused rather than reported as granted, becauseexpand_write_claim
returns the unchanged claim withOkwhen every list is empty.
Collapsing six tools into one action set also collapses the gating:
agent
is deliberately exempt from both name-keyed gates (posture_permits_tool
short-circuits it so delegation depth governs spawning, and
execution_envelopeclassifies itBoundedso a read-only member can fan
out read-only work), so a capability folded into it inherits no gate.claim
is therefore gated per action, reproducing the envelope check that kept
agents/coordinateoff a read-only role's catalog — in the catalog and again
at dispatch, since catalog shaping is not an authority boundary. The other
actions keep exactly the visibility they had.
One placement table now decides which wire channel a message role belongs
in, and unrepresentable role/dialect pairs are refused at the outbound seam
(DeepSeekClient::prepare_outbound_request) instead of at the provider.
Chat Completions and OpenAI Responses used to drop an unfamiliar role
silently, Anthropic Messages forwardedmessage.roleverbatim and took an
opaque provider 400 for it, and Google cloud-code was alone in failing
closed. Positionedsystemanddeveloperhistory — including compaction
and branch summaries — is carried natively by Chat Completions and Responses
and projected, in place, onto Anthropic's user channel. It is neither
hoisted nor dropped. Genuinely unknown roles keep the previous
dialect-specific fail-closed/omit behavior, now decided in one table. The
dead"tool"arm in the Responses adapter is gone — nothing constructs that
role.
Message roles are a closed
Roleenum (crates/core/src/role.rs) instead of
a free-formStringonMessage. Four wire adapters each decided
independently what an unfamiliar role meant, and a typo in a role string was
a silent transcript edit rather than a compile error.Rolekeeps an
Unrecognized(String)variant and serializes viaas_str(), so a saved
session's bytes are unchanged, a transcript written by a newer build still
loads here, andassistant_interruptedstays a distinct session item — no
session schema bump and no migration ladder.
Portable config bundles:
codewhale config export --portablewrites a
deterministic, secret-free bundle (credential and machine-specific keys
dropped), andcodewhale config import <FILE|URL|->applies one with a
strict versioned envelope, a printed added/changed/skipped/conflicting/
rejected plan, consent gating (--yesrequired headless), a timestamped
backup with rollback, and idempotent re-import. Credential-shaped entries
are rejected by key name and value shape — rejections name the field,
never the value. Remote imports revalidate the HTTPS-or-loopback-HTTP policy
on every same-scheme redirect hop, and duplicate keys across applicable
section labels fail before any backup or write instead of silently resolving
by section order. Structured TOML values round-trip with their original
types and exact named-provider identity; recursive sanitization covers
arrays/tables and camel-case, dotted, cookie, and access-key spellings while
retaining ordinary token-count metrics. Machine-bound project trust,
credential-source consent, automatic hook/LSP execution, and local-path
authority are non-portable and fail before mutation. A missing target is
created transactionally and removed again on rollback, and project/global
scope validation rejects the wrong document before import or export.
Imported tables deep-merge portable fields into the target, so omitted
machine-local provider credentials, endpoints, and executable definitions
remain intact instead of being erased by a sanitized bundle.
/rcis now a shared-session mirror instead of a terminal takeover.
Attaching the web app no longer locks the local composer or hides
approvals: both surfaces can prompt while idle (one turn runs at a
time), approval cards stay visible in the terminal and are shared with
the web with first-decision-wins semantics (the losing side is told, a
web decision dismisses the local card), and structured questions are
answered locally instead of cancelled. Fail-closed behavior survives —
the post-failure reconnect lockout, integrity-gated/rc stop, and the
fail-closed shared-approval channel on transport loss are unchanged.
The takeover vocabulary ("web owns prompts and approvals") is gone from
every surface.
Auto-mode provider readiness no longer reports "key saved · not checked"
forever. Readiness checks are recorded against the concrete model the
router ran, but auto-mode reads resolved against the literalauto
identity, which never matched any recorded check — so the setup receipt,
model picker, and fleet setup view showed an eternal unchecked badge
even after hundreds of successful turns. The read now falls back to the
most recent check on the same route (provider + endpoint + auth class);
concrete-model reads keep exact per-model scoping.
The focused sub-agent transcript now scrolls like the main transcript.
The frame renderer sampled the ocean column through aChatWidgetwhose
constructor consumedpending_scroll_delta— every PageUp/PageDown and
wheel event was swallowed by an invisible widget before the focused pane
could read it. The delta is now parked across the sample; the pane pins
on user scroll-up, follows new child activity at tail, and
jump-to-bottom releases the pin.
Every Codex OAuth Responses request carried
max_output_tokens, a parameter
that endpoint rejects outright ("Unsupported parameter: max_output_tokens"),
so every gpt-5.6-sol turn — including every sub-agent on that route — failed
at the first request. Codex Responses bodies now ship without a client-side
output cap; the backend applies its own. Every other Responses route keeps
the central cap on the wire, exactly as before.
The model-facing
lsptool now supports a boundedread_lintsoperation
for multi-file, workspace-relative LSP diagnostics without adding another
tool catalog entry (); part of thedeepseek-tui-era identifier retirement in was reported as three
problems instead of one.
/statuswas 31 rows. On an 80x24 terminal the transcript viewport is 18, so
typing/statuslanded you on the tail of the report: the version, route,
directory, mode and sandbox rows had already scrolled past, and what stayed on
screen was fivenot reportedrows and a$0.0000. The report is 18 rows on a
fresh session —Window override:is present unless the value is already
configured. That is the viewport's height, so once/statusitself occupies a
history cell the title row still scrolls off; it does not fit that terminal
whole. Provider, model and reasoning effort are oneRoute:lockup, the way
the header rail already writes them. Mode and its permissions are one statement
of posture.Rate limits:is gone — it was apush_rowof a string literal
and could never say anything but "not available from provider telemetry". The
per-turn token ledger is gone too, because/tokensis that ledger's whole
subject and/statuswas printing six rows of it at the same weight as the
sandbox policy; the two facts that lived nowhere else, the cumulative in/out
split and the cumulative cache totals, survive on oneSession tokens:row.
Footer items:no longer prints ten internal config keys across the full
width —/statuslineowns them, and the report now points there in the same
row that points at/tokens. Two blank gutters do the grouping; the
===================rule under the title is gone.
The
/statuswindow-override key has its own labelled row. It used to be
parenthesised onto the end of the provenance row, which pushed
context_window in config.tomlpast the right edge at 80 columns and wrapped
the sentence.Window source:states the provenance andWindow override:
names the exact key — and the override row is omitted entirely when the value
is already configured, rather than advising you to set what you have set.
/helpno longer truncates anything. Every label and description used to run
through atruncate_to_widththat appended…, which in a two-hundred-row
list promises text no keystroke can reveal and lands mid-token:
(aliases: /qin…left the parenthesis hanging open. Descriptions now shed
whole fields — the alias parenthetical first, then trailing clauses at their
own joints, and only where there is no joint at all, the sentence's short form
on a whole word with no mark, keeping the head noun of a simple verb +
modifier + noun phrase rather than the adjectives that qualified it. The
focused row's description is restated under the filter at the panel's full
width, only when the row itself could not hold it, so a wide terminal does
not say the same sentence twice. At 60 columns that restatement is itself
shed — the/advisordetail stops beforesession— so the detail is longer
than the row, not a copy of the original sentence.
The
/helplabel column is measured instead of assumed. It was a flat 28
columns at every terminal size, so at 60 columns twenty blank cells sat
between/advisorand a description cut down to 21. Each group now sizes its
column to the labels it actually holds, which nearly doubles the description
column on a narrow terminal, and the label — the string you have to type —
reads one step brighter than the description that qualifies it.
/helpstopped spending rows on itself. The match count moved onto the filter
row it describes, the blank spacer under it is gone, and the footer no longer
repeatstype to filterwhile the filter box saysType to filtertwo lines
above — at 60 columns that duplicate was what pushed the footer onto a second
row. A group header also stopped printing▸ ▾: the selection cursor and the
collapsed chevron are the same glyph, and a focused collapsed group was
showing it twice for two different facts. Help now opens focused on the first
entry rather than the header above it.
The bottom status rail is no longer one run-on sentence. At 120 columns it
read▌· idle · Ollama · deepseek-v4-flash · max · Anonymous usage counts are on. … ⌥V:output · /context:context · fn+F1:keys— live state, route
identity, a telemetry consent notice, and keyboard hints all strung together
by the same middle dot in the same ink, so nothing was grouped and the eye
had nothing to skim by. At 80 columns it simply stopped mid-notice, and at 60
the row overflowed and was clipped by the terminal mid-word. The rail now
divides its groups with a blank gutter instead of another dot (the dot is
kept for peers inside a group), the model name reads one step brighter than
the qualifiers that narrow it, andEsc to interruptreads in the same hint
weight as the right-hand chords rather than in the separator weight.
Nothing on the status rail is ever truncated now. A notice sheds whole
sentences to fit, and if one sentence is still too long it sheds at the inner
joints — a colon, a semicolon — with the trailing mark cut so the phrase that
survives does not itself advertise that more was coming. Route identity sheds
the provider, then the reasoning effort, rather than rendering
deepseek-v4-flash-prev…; a clipped model name is worse than no model name
because routes share prefixes. Clauses rejoin without a Latin space after a
full-width stop, so the Japanese receipt reads as Japanese.
A notice now stands the standing facts down instead of queueing behind them.
Route identity and the ledger chips are still there in ten seconds; the
notice is not, so it takes the row and the key hints yield last. This is what
makes the telemetry receipt readable at 80 columns, where it used to be
simultaneously always present and never legible.
The status rail no longer advertises
/context:context. It was spending
eighteen columns of a 24-row screen to name a slash command that announces
itself the moment you type/; the rail advertises chords you cannot
discover any other way. The rail now reads the same at 80 columns as at 200.
The idle screen no longer has an absolute path stretched across it. The
workspace caption between the wordmark and "What do you want to accomplish?"
was composed at full length and then truncated to the lane width, which made
the centering inset(width - caption.width()) / 2evaluate to zero — so a
line that was written to be centered rendered flush-left and full-bleed,
cutting the centered whale/wordmark/prompt composition in half. The clipping
also destroyed the information it was supposed to carry: at 80 columns the
line read/private/tmp/claude-501/-Volumes-.../34267917-11f4-4d15-911a-…,
which tells the reader nothing about where they are. The caption now sheds
detail instead of being cut — MCP count first, then branch, then leading path
components — so it always fits with room to center, and the folder you are
standing in is the last thing to go. Elisions land on a path separator rather
than mid-directory.
Removed the placeholder engine tree in
crates/core/src/engine/. Its
Engine::runacceptedOp::SendMessage, appended to a journal, and emitted
TurnComplete { status: "completed" }without ever contacting a model, and
TurnExecutorwas a struct with a field-copy constructor and a
step < max_stepscomparison. Nothing in the workspace referenced any of it —
the only mention ofcodewhale_core::engineanywhere was a doc comment inside
the tree itself — but its comments ("the real turn loop is wired here in the
next slice") were whatdocs/ARCHITECTURE.mdleaned on to claim that
crates/coreowns the agent loop. There is now exactly one turn loop in the
workspace,Engine::run_turn, and a guard test fails if a second one appears.
docs/ARCHITECTURE.mdandAGENTS.mdnow say where it actually lives.
First-run onboarding no longer silently truncates its explanation in
languages that do not put spaces between words.wrap_wordssplit on
whitespace, so a Japanese sentence arrived as a single token, the
line-break check (which only fires once a line is non-empty) never
triggered, and the over-wide line was clipped by the terminal. At 80
columns the provider screen read
"Hosted providers need a key, but loca" and stopped — losing exactly the
half that tells the reader local runtimes need no key, on the screen where
they choose a provider. Space-less scripts now break by display width on
grapheme clusters, and a line may not begin with closing punctuation
(。,、,」,)and friends). Wrapping for languages that do use
spaces is unchanged.
Project instructions are bounded by one budget and no longer treat other
agents' files as law by default. Previously.claude/instructions.mdand
CLAUDE.mdsat at ranks 2 and 3 of the canonical instruction list — above
Codewhale's own.codewhale/instructions.md—.claude/rules/was an
auto-discovered rules directory, and.cursorrules,.cursor/rules,
.clinerules,.windsurf/rules,.gemini,.github/copilot-instructions.md
and.github/muse-instructions.mdwere all imported into the system prompt
with no opt-in. Dropping aCLAUDE.mdwritten for a different tool into a
repository silently made it standing authority here, which is an injection
surface rather than a convenience. Codewhale now readsAGENTS.md, the
cross-agent.agents/AGENTS.md, and its own instruction files by default;
every other agent's format is opt-in by name through
project_instruction_imports(envCODEWHALE_PROJECT_INSTRUCTION_IMPORTS),
imported files rank below Codewhale's own, and a workspace that contains an
un-imported format says so in a warning naming the exact setting.
Separately, a symlinked candidate rules directory —
.cursor/rules,
.windsurf/rules, or.geminipointing outside the workspace — was
traversed and its contents imported as instruction authority, because the
directory check followed the link while only the files inside it were
checked. The two instruction loaders now apply the same no-follow rule that
.codewhale/rules/already had.
The three separate ceilings on standing instructions (200 KiB for the
root->workspace chain, 500 KiB for the rules block, 40 KiB for imported
fragments, and a global layer that was merged in after the chain budget had
already closed and so counted against nothing) are replaced by a single
48 KiB aggregate budget covering all of them together. Instructions claim it
before rules, and are trimmed from the broadest scope inward so the
nearest-scope file is the last thing dropped rather than the first thing
stranded. Truncation still leaves an explicit marker.
Editing the workspace no longer grants the shell outbound network access.
workspace-writesandboxes are created network-restricted;curl, package
installs, andgit fetchinside a sandboxed shell are denied by the OS
sandbox unless network is granted explicitly. This closes a real gap rather
than tightening a working boundary: the elevation added in ) — tool-call stage
extraction with the existing execution and policy contracts preserved
() — bounded
multi-fileread_lintssupport (.- Lstarsky0 (.
- Paulo Aboim Pinto ().
- RepentStar (.
See for older releases.
SOCIAL SHARE CARD GENERATOR