The terminal arm serves six typed methods: list, run, send, wait, cancel
and reset. Any other method is an unknown_method refusal and unknown fields
are invalid_input. Nothing is passed through.
The owner stays tools/terminal_session. run, send, wait, cancel and reset
are each one nested core call on the existing terminal/* tool through the
invocation's own context, so planning, deny lists, the approval card, the
live posture, a child agent's grant, the per-invocation call cap and the
receipt on the tool result are the ones a core/call already has. list is a
read of the same registry, scoped to the invocation's workspace and thread.
A plugin's terminals are named dsh:<plugin id>:<name> inside the thread's
own owner, so a plugin cannot name the model's shells or another plugin's,
while the thread's existing terminal routes, resize, kill, delete-thread
cleanup and the restart instance id keep working on them unchanged.
Before the card, the arm refuses when the caller's shell policy or deny
list would refuse the direct tool (re-read from the live posture), when the
named terminal is not this plugin's, and when the plugin already holds 8
terminals in the session. Names, commands, input and timeouts are bounded.
Registry reads run on the blocking pool behind a four-permit gate and stop
waiting when the call is cancelled. A call whose cancel has fired does
nothing. Gate refusals reach the plugin as a short typed refusal; the
detail goes to the log. On platforms without the terminal owner every
method answers not_available after decoding.
Not reachable in production: no service is granted, the adapter
(src/dsh/terminal.ts) is not registered and nothing is advertised.
Known limits: list leaves a log line, not a receipt on the tool result
(arms cannot add one). A plugin's shells are not ended when the plugin is
disabled or revoked; they end with the thread. A failed command returns
its output without the exit code. No resize, kill or non-consuming read
for the plugin itself.
The two seam tests that expected every stub to answer not_available now
skip the terminal arm as they skip the network arm.
Checks (Linux farm): cargo nextest run --locked -p codewhale-engine --lib
extension_host: 191 run, 191 passed, 6979 skipped, including the four new
terminal tests. cargo clippy --locked -p codewhale-engine --all-targets
-- -D warnings: clean (run before a two-line test-only change). Extension
host: typecheck and build clean, node 451 tests 444 pass 7 skipped, bun 451
tests 445 pass 6 skip.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
cargo-deny's advisories job failed on ci/coord-push6 (run 42991087859) with
RUSTSEC-2026-0486, unsound, against rustix 1.1.4, which main also pins. The
advisory's solution is rustix >= 1.1.6. `cargo update -p rustix --precise 1.1.6`
also re-resolved windows-sys for nine crates that share it (colored, errno,
fd-lock, nu-ansi-term, terminal_size and others), each within its declared
range. Lockfile only.
Checks: hosted CI on this exact commit (cargo-deny, Windows, macOS, Linux).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The client still sends session/set_model to an agent that offers only the
pre-v1 models object. The check never saw that frame, so the deviation was
kept without being listed. A third leg drives the fixture agent with a plan
flag that offers the old models object, applies the model through it, and
allowlists the two frames that result: the client's session/set_model request
and the fixture's models field in its session/new response.
The two existing entries stay: the options fixture sends two fields that
exist only behind unstable schema features, and each entry names its feature.
Check on Linux x86_64: the_acp_client_and_its_fixture_agents_speak_official_v1_shapes
passed in the same 27-test run as the port.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
The Engine's one ACP client keeps its whole public API and replaces only the
wire. Each client now runs one named connection thread with a current-thread
runtime that drives the official agent-client-protocol v1 client over the
worker's pipes, behind the shared bounded line reader. The fleet host still
owns the process; the SDK is given pipes only, and no process, stdio or
unstable feature is enabled. The caller's thread keeps its loop: one request
at a time, the same deadline and the same 25 ms startup-abort poll, now over
an event channel instead of raw lines.
What we send is built from the official types (initialize, session/new, load,
resume, set_config_option, set_mode, prompt, session/cancel, the permission
outcome and the elicitation decline). session/set_model stays hand-built: it
is a pre-v1 method with no official type, sent only to an agent that offers
the old models object. What an agent sends is still read as JSON, so legacy
fields keep working.
Every handler and response callback on the connection thread returns Ok and
reports failure to the caller as an event or through the responder, because
an Err there ends the whole connection. A request is consumed by its response
callback and never dropped, so a missed deadline sends nothing to the agent.
Wire changes: request ids are UUID strings; fields at their defaults may now
appear; session/resume no longer sends an empty mcpServers; a malformed
response from an agent is dropped, so its request fails at the deadline; a
malformed line that is not a response is answered with an id-null -32600.
Behavior changes beyond the wire:
- Sign-in is detected by the -32000 error code only, no longer by any error
whose text contains "auth".
- A permission request the agent withdraws with $/cancel_request is answered
cancelled, never with a selection. The handler is not interrupted; that
needs a change to the handler interface.
- An oversized or non-UTF-8 line ends the connection with the shared reader's
refusal text.
- An agent's error text now includes its string data, bounded.
Tests. invalid_acp_envelopes_and_protocol_versions_are_refused changes: the
case with no jsonrpc member and the case with neither result nor error now
fail at a 3 s deadline instead of at once, because the SDK drops a malformed
response; the protocol version 2 case is unchanged. Added: a line over 8 MiB
ends the connection unparsed; another session's update is ignored and its
permission request answered cancelled; a withdrawn permission request is
answered cancelled; a missed deadline sends no $/cancel_request and ids are
strings; and a runtime test that an agent ignoring session/cancel is stopped
after the 10 s grace with the killed reason, using a new fixture plan flag
(the runtime launches the fixture with an empty environment, so the flag is
read from the plan file like the fixture's other switches).
Checks on Linux x86_64: cargo nextest run -p codewhale-engine --lib --test
acp_client_conformance fleet::acp external_agent the_acp_client: 27 run,
27 passed. cargo clippy -p codewhale-engine -p codewhale-app-server --tests:
clean. Windows was not compiled.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
The comment above the retry loop in publish_private_state_file still said
every holder of the stable file lets go quickly and that only the rename is
re-attempted. Since fc78dc86d8 a plugin UI grant can hold the file for up to
an hour, and access denied falls back to std::fs::rename with a flush through
a handle opened beforehand. Wording from the review in
artifacts/win-plugin-ui-20261111/REVIEW.md, question 8.
Known limit, recorded here since the fix's commit message omitted it: Windows
10 before 1709 and Server 2016 lack POSIX rename, so the 409 remains there for
as long as a grant lives.
Checks: rustfmt --check on the file. Comment-only.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- The snapshot and the topic builder move to lib/release-docs.ts, imported
only by /llms-full.txt and /docs-markdown. llms.txt reads a small
agent-docs.release-index.json (tag, present and omitted slugs) that the same
script writes and the check compares with the snapshot. agent-docs.ts is
back to the main-branch file only, so the docs pages no longer bundle it.
- release.yml, job sync-release-record only: run derive-release-docs after
derive-facts, check-release-docs in the prove step, and carry the snapshot
and its index in the proposed paths.
- derive-release-docs now exits 1 when the tag is not in the checkout and the
committed snapshot is for a different release than the published one.
- web/maintainer/derived.txt no longer lists the snapshot: that job checks out
without tags and cannot regenerate it.
- The zh runtime-reference description drops the words for "main branch".
- A source missing at the tag reads "<path> was added after <tag> and is not
part of this release; the document below is what <tag> has."
Checks: npm test 91 files / 1304 tests, lint, tsc, check:locales, check:docs,
check:facts, build, and scripts/preflight.sh --check pass.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The machine-readable docs embedded main-branch documents under a label that
said so, so they could describe unreleased work. They now serve a committed
snapshot of the documents as they were at the published release tag.
- web/data/agent-docs.release.json is produced by derive-release-docs.mjs from
`git show <tag>:<path>` for every registered docs source, and records the tag
it was made from. Run by prebuild; with no tag in the checkout it keeps the
committed file and warns, as the release baseline does.
- check-release-docs.mjs (in check:facts and preflight --check) fails when the
snapshot tag differs from the published release read by facts-lib, when a
hash does not match, when a registered topic is missing, and, if the tag
resolves, when the snapshot differs from the tagged source.
- data/agent-docs.generated.json and its check are unchanged and still feed
the docs pages.
- A topic with no source at the tag is omitted from the routes and the index.
A topic with some sources keeps them and says which file does not exist at
the tag: today only docs/RUNTIME_ROUTES.md.
- The runtime reference description no longer says "main branch".
Checks: npm test 91 files / 1302 tests, lint, tsc, check:locales, build, and
scripts/preflight.sh --check pass.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
After a hard kill, the Engine's socket owner record can outlive both the
process and its data directory. Every later start then failed with
"Failed to resolve ... tasks/runtime".
Host startup now passes its selected store into the owner probe. The
record is retired only when all of these hold: the owning process is
dead, the record's data directory equals the selected store's root in
the form RuntimeStoreBinding::for_store_dir records
(checked_runtime_store_root, which creates nothing), and that root is
definitively NotFound. The only path inspected is the selected store's
own; the recorded path is compared, never followed. A record naming
another store, or the selected store while it still exists, keeps the
existing refusal and stays on disk, which is what the test
stale_publication_cannot_select_another_store_or_execution_scope
protects.
admit_runtime_host passes the store it already holds: one call-site line
in runtime_api.rs.
The regression probes first with a different selected store while the
named store is gone and requires the record to stay, then with the named
store and requires the discard.
Known limits. For a store that is gone, the root can only be normalised
lexically, while a host records the symlink-resolved root. A selected
store spelled through a symlink (a home under macOS /tmp or
/var/folders), one spelled in another letter case, and a Windows
verbatim root do not match, so those starts keep the existing refusal.
A store that is only temporarily absent, such as an unmounted volume,
now gets a first start with an empty store, as it already does after a
clean stop; no store is deleted.
This squashes the first change and its repair after review.
Checks (Linux x86_64 build farm, cargo nextest, CARGO_INCREMENTAL=0,
RUST_MIN_STACK=8388608):
- run 20261111T063638Z-stalefix-nextest-fc68fe: 58 passed, 0 failed
- control with the previous probe behaviour, run
20261111T064458Z-stalefix-control-523e53: 16 passed, 1 failed (the
regression, as intended)
- clippy for the crate with CI's flags, run
20261111T070931Z-stalefix-clippy2-ba438c: exit 0
- independent review rerun on a later base, run
20261111T073145Z-rvstale2-nextest-216f38: 58 run, 58 passed
Not run: Windows, macOS, hosted CI, an installed binary.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Fable 5.2 <[email protected]>
The trusted local ACP profile now offers reviewed plugin tools and the tools
of MCP servers configured in Codewhale, under the same permission setting.
The reference still said MCP was unavailable on this transport, which
contradicted docs/EXTENSIONS.md. Client-supplied MCP servers stay unavailable.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
The journey looked for the workspace path as a substring of the frames
re-serialized as JSON. A Windows path contains backslashes, which JSON
escapes, so that substring can never match there, and the three-OS suite runs
this test on every landing. The test now parses the plugin's completed tool
result, takes its workspace value and compares it with the session's
workspace after canonicalizing both, so it also holds where the temporary
directory is reached through a symlink or a short name.
Checks on the stack tip (farm, Linux x86_64, cargo nextest; run
20261111T062016Z-acpplugland-tests-74e101: 97 run, 97 passed, 0 failed):
- acp_journey: 2 passed, 0 failed
Not run: Windows and macOS, which are the platforms this change is for.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
The ordinary tool build drops MCP tools for a child engine. The ACP build now
does the same: for a child it adds no MCP tool to the catalog or to the turn's
allow list. No child engine runs an ACP turn today (children use the Child
profile and ACP turns offer no spawn tool), so this closes a path that cannot
be reached yet and keeps the two builds from drifting apart.
acp_tool_build_with_mcp becomes pub(super) so the child-host tests can call
it. The new test child_engine_gets_no_mcp_tools_from_the_acp_tool_build hands
the same live MCP tool to a root ACP engine (in the catalog, on the allow
list) and to a child engine (in neither).
Checks on the stack tip (farm, Linux x86_64, cargo nextest; run
20261111T062016Z-acpplugland-tests-74e101: 97 run, 97 passed, 0 failed):
- codewhale-engine tests matching acp, host_profile or child_host:
40 passed, 0 failed, the new test among them
- cargo clippy --workspace --all-targets --all-features --locked (CI flags):
exit 0, no lint output (run 20261111T062101Z-acpplugland-clippy-4c7ea7)
Not run: the new test against the build without this change. It asserts on
the three values the change alters (the MCP names, the catalog and the allow
list), and the root half shows they are set when the engine is not a child.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
The ACP tool build now installs the same extension-host plugin tools and the
session's connected MCP tools that an ordinary turn gets, through the same
host attachment and MCP pool; nothing new loads or registers tools. Plugin and
MCP calls reach the editor as ACP tool calls and ask through the permission
setting. initialize advertises the real state under agentCapabilities._meta
(plugins and configuredMcpServers follow the extension_host and mcp flags;
client-supplied mcpServers stay refused). acp_journey drives one fixture plugin
tool and one fixture MCP tool from an ACP prompt.
Landing: the journey test connects through the shared ACP test client
(tests/acp_support) that the schema check introduced, the ACP line in
CHANGELOG.md says what is now attached, and the agent docs are regenerated
for the EXTENSIONS.md sentence. This commit does not build alone: the next
one calls acp_turn_tool_build.
Checks on the stack tip (farm, Linux x86_64, cargo nextest, CARGO_INCREMENTAL=0,
RUST_MIN_STACK=8388608; run 20261111T062016Z-acpplugland-tests-74e101:
97 run, 97 passed, 0 failed):
- acp_journey: 2 passed, 0 failed
(editor_drives_a_session_through_permission_cancel_and_load,
plugin_and_mcp_tools_run_as_acp_tool_calls_through_the_permission_dial)
- acp_conformance: 1 passed, 0 failed
- acp_client_conformance: 1 passed, 0 failed
- single_turn_loop: 14 passed, 0 failed
- codewhale-app-server tests matching acp_server: 39 passed, 0 failed
- codewhale-engine tests matching acp, host_profile or child_host:
40 passed, 0 failed
- cargo clippy --workspace --all-targets --all-features --locked (CI flags):
exit 0, no lint output (run 20261111T062101Z-acpplugland-clippy-4c7ea7)
- Mac: cargo fmt --all -- --check clean; scripts/preflight.sh --check OK
Not run: Windows, macOS, hosted CI, a real editor.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Fable 5.2 <[email protected]>
DSH-13. The remote arm decodes four typed methods (status, pair, clients,
revoke) and refuses any other method as unknown_method. A plugin never
receives a pairing secret or a client token: pair returns only a grant id,
the host reads the secret through RemoteGrants::awaiting_host to present
it to the person, a remote client redeems it once (a replay is refused),
and RemoteGrants::authorize admits that client only for the session the
grant was made in. Grants are bound to plugin id and session, so another
plugin or another session cannot list or revoke them, and they outlive the
invocation that asked, so a later invocation in the same session can
still revoke them. Pairings expire after 10 minutes, clients after 30, and
at most 4 pairings may await the host per plugin and session. No public
listener or tunnel exists: status says so and listen/tunnel are unknown
methods.
Known limits: the Runtime API does not yet read or redeem these grants
(exact request in the review packet), and whether a plugin may use the
remote service at all is not checked before the arm (SEAM-CONTEXT).
The TypeScript side adds the remote adapter (ctx.remote) that forwards
each method through host.broker with a typed payload, plus its
compatibility name and regenerated dist.
Checks, on the landing tip (this commit with the test commit after it):
- Linux x86_64: sh scripts/with-hermetic-test-home.sh cargo nextest run
--locked --no-fail-fast -p codewhale-engine -E 'test(extension_host)'
191 tests run: 191 passed (1 slow), 6981 skipped
- Linux x86_64: cargo clippy --workspace --all-targets --all-features
--locked -- -D warnings -A clippy::uninlined_format_args
-A clippy::too_many_arguments -A clippy::unnecessary_map_or
exit 0, no warnings
- extension-host: npm run typecheck exit 0; npm run build exit 0, tree
unchanged; npm test 451 tests, 444 pass, 0 fail, 7 skipped
Tests under the broker grant check (folded in from the landing):
The remote arm lands on top of the one grant check: broker tickets are
minted only for granted services, and no service is granted outside
tests. Two tests have to say so:
- the remote journey test grants workspace and remote for its own thread
with TestGrants, as the network arm's test does, so it gets the remote
ticket it pairs with and the workspace ticket it presents as the wrong
service;
- the grant test's "the landed stub still refuses" assertion skips remote
as it skips network: the filled arm decodes first and answers
unknown_method for the probe method. The remote journey test asserts
that refusal through the real table.
No production change. The remote service is granted to no plugin, so no
remote ticket is minted and the arm cannot be reached until a plugin's
reviewed record grants it.
Checks, on this commit (Linux x86_64): the engine's extension_host tests
under cargo nextest, 191 tests run: 191 passed (1 slow), 6981 skipped.
The remote journey test, the grant test and the ticket test passed by
name.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Fable 5.2 <[email protected]>
On Windows, disabling or revoking a plugin whose UI had just been opened was
refused with 409:
disabled failed for 'panel': failed to atomically and durably persist
...\runtime\plugins\state.json: Access is denied. (0x80070005)
A plugin UI grant keeps the plugin state file open for up to an hour, so that
every asset request can prove the state file is still the one the grant was
minted against. The Windows publication replaced the file with MoveFileExW, and
Windows refuses that replacement for as long as any handle is open on the
destination, whatever its share mode. The bounded retry around it was sized for
a scanner or an ACL pass that lets go within about a second, so it ran out and
the refusal surfaced. While any grant was live, every state change of every
plugin was refused, from this process or another: trust, enable, disable,
revoke and the state prune of an uninstall.
When MoveFileExW answers access denied, publication now opens the hardened
temporary and falls back to std::fs::rename, which replaces the destination
with POSIX rename semantics. A reader that shares delete access keeps its
handle on the superseded file and no longer blocks the replacement, which is
what Unix already did. That path does not write through, so the published file
is flushed through the handle opened before the rename, and a flush failure is
reported. A holder that denies delete sharing still gets the bounded retry and
then the refusal, with the stable file untouched.
The grant code is unchanged. After a replacement the grant's handle identifies
the superseded file, its identity check fails, and the grant is stale, as on
Unix.
Not covered: a volume without POSIX rename (FAT, exFAT, some network shares)
keeps the old refusal while a grant is open.
The plugin UI tests now print the response body when disable, enable or revoke
is refused; a failing run used to show only the status.
No released version is affected: the grant arrived after v0.10.2, so there is
no CHANGELOG line.
Evidence
- Hosted Windows before the fix (run 42972872196, the two test changes alone):
the new registry test failed with "failed to atomically and durably persist
...\state.json: Access is denied. (0x80070005)" (3 run, 2 passed, 1 failed),
and plugin_ui ran 3, passed 1, failed 2 with the 409 above for disable and
the same error for revoke.
- Hosted Windows with this change on main ecfbdbccaa (run 42978481553):
codewhale-plugins --lib 200 run, 200 passed, 1 skipped; plugin_ui 3 run, 3
passed under nextest in the hermetic test home, and "test result: ok. 3
passed; 0 failed" under cargo test. Run 42975064244 gave the same result on
main 8ddd9025c0.
- Linux build farm with this change on main ecfbdbccaa: nextest 214 run, 214
passed (3 plugin_ui, 211 codewhale-plugins). On main 8ddd9025c0, clippy for
both crates with all targets and all features was clean.
- cargo fmt --all -- --check and scripts/preflight.sh --check pass.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
Every /docs-markdown topic except install embeds the repository's
documents as they are on main, and /llms-full.txt carries all of them after
the llms.txt text. Those documents describe behaviour the published release
does not have (a mode set without Operate, and commands and settings that
exist only in source), and nothing in the served text said so: the modes
topic was introduced with the released description and then contradicted
by the document under it.
Each of those documents now starts with the sentence the reference pages
already show for a main-branch document, naming the published tag, and a
link to the repository at that tag. The sentence is the existing
REFERENCE_GUIDE_COPY.scope string; nothing is added to a dictionary. It is
left out when the release is unknown, as on the reference pages.
This labels the statements; it does not remove them. Serving the documents
as they are at the release tag needs the generator in
scripts/docs-sources-lib.mjs to read the tag, which this change does not
touch. A link per document at the tag is not printed because a document can
exist on main and not at the tag.
Test: one more case in lib/llms-txt.test.ts counts the label in the full
text: once for every topic except install. Removing the label fails it.
Checks from web/: tsc --noEmit and eslint are clean; vitest on the four
files that read these texts, 403 of 403.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
An agent asked how to install Codewhale and what it can do today reads
/llms.txt. The file now opens with the locked headline and subhead and says
"The latest release is v{version}." from the published-release object.
The install text is rendered from lib/content/install.ts, the module the
/install page renders: one command per platform and channel, what the
installer adds to PATH and the opt-out, the verify command and its output,
the three first steps, usage counting and how to turn it off, update,
removal, known problems, the other channels and the mirrors. The page's own
conditions pick the wording: the uninstall command appears only when the
published release has it, and the version-free strings are used when the
release is unknown.
The six parts of the product are printed with their state words from
lib/content/product-map.ts and the locked map strings. A row whose computed
state no longer matches the state its sentence was written for prints the
state without the sentence, so a desktop release object flips that line to
"Released" and drops "no public download yet".
/docs-markdown/install.md serves the same install text as a page, in place
of the main-branch install guide, with the facts stamp at its end. Like
/install, it links the full guide at the release tag. /llms-full.txt
carries the new head and that page.
Removed from llms.txt because they contradict the released version or the
copy rules:
- the old identity line and summary sentence;
- the publish date on the release line (the changelog carries it); the date
the release was read stays, inside the provenance stamp;
- the "Unreleased on main" list of changelog headlines;
- the entries for pages that now redirect (product, runtime, constitution,
roadmap, feed, digest, community) and for /signin, which the sitemap no
longer lists;
- the hand-typed states on the /app entry, which now prints the typed
surface states the page prints.
The install entries use the install page's own description.
Not changed: the routes stay static and read the build's facts, while
/install revalidates against the runtime facts snapshot, so after a new
release the page can name it before the text does, until the next deploy.
The licence, default-model and sandbox lines still read the source tree.
Tests: lib/llms-txt.test.ts is rewritten around invariants (9 tests): the
head and the index, no link to a redirected route, the version equals the
release object and the source version never appears, every statement the
rendered /install page makes from the install content is in both texts,
each map row with its computed state and a fixture desktop release flipping
only the desktop line, no price, plan or date outside the stamp, unknown
stays unknown, and the routes. The three assertions in
lib/facts-invariants.test.ts that quoted the old release line now quote the
new one. Nine single-line mutations of the builders each fail one of these
tests.
Checks from web/: prebuild, check:facts, check:docs (29 topics) and
check:locales pass; tsc --noEmit and eslint are clean; node --test 7 of 7;
vitest 90 files, 1294 of 1294; next build succeeds.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
2026-11-10 23:04:05 -08:00
58 changed files with 4515 additions and 531 deletions
@@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
- **External agents over ACP:** Codewhale now drives external agents through the official Agent Client Protocol SDK. Request ids on the wire are strings, and an agent's error is treated as a sign-in problem only when it carries the protocol's auth-required code.
- **Website home page:** the page is reorganized around what a visitor does: a dated OrcaRouter offer strip with a visible referral tag, one proof strip (release, stars, contributors), a models section with the newest first-party models, one capabilities section that includes Computer Use, an "Everything is a plugin" teaser and a community panel whose live GitHub numbers fill in from the browser and hide when unavailable. Unused home copy was removed from all 18 site languages.
- **Website models and plugins:** /models leads with recently added models, three ways to connect (DeepSeek, first-party providers, any OpenAI-compatible endpoint) and a catalog grouped by maker; Ollama and OpenRouter are listed but no longer featured. /plugins leads with "Everything is a plugin" (credited to DeepSeek Harness) and shows MCP connectors from the connection catalog.
- **Model catalog:** Claude Fable 5.2 (`claude-fable-5-2`) and Gemini 4 Carbon (`gemini-4-carbon`) are in the bundled catalog, and a daily catalog-refresh workflow proposes new first-party models from models.dev as a pull request. The workflow has not run in GitHub Actions yet.
@@ -38,9 +39,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- **Docs for agents match the release:**`/llms-full.txt` and `/docs-markdown/*` now serve the documents as they were at the published release tag, from a committed snapshot that is checked against the published release.
- **Extension host (experimental): trusted plugins ask for review once more.** With `extension_host` turned on, plugins you already trusted are shown for review again after this update, because the capability table they were reviewed against gained the `remote` service. No plugin can use that service yet: none is granted it.
- **Website text for agents:** llms.txt now gives agents the install steps for the released version, the current release and the state of each part of Codewhale, from the same content as the pages. `/docs-markdown/install.md` serves those install steps as a page, and the main-branch documents beside it now say which release they can run ahead of.
- **Website design:** codewhale.net now uses the desktop app's 5.3 light and dark colour roles and Geist / Geist Mono, replacing the warm-paper palette and Shannon Sans; the vendored design copy is synced to 5.3.0 and the whale-road ocean bands are unchanged.
- ACP: editors can start, list, load, prompt and cancel Codewhale sessions and approve or reject tool permissions through the Runtime; prompts with image or audio blocks and relative session directories are now refused instead of ignored, and edit and read tools report the right kind. Plugins, DSH and MCP tools are not yet attached to ACP-driven sessions. A real-editor journey is not yet proved.
- ACP: editors can start, list, load, prompt and cancel Codewhale sessions and approve or reject tool permissions through the Runtime; prompts with image or audio blocks and relative session directories are now refused instead of ignored, and edit and read tools report the right kind. Sessions an editor drives now run the plugin tools (TypeScript and DSH, when the extension host is on) and the MCP servers configured in Codewhale; their calls show as ACP tool calls and ask through the session's permission setting, and `initialize` reports the state under `agentCapabilities._meta.codewhale`. MCP servers an editor passes in `mcpServers` and configured shell hooks still do not run in ACP sessions. A real-editor journey is not yet proved.
- Extension host: the `sessions` and `systemPrompt` services now come from a reviewed DSH adapter instead of inline shims, and `sessions.flush` refuses an empty checkpoint ticket. Session reads and events and the `agent/pre-step` hook are still not available to plugins. (CW-7959)
- Extension host: DSH service adapters now have one ticketed `dsh/broker` request to the core, each ticket is bound to the plugin, the service and the session, and every service (workspace, llm, network, settings, catalog, terminal and remote) refuses with a typed `not_available` until its broker lands. Nothing new is offered to plugins yet. (CW-8012)
- Fleet member starts are now durable: replaying an operation key after a timeout or restart returns the original worker, and GET /v1/threads/{id}/members/operations/{key} reports the outcome. A member start now answers with `state``accepted` (200) or `unknown` (202) instead of `started` or `queued`. Verified by local HTTP tests on Linux; a real Engine restart is simulated, not exercised. (CW-7964)
@@ -53,6 +57,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- **Starting after a hard kill:** the Engine now starts when its process was killed and its data directory was then deleted. A socket owner record whose process is dead and whose data directory, the one this start selected, no longer exists is discarded instead of failing every start with "Failed to resolve ... tasks/runtime". A home reached through a symbolic link, such as one under macOS /tmp, is not covered yet.
- The install guide's note for a first message that gets no answer now describes v0.10.2: connect a model with `/provider`, and restart if messages stay saved for later after you connect. It described v0.10.0's key screen. The Chinese guide carries the same correction.
- Project activity on the site now comes from a server-side snapshot with a freshness record, the browser no longer calls GitHub, and count sentences use the right plural form in every language.
- The install page now gives the exact steps for the released version on every platform, including what the installer adds to PATH, how to turn off usage counting, and how to remove it, and the install command no longer gets cut off on phones.
"Client-supplied MCP servers are unsupported by Codewhale's ACP adapter. Use an empty mcpServers list, or configure MCP through codewhale app-server --http and the Runtime API. ACP v1 stdio MCP support is incomplete.",
"Client-supplied MCP servers are unsupported by Codewhale's ACP adapter. Use an empty mcpServers list; sessions use the MCP servers and plugins configured in Codewhale itself.",
)),
Some(_)=>Err(AcpError::invalid_params("mcpServers must be an array")),
constMCP_FIXTURE: &str=r#"import readline from 'node:readline';
readline.createInterface({input:process.stdin}).on('line', line => {
const r = JSON.parse(line); if (r.id === undefined) return;
let result = {};
if (r.method === 'initialize') result = {protocolVersion:'2024-11-05',capabilities:{tools:{}},serverInfo:{name:'fixture',version:'1'}};
if (r.method === 'tools/list') result = {tools:[{name:'echo',description:'Echo text',inputSchema:{type:'object',properties:{text:{type:'string'}},required:['text']}}]};
if (r.method === 'tools/call') result = {content:[{type:'text',text:'mcp-echo:' + r.params.arguments.text}]};
this.raise(expr.start,"Label '"+maybeName+"' is already declared");
}
}
@@ -12795,7 +12795,8 @@ var init_compatibility = __esm({
"prompt",
"avatars",
"mcp",
"shellHooks"
"shellHooks",
"remote"
]
},
refused_services:[
@@ -14483,6 +14484,51 @@ var init_agents = __esm({
}
});
// src/dsh/remote.ts
functionlabel(value){
if(value===void0)returnvoid0;
if(typeofvalue!=="string")thrownewRpcError(ErrorCode.InvalidParams,"remote.pair label must be a string");
returnvalue;
}
functiongrant(value){
if(typeofvalue!=="string"||value.length===0||value.length>128)thrownewRpcError(ErrorCode.InvalidParams,"remote.revoke needs the grant id remote.pair returned");
this.raise(expr.start,"Label '"+maybeName+"' is already declared");
}
}
@@ -15147,7 +15147,8 @@ var init_compatibility = __esm({
"prompt",
"avatars",
"mcp",
"shellHooks"
"shellHooks",
"remote"
]
},
refused_services:[
@@ -16662,6 +16663,52 @@ var init_agents = __esm({
}
});
// src/dsh/remote.ts
functionlabel(value){
if(value===void0)returnvoid0;
if(typeofvalue!=="string")thrownewRpcError(ErrorCode.InvalidParams,"remote.pair label must be a string");
returnvalue;
}
functiongrant(value){
if(typeofvalue!=="string"||value.length===0||value.length>128)thrownewRpcError(ErrorCode.InvalidParams,"remote.revoke needs the grant id remote.pair returned");
this.raise(expr.start,"Label '"+maybeName+"' is already declared");
}
}
@@ -12773,7 +12773,8 @@ var init_compatibility = __esm({
"prompt",
"avatars",
"mcp",
"shellHooks"
"shellHooks",
"remote"
]
},
refused_services:[
@@ -14159,6 +14160,51 @@ var init_agents = __esm({
}
});
// src/dsh/remote.ts
functionlabel(value){
if(value===void0)returnvoid0;
if(typeofvalue!=="string")thrownewRpcError(ErrorCode.InvalidParams,"remote.pair label must be a string");
returnvalue;
}
functiongrant(value){
if(typeofvalue!=="string"||value.length===0||value.length>128)thrownewRpcError(ErrorCode.InvalidParams,"remote.revoke needs the grant id remote.pair returned");
if(typeofvalue!=='string')thrownewRpcError(ErrorCode.InvalidParams,'remote.pair label must be a string')
returnvalue
}
functiongrant(value: unknown):string{
if(typeofvalue!=='string'||value.length===0||value.length>128)thrownewRpcError(ErrorCode.InvalidParams,'remote.revoke needs the grant id remote.pair returned')
@@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
- **External agents over ACP:** Codewhale now drives external agents through the official Agent Client Protocol SDK. Request ids on the wire are strings, and an agent's error is treated as a sign-in problem only when it carries the protocol's auth-required code.
- **Website home page:** the page is reorganized around what a visitor does: a dated OrcaRouter offer strip with a visible referral tag, one proof strip (release, stars, contributors), a models section with the newest first-party models, one capabilities section that includes Computer Use, an "Everything is a plugin" teaser and a community panel whose live GitHub numbers fill in from the browser and hide when unavailable. Unused home copy was removed from all 18 site languages.
- **Website models and plugins:** /models leads with recently added models, three ways to connect (DeepSeek, first-party providers, any OpenAI-compatible endpoint) and a catalog grouped by maker; Ollama and OpenRouter are listed but no longer featured. /plugins leads with "Everything is a plugin" (credited to DeepSeek Harness) and shows MCP connectors from the connection catalog.
- **Model catalog:** Claude Fable 5.2 (`claude-fable-5-2`) and Gemini 4 Carbon (`gemini-4-carbon`) are in the bundled catalog, and a daily catalog-refresh workflow proposes new first-party models from models.dev as a pull request. The workflow has not run in GitHub Actions yet.
@@ -38,9 +39,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- **Docs for agents match the release:**`/llms-full.txt` and `/docs-markdown/*` now serve the documents as they were at the published release tag, from a committed snapshot that is checked against the published release.
- **Extension host (experimental): trusted plugins ask for review once more.** With `extension_host` turned on, plugins you already trusted are shown for review again after this update, because the capability table they were reviewed against gained the `remote` service. No plugin can use that service yet: none is granted it.
- **Website text for agents:** llms.txt now gives agents the install steps for the released version, the current release and the state of each part of Codewhale, from the same content as the pages. `/docs-markdown/install.md` serves those install steps as a page, and the main-branch documents beside it now say which release they can run ahead of.
- **Website design:** codewhale.net now uses the desktop app's 5.3 light and dark colour roles and Geist / Geist Mono, replacing the warm-paper palette and Shannon Sans; the vendored design copy is synced to 5.3.0 and the whale-road ocean bands are unchanged.
- ACP: editors can start, list, load, prompt and cancel Codewhale sessions and approve or reject tool permissions through the Runtime; prompts with image or audio blocks and relative session directories are now refused instead of ignored, and edit and read tools report the right kind. Plugins, DSH and MCP tools are not yet attached to ACP-driven sessions. A real-editor journey is not yet proved.
- ACP: editors can start, list, load, prompt and cancel Codewhale sessions and approve or reject tool permissions through the Runtime; prompts with image or audio blocks and relative session directories are now refused instead of ignored, and edit and read tools report the right kind. Sessions an editor drives now run the plugin tools (TypeScript and DSH, when the extension host is on) and the MCP servers configured in Codewhale; their calls show as ACP tool calls and ask through the session's permission setting, and `initialize` reports the state under `agentCapabilities._meta.codewhale`. MCP servers an editor passes in `mcpServers` and configured shell hooks still do not run in ACP sessions. A real-editor journey is not yet proved.
- Extension host: the `sessions` and `systemPrompt` services now come from a reviewed DSH adapter instead of inline shims, and `sessions.flush` refuses an empty checkpoint ticket. Session reads and events and the `agent/pre-step` hook are still not available to plugins. (CW-7959)
- Extension host: DSH service adapters now have one ticketed `dsh/broker` request to the core, each ticket is bound to the plugin, the service and the session, and every service (workspace, llm, network, settings, catalog, terminal and remote) refuses with a typed `not_available` until its broker lands. Nothing new is offered to plugins yet. (CW-8012)
- Fleet member starts are now durable: replaying an operation key after a timeout or restart returns the original worker, and GET /v1/threads/{id}/members/operations/{key} reports the outcome. A member start now answers with `state``accepted` (200) or `unknown` (202) instead of `started` or `queued`. Verified by local HTTP tests on Linux; a real Engine restart is simulated, not exercised. (CW-7964)
@@ -53,6 +57,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- **Starting after a hard kill:** the Engine now starts when its process was killed and its data directory was then deleted. A socket owner record whose process is dead and whose data directory, the one this start selected, no longer exists is discarded instead of failing every start with "Failed to resolve ... tasks/runtime". A home reached through a symbolic link, such as one under macOS /tmp, is not covered yet.
- The install guide's note for a first message that gets no answer now describes v0.10.2: connect a model with `/provider`, and restart if messages stay saved for later after you connect. It described v0.10.0's key screen. The Chinese guide carries the same correction.
- Project activity on the site now comes from a server-side snapshot with a freshness record, the browser no longer calls GitHub, and count sentences use the right plural form in every language.
- The install page now gives the exact steps for the released version on every platform, including what the installer adds to PATH, how to turn off usage counting, and how to remove it, and the install command no longer gets cut off on phones.
Native hooks run first, then mod listeners in core registration order. Denial always wins; the last accepted input revision wins. Rust re-prepares revised arguments and reruns the existing authority, policy, and approval checks. Input revisions use the existing 32 KiB hook limit; context and reasons use the existing sanitizers. Unloading a fiber removes its listener, and disabling or revoking an owner retires its admitted handles before host teardown. Other workspaces do not receive the call.
This bridge runs on Engine sessions, including model tools, gated code-mode calls, and `exec.core` calls. Plugins, DSH and MCP tools are not yet attached to sessions driven over ACP (`codewhale serve --acp`); the ACP server's capabilities say so. Attaching them is in progress for 0.10.3. Prepend/global listener ordering, DSH runtime/agent handles, around-execution wrappers, and post-result rewriting are not provided. Native `tool_call_after` remains an observer contract.
This bridge runs on Engine sessions, including model tools, gated code-mode calls, and `exec.core` calls. Sessions an editor drives over ACP (`codewhale serve --acp`) get the same extension host, under the same `[features]` flags (`extension_host`, `mcp`): reviewed plugin tools (TypeScript and DSH) and the MCP servers configured in Codewhale appear as ACP tool calls and ask through the session's permission setting; Plan stays read-only. The ACP server's capabilities say so (`agentCapabilities._meta.codewhale`). Servers an ACP client passes in `mcpServers` and configured shell hooks are not attached. Prepend/global listener ordering, DSH runtime/agent handles, around-execution wrappers, and post-result rewriting are not provided. Native `tool_call_after` remains an observer contract.
"cd web && npm ci (if node_modules is missing) && npm run prebuild, then commit web/lib/facts.generated.ts and web/data/*.generated.json (check-facts.mjs names the stale file); for a stale docs/RUNTIME_ROUTES.md run node web/scripts/check-runtime-routes.mjs --write, then node web/scripts/derive-facts.mjs"\
"cd web && npm ci (if node_modules is missing) && npm run prebuild, then commit web/lib/facts.generated.ts and web/data/*.generated.json and web/data/agent-docs.release.json and agent-docs.release-index.json (check-facts.mjs and check-release-docs.mjs name the stale file); for a stale docs/RUNTIME_ROUTES.md run node web/scripts/check-runtime-routes.mjs --write, then node web/scripts/derive-facts.mjs"\
echo"== website generated facts: SKIPPED, node is not on PATH; run cd web && npm run check:facts && node scripts/check-runtime-routes.mjs before pushing" >&2
"content":"# Writing an extension tool or command\n\nThe experimental TypeScript extension host runs reviewed plugin code in a\nshared Node process (or, as an opt-in, Bun). Rust still owns sessions, tool admission, approval and\nexecution. Extensions currently contribute tools and slash commands; they\ncannot provide an approval service, run a second agent loop or replace\nbuilt-in tools or commands.\n\n## Quick start\n\nStart with [hello-extension](examples/plugins/hello-extension/hello.mts) and its\n[plugin.json](examples/plugins/hello-extension/plugin.json). The example is\nexecuted unchanged by the host tests. It returns a greeting and does not read\nfiles or use the network.\n\n1. Explicitly enable `[features] extension_host = true` in your configuration,\n or start Codewhale with `codewhale --enable extension_host` (the flag goes\n before any subcommand).\n2. Install the example's directory using `/plugin install <local-directory>`.\n3. Run `/plugin validate hello-extension`, then `/plugin show hello-extension`.\n4. Run `/plugin enable hello-extension` to open its content/capability review.\n Read the review and personally run its exact `/plugin trust <token>` command,\n then enable the plugin. Trust alone does not execute the code.\n5. Ask Codewhale to use `hello_greet`. The tool follows the normal approval gate.\n6. Run `/hello-greet Codewhale`. The command runs when you type it, and shows\n its answer in the transcript with its origin, `extension:hello-extension`.\n\nAn authoring agent should stop after installation and validation and present\nthe review to the person. Do not trust or enable a bundle automatically.\n\n## Locations, installation and trust\n\nUser bundles live under `~/.codewhale/plugins/`; workspace bundles live under\n`<workspace>/.codewhale/plugins/`. New bundles use Agent Plugins v1.0.0\n`plugin.json`. Declare `extensions[\"net.codewhale\"].native.path` as one regular\n`.mjs`, `.js` or `.mts` file inside the bundle. Directories and other extensions\nfail validation when the host is enabled. With the flag off, native entries\nremain inventory-only.\n\nA plugin may split its code across several entries with `native.paths` (up to\n64, alone or beside `path`). They are activated in the order declared, as the\nfibers of one plugin: they share one owner, so one disable, review change or\ncrash tears all of them down together. Each entry is a separate module with its\nown `apply`, and tool and command names must be unique across them. If an entry\nfails to activate (throws, requires a service the host does not provide, or has\na registration refused), only that entry is withdrawn: entries that already\nactivated stay registered, later entries still activate, and the plugin fails\nonly when no entry activates. Listing the same file twice is one entry.\n\n## Runtime\n\n`[extension_host] runtime` selects what runs the host. Node is the default.\nBun is an opt-in: it is qualified on macOS only, and becomes the default only\nafter an explicit, recorded cutover.\n\n```toml\n[extension_host]\nruntime = \"node\" # default: Node only\n# runtime = \"bun\" # Bun only; fails rather than falling back to Node\n# runtime = \"auto\" # Bun >= 1.4.0 if one is found and starts, otherwise Node\n# bun = \"~/.bun/bin/bun\" # the only Bun tried when set\n# node = \"/opt/homebrew/bin/node\" # the only Node tried when set\n# mcp_backend = \"rust\" # temporary opt-out; default is \"host\"\n```\n\nThe default `mcp_backend = \"host\"` uses the pinned MCP SDK independently of\n`[features] extension_host`, which controls optional Native extensions. Existing\nstdio and HTTP servers use modern discovery when supported; explicitly configured\nlegacy SSE servers use their legacy handshake. Rust retains process and network\nauthority, credential resolution, catalog admission, tool approval and cancellation.\nThe SDK receives opaque session selectors and exact operation grants. Modern\nrequests carry Rust-approved protocol metadata and headers without a session id.\n\nA typed discovery refusal can admit a legacy handshake. Only an explicit HTTP\ntransport refusal during that handshake can admit legacy SSE with a fresh exact\nRust grant. Legacy HTTP retains its GET preflight and bounded OAuth refresh;\nmodern HTTP does not inherit those session or resend behaviors. Incomplete result\nflows are reported without automatically repeating the operation. Credentials,\nconfigured URLs, OAuth browser/login operations and Computer Use decision keys\nstay in Rust.\n\nBoth backends share recorded semantic MCP goldens and retain separate transport\ngoldens, including discovery, authentication and session evidence. The Host default\nrequires those fixtures and the actual broker acceptance checks, plus the platform\nisolation and measured gates in Ops CURRENT_DECISIONS §26. An explicit\n`mcp_backend = \"rust\"`remainsavailableforonereleaseafterthequalifieddefault\nflip,beforethenativeprotocoLine truncated
"content":"# Writing an extension tool or command\n\nThe experimental TypeScript extension host runs reviewed plugin code in a\nshared Node process (or, as an opt-in, Bun). Rust still owns sessions, tool admission, approval and\nexecution. Extensions currently contribute tools and slash commands; they\ncannot provide an approval service, run a second agent loop or replace\nbuilt-in tools or commands.\n\n## Quick start\n\nStart with [hello-extension](examples/plugins/hello-extension/hello.mts) and its\n[plugin.json](examples/plugins/hello-extension/plugin.json). The example is\nexecuted unchanged by the host tests. It returns a greeting and does not read\nfiles or use the network.\n\n1. Explicitly enable `[features] extension_host = true` in your configuration,\n or start Codewhale with `codewhale --enable extension_host` (the flag goes\n before any subcommand).\n2. Install the example's directory using `/plugin install <local-directory>`.\n3. Run `/plugin validate hello-extension`, then `/plugin show hello-extension`.\n4. Run `/plugin enable hello-extension` to open its content/capability review.\n Read the review and personally run its exact `/plugin trust <token>` command,\n then enable the plugin. Trust alone does not execute the code.\n5. Ask Codewhale to use `hello_greet`. The tool follows the normal approval gate.\n6. Run `/hello-greet Codewhale`. The command runs when you type it, and shows\n its answer in the transcript with its origin, `extension:hello-extension`.\n\nAn authoring agent should stop after installation and validation and present\nthe review to the person. Do not trust or enable a bundle automatically.\n\n## Locations, installation and trust\n\nUser bundles live under `~/.codewhale/plugins/`; workspace bundles live under\n`<workspace>/.codewhale/plugins/`. New bundles use Agent Plugins v1.0.0\n`plugin.json`. Declare `extensions[\"net.codewhale\"].native.path` as one regular\n`.mjs`, `.js` or `.mts` file inside the bundle. Directories and other extensions\nfail validation when the host is enabled. With the flag off, native entries\nremain inventory-only.\n\nA plugin may split its code across several entries with `native.paths` (up to\n64, alone or beside `path`). They are activated in the order declared, as the\nfibers of one plugin: they share one owner, so one disable, review change or\ncrash tears all of them down together. Each entry is a separate module with its\nown `apply`, and tool and command names must be unique across them. If an entry\nfails to activate (throws, requires a service the host does not provide, or has\na registration refused), only that entry is withdrawn: entries that already\nactivated stay registered, later entries still activate, and the plugin fails\nonly when no entry activates. Listing the same file twice is one entry.\n\n## Runtime\n\n`[extension_host] runtime` selects what runs the host. Node is the default.\nBun is an opt-in: it is qualified on macOS only, and becomes the default only\nafter an explicit, recorded cutover.\n\n```toml\n[extension_host]\nruntime = \"node\" # default: Node only\n# runtime = \"bun\" # Bun only; fails rather than falling back to Node\n# runtime = \"auto\" # Bun >= 1.4.0 if one is found and starts, otherwise Node\n# bun = \"~/.bun/bin/bun\" # the only Bun tried when set\n# node = \"/opt/homebrew/bin/node\" # the only Node tried when set\n# mcp_backend = \"rust\" # temporary opt-out; default is \"host\"\n```\n\nThe default `mcp_backend = \"host\"` uses the pinned MCP SDK independently of\n`[features] extension_host`, which controls optional Native extensions. Existing\nstdio and HTTP servers use modern discovery when supported; explicitly configured\nlegacy SSE servers use their legacy handshake. Rust retains process and network\nauthority, credential resolution, catalog admission, tool approval and cancellation.\nThe SDK receives opaque session selectors and exact operation grants. Modern\nrequests carry Rust-approved protocol metadata and headers without a session id.\n\nA typed discovery refusal can admit a legacy handshake. Only an explicit HTTP\ntransport refusal during that handshake can admit legacy SSE with a fresh exact\nRust grant. Legacy HTTP retains its GET preflight and bounded OAuth refresh;\nmodern HTTP does not inherit those session or resend behaviors. Incomplete result\nflows are reported without automatically repeating the operation. Credentials,\nconfigured URLs, OAuth browser/login operations and Computer Use decision keys\nstay in Rust.\n\nBoth backends share recorded semantic MCP goldens and retain separate transport\ngoldens, including discovery, authentication and session evidence. The Host default\nrequires those fixtures and the actual broker acceptance checks, plus the platform\nisolation and measured gates in Ops CURRENT_DECISIONS §26. An explicit\n`mcp_backend = \"rust\"`remainsavailableforonereleaseafterthequalifieddefault\nflip,beforethenativeprotocoLine truncated
"content":"# Installing Codewhale\n\n> 阅读简体中文版:[zh_hans/INSTALL.md](zh_hans/INSTALL.md)\n\nCodewhale is an open-source coding agent that runs in your terminal. You give\nit a task (\"fix the failing test\", \"add a CLI flag\"). It reads your repository,\nedits files and runs commands. In the default **Ask** posture it applies file\nedits inside the workspace immediately (and shows you the diff), but asks before\nrunning shell commands, so commit or stash anything you care about first. It\nworks with many model providers. **DeepSeek** is the default.\n\nThe command is `codewhale`. `codew` is a shorter alias for the same program.\n\nThe installation paths were first checked with **v0.10.0** (released 2026-09-22) on a\nfresh **Ubuntu 24.04 x86_64** machine. The dated\n[install receipts](https://github.com/codewhale-hq/Codewhale/blob/37ecdfcc49bc68a9b0d058b97c3946e62c34bd31/docs/install-report/v0.10.0-2026-09-23/RECEIPTS.md) record the commands and output checked at that time. Steps that\ncould not be run on that machine are marked **(untested on this VM: reason)**.\nmacOS, Windows and Android are out of scope, apart from a few notes. A second\npass re-ran the installer, manual-download, archive and npm paths, the no-key\nchecks and zsh completion on **macOS 26.1 (Apple silicon)**; see\n[macOS notes](#macos-notes). Steps that need a model call were not re-run\nthere.\n\nInstall commands that use `latest` resolve to the latest **published** GitHub\nRelease or package. Between releases, `main` may already describe the next\nversion (for example a v0.10.2 source candidate before its tag). A\nprebuilt candidate is available through the official installer only after its\ntag, checksums and release assets exist. Contributors can build `main` now.\n\n## No account needed\n\nCodewhale needs no account. Keys you save with `/provider` or\n`codewhale auth set` stay on your machine; see\n[API keys and providers](#8-api-keys-and-providers). New Codewhale accounts\nare not open, so this guide does not cover account key storage.\n\n## Test and contribute to current source\n\n`latest` selects the latest published release. To test unreleased repairs,\nbuild canonical GitHub `main`, or CNB `main` after verifying its commit matches.\nThe source version number alone does not establish a published package.\n\n```bash\ngit clone --branch main https://github.com/codewhale-hq/CodeWhale.git\ncd CodeWhale\ncargo build --release --locked -p codewhale-cli\ntarget/release/codewhale --version\n```\n\nSee the Cargo section for build prerequisites; use the repository's stable Rust\nselection (declared minimum 1.89). Windows uses `target/release/codewhale.exe`\nand needs MSVC. A development build is separate from a signed release package.\nFor mainland-China downloads and source validation, see [CNB mirror](CNB_MIRROR.md).\nThe [Tencent Lighthouse bootstrap](../scripts/tencent-lighthouse/README.md) is\nbilingual. An npm registry mirror serves the wrapper; native binary downloads\nstill need an available release asset source.\n\n---\n\n## 60-second quickstart (Linux or macOS)\n\n```bash\n# 1. Install. Downloads two checksum-verified binaries into ~/.local/bin (no sudo).\ncurl -fsSL https://codewhale.net/install.sh | sh\n\n# 2. Make sure ~/.local/bin is on your PATH, now and in future terminals.\necho 'export PATH=\"$HOME/.local/bin:$PATH\"' >> ~/.bashrc # zsh: use ~/.zshrc\nexport PATH=\"$HOME/.local/bin:$PATH\"\ncodewhale --version # -> codewhale 0.10.0 (1be1a703b975)\n\n# 3. Give it a DeepSeek API key (from https://platform.deepseek.com/api_keys).\ncodewhale auth set --provider deepseek # prompts for the key; nothing is echoed\ncodewhale auth status --provider deepseek # \"active source: secret store\"\n\n#4.Runyourfirsttaskinsideagitrepository.\ncd~/your-project\ncodewhale\n```\n\nIntheTUI,typesomethingconcrete:\n\n```text\ncreateaPythonfileprimes.pythatprintsthefirst10primes,runit,andshowmetheoutput\n```\n\nCodewhalewritesthefile,showsyouadiff,thenasks**APPROVAL:bash\npython3primes.py–Doyouwanttoproceed?**Press`y`toallowitonce.It\nrunsthecommandandreportstheoutput.Press`Ctrl-D`(withanemptyinput\nbox)toquit.Itprintsthecommandtoresumethesessionlater.\n\n>**Ifnothinghappensafteryousendyourfirstmessage,**youhavenokey\n>configured.v0.10.0doesn'twarnyouinthatcase.Press**F3**.IfDeepSeek\n>shows`missingkey`,pressEnter,pastethekey,thenpickamodeland\n>confirm.\n\n---\n\n##Contents\n\n1.[Beforeyoustart](#1-before-you-start)\n2.[Install:recommendedinstaller](#2-recommended-installer-curl--sh)\n3.[Install:manualdownloadfromGitHubReleases](#3-manual-download-from-github-releases)\n4.[Install:npm](#4-npm)\n5.[Install:Cargo/buildfromsource](#5-cargo-and-building-from-source)\n6.[Install:Homebrew(Linux)andNix](#6-homebrew-on-linux-and-nix)\n7.[Updatingandrollingback]Line truncated
"content":"# Codewhale User Guide\n\nThis guide is for your first hour with Codewhale. It explains the main\nworkflow, the important safety controls, and where to go next when you need a\ncomplete reference.\n\nCodewhale has deeper reference documents for installation, configuration,\nproviders, modes, keybindings, tools, and operations. Use this page as a guided\nwalkthrough, then follow the \"Next\" links when you need every option.\n\n## 1. Welcome to Codewhale\n\nCodewhale is a terminal coding agent. You run it from a workspace, give it a\ntask, and it can use structured tools to inspect files, run commands, edit\ncode, and report back with evidence.\n\nThe important difference from a normal chat model is that Codewhale is built\naround a harness:\n\n- It keeps the active workspace and session visible.\n- It routes each turn through explicit modes and approval rules.\n- It shows tool calls in the transcript instead of hiding the work.\n- It can preserve sessions, fork conversations, and continue later.\n- It can run sub-agents for focused background work.\n\nYou can use Codewhale for small questions:\n\n```text\nExplain the authentication flow in this repository.\n```\n\nYou can also use it for multi-step work:\n\n```text\nFind the failing validation path, propose a fix, and wait for my approval\nbefore editing files.\n```\n\nFor a new repository, start conservatively. Ask Codewhale to explore and plan\nbefore asking it to change files. That gives you a reviewable path and makes it\neasier to catch wrong assumptions early.\n\nNext: [ARCHITECTURE.md](ARCHITECTURE.md) explains the internal harness and\nruntime model.\n\n## 2. First Launch\n\nFor a new macOS or Linux installation, use the official GitHub release.\nThe installer verifies the release checksums and provides the same runtime\nunder the `codewhale` and `codew` command names:\n\n```bash\ncurl -fsSL https://codewhale.net/install.sh | sh\n```\n\nWindows users should choose the matching installer or archive from\n[GitHub Releases](https://github.com/codewhale-hq/CodeWhale/releases/latest).\nFor an existing direct install, use `codewhale update --check`, then\n`codewhale update`. npm and Cargo remain secondary packaging routes; Cargo\nalso supports source builds where a compatible prebuilt is unavailable.\nFor occupied directories, package-managed installs, and PATH setup, follow\n[the installation and migration guide](INSTALL.md#recommended-official-github-releases).\nAndroid/Termux uses its own [preview archive or source-build path](INSTALL.md#android--termux-arm64).\n\nDocker is also available when you want an isolated runtime. The published\nimage is currently 0.10.0; to test current `main`, [build it from source](DOCKER.md#building-locally).\n\n```bash\ndocker volume create codewhale-home\ndocker run --rm -it \\\n -e DEEPSEEK_API_KEY=\"$DEEPSEEK_API_KEY\" \\\n -v codewhale-home:/home/codewhale/.codewhale \\\n -v \"$PWD:/workspace\" \\\n -w /workspace \\\n ghcr.io/hmbown/codewhale:latest\n```\n\nOnce the install directory is on PATH, launch Codewhale from the repository or\ndirectory you want it to work in:\n\n```bash\ncodewhale\n```\n\nFor the default GitHub installer destination, you can use\n`\"$HOME/.local/bin/codewhale\"` until that directory is on PATH.\n\nOn first launch, Codewhale opens the composer, even with no key or route.\nWith no key or route, workspace trust is asked once a provider is connected;\notherwise only when the folder requires a decision. A provider is asked for\nwhen you send your first message: the message stays in the box while you sign\nin or use your own key, and a local model is offered only when a local Ollama\nserver answers. The provider step also has an explicit offline route.\n\nEverything optional stays available after that. Use `/setup` for the\nprogressive setup and repair guide, `/settings` for the full typed editor, and\n`/constitution` when you want your own constitution on top of the bundled one.\nThe localized telemetry choice appears only after the workspace is ready and\ndoes not block the composer.\n\nDeepSeek is the default provider. If you want to configure its key before or\nafter the first launch, the most direct setup path is:\n\n```bash\ncodewhale auth set --provider deepseek\n```\n\nYou can also provide a key through the environment:\n\n```bash\nexport DEEPSEEK_API_KEY=\"your-key\"\ncodewhale\n```\n\nNewCodewhaleconfigisstoredunder`~/.codewhale/config.toml`.Legacy\n`~/.deepseek/config.toml`filesarestillsupportedforusersmigratingfrom\ntheoldname.\n\nUse`/constitution`torevieworchangestandingguidance.Aftersetup,runa\ndoctorcheck:\n\n```bash\ncodewhaledoctor\n```\n\nUsetheJSONformwhenyouneedamachine-readablereportforanissue:\n\n```bash\ncodewhaledoctor--json\n```\n\nBothformsareofflinebydefault.Theyreportstructuralconfigurationand\nliteralunknown/not-probedcredentialstateswithoutloadingworkspace`.env`\ncredentials,opeLine truncated
"content":"# Installing Codewhale\n\n> 阅读简体中文版:[zh_hans/INSTALL.md](zh_hans/INSTALL.md)\n\nCodewhale is an open-source coding agent that runs in your terminal. You give\nit a task (\"fix the failing test\", \"add a CLI flag\"). It reads your repository,\nedits files and runs commands. In the default **Ask** posture it applies file\nedits inside the workspace immediately (and shows you the diff), but asks before\nrunning shell commands, so commit or stash anything you care about first. It\nworks with many model providers. **DeepSeek** is the default.\n\nThe command is `codewhale`. `codew` is a shorter alias for the same program.\n\nThe installation paths were first checked with **v0.10.0** (released 2026-09-22) on a\nfresh **Ubuntu 24.04 x86_64** machine. The dated\n[install receipts](https://github.com/codewhale-hq/Codewhale/blob/37ecdfcc49bc68a9b0d058b97c3946e62c34bd31/docs/install-report/v0.10.0-2026-09-23/RECEIPTS.md) record the commands and output checked at that time. Steps that\ncould not be run on that machine are marked **(untested on this VM: reason)**.\nmacOS, Windows and Android are out of scope, apart from a few notes. A second\npass re-ran the installer, manual-download, archive and npm paths, the no-key\nchecks and zsh completion on **macOS 26.1 (Apple silicon)**; see\n[macOS notes](#macos-notes). Steps that need a model call were not re-run\nthere.\n\nInstall commands that use `latest` resolve to the latest **published** GitHub\nRelease or package. Between releases, `main` may already describe the next\nversion (for example a v0.10.2 source candidate before its tag). A\nprebuilt candidate is available through the official installer only after its\ntag, checksums and release assets exist. Contributors can build `main` now.\n\n## No account needed\n\nCodewhale needs no account. Keys you save with `/provider` or\n`codewhale auth set` stay on your machine; see\n[API keys and providers](#8-api-keys-and-providers). New Codewhale accounts\nare not open, so this guide does not cover account key storage.\n\n## Test and contribute to current source\n\n`latest` selects the latest published release. To test unreleased repairs,\nbuild canonical GitHub `main`, or CNB `main` after verifying its commit matches.\nThe source version number alone does not establish a published package.\n\n```bash\ngit clone --branch main https://github.com/codewhale-hq/CodeWhale.git\ncd CodeWhale\ncargo build --release --locked -p codewhale-cli\ntarget/release/codewhale --version\n```\n\nSee the Cargo section for build prerequisites; use the repository's stable Rust\nselection (declared minimum 1.89). Windows uses `target/release/codewhale.exe`\nand needs MSVC. A development build is separate from a signed release package.\nFor mainland-China downloads and source validation, see [CNB mirror](CNB_MIRROR.md).\nThe [Tencent Lighthouse bootstrap](../scripts/tencent-lighthouse/README.md) is\nbilingual. An npm registry mirror serves the wrapper; native binary downloads\nstill need an available release asset source.\n\n---\n\n## 60-second quickstart (Linux or macOS)\n\n```bash\n# 1. Install. Downloads two checksum-verified binaries into ~/.local/bin (no sudo).\ncurl -fsSL https://codewhale.net/install.sh | sh\n\n# 2. Make sure ~/.local/bin is on your PATH, now and in future terminals.\necho 'export PATH=\"$HOME/.local/bin:$PATH\"' >> ~/.bashrc # zsh: use ~/.zshrc\nexport PATH=\"$HOME/.local/bin:$PATH\"\ncodewhale --version # -> codewhale 0.10.0 (1be1a703b975)\n\n# 3. Give it a DeepSeek API key (from https://platform.deepseek.com/api_keys).\ncodewhale auth set --provider deepseek # prompts for the key; nothing is echoed\ncodewhale auth status --provider deepseek # \"active source: secret store\"\n\n#4.Runyourfirsttaskinsideagitrepository.\ncd~/your-project\ncodewhale\n```\n\nIntheTUI,typesomethingconcrete:\n\n```text\ncreateaPythonfileprimes.pythatprintsthefirst10primes,runit,andshowmetheoutput\n```\n\nCodewhalewritesthefile,showsyouadiff,thenasks**APPROVAL:bash\npython3primes.py–Doyouwanttoproceed?**Press`y`toallowitonce.It\nrunsthecommandandreportstheoutput.Press`Ctrl-D`(withanemptyinput\nbox)toquit.Itprintsthecommandtoresumethesessionlater.\n\n>**Ifnothinghappensafteryousendyourfirstmessage,**youhavenokey\n>configured.v0.10.0doesn'twarnyouinthatcase.Press**F3**.IfDeepSeek\n>shows`missingkey`,pressEnter,pastethekey,thenpickamodeland\n>confirm.\n\n---\n\n##Contents\n\n1.[Beforeyoustart](#1-before-you-start)\n2.[Install:recommendedinstaller](#2-recommended-installer-curl--sh)\n3.[Install:manualdownloadfromGitHubReleases](#3-manual-download-from-github-releases)\n4.[Install:npm](#4-npm)\n5.[Install:Cargo/buildfromsource](#5-cargo-and-building-from-source)\n6.[Install:Homebrew(Linux)andNix](#6-homebrew-on-linux-and-nix)\n7.[Updatingandrollingback]Line truncated
"content":"# Keybindings\n\n> 阅读简体中文版:[zh_hans/KEYBINDINGS.md](zh_hans/KEYBINDINGS.md)\n\nThis is the source-of-truth catalog of every keyboard shortcut the TUI recognizes. Bindings are grouped by **context** — the focus or modal state they fire in. A binding listed under \"Composer\" only takes effect when the composer is focused; one under \"Transcript\" only when the transcript has focus; and so on.\n\nGlobal key chords are not yet user-configurable — tracked for a future release (#436, #437). Hotbar slot actions are configurable with `[[hotbar]]` and `/hotbar`; the Hotbar activation chord remains `Alt-1` through `Alt-8`.\n\n## Global (any context)\n\n| Chord | Action |\n|----------------------|---------------------------------------------------------------|\n| `/help`, `F1` or `Ctrl-/` | Toggle the help overlay. `/help` is what the chrome advertises: it is the one route every terminal delivers, where `F1` is eaten by tmux and some emulators and `Ctrl-/` encoding varies |\n| `F2` | Toggle the typed Settings editor |\n| `F3` | Open the provider/model picker (same as `/provider`) |\n| `Ctrl-K` | Open the command palette (slash-command finder) |\n| `Ctrl-C` | Cancel current turn / dismiss modal / arm-then-confirm quit |\n| `Ctrl-B` | Release the current shell wait so the turn can continue; foreground commands move into `/jobs`, and background tasks keep running. Use `/jobs` or `Bash` with `action: \"wait\"`toinspectoutput|\n|`Ctrl-D`|Quit(onlywhenthecomposerisempty)|\n|`Tab`|Whenthecomposerisempty,cycleTUImode:Plan→Work→Operate→Plan|\n|`Shift+Tab`|Cyclepermissionposture:Ask→Auto-Review→FullAccess.Liveregardlessofcomposercontentsorwhetheraturnisrunning(suppressedonlywhileamodalotherthanConfigisopen)|\n|`Ctrl-T`|Cyclereasoningeffortfortheactivemodel.Walksthesameladderas`/model`and`/effort`(catalogordocumentedroutedialect).Always-thinkingmodelsomit`off`;Grok4.6includes`xhigh`.|\n|`Ctrl-Shift-T`|Togglelivetranscriptoverlay(sticky-tailauto-scroll)|\n|`Ctrl-R`|Opentheresume-sessionpicker|\n|`Ctrl-L`|Compacttheconversationcontext(statuslineshowsprogress;no-opwhileacompactionisalreadyrunning)|\n|`Ctrl-O`|Openthereasoningdetailfortheselectedorcurrentturn,regardlessofcomposercontents|\n|`Ctrl-Alt-O`|Openthewhole-turnTurnInspector,regardlessofcomposercontents|\n|`Alt-V`/`Option-V`(macOS)|Openthedetailspagerfortheselectedtool/sub-agentcard;withnothingselected,thenewestvisibleerror,elsethefirstvisiblecardwithdetails,elsethemostrecentone.ClickacardfirsttokeepAlt-Vonitwhilenewercardsarrive.TerminalsthatemitthelegacyOption-Vglypharealsohandled|\n|`Ctrl-Shift-E`/`Cmd-Shift-E`|Togglethefiletreeintheworkbar|\n|`Ctrl-]`or`Ctrl-Tab`|Cyclethepetviewwhilethepetmainviewisshowing;otherwisecycletheworkdock:TODO→Agents→Jobs→Background.`Ctrl-]`isportable;`Ctrl-Tab`requiresthekittykeyboardprotocol.Terminal.appusesittoswitchterminaltabs|\n|`Ctrl-Shift-Tab`|Cyclethepetvieworworkdockbackwardswheretheterminalsendsthekittykeyboardchord.Terminal.appusesittoswitchterminaltabs|\n|`Alt-G`/`Alt-Shift-G`|Scrolltranscripttotop/bottomwhenthecomposerisempty|\n|`Alt-1`-`Alt-8`|DispatchHotbarslots1-8whennomodalorinlinepickerisopen|\n|`Alt-!`/`Alt-@`/`Alt-#`/`Alt-$`|Selecttheworkbarpanel:Tasks/Agents/Context/Files|\n|`Ctrl-Alt-0`|Toggletheworkbaroff/backtothebottomplacement|\n|`Alt-L`|Openthepagerforthelastmessage(composerempty)|\n|`Alt-P`/`Alt-A`/`Alt-Y`|JumptoPlan/Work,orrequestFullAccess(`Alt-Y`isthelegacypermissionchannel—Work+FullAccess—notaseparatemode;ithonorsalockedapprovalpolicy)|\n|`Ctrl-X`(Activityworkbar)|Cancelallrunningbackgroundshelljobs|\n|`Esc`|Closetopmostmodal·cancelslashmenu·dismisstoast.Duringacompactthatisservinganin-flightturn,Escstopstheturn(thecompactiscollateral).|\n\n##Composer\n\nEditingthemessageyou'reabouttosend.\n\n|Chord|Action|\n|-----------------------------|---------------------------------------------------------|\n|`Enter`|Sendwhenidle;queuewhilebusLine truncated
"content":"# Installing Codewhale\n\n> 阅读简体中文版:[zh_hans/INSTALL.md](zh_hans/INSTALL.md)\n\nCodewhale is an open-source coding agent that runs in your terminal. You give\nit a task (\"fix the failing test\", \"add a CLI flag\"). It reads your repository,\nedits files and runs commands. In the default **Ask** posture it applies file\nedits inside the workspace immediately (and shows you the diff), but asks before\nrunning shell commands, so commit or stash anything you care about first. It\nworks with many model providers. **DeepSeek** is the default.\n\nThe command is `codewhale`. `codew` is a shorter alias for the same program.\n\nThe installation paths were first checked with **v0.10.0** (released 2026-09-22) on a\nfresh **Ubuntu 24.04 x86_64** machine. The dated\n[install receipts](https://github.com/codewhale-hq/Codewhale/blob/37ecdfcc49bc68a9b0d058b97c3946e62c34bd31/docs/install-report/v0.10.0-2026-09-23/RECEIPTS.md) record the commands and output checked at that time. Steps that\ncould not be run on that machine are marked **(untested on this VM: reason)**.\nmacOS, Windows and Android are out of scope, apart from a few notes. A second\npass re-ran the installer, manual-download, archive and npm paths, the no-key\nchecks and zsh completion on **macOS 26.1 (Apple silicon)**; see\n[macOS notes](#macos-notes). Steps that need a model call were not re-run\nthere.\n\nInstall commands that use `latest` resolve to the latest **published** GitHub\nRelease or package. Between releases, `main` may already describe the next\nversion (for example a v0.10.2 source candidate before its tag). A\nprebuilt candidate is available through the official installer only after its\ntag, checksums and release assets exist. Contributors can build `main` now.\n\n## No account needed\n\nCodewhale needs no account. Keys you save with `/provider` or\n`codewhale auth set` stay on your machine; see\n[API keys and providers](#8-api-keys-and-providers). New Codewhale accounts\nare not open, so this guide does not cover account key storage.\n\n## Test and contribute to current source\n\n`latest` selects the latest published release. To test unreleased repairs,\nbuild canonical GitHub `main`, or CNB `main` after verifying its commit matches.\nThe source version number alone does not establish a published package.\n\n```bash\ngit clone --branch main https://github.com/codewhale-hq/CodeWhale.git\ncd CodeWhale\ncargo build --release --locked -p codewhale-cli\ntarget/release/codewhale --version\n```\n\nSee the Cargo section for build prerequisites; use the repository's stable Rust\nselection (declared minimum 1.89). Windows uses `target/release/codewhale.exe`\nand needs MSVC. A development build is separate from a signed release package.\nFor mainland-China downloads and source validation, see [CNB mirror](CNB_MIRROR.md).\nThe [Tencent Lighthouse bootstrap](../scripts/tencent-lighthouse/README.md) is\nbilingual. An npm registry mirror serves the wrapper; native binary downloads\nstill need an available release asset source.\n\n---\n\n## 60-second quickstart (Linux or macOS)\n\n```bash\n# 1. Install. Downloads two checksum-verified binaries into ~/.local/bin (no sudo).\ncurl -fsSL https://codewhale.net/install.sh | sh\n\n# 2. Make sure ~/.local/bin is on your PATH, now and in future terminals.\necho 'export PATH=\"$HOME/.local/bin:$PATH\"' >> ~/.bashrc # zsh: use ~/.zshrc\nexport PATH=\"$HOME/.local/bin:$PATH\"\ncodewhale --version # -> codewhale 0.10.0 (1be1a703b975)\n\n# 3. Give it a DeepSeek API key (from https://platform.deepseek.com/api_keys).\ncodewhale auth set --provider deepseek # prompts for the key; nothing is echoed\ncodewhale auth status --provider deepseek # \"active source: secret store\"\n\n#4.Runyourfirsttaskinsideagitrepository.\ncd~/your-project\ncodewhale\n```\n\nIntheTUI,typesomethingconcrete:\n\n```text\ncreateaPythonfileprimes.pythatprintsthefirst10primes,runit,andshowmetheoutput\n```\n\nCodewhalewritesthefile,showsyouadiff,thenasks**APPROVAL:bash\npython3primes.py–Doyouwanttoproceed?**Press`y`toallowitonce.It\nrunsthecommandandreportstheoutput.Press`Ctrl-D`(withanemptyinput\nbox)toquit.Itprintsthecommandtoresumethesessionlater.\n\n>**Ifnothinghappensafteryousendyourfirstmessage,**youhavenokey\n>configured.v0.10.0doesn'twarnyouinthatcase.Press**F3**.IfDeepSeek\n>shows`missingkey`,pressEnter,pastethekey,thenpickamodeland\n>confirm.\n\n---\n\n##Contents\n\n1.[Beforeyoustart](#1-before-you-start)\n2.[Install:recommendedinstaller](#2-recommended-installer-curl--sh)\n3.[Install:manualdownloadfromGitHubReleases](#3-manual-download-from-github-releases)\n4.[Install:npm](#4-npm)\n5.[Install:Cargo/buildfromsource](#5-cargo-and-building-from-source)\n6.[Install:Homebrew(Linux)andNix](#6-homebrew-on-linux-and-nix)\n7.[Updatingandrollingback]Line truncated
"content":"# Provider Registry\n\n> 阅读简体中文版:[zh_hans/PROVIDERS.md](zh_hans/PROVIDERS.md)\n\nSpeech recognition, synthesis, translation and video have separate execution\npaths. See [media models](MEDIA_MODELS.md) for current support and reviewed\nupstream candidates; a chat catalogue row alone does not add a media backend.\n\nThis registry describes provider behavior that is wired into the current\nCodewhale codebase. It is intentionally conservative: shipped entries are\nlimited to provider IDs, config keys, auth paths, base URLs, model resolution,\nand capability metadata that the code already knows about.\n\nDeepSeek remains the default provider, but every entry in `ProviderKind::ALL`\nis a first-class selectable provider route. `ALL` is the catalog/picker\nsurface — one identity per vendor. Dual-wire dialect kinds (`*Anthropic`, e.g.\n`deepseek-anthropic`) and the Model Studio plan variants stay on the enum for\nserde and `provider_for_kind` but are deliberately **not** catalog rows:\na plan is `mode`/`base_url` and a dialect is `wire = openai|anthropic` on the\nprimary provider config (`crates/config/src/provider_kind.rs:221-226`). Hosted\nroutes, generic OpenAI-compatible endpoints, the OpenAI Codex/ChatGPT route,\nnative Anthropic, and local runtimes all run the same terminal harness against\nthe selected provider/model/base URL.\n\nA host reached over plain Chat Completions is an ordinary named provider,\nnot a `ProviderKind`: enum variants are reserved for distinct *wires*\n(Anthropic Messages, Codex Responses, Google thought signatures). Any such\nhost is a `[providers.<name>]` table with a base URL, a model, and a key env\n(`docs/CONFIGURATION.md`); `/provider` and `/setup` keep a \"paste a Base URL\nand a key\"pathforexactlythis.Offeringscomefromlive`GET/v1/models`\nplustheCodewhalecatalogratherthanacompiledroster(#5350,#6289).\n\nKnown-goodhosts.Theseshipasbundleddescriptorrowsin\n`crates/config/assets/provider_descriptors.json`(compiledinby\n`crates/config/src/descriptors.rs`):eachrowsayshowtoreachthehost—wire,\nbaseURL,keyenv,aliases—whilemodelidsstaylivefrom`GET/v1/models`\nandtheCodewhalecatalog;theexamplemodelisonlyabootstraphint.Verify\nagainstthevendor'sowndocsbeforetrustinganyvaluehere:\n\n|Host|BaseURL|Examplemodels|APIkeyenv|\n|---|---|---|---|\n|SenseNova|`https://token.sensenova.cn/v1`|`deepseek-v4-flash`|`SENSENOVA_API_KEY`|\n|Baseten|`https://inference.baseten.co/v1`|`deepseek-ai/DeepSeek-V4-Pro`|`BASETEN_API_KEY`|\n|Groq|`https://api.groq.com/openai/v1`|`llama-3.3-70b-versatile`|`GROQ_API_KEY`|\n|Cerebras|`https://api.cerebras.ai/v1`|`llama-3.3-70b`|`CEREBRAS_API_KEY`|\n|CommandCode|`https://api.commandcode.ai/provider/v1`|`deepseek/deepseek-v4-flash`|`COMMAND_CODE_API_KEY`|\n|AlibabaModelStudio(DashScope)|`https://dashscope-intl.aliyuncs.com/compatible-mode/v1`|`qwen3.8-flash`|`DASHSCOPE_API_KEY`|\n|AICraft|`https://aicraftapi.com/v1`|`claude-4.6-sonnet`;DeepSeek/Claude/Gemini/Qwen/GLM/MiniMax/Doubaofamilies|`AICRAFT_API_KEY`|\n|Tsubasa|`https://api.tsubasa.sh/v1`|`tsubasa-pro`,`tsubasa-fast`(32,768-tokencontext)|`TSUBASA_API_KEY`|\n|CheaperInference|`https://api.cheaperinference.com/v1`|`gpt-5.4-mini`,`claude-sonnet-5`,`gemini-3.1-pro`|`CHEAPER_INFERENCE_API_KEY`|\n|Yolo-Auto|`https://yolo-auto.com/v1`|`qwen3.8-flash`;`yolo`/`yolo-small`|`YOLO_AUTO_API_KEY`|\n|StandardCompute|`https://api.stdcmpt.com/v1`|`standardcompute`(router);`deepseek/deepseek-v4-pro`,`anthropic/claude-sonnet-5.5`|`STANDARDCOMPUTE_API_KEY`|\n\nAICraft'srosterspansDeepSeek,AnthropicClaude,GoogleGemini,Qwen,GLM,\nMiniMaxandDoubaoidsonitsOpenAI-compatibleendpoint.Theauthorityis\n`GEThttps://aicraftapi.com/v1/models`withyourkey—pickamodelfromthat\nlist,notfromthistable.\nTsubasaimplementsonly`GET/v1/models`andChatCompletions.Itstwopublic\nmodelidssharea32,768-tokencontext,smallerthanthe128,000tokensCodewhaleassumes\nforanunknownmodel,sosetitontherouteaftersaving:\n`codewhaleconfigsetproviders.tsubasa.context_window32768`.\n\nCheaperInferenceisanOpenAI-compatiblegatewaywithonekeyformodelsfrom\nseverallabs.Modelidsarebare,suchas`gpt-5.4-mini`or`claude-sonnet-5`.\nPricingvariesbymodelandroute;consulttheprovider’scurrentcatalog.\nTheauthorityis`GEThttps://api.cheaperinference.com/v1/models`withyourkey.\nDocs:<https://cheaperinference.com/docs>.\n\nStandardComputeoffersflatmonthlyplanswithacomputebudget.Bydefault,\nrequestsreturnHTTP402whenitisused;eligibleplanscanenablePayasyougo\ntocontinuewithadditionalprepaidcharges.Seeits\n[budgetpolicy](https://docs.standardcompute.com/budgets-and-limits).\nThe`standardcompute`modelidLine truncated
"content":"# Codewhale Agent — one identity, many surfaces\n\n\"Codewhale Agent\" is one product identity with many transport surfaces.\nThis page is the identity map: what each surface is, where each lives, and\nthe rules that keep them one product instead of several.\n\n## The one identity\n\n| Surface | Identity object | Where it lives | State |\n|---|---|---|---|\n| GitHub PR review | GitHub App bot `codewhale-agent[bot]` | GitHub org settings; repo variable `CODEWHALE_APP_ID` + secret `CODEWHALE_APP_PRIVATE_KEY` | `codewhale review --pr N --post` ships with the `codewhale-review.yml` workflow; it no-ops green until the App is configured (founder-gated), then posts as the App instead of the workflow token. Posting is always opt-in via `--post`. |\n| Commit co-author | `.github/AUTHOR_MAP` | repo | existing; harvested co-author credit lands in the contributor graph |\n| Chat channels | per-user bot registration bound to a Codewhale membership | control-plane `services/control-plane/src/integrations/chat/` BotGateway and adapters; contracts in `packages/contracts` | Source includes Telegram and Slack bot pairing and commands. Default read-only command allowlist; opt-in write verbs; approve/deny keyboards route as permission decisions. Availability depends on the deployed gateway and configured bot credentials. |\n\nSlack bot commands control Codewhale; they do not give an agent tools to search\nor read a Slack workspace. The Slack bot requires a user-managed Slack app,\ncredentials and webhook setup. Hosted Slack delivery and a real authenticated\nSlack task have not been qualified, so this source implementation is not a\nready-to-use Slack integration claim. External Slack tools require a separately\nconfigured and authenticated MCP server or API integration.\n\n## Rules that make it one identity\n\n- Every surface is I/O around the single `Engine::run_turn`\n (`crates/tui/src/core/engine/turn_loop.rs`). No surface gets its own\n engine or turn loop.\n- Every surface authenticates to the same Codewhale membership\n (`codewhale login` account session). Membership gates cloud agents;\n provider brands stay internal and invisible.\n- Approvals from any surface route back as permission decisions into the\n same engine. A bare message never executes a gated action.\n- Every command and approval is audited, on every channel.\n- All surfaces are FREE. Rate limits are anti-abuse only. There is no\n per-channel fee, message metering, team gating, or trial timer — and a\n billing hook on a channel feature is forbidden by the money model.\n\n## Naming\n\n- Product: Codewhale. Agent surface: \"Codewhale Agent\".\n- Bot handle wherever it can be chosen: `codewhale-agent`.\n- The agent speaks as \"Codewhale cloud agent\" — never as a provider brand.\n\n## Channel contract\n\nPer-channel command allowlists (default read-only: `status`, `jobs`,\n`receipts`, `help`; opt-in: `new task`, `approve`, `deny`), inline\napprove/deny keyboards routed as permission decisions, `assistant_changes`\nreply mode (message only when something changed), per-channel message\nformat adapters (including Telegram markdown and Slack blocks in source),\nquiet hours + digest, automation output routable\nto any paired channel, one pairing code bound inside the authenticated\napp, unbind revokes instantly.\n\nWeChat note: personal WeChat is supported only through Tencent's official\nClawBot (iLink) channel, never through unofficial account automation. The\nopen-source `integrations/weixin-bridge` uses it with a local\n`codewhale serve --http`, and it stops when Tencent's risk control objects.\nOfficial Account (公众号/服务号) APIs are not used.\n\n## Pointer\n\nChannel contracts live in `packages/contracts` (CWC repo); the gateway and\nadapters live in `services/control-plane/src` (CWC repo). The GitHub\nsurface lives in `.github/workflows/codewhale-review.yml` here.\n"
"content":"# Configuration\n\ncodewhale reads configuration from a TOML file plus environment variables.\nAt process startup it may also load literal built-in-provider credentials from\na workspace-local `.env` file. Use the tracked `.env.example` as the template;\ncopy it to `.env`, then add only credential values.\n\nA workspace is not configuration authority. Codewhale therefore ignores\nconfig/profile/home paths, provider/model/base-URL routing, MCP/plugin state,\napproval/sandbox/shell posture, executable paths, runtime settings, and every\nother non-credential `.env` entry. Variable expansion is rejected so a\nrepository cannot substitute an ambient secret into a credential value. Use\n`config.toml`, CLI flags, or values exported by the launching shell for those\nexplicit control-plane settings. `.env` is read from a stable regular-file\nhandle, is capped at 1 MiB, and symbolic links, reparse points, and multiply\nlinked files are rejected.\n\n## Reading and checking configuration from the CLI\n\n`codewhale config get <key>` reads scalar keys, whole tables such as `tools`,\nand nested paths such as `tools.user_input_timeout_seconds`. Displayed tables\nand nested values apply the same recursive credential redaction as `config dump`.\n\n`config set` supports its named scalar keys and the provider, route, and\nnotification commands. It refuses a key that nothing reads and suggests the\nnearest real key (`config set calm_mod on` names `calm_mode`). A settings.toml\nkey such as `calm_mode` or `tool_collapse` is validated and written to the\nuser-global settings.toml, not config.toml; `config get` reads it back from\nthere even when an old config.toml copy (which nothing reads) is still present,\nand names that copy so you can `config unset` it. Settings keys have no project\nscope, so `--project` refuses them. A config.toml key is checked against the\ntype its reader expects and stored as a TOML boolean or number where the\nreader needs one (`yolo = true`, `max_subagents = 4`); `reasoning_effort`\naccepts the same aliases as `/effort` (#6563). Other dotted writes fail before\nmodifying the file and name the TOML table to edit. For example, set a tools\ntimeout in the file as:\n\n```toml\n[tools]\nuser_input_timeout_seconds = 0\n```\n\n`codewhale config doctor` checks credential presence and endpoint shape, and\nwarns about each config.toml root key that nothing reads: a settings.toml key\nleft in config.toml is named as misplaced, and any other unread key gets a\ndid-you-mean. Keys read by any runtime reader are not reported. The warnings do\nnot fail the check, and a clean result does not validate every runtime setting's\nvalue (#6083, #6563).\n\n## Constitution, project instructions, and repo authority\n\nCodewhale has several instruction surfaces. They are deliberately separate so a\npersonal constitution, repo policy, project instructions, and runtime security\ncontrols do not blur together.\n\n- **Bundled global Constitution** — the compiled base law in the binary. It is\n the default floor for every session.\n- **User-global constitution** — the normal guided setup output. Manage it with\n `/constitution` or `/setup`; Codewhale stores structured data at\n `$CODEWHALE_HOME/constitution.json` (default `~/.codewhale/constitution.json`)\n and renders it into a separate `<codewhale_user_constitution>` prose block.\n This can express preferences and stop conditions, but it does not change\n runtime approval policy, sandbox, shell, network, trust, or MCP permissions.\n- **Repo-local constitution** — optional project policy in\n `.codewhale/constitution.json`, described below.\n- **`AGENTS.md`** — cross-agent **project instructions** (prose). This is the\n canonical file for \"how should an agent work in this repo.\" Run `/init` to\n scaffold one. Instruction files written for other tools (`CLAUDE.md`,\n `.claude/instructions.md`, `.cursorrules`, `.github/copilot-instructions.md`\n and similar) are read only when you opt in with the top-level\n `project_instruction_imports` key, for example `[\"claude\"]`, `[\"cursor\"]` or\n `[\"all\"]`(see`config.example.toml`);Codewhalewarnswhenitfindsoneit\ndidnotload.\n-**Memoryandhandoffs**—recalledstate.Useful,butlowerauthoritythan\nconstitutionsandprojectinstructions.\n\n###Managingtheuser-globalconstitution(`/setup`and`/constitution`)\n\nThebundled**workingagreement**isthesafedefaultandnolongeraddsa\nrequiredfirst-runscreen.Customizeitlaterthrough`/constitution`orthe\nprogressive`/setup`guide.Provider/modelreadiness,workspacetrust,and\nruntimeposturestayseparatefromthisguidance.\n\nOnthe**Workingagreement**(constitution)step:\n\n-**`1`–`6`**tunetheguideddraft.**`G`**previewsit,and**`G`**again\nratifiesandsavesafreshstructured`constitution.json`.\n-**`A`**(shownonlywhenaproviderisconfigured)asksyourfirstconfigured\nmodeltoLine truncated
"content":"# Provider Registry\n\n> 阅读简体中文版:[zh_hans/PROVIDERS.md](zh_hans/PROVIDERS.md)\n\nSpeech recognition, synthesis, translation and video have separate execution\npaths. See [media models](MEDIA_MODELS.md) for current support and reviewed\nupstream candidates; a chat catalogue row alone does not add a media backend.\n\nThis registry describes provider behavior that is wired into the current\nCodewhale codebase. It is intentionally conservative: shipped entries are\nlimited to provider IDs, config keys, auth paths, base URLs, model resolution,\nand capability metadata that the code already knows about.\n\nDeepSeek remains the default provider, but every entry in `ProviderKind::ALL`\nis a first-class selectable provider route. `ALL` is the catalog/picker\nsurface — one identity per vendor. Dual-wire dialect kinds (`*Anthropic`, e.g.\n`deepseek-anthropic`) and the Model Studio plan variants stay on the enum for\nserde and `provider_for_kind` but are deliberately **not** catalog rows:\na plan is `mode`/`base_url` and a dialect is `wire = openai|anthropic` on the\nprimary provider config (`crates/config/src/provider_kind.rs:221-226`). Hosted\nroutes, generic OpenAI-compatible endpoints, the OpenAI Codex/ChatGPT route,\nnative Anthropic, and local runtimes all run the same terminal harness against\nthe selected provider/model/base URL.\n\nA host reached over plain Chat Completions is an ordinary named provider,\nnot a `ProviderKind`: enum variants are reserved for distinct *wires*\n(Anthropic Messages, Codex Responses, Google thought signatures). Any such\nhost is a `[providers.<name>]` table with a base URL, a model, and a key env\n(`docs/CONFIGURATION.md`); `/provider` and `/setup` keep a \"paste a Base URL\nand a key\"pathforexactlythis.Offeringscomefromlive`GET/v1/models`\nplustheCodewhalecatalogratherthanacompiledroster(#5350,#6289).\n\nKnown-goodhosts.Theseshipasbundleddescriptorrowsin\n`crates/config/assets/provider_descriptors.json`(compiledinby\n`crates/config/src/descriptors.rs`):eachrowsayshowtoreachthehost—wire,\nbaseURL,keyenv,aliases—whilemodelidsstaylivefrom`GET/v1/models`\nandtheCodewhalecatalog;theexamplemodelisonlyabootstraphint.Verify\nagainstthevendor'sowndocsbeforetrustinganyvaluehere:\n\n|Host|BaseURL|Examplemodels|APIkeyenv|\n|---|---|---|---|\n|SenseNova|`https://token.sensenova.cn/v1`|`deepseek-v4-flash`|`SENSENOVA_API_KEY`|\n|Baseten|`https://inference.baseten.co/v1`|`deepseek-ai/DeepSeek-V4-Pro`|`BASETEN_API_KEY`|\n|Groq|`https://api.groq.com/openai/v1`|`llama-3.3-70b-versatile`|`GROQ_API_KEY`|\n|Cerebras|`https://api.cerebras.ai/v1`|`llama-3.3-70b`|`CEREBRAS_API_KEY`|\n|CommandCode|`https://api.commandcode.ai/provider/v1`|`deepseek/deepseek-v4-flash`|`COMMAND_CODE_API_KEY`|\n|AlibabaModelStudio(DashScope)|`https://dashscope-intl.aliyuncs.com/compatible-mode/v1`|`qwen3.8-flash`|`DASHSCOPE_API_KEY`|\n|AICraft|`https://aicraftapi.com/v1`|`claude-4.6-sonnet`;DeepSeek/Claude/Gemini/Qwen/GLM/MiniMax/Doubaofamilies|`AICRAFT_API_KEY`|\n|Tsubasa|`https://api.tsubasa.sh/v1`|`tsubasa-pro`,`tsubasa-fast`(32,768-tokencontext)|`TSUBASA_API_KEY`|\n|CheaperInference|`https://api.cheaperinference.com/v1`|`gpt-5.4-mini`,`claude-sonnet-5`,`gemini-3.1-pro`|`CHEAPER_INFERENCE_API_KEY`|\n|Yolo-Auto|`https://yolo-auto.com/v1`|`qwen3.8-flash`;`yolo`/`yolo-small`|`YOLO_AUTO_API_KEY`|\n|StandardCompute|`https://api.stdcmpt.com/v1`|`standardcompute`(router);`deepseek/deepseek-v4-pro`,`anthropic/claude-sonnet-5.5`|`STANDARDCOMPUTE_API_KEY`|\n\nAICraft'srosterspansDeepSeek,AnthropicClaude,GoogleGemini,Qwen,GLM,\nMiniMaxandDoubaoidsonitsOpenAI-compatibleendpoint.Theauthorityis\n`GEThttps://aicraftapi.com/v1/models`withyourkey—pickamodelfromthat\nlist,notfromthistable.\nTsubasaimplementsonly`GET/v1/models`andChatCompletions.Itstwopublic\nmodelidssharea32,768-tokencontext,smallerthanthe128,000tokensCodewhaleassumes\nforanunknownmodel,sosetitontherouteaftersaving:\n`codewhaleconfigsetproviders.tsubasa.context_window32768`.\n\nCheaperInferenceisanOpenAI-compatiblegatewaywithonekeyformodelsfrom\nseverallabs.Modelidsarebare,suchas`gpt-5.4-mini`or`claude-sonnet-5`.\nPricingvariesbymodelandroute;consulttheprovider’scurrentcatalog.\nTheauthorityis`GEThttps://api.cheaperinference.com/v1/models`withyourkey.\nDocs:<https://cheaperinference.com/docs>.\n\nStandardComputeoffersflatmonthlyplanswithacomputebudget.Bydefault,\nrequestsreturnHTTP402whenitisused;eligibleplanscanenablePayasyougo\ntocontinuewithadditionalprepaidcharges.Seeits\n[budgetpolicy](https://docs.standardcompute.com/budgets-and-limits).\nThe`standardcompute`modelidLine truncated
"content":"# Provider Registry\n\n> 阅读简体中文版:[zh_hans/PROVIDERS.md](zh_hans/PROVIDERS.md)\n\nSpeech recognition, synthesis, translation and video have separate execution\npaths. See [media models](MEDIA_MODELS.md) for current support and reviewed\nupstream candidates; a chat catalogue row alone does not add a media backend.\n\nThis registry describes provider behavior that is wired into the current\nCodewhale codebase. It is intentionally conservative: shipped entries are\nlimited to provider IDs, config keys, auth paths, base URLs, model resolution,\nand capability metadata that the code already knows about.\n\nDeepSeek remains the default provider, but every entry in `ProviderKind::ALL`\nis a first-class selectable provider route. `ALL` is the catalog/picker\nsurface — one identity per vendor. Dual-wire dialect kinds (`*Anthropic`, e.g.\n`deepseek-anthropic`) and the Model Studio plan variants stay on the enum for\nserde and `provider_for_kind` but are deliberately **not** catalog rows:\na plan is `mode`/`base_url` and a dialect is `wire = openai|anthropic` on the\nprimary provider config (`crates/config/src/provider_kind.rs:221-226`). Hosted\nroutes, generic OpenAI-compatible endpoints, the OpenAI Codex/ChatGPT route,\nnative Anthropic, and local runtimes all run the same terminal harness against\nthe selected provider/model/base URL.\n\nA host reached over plain Chat Completions is an ordinary named provider,\nnot a `ProviderKind`: enum variants are reserved for distinct *wires*\n(Anthropic Messages, Codex Responses, Google thought signatures). Any such\nhost is a `[providers.<name>]` table with a base URL, a model, and a key env\n(`docs/CONFIGURATION.md`); `/provider` and `/setup` keep a \"paste a Base URL\nand a key\"pathforexactlythis.Offeringscomefromlive`GET/v1/models`\nplustheCodewhalecatalogratherthanacompiledroster(#5350,#6289).\n\nKnown-goodhosts.Theseshipasbundleddescriptorrowsin\n`crates/config/assets/provider_descriptors.json`(compiledinby\n`crates/config/src/descriptors.rs`):eachrowsayshowtoreachthehost—wire,\nbaseURL,keyenv,aliases—whilemodelidsstaylivefrom`GET/v1/models`\nandtheCodewhalecatalog;theexamplemodelisonlyabootstraphint.Verify\nagainstthevendor'sowndocsbeforetrustinganyvaluehere:\n\n|Host|BaseURL|Examplemodels|APIkeyenv|\n|---|---|---|---|\n|SenseNova|`https://token.sensenova.cn/v1`|`deepseek-v4-flash`|`SENSENOVA_API_KEY`|\n|Baseten|`https://inference.baseten.co/v1`|`deepseek-ai/DeepSeek-V4-Pro`|`BASETEN_API_KEY`|\n|Groq|`https://api.groq.com/openai/v1`|`llama-3.3-70b-versatile`|`GROQ_API_KEY`|\n|Cerebras|`https://api.cerebras.ai/v1`|`llama-3.3-70b`|`CEREBRAS_API_KEY`|\n|CommandCode|`https://api.commandcode.ai/provider/v1`|`deepseek/deepseek-v4-flash`|`COMMAND_CODE_API_KEY`|\n|AlibabaModelStudio(DashScope)|`https://dashscope-intl.aliyuncs.com/compatible-mode/v1`|`qwen3.8-flash`|`DASHSCOPE_API_KEY`|\n|AICraft|`https://aicraftapi.com/v1`|`claude-4.6-sonnet`;DeepSeek/Claude/Gemini/Qwen/GLM/MiniMax/Doubaofamilies|`AICRAFT_API_KEY`|\n|Tsubasa|`https://api.tsubasa.sh/v1`|`tsubasa-pro`,`tsubasa-fast`(32,768-tokencontext)|`TSUBASA_API_KEY`|\n|CheaperInference|`https://api.cheaperinference.com/v1`|`gpt-5.4-mini`,`claude-sonnet-5`,`gemini-3.1-pro`|`CHEAPER_INFERENCE_API_KEY`|\n|Yolo-Auto|`https://yolo-auto.com/v1`|`qwen3.8-flash`;`yolo`/`yolo-small`|`YOLO_AUTO_API_KEY`|\n|StandardCompute|`https://api.stdcmpt.com/v1`|`standardcompute`(router);`deepseek/deepseek-v4-pro`,`anthropic/claude-sonnet-5.5`|`STANDARDCOMPUTE_API_KEY`|\n\nAICraft'srosterspansDeepSeek,AnthropicClaude,GoogleGemini,Qwen,GLM,\nMiniMaxandDoubaoidsonitsOpenAI-compatibleendpoint.Theauthorityis\n`GEThttps://aicraftapi.com/v1/models`withyourkey—pickamodelfromthat\nlist,notfromthistable.\nTsubasaimplementsonly`GET/v1/models`andChatCompletions.Itstwopublic\nmodelidssharea32,768-tokencontext,smallerthanthe128,000tokensCodewhaleassumes\nforanunknownmodel,sosetitontherouteaftersaving:\n`codewhaleconfigsetproviders.tsubasa.context_window32768`.\n\nCheaperInferenceisanOpenAI-compatiblegatewaywithonekeyformodelsfrom\nseverallabs.Modelidsarebare,suchas`gpt-5.4-mini`or`claude-sonnet-5`.\nPricingvariesbymodelandroute;consulttheprovider’scurrentcatalog.\nTheauthorityis`GEThttps://api.cheaperinference.com/v1/models`withyourkey.\nDocs:<https://cheaperinference.com/docs>.\n\nStandardComputeoffersflatmonthlyplanswithacomputebudget.Bydefault,\nrequestsreturnHTTP402whenitisused;eligibleplanscanenablePayasyougo\ntocontinuewithadditionalprepaidcharges.Seeits\n[budgetpolicy](https://docs.standardcompute.com/budgets-and-limits).\nThe`standardcompute`modelidLine truncated
"content":"# Configuration\n\ncodewhale reads configuration from a TOML file plus environment variables.\nAt process startup it may also load literal built-in-provider credentials from\na workspace-local `.env` file. Use the tracked `.env.example` as the template;\ncopy it to `.env`, then add only credential values.\n\nA workspace is not configuration authority. Codewhale therefore ignores\nconfig/profile/home paths, provider/model/base-URL routing, MCP/plugin state,\napproval/sandbox/shell posture, executable paths, runtime settings, and every\nother non-credential `.env` entry. Variable expansion is rejected so a\nrepository cannot substitute an ambient secret into a credential value. Use\n`config.toml`, CLI flags, or values exported by the launching shell for those\nexplicit control-plane settings. `.env` is read from a stable regular-file\nhandle, is capped at 1 MiB, and symbolic links, reparse points, and multiply\nlinked files are rejected.\n\n## Reading and checking configuration from the CLI\n\n`codewhale config get <key>` reads scalar keys, whole tables such as `tools`,\nand nested paths such as `tools.user_input_timeout_seconds`. Displayed tables\nand nested values apply the same recursive credential redaction as `config dump`.\n\n`config set` supports its named scalar keys and the provider, route, and\nnotification commands. It refuses a key that nothing reads and suggests the\nnearest real key (`config set calm_mod on` names `calm_mode`). A settings.toml\nkey such as `calm_mode` or `tool_collapse` is validated and written to the\nuser-global settings.toml, not config.toml; `config get` reads it back from\nthere even when an old config.toml copy (which nothing reads) is still present,\nand names that copy so you can `config unset` it. Settings keys have no project\nscope, so `--project` refuses them. A config.toml key is checked against the\ntype its reader expects and stored as a TOML boolean or number where the\nreader needs one (`yolo = true`, `max_subagents = 4`); `reasoning_effort`\naccepts the same aliases as `/effort` (#6563). Other dotted writes fail before\nmodifying the file and name the TOML table to edit. For example, set a tools\ntimeout in the file as:\n\n```toml\n[tools]\nuser_input_timeout_seconds = 0\n```\n\n`codewhale config doctor` checks credential presence and endpoint shape, and\nwarns about each config.toml root key that nothing reads: a settings.toml key\nleft in config.toml is named as misplaced, and any other unread key gets a\ndid-you-mean. Keys read by any runtime reader are not reported. The warnings do\nnot fail the check, and a clean result does not validate every runtime setting's\nvalue (#6083, #6563).\n\n## Constitution, project instructions, and repo authority\n\nCodewhale has several instruction surfaces. They are deliberately separate so a\npersonal constitution, repo policy, project instructions, and runtime security\ncontrols do not blur together.\n\n- **Bundled global Constitution** — the compiled base law in the binary. It is\n the default floor for every session.\n- **User-global constitution** — the normal guided setup output. Manage it with\n `/constitution` or `/setup`; Codewhale stores structured data at\n `$CODEWHALE_HOME/constitution.json` (default `~/.codewhale/constitution.json`)\n and renders it into a separate `<codewhale_user_constitution>` prose block.\n This can express preferences and stop conditions, but it does not change\n runtime approval policy, sandbox, shell, network, trust, or MCP permissions.\n- **Repo-local constitution** — optional project policy in\n `.codewhale/constitution.json`, described below.\n- **`AGENTS.md`** — cross-agent **project instructions** (prose). This is the\n canonical file for \"how should an agent work in this repo.\" Run `/init` to\n scaffold one. Instruction files written for other tools (`CLAUDE.md`,\n `.claude/instructions.md`, `.cursorrules`, `.github/copilot-instructions.md`\n and similar) are read only when you opt in with the top-level\n `project_instruction_imports` key, for example `[\"claude\"]`, `[\"cursor\"]` or\n `[\"all\"]`(see`config.example.toml`);Codewhalewarnswhenitfindsoneit\ndidnotload.\n-**Memoryandhandoffs**—recalledstate.Useful,butlowerauthoritythan\nconstitutionsandprojectinstructions.\n\n###Managingtheuser-globalconstitution(`/setup`and`/constitution`)\n\nThebundled**workingagreement**isthesafedefaultandnolongeraddsa\nrequiredfirst-runscreen.Customizeitlaterthrough`/constitution`orthe\nprogressive`/setup`guide.Provider/modelreadiness,workspacetrust,and\nruntimeposturestayseparatefromthisguidance.\n\nOnthe**Workingagreement**(constitution)step:\n\n-**`1`–`6`**tunetheguideddraft.**`G`**previewsit,and**`G`**again\nratifiesandsavesafreshstructured`constitution.json`.\n-**`A`**(shownonlywhenaproviderisconfigured)asksyourfirstconfigured\nmodeltoLine truncated
"content":"# Legacy `.deepseek/` compatibility paths — audit & migration status (#3068)\n\n> 阅读简体中文版:[zh_hans/LEGACY_PATHS.md](zh_hans/LEGACY_PATHS.md)。\n\n## v0.9.10 cleanse ledger\n\nThis is the release-window decision record for residue that can otherwise look\ninterchangeable in a text search. A compatibility entry must name the contract\nthat still requires it; new production code must use a current path.\n\n| Class | Surface | Decision / contract |\n|---|---|---|\n| DELETE | Retired underwater build-version test override | Removed in `1cc4d1d1b`; the owner test was already deleted and the production-only build version remains authoritative. |\n| MIGRATE | Cross-task Agent Mail | `bd998495a` and managed-app `4ff7bc50d` replace guessed event aliases and same-session mailbox conflation with one durable typed runtime protocol. |\n| MIGRATE | TUI `status_message` writes | Active residue; touched flows must move to the typed toast/current-state owner. No new writes are allowed. |\n| COMPATIBILITY | `.deepseek/` state and stored provider aliases | Read fallback for upgraded installations and stored provider identity; writes remain Codewhale-only. Details are below. |\n| COMPATIBILITY | `agent_message` transcript/protocol decoding | Persisted transcripts and tool wire values require decoding; it is not the cross-task Agent Mail transport. |\n| COMPATIBILITY | Managed runtime envelope schema 1 | Previously persisted/enrolled runner replay; current local Codewhale Agent Mail producer is schema 2 and both normalize to the managed envelope. |\n| CURRENT | DeepSeek provider support | A real selectable provider, not product branding; provider-scoped names and configuration remain. |\n| CURRENT | Same-session subagent mailbox | Start/status/peek/message/followup/interrupt/wait/cancel stays scoped to a parent runtime session. |\n| CURRENT | Web locale dictionaries | The application and documentation routes have no page-local `isZh` branches; new copy continues through dictionaries. |\n| CURRENT | Safety, authorization, protocol, persistence, migration, and data-integrity tests | These protect external or durable contracts and are not copy/layout cleanup candidates. |\n\nSnapshot on 2026-08-19: `10,954` Rust `#[test]` attributes under\n`crates/tui`, `523` TUI `status_message` source lines, and zero page-local\n`isZh` files under `apps/web/app` in the paired managed-app candidate. Counts\nare inventory signals, not deletion targets.\n\nCodewhale was renamed from DeepSeek-TUI. To avoid breaking existing installs, the runtime reads\nstate from the new `~/.codewhale/` location but **falls back** to the legacy `~/.deepseek/` location,\nand always **writes** to `~/.codewhale/`. This doc audits each legacy reference and records a\nkeep / deprecate / remove decision so the migration is auditable.\n\n## The canonical resolver (use this for new code)\n\nState-dir resolution is consolidated in `crates/config/src/lib.rs`:\n\n| Symbol | Line | Purpose |\n|---|---|---|\n| `CODEWHALE_APP_DIR` / `LEGACY_APP_DIR` | 5369 | re-exported from `crates/paths` (defined at `crates/paths/src/lib.rs` 13 / 16) |\n| `codewhale_home()` | 5375 | `~/.codewhale` |\n| `legacy_deepseek_home()` | 5392 | `~/.deepseek` (legacy) |\n| `resolve_state_dir(subdir)` | 5434 | **read** path: `~/.codewhale/<subdir>`, falling back to `~/.deepseek/<subdir>` when only the legacy dir exists |\n| `ensure_state_dir(subdir)` | 5458 | **write** path: always creates under `~/.codewhale/<subdir>` |\n\nMigration contract: read-with-fallback, write-to-new. This preserves the v0.8.44 migration for\nusers who still have `~/.deepseek/` while steering all new writes to `~/.codewhale/`.\n\n## Per-path decisions\n\n**Decision for all legacy references below: keep-as-fallback.** Removing the `.deepseek` fallback\nwould strand users who upgraded in place and never re-ran onboarding. Revisit only after a release\nthat actively migrates `~/.deepseek/` → `~/.codewhale/` on first run and a deprecation window.\n\n| Reference | Routed through `resolve_state_dir`? | Decision |\n|---|---|---|\n| `config::resolve_state_dir` / `ensure_state_dir` | n/a (the resolver itself) | keep — canonical |\n| `crates/tui/src/skills/mod.rs` (`~/.deepseek/skills`) | no — hardcoded | keep-as-fallback; route through resolver in a follow-up refactor |\n| `crates/tui/src/prompts.rs` (`LEGACY_HANDOFF_RELATIVE_PATH = \".deepseek/handoff.md\"`)|no—explicitlegacyconst|keep—explicitlegacyhandofffallback|\n|`crates/tui/src/workspace_trust.rs`|no—hardcoded|keep-as-fallback;follow-up|\n|`crates/tui/src/session_manager.rs`|no—hardcoded|keep-as-fallback;follow-up|\n|`crates/runtime/src/skill_state.rs`|no—hardcoded|keep-as-fallback;follow-up|\n|`crates/tui/src/tools/skill.rs`|no—hardcoded|keep-as-fallback;follow-up|\n|`crates/tui/src/snapshot/mod.rs`|no—hardcoded|keep-as-fallback;follow-up|\n|`crates/runtime/src/Line truncated
"content":"# Modes and Permission Postures\n\n> 阅读简体中文版:[zh_hans/MODES.md](zh_hans/MODES.md)\n\nCodewhale has three related concepts:\n\n- **TUI mode**: what kind of visible interaction you're in (Plan/Work/Operate).\n- **Permission posture**: how aggressively the UI asks before executing tools.\n- **Workflow**: named steps that coordinate sub-agents, with dependencies and\n results, available in any TUI mode.\n\nModel selection is separate. `--model auto` and `/model auto` route each turn to\na concrete model and thinking level; they are not TUI modes and are not part of\nthe `Tab` cycle.\n\nWorkflow runs named steps through the same sub-agent runtime; Fleet manages\ntheir saved roles and model assignments. A workflow adds ordering, result\nhandoffs, gates and a progress view. The active mode and permission posture\nstill control what each step may execute.\n\nBefore assigning steps, query `agent(action=\"roster\")` for the saved Fleet\nmodels and roles. A plan child can select a listed model with `model`, or use\nthe saved `role`/`profile` assignment. An Exact Fleet fixes each member's route.\n\n## TUI Modes\n\nPress `Tab` to complete composer menus or cycle through the visible modes\nwhen the composer is empty: **Plan → Work → Operate → Plan**. `Tab` never sends\nor queues composer text; use `Enter` to send or queue it.\nPress `Shift+Tab` to cycle permission posture (Ask → Auto-Review → Full Access).\nEvery change of effective permission writes a `Permissions changed to …` line\nin the transcript.\nPress `Ctrl+T` to cycle reasoning effort.\nRun `/mode` to open the mode picker, or switch directly with `/mode work`,\n`/mode plan`, or `/mode operate`.\n\n- **Plan**: design-first prompting. The stable primitive names remain familiar, but the runtime centrally refuses file mutation and shell execution. Read-only inspection and policy-allowed research, including deferred Web search/fetch, remain available. When a Plan turn successfully finishes a nonempty response, including a prose-only plan, the TUI asks how to continue: Work (Ask), Work (Auto-Review), or keep planning. Work sends the exact approved response as a normal follow-up and preserves that turn's existing To-dos, or adds one pending plan item without replacing unrelated work.\n- **Work** (internally `agent`): ordinary multi-step execution. The first-turn toolbox includes `read`, `write`, `edit`, `file_search`, `bash`, `agent`, `workflow`, `todo_write`, and `load_skill`, plus `create_goal`, `get_goal`, `update_goal`, and `task_shell_wait` so goal and background-shell controls are available without discovery. The agent calls `create_goal` when a request is a durable objective; the host never infers a goal from wording, and the contract must not tell the model otherwise. Approval, sandbox, repository law, and managed policy decide what may execute.\n- **Operate**: Work at full strength, the \"ultra\"mode.SametoolsandauthorityasWork;thedifferenceishowtheyareused.Eachsubstantiverequestbecomesagoal(`create_goal`),theplanstaysvisible(`todo_write`),multi-partworkrunsthrough`workflow`andparallel`agent`workers,non-trivialchangesgetanindependentreviewerbeforetheyarecalleddone,longcommandskeeprunninginthebackground,andrecurringorwatch-typeworkisofferedasan`automation`(createdafterapproval).Itkeepsgoinguntilthegoalisverified,blockedonyou,orpaused.\n\n`Act`and`/modeact`remaincompatibilityaliasesforWork.Savedsettings\nstillnormalizetotheinternalvalue`agent`.\n\n###Toolavailabilitybymode\n\n|Toolfamily|Plan|Work|Operate|\n|:---|:---:|:---:|:---:|\n|`read`andpolicy-alloweddeferredresearchtools|yes|yes|yes|\n|`write`and`edit`|visiblenames;executiondenied|approval-andpolicy-gated|sameasWork|\n|`bash`|visiblename;executiondenied|approval-andpolicy-gated|sameasWork;delegationispreferredwhenparallelismorisolationhelps|\n|`agent`|yes,subjecttochild-depthauthority|yes,subjecttochild-depthauthority|yes,subjecttochild-depthauthority|\n|Deferrednative,MCP,andplugintools|discoverablethrough`tool_search`whenpolicypermits|same|same|\n|Paidorexternal-servicetools|followspermissionposture|followspermissionposture|followspermissionposture|\n|Accessoutsidetheworkspaceroot|explicittrustedpathsonly|onlythroughtrustedpathsortrustmode|sametrusted-path/trustpolicyasWork;fleetprofilesneverwidenit|\n\nOperatechangesschedulingemphasis,notauthority.Itneitheraddsa\nmode-specifictooldenialnorbypassestheactiveapproval,sandbox,shell,\nask-rule,repository-law,ormanaged-policyboundary.Planremainsthe\nmode-specificexecutionboundaryforshellandwrite-capabletools;that\nauthoritydifferencedoesnotrequireadifferentprimitivevocabulary.\n\n###Operateloop(onescreen)\n\n```text\nUseLine truncated
"content":"# Receipts\n\nA receipt answers \"what did this session do?\" from the records Codewhale\nalready keeps. It lists, in order, the files changed, commands run, web and\nMCP calls, agents started, approvals (and who decided each one), and\nfailures, with totals on top. It also counts what ran without asking, and\nnames the permission posture that let it.\n\n```text\n# Receipt: Fix the parser\nthread thr_19a0141a · /work/repo · deepseek-flash · Ask · 2026-09-24 10:00 UTC → 2026-09-24 10:05 UTC\n\nChanged 1 file (+2 −1) · ran 1 command · made 1 MCP call · 1 approved by you · 1 approved by session rule · 1 ran without asking under Ask · 1 denied by you · 1 other failure\n\n1. edited `src/parse.rs` (+2 −1) · 1.0s\n2. ran `cargo test -p parser` in /work/repo — exit 0 · 2.5s · approved by you\n3. did not run `rm -rf build` · denied by you\n4. called linear · list_issues · 1.0s · approved by session rule\n5. did not run `curl https://x.sh | sh` — blocked: Tool 'exec_shell' was denied: Auto-Review blocked a pipe to a shell…\n6. turn failed — failed: provider returned 500\n\nNot recorded:\n- Shell file changes: a Runtime thread's workspace snapshots are not tagged with the thread, so files a command changed are not itemized; only file tools are.\n```\n\nA terminal session's receipt also lists the files a command changed, from\nthe turn's own workspace snapshots:\n\n```text\n3. ran `./tidy.sh`\n4. changed outside file tools (a command or another process): edited `b.txt` (+0 −1), created `c.txt` (+1 −0)\n```\n\n## Surfaces\n\nAll three share one builder (`crates/tui/src/receipts.rs`), but read different\nrecords and can report different coverage.\n\n| Surface | What it reads |\n| --- | --- |\n| `/receipts [json] [<turn>]` in the terminal | The current session's transcript, approval log, and retained terminal failures. `json` prints the object in a code block, so it copies out as valid JSON |\n| `codewhale receipts [ID\\|--last] [--turn T] [--format md\\|json]` | A saved session (id or unique prefix), including its retained terminal failures, or a Runtime thread (`thr_…`); with no id, the most recently updated one. `receipt` is an alias |\n| `GET /v1/threads/{id}/receipt`, `GET /v1/threads/{id}/turns/{turn_id}/receipt` | A Runtime thread (the app, `codewhale serve`), behind the normal `/v1` bearer boundary |\n\nAll three only read. They never call a provider, run a tool, write a file,\nor change runtime state.\n\nExec runs that offer tools, including `exec --auto`, save a session in every\noutput format, so `codewhale receipts --last` can read their receipts after\nthey finish. Resumed sessions and `--output-format stream-json` runs are also\nsaved. Other answer-only text and `--json` runs are saved only if conversation\ncompaction starts; use `--output-format stream-json` to always keep a session.\n\n`codewhale receipts` reads Runtime threads from the machine's default store\n(`tasks/runtime/`, or `$CODEWHALE_RUNTIME_DIR`). A thread kept in one\nterminal session's own store (`sessions/<id>/runtime/`) is not found by id;\nthe API reads whatever store its server owns.\n\n## Where the facts come from\n\n| Session kind | Record | Holds |\n| --- | --- | --- |\n| Terminal session | `sessions/<id>.json` | Every tool call and its result text, in order; each prompt's `<turn_meta>` names the posture the turn ran under; `turn_outcomes` records terminal turn status and errors |\n| Terminal session | `sessions/<id>/approval_receipts.jsonl` | Every approval ask and decision, with time and who decided |\n| Runtime thread | `tasks/runtime/turns`, `items` | Every tool call with its input, status, start and end time, and structured result (exit code, diff, agent status); each turn's `permission_posture` |\n| Runtime thread | `tasks/runtime/events/<thread>.jsonl` | `approval.required` / `approval.decided`, with the flags that say who decided |\n\nThe saved-session CLI and live `/receipts` read terminal turn failures,\nincluding model errors in exec runs, as `turn_failed` actions and count them\ntoward failures even when no tool failed. Truncating the conversation removes\noutcomes beyond the retained history, so later turns cannot inherit their failures.\nSaved sessions keep only the newest 64 recorded failed-turn outcomes. Older outcomes\nand legacy sessions without this record cannot provide a complete terminal\nfailure history from tool results alone.\n\n### Who decided an approval\n\n| Receipt says | Meaning | Recorded as |\n| --- | --- | --- |\n| approved by you / denied by you | A person answered: the terminal card, the app, the web mirror, or an API client acting for them | `decided_by: \"user\"`; Runtime event with no `auto` flag |\n| … by session rule | A remembered \"for this session\" rule answered | `decided_by: \"session_rule\"`;Runtimeeventwith`auto`anda`grant_id`|\n|…byposture|Themodeorpermissionpostureansweredwithoutaprompt|`decided_by:Line truncated
"content":"# Configuration\n\ncodewhale reads configuration from a TOML file plus environment variables.\nAt process startup it may also load literal built-in-provider credentials from\na workspace-local `.env` file. Use the tracked `.env.example` as the template;\ncopy it to `.env`, then add only credential values.\n\nA workspace is not configuration authority. Codewhale therefore ignores\nconfig/profile/home paths, provider/model/base-URL routing, MCP/plugin state,\napproval/sandbox/shell posture, executable paths, runtime settings, and every\nother non-credential `.env` entry. Variable expansion is rejected so a\nrepository cannot substitute an ambient secret into a credential value. Use\n`config.toml`, CLI flags, or values exported by the launching shell for those\nexplicit control-plane settings. `.env` is read from a stable regular-file\nhandle, is capped at 1 MiB, and symbolic links, reparse points, and multiply\nlinked files are rejected.\n\n## Reading and checking configuration from the CLI\n\n`codewhale config get <key>` reads scalar keys, whole tables such as `tools`,\nand nested paths such as `tools.user_input_timeout_seconds`. Displayed tables\nand nested values apply the same recursive credential redaction as `config dump`.\n\n`config set` supports its named scalar keys and the provider, route, and\nnotification commands. It refuses a key that nothing reads and suggests the\nnearest real key (`config set calm_mod on` names `calm_mode`). A settings.toml\nkey such as `calm_mode` or `tool_collapse` is validated and written to the\nuser-global settings.toml, not config.toml; `config get` reads it back from\nthere even when an old config.toml copy (which nothing reads) is still present,\nand names that copy so you can `config unset` it. Settings keys have no project\nscope, so `--project` refuses them. A config.toml key is checked against the\ntype its reader expects and stored as a TOML boolean or number where the\nreader needs one (`yolo = true`, `max_subagents = 4`); `reasoning_effort`\naccepts the same aliases as `/effort` (#6563). Other dotted writes fail before\nmodifying the file and name the TOML table to edit. For example, set a tools\ntimeout in the file as:\n\n```toml\n[tools]\nuser_input_timeout_seconds = 0\n```\n\n`codewhale config doctor` checks credential presence and endpoint shape, and\nwarns about each config.toml root key that nothing reads: a settings.toml key\nleft in config.toml is named as misplaced, and any other unread key gets a\ndid-you-mean. Keys read by any runtime reader are not reported. The warnings do\nnot fail the check, and a clean result does not validate every runtime setting's\nvalue (#6083, #6563).\n\n## Constitution, project instructions, and repo authority\n\nCodewhale has several instruction surfaces. They are deliberately separate so a\npersonal constitution, repo policy, project instructions, and runtime security\ncontrols do not blur together.\n\n- **Bundled global Constitution** — the compiled base law in the binary. It is\n the default floor for every session.\n- **User-global constitution** — the normal guided setup output. Manage it with\n `/constitution` or `/setup`; Codewhale stores structured data at\n `$CODEWHALE_HOME/constitution.json` (default `~/.codewhale/constitution.json`)\n and renders it into a separate `<codewhale_user_constitution>` prose block.\n This can express preferences and stop conditions, but it does not change\n runtime approval policy, sandbox, shell, network, trust, or MCP permissions.\n- **Repo-local constitution** — optional project policy in\n `.codewhale/constitution.json`, described below.\n- **`AGENTS.md`** — cross-agent **project instructions** (prose). This is the\n canonical file for \"how should an agent work in this repo.\" Run `/init` to\n scaffold one. Instruction files written for other tools (`CLAUDE.md`,\n `.claude/instructions.md`, `.cursorrules`, `.github/copilot-instructions.md`\n and similar) are read only when you opt in with the top-level\n `project_instruction_imports` key, for example `[\"claude\"]`, `[\"cursor\"]` or\n `[\"all\"]`(see`config.example.toml`);Codewhalewarnswhenitfindsoneit\ndidnotload.\n-**Memoryandhandoffs**—recalledstate.Useful,butlowerauthoritythan\nconstitutionsandprojectinstructions.\n\n###Managingtheuser-globalconstitution(`/setup`and`/constitution`)\n\nThebundled**workingagreement**isthesafedefaultandnolongeraddsa\nrequiredfirst-runscreen.Customizeitlaterthrough`/constitution`orthe\nprogressive`/setup`guide.Provider/modelreadiness,workspacetrust,and\nruntimeposturestayseparatefromthisguidance.\n\nOnthe**Workingagreement**(constitution)step:\n\n-**`1`–`6`**tunetheguideddraft.**`G`**previewsit,and**`G`**again\nratifiesandsavesafreshstructured`constitution.json`.\n-**`A`**(shownonlywhenaproviderisconfigured)asksyourfirstconfigured\nmodeltoLine truncated
"content":"# Tool surface\n\n> 阅读简体中文版:[zh_hans/TOOL_SURFACE.md](zh_hans/TOOL_SURFACE.md)。\n\nThis document describes the current model-facing tool contract. The v0.9.1\ncutover that produced it is recorded in `docs/RUNTIME_SIMPLIFICATION_DESIGN.md`;\nread the workspace version from `Cargo.toml`, not from this line. The registry\nremains larger than the first-turn catalog so\nsaved transcripts can replay and uncommon capabilities can be loaded on demand.\nThe model should learn one canonical name for each common operation.\n\nImplementation sources:\n\n- `crates/tui/src/core/engine/tool_catalog.rs` owns the eager/deferred catalog.\n- `crates/tui/src/tools/registry.rs` registers canonical tools and hidden aliases.\n- `crates/tui/src/tools/{file,file_tool,shell}.rs` own the small foreground\n primitive behavior and schemas; the other native tools remain searchable.\n- `docs/RUNTIME_SIMPLIFICATION_DESIGN.md` records the v0.9.1 cutover and receipt.\n\n## Default-active contract\n\nNew turns start with thirteen eager native names plus synthetic `tool_search`:\n\n1. `read`\n2. `write`\n3. `edit`\n4. `file_search`\n5. `bash`\n6. `agent`\n7. `workflow`\n8. `todo_write`\n9. `create_goal`\n10. `get_goal`\n11. `update_goal`\n12. `task_shell_wait`\n13. `load_skill`\n14. `tool_search` (synthetic, always active)\n\nThe thirteen native names are `DEFAULT_ACTIVE_NATIVE_TOOLS` in\n`crates/tui/src/core/engine/tool_catalog.rs`, pinned by\n`default_active_contract_keeps_discovery_and_core_tools_eager`. An authority\nboundary may remove `agent` at the maximum child depth, and Plan registers only\nthe foreground shell surface (so no `task_shell_wait`), but route size alone\nmust not change this core vocabulary.\n\nThe direct schemas deliberately stay small:\n\n| Tool | Input | Purpose |\n|---|---|---|\n| `read` | `path`, optional `offset`, optional `limit` | Read a bounded file window with explicit continuation or truncation notices. |\n| `write` | `path`, `content` | Create or replace a file. |\n| `edit` | `path`, `edits` | Apply one or more unambiguous text replacements against one original snapshot. |\n| `file_search` | `query`, optional `path`, `limit`, `extensions`, `exclude` | Find files by name, path fragment or glob and return bounded, ranked matches. Respects ignore files; `exclude` replaces default exclusions. |\n| `bash` | `command`, optional `timeout`, optional `description` | Run one cancellable foreground shell command and return a bounded tail. The optional description is display-only. |\n| `agent` | delegated task and optional scope/context controls | Start or inspect focused child work. |\n| `workflow` | plan/script/source_path plus run controls | Coordinate multi-agent phases with dependencies and completion checks. |\n| `todo_write` | complete replacement list of `{content, status}` items | Keep optional, agent-owned progress notes for genuinely multi-step work. |\n| `create_goal` | objective plus optional budget | Start the session goal the turn works toward. |\n| `get_goal` | none | Read the active goal and its progress. |\n| `update_goal` | terminal status | Mark the goal complete or blocked. |\n| `task_shell_wait` | `task_id`, optional `wait`, `timeout_ms`, `cancel`, `gate` | Poll or stop a background shell task. Completion still arrives as a runtime event, so use it for early output, true barriers, or gate evidence. |\n| `load_skill` | optional `name`, `query` | Load a named skill's `SKILL.md` body and companion-file list; omit `name` or use `name=\"list\"`tolisteligibleskills,oruse`query`tosearchnamesanddescriptions.|\n|`tool_search`|`query`,optionalmatchingcontrols|Discoverpolicy-alloweddeferredtoolsandaddselectedschemastothisconversation'stoolbox.|\n\nModeisanauthoritydecision,notasynonymsystem.Plan,Work,andOperate\nusethesameprimitiveidentities.Plancentrallyrefuses`write`,`edit`,and\n`bash`;WorkandOperatestillpassthosecallsthroughapproval,sandbox,\ntrusted-path,repository-law,andmanaged-policygates.FullAccesschanges\nordinaryapprovalbehaviorbutdoesnotbypasshardsafetyorrepositorylaw.\n\n`update_plan`remainsregisteredonlyforsaved-artifactcompatibilityandis\nnotmodel-visible.`tasks`,`Git`,`Run`,`Web`,`remember`,andother\nspecializedcapabilitiesaresearchableratherthanfirst-turnceremony.\n\nOntheTUI'sdefault(calm)shellcardtheBashdescriptionistheheadline,\nshownaswrittenapartfromstrippedterminalescapes,collapsedwhitespaceand\nalengthbound,andthecommandsitsonitsown`$`rowdirectlyunderitin\neverystateandateverywidth,includingautomaticruns.Acardwithouta\ndescriptionleadswiththe`$`commandinstead.Thelabeledcard\n(`show_tool_details=true`with`calm_mode=false`)keepsthecommandinits\nheaderandthedescriptionasamuted`note:`row.Thedescriptionnever\nreplacesthecommandandisexcludedfromexecution,exactLine truncated
"content":"# Historical Tool-Surface Lifecycle Policy (v0.8.53)\n\n> 阅读简体中文版:[zh_hans/TOOL_LIFECYCLE.md](zh_hans/TOOL_LIFECYCLE.md)\n\n**Status:** Historical design record, not current runtime documentation. The\nv0.9.1 canonical action surface and replay-only alias contract are documented in\n[`RUNTIME_SIMPLIFICATION_DESIGN.md`](RUNTIME_SIMPLIFICATION_DESIGN.md) and\n[`TOOL_SURFACE.md`](TOOL_SURFACE.md). No catalog code landed in this old cycle — the code\nwork is **deferred**. This document is the umbrella policy for GitHub **#2681**,\nwith **#2682** and **#2683** as concrete instances of the planned diet. It\ndescribes *what will be done* and the invariants any future diet PR must hold.\n\n**Scope of related open work (do not contradict):**\n- PR **#2684** — subagent role vocabulary, lifecycle signals, eval ergonomics.\n Legacy subagent-name cleanup + guardrail tests in this policy rebase on #2684.\n- PR **#2685** — git-history active + RLM/field errors.\n\n**What actually happened, so you can read the rest as history:** the \"hidden\ncompatibility\" plan below was *not* what shipped in full. `exec_wait` and\n`exec_interact` were removed rather than kept dispatchable. The work-progress\nsurface is different: `todo_write` is the sole model-visible name, while\n`work_update`, `TodoWrite`, `todo`, `checklist_write`, and `checklist_update`\nstay registered as hidden-compatibility aliases of `TodoWriteTool` so old\ntranscripts replay; `checklist_add`, `checklist_list`, `todo_add`,\n`todo_update`, and `todo_list` are not registered and are not callable.\n`tts`/`speech` remain dispatchable. See [`TOOL_SURFACE.md`](TOOL_SURFACE.md)\nfor the shipped contract. Read §4 and §8 as a rejected catalog-diet proposal,\nnot as a guarantee that every listed alias is gone.\n\n**All file:line citations here were correct at v0.8.52/0.8.53 and are now\nexpired.** They have not been rewritten, because renumbering a historical\nrecord makes it look current. Symbols this document cites that no longer exist\nat all include `ARCEE_FIRST_TURN_NATIVE_TOOLS` and `apply_provider_tool_policy`\n(both removed by `1bfcced43c`, \"fix(engine): remove Arcee tool catalog\nexception\"), and the planned `HIDDEN_COMPATIBILITY_TOOLS` / `DEPRECATED_ALIASES`\nsets, which were never written. Resolve any symbol here against the tree before\nacting on it.\n\n---\n\n## 1. Purpose and the weaker-model problem\n\nCodewhale ships a large native tool surface. The first-turn *active* partition\nof that surface is what every model sees before it has run a single\n`tool_search_*` call. Today that active set contains several **near-duplicate\ntools** that map to the *same* implementation under different names:\n\n- `exec_wait` and `exec_shell_wait` are both `ShellWaitTool`\n (`crates/tui/src/tools/registry.rs:526,529`).\n- `exec_interact` and `exec_shell_interact` are both `ShellInteractTool`\n (`registry.rs:527,530`).\n- `tts` and `speech` are both `SpeechTool`\n (`registry.rs:787-792`, both deferred).\n- `todo_write` is the single model-visible `TodoWriteTool` surface;\n `todo_write`, `TodoWrite`, `todo`, `checklist_write`, and\n `checklist_update` are hidden compat aliases of it.\n\nFor a strong model, redundant names are harmless noise. For **weaker / smaller\nmodels** (the Arcee Trinity lane, `deepseek-v4-flash` child executors, and any\nnon-thinking executor), every additional near-duplicate in the visible set is a\nreal cost:\n\n- It widens the choice space with options that do *nothing distinct*, increasing\n wrong-tool selection and oscillation between synonyms.\n- It spends scarce first-turn catalog budget (Section 5) on zero-information\n entries.\n- It dilutes the \"one name = one thing\"contractthatletsasmallmodelreason\naboutthesurfaceatall.\n\nThelifecyclepolicyexiststo**shrinkanddisciplinethemodel-visible\nsurface**withouteverbreakingtheabilitytoreplayanoldtranscriptthat\nreferencedanow-retiredname.\n\n###Canonicalwork-trackingsurfaceforv0.9.1\n\nThemodel-visibleprogresssurfaceisasingletool:`todo_write`(#4132).\nAgentsandFleetworkersuseitforconcreteTo-do/Workprogressunderthe\nactiveruntimethreadordurabletask.\n\n`task_*`andtheFleet/Workflowledgerremainthedurablelifecycleowners.\nChecklistmetadataisthemodel-visibleprojectionofprogress:\n`task_updates.checklist`carriesthecurrentitems,completionpercentage,and\nin-progressitem.\n\n**TheTo-doistheonlyWorksurface.**`update_plan`isconversational\nreasoning—strategy,context,androutenotesforcomplexinitiatives.Itis\nnotaprogresssurface,mustnotduplicateTo-doitems,andplan-onlystateis\nneverrenderedasaTo-dosnapshot.\n\n**Norequestre-statesthelist.**ThemodellearnswhatisontheTo-dotheway\nitlearnsanythingelse:fromitsown`todo_write`call,whoseinputisthe\nwholelist,andtheshortreceiptitreturned(countsandtheLine truncated
"content":"# Fleet and sub-agents\n\n> 阅读简体中文版:[zh_hans/SUBAGENTS.md](zh_hans/SUBAGENTS.md)\n\nFleet manages saved models and role assignments for these same sub-agents.\nUse `agent` for an individual assignment and `workflow` for phases with\ndependencies and completion checks. See [Workflow authoring](WORKFLOW_AUTHORING.md)\nfor plans that use the Fleet model shortlist.\n\nFleet roles are the user-facing vocabulary for delegated work: a parent\nlaunches a focused `general`, `explore`, `planner`, `reviewer`, `implement`,\n`test`, or `advisor` through `agent` and gets back an `agent_id`, declared\ndeliverables, and effective limits while the worker runs. The default receipt is\ncompact; request addressed detail when you need the transcript handle or ledger.\nThe internal runtime type is `FleetRole` (formerly\n`SubAgentType`); the older role spellings (`worker`, `scout`, `plan`,\n`review`, `builder`, `verifier`, `consultant`, `oracle`, …) remain accepted only as a persisted/deserialize\ncompatibility adapter during v0.9.x. New prompts and config should use fleet\nnames.\n\nChild assignments run through the same `Engine::run_turn` planner, executor,\nSession and approval inbox as ordinary turns. The private captured ChildGrant\nnarrows tool actions, shell, workspace, network and descendant depth at the final\nexecution boundary. The existing SubAgentManager still owns launch slots,\ncoordination, worker checkpoints and terminal delivery; it does not run another\nmodel/tool loop. Work uses the full explicit owner limit. At the limit the host\npreserves a partial-result receipt without an extra model reporting request.\n\nTransient provider failures use Core's dispatch and retry boundaries. An exhausted\nbudget or interrupted request preserves the recorded checkpoint and a continuation\nhandle. Each actually dispatched attempt carries its original session, route and\nsource identity: provider usage is billed once, then projected to the worker\nledger. A successful response without usage and an unknown request outcome retain\ndistinct coverage gaps; neither is priced as zero. For work that must survive\nprocess restarts, sleep or remote execution, use Fleet or a Workflow-backed run.\n\nSub-agents inherit the parent's permitted tool registry, including `agent`\ncoordination. Spawning obeys one absolute depth ceiling: the root is depth 0,\nits child is depth 1, and a child at `max_spawn_depth` cannot spawn again.\nThe default has no product depth cap. Explicit operator, role and saved-profile\nlimits still narrow descendant authority; model requests cannot choose caps. Recovery and transcript\nforking retain the source's position and bounds; they do not buy another\ngeneration. The removed `agent_open`/`agent_eval`/`agent_close` lifecycle tools\nare absent from every registry.\n\nHealthy children continue after an ordinary parent response. Their completion\nreturns through the existing Engine inbox and can wake the parent for another\nnormal turn. Explicit interruption or cancellation remains authoritative.\n`detached: true` additionally opts a subtree out of parent-turn cancellation;\nit does not remove child budgets or the headless host's deadline.\n\nThis doc covers roles and individual worker controls. Use `workflow` to coordinate\nmultiple assignments through the same worker runtime; see the per-role prompts in\n`crates/tui/src/tools/subagent/mod.rs` (`*_AGENT_INTRO`) and the in-line\ntool description.\n\n## Role taxonomy\n\nThe `type` field on `agent` selects a fleet posture for the child\n(`agent_type` is accepted as a compatibility alias). Each role is a distinct\nstance toward the work — not just a different label.\n\n## Maintainer posture\n\nSub-agents help Codewhale move faster, but the parent agent still owns the\nmaintainer decision. Use children to gather evidence, review patches, and run\nverification while keeping the community posture in\n[`AGENT_ETHOS.md`](AGENT_ETHOS.md): issues are open intake, PR gates are\nreview-load controls, and harvested work needs clear contributor credit.\n\nWhen a child reviews community work, the parent should still inspect the PR\ndiff, linked issues, tests, and CI before merging, harvesting, closing, or\ndeferring it. A sub-agent's result is a working set, not a substitute for\nstewardship.\n\n| Role | Stance | Writes? | Network? | Shell posture | Typical use |\n|---------------|----------------------------------------|---------|----------|---------------|----------------------------------------------|\n| `general` | flexible; do whatever the parent says | yes | yes | yes | the default; multi-step tasks |\n| `explore` | read-only; map the relevant code fast | no | yes | bounded inspection | \"find every call site of `Foo`; check the PR with gh\"|\n|`planner`|analyseandproduceastrategy|noLine truncated
"content":"# Agent fleet\n\n> Baca terjemahan bahasa Indonesia: [id/FLEET.md](id/FLEET.md)\n\nAgent fleet is the local-first roster and member-selection layer for durable\nmulti-worker runs. It does not execute or authorize work. After fleet resolves\nwho should participate, the delegated coordinator launches a headless\n`codewhale exec` run and the Runtime tracks it durably. See\n[AGENT_RUNTIME.md](AGENT_RUNTIME.md) for how sub-agents, `exec`, and\nfleet-backed workers converge on one runtime. In product language, a user may\nstill \"open a sub-agent\"; in architecture language, durable nested work uses a\nfleet member identity with delegated runtime execution.\n\n## Naming and compatibility boundary\n\n**Fleet** is the public product noun. The durable ledger, saved rosters, config\ntables, and `--fleet` flag share that name:\n\n| Surface | Canonical |\n| --- | --- |\n| CLI | `codewhale fleet …` |\n| Slash command | `/fleet …` |\n\nThese shared names are load-bearing wherever changing them would break\nexisting workspaces, receipts, or scripts:\n\n- the durable ledger `.codewhale/fleet.jsonl` and the log directories\n `.codewhale/fleet/` and `.codewhale/fleet-host/`;\n- saved rosters `fleets/<name>.toml` and their `schema = \"fleet\"`header,under\n`$CODEWHALE_HOME/`ortheworkspace's`.codewhale/`(checked-inrostersatthe\nworkspaceroot's`fleets/`arestillread);\n-the`[fleet]`configtable(inline`[fleets.*]`tableswereremovedin0.9.14;namedfleetslivein`fleets/<name>.toml`files);\n-the`codewhaleworkflowrun--fleet<name>`flag;\n-wire,receipt,andcontrol-planeoperationidssuchas`fleet.status`.\n\nTherestofthisdocumentusesfleetasthepublicproductnounandretains\ntheseliteralpaths,keys,flags,andids.\n\nUseafleetrosterratherthananonymousshort-lived`agent`fanoutwhenevera\ndelegatedrunneedsstablememberidentitiesacrossretries,sleep/restart,\nremoteexecution,receipts,oraledgeredaudittrail.TheinitialCLIsurface\nis:\n\nForaguidedstart-to-monitorwalkthroughthatcombinesfleettaskspecswith\nWorkflowauthoring,see[fleet+WorkflowTutorial](FLEET_WORKFLOW_TUTORIAL.md).\n\n```sh\ncodewhalefleetinit\ncodewhalefleetruntasks.json--check#validateonly;nothingiscreatedorlaunched\ncodewhalefleetruntasks.json--max-workers4\ncodewhalefleetstatus\ncodewhalefleetinspect<worker-id>\ncodewhalefleetlogs<worker-id>\ncodewhalefleetartifacts<worker-id>\ncodewhalefleetinterrupt<worker-id>\ncodewhalefleetrestart<worker-id>\ncodewhalefleetresume<run-id>\ncodewhalefleetstop--all\n```\n\n`codewhalefleetresume<run-id>`istherestart-recoveryverb:itreplaysthe\nledger,reconcilesanyin-flightleasewhoseworkerstoppedheartbeating\n(retryingwithinthetask'sbudget,elsefailingandescalatingperthealert\npolicy),andprintsthepost-resumestatus.Itlaunchesnonewworkandis\nidempotent,soitissafetorunafteramanagerexit,laptopsleep,orruntime\nrestart.\n\nCoordinatorstateforfleet-backedrunsisstoredundertheworkspacein\n`.codewhale/fleet.jsonl`.Workerlogsandadapterlogsarestoredunder\n`.codewhale/fleet/`and`.codewhale/fleet-host/`.\n\n##Publiccontract:identity,membership,andselection\n\n**Fleet**=theuser'smodelinventory:whoisintherosterandwhichmemberisselected.\n\nApublicfleetidentityconsistsonlyof:\n\n-astablememberidandanoptionaluser-facingname;\n-asemanticrole,suchas`explore`,`implement`,or`reviewer`(thelegacy\nspellings`worker`,`scout`,`builder`,`verifier`,`consultant`,and`oracle`\narestillacceptedoninputandmapto`general`,`explore`,`implement`,\n`test`,and`advisor`);\n-anexactprovider/modelidentity,oranexplicitinheritedroute;\n-visiblerosterstateororigin.\n\nProjectorworkspacetrust,filesystemandnetworkreach,secretaccess,\napprovalmode,sandboxing,toolauthorization,andeveryotherformofruntime\nauthorityareseparatedelegated-coordinationandRuntimepolicyinputs.They\nareneverfleetidentityfields,andtheyneverselectorrerouteafleet\nmember.TheRuntimeappliesandclampsthosepoliciesonlyaftermember\nselection;iftheselectedmembercannotruninsidetheeffectiveenvelope,\nlaunchfailsclosedinsteadofchoosingsomebodyelse.\n\nNatural-languagememberselectionisdeterministic.Acallermayname:\n\n-anexactmemberid,optionallyas`member:<id>`or`id:<id>`;\n-auniqueuser-facingmembername,optionallyas`name:<name>`;\n-auniquesemanticrole,forexample`explore`or`role:explore`;\n-anexactpinnedmodelid,forexample`deepseek-v4-flash`,oritsoffline\ndisplayname,forexample`DeepSeekV4Flash`;or\n-anexact`route:<provider>/<model>`.\n\nAnunqualifiedexactmemberidwins.Everyothermatchsucceedsonlywhenit\nidentifiesonedistinctrostermember.Multiplematchesproduceanambiguity\nerroLine truncated
"content":"# Modes and Permission Postures\n\n> 阅读简体中文版:[zh_hans/MODES.md](zh_hans/MODES.md)\n\nCodewhale has three related concepts:\n\n- **TUI mode**: what kind of visible interaction you're in (Plan/Work/Operate).\n- **Permission posture**: how aggressively the UI asks before executing tools.\n- **Workflow**: named steps that coordinate sub-agents, with dependencies and\n results, available in any TUI mode.\n\nModel selection is separate. `--model auto` and `/model auto` route each turn to\na concrete model and thinking level; they are not TUI modes and are not part of\nthe `Tab` cycle.\n\nWorkflow runs named steps through the same sub-agent runtime; Fleet manages\ntheir saved roles and model assignments. A workflow adds ordering, result\nhandoffs, gates and a progress view. The active mode and permission posture\nstill control what each step may execute.\n\nBefore assigning steps, query `agent(action=\"roster\")` for the saved Fleet\nmodels and roles. A plan child can select a listed model with `model`, or use\nthe saved `role`/`profile` assignment. An Exact Fleet fixes each member's route.\n\n## TUI Modes\n\nPress `Tab` to complete composer menus or cycle through the visible modes\nwhen the composer is empty: **Plan → Work → Operate → Plan**. `Tab` never sends\nor queues composer text; use `Enter` to send or queue it.\nPress `Shift+Tab` to cycle permission posture (Ask → Auto-Review → Full Access).\nEvery change of effective permission writes a `Permissions changed to …` line\nin the transcript.\nPress `Ctrl+T` to cycle reasoning effort.\nRun `/mode` to open the mode picker, or switch directly with `/mode work`,\n`/mode plan`, or `/mode operate`.\n\n- **Plan**: design-first prompting. The stable primitive names remain familiar, but the runtime centrally refuses file mutation and shell execution. Read-only inspection and policy-allowed research, including deferred Web search/fetch, remain available. When a Plan turn successfully finishes a nonempty response, including a prose-only plan, the TUI asks how to continue: Work (Ask), Work (Auto-Review), or keep planning. Work sends the exact approved response as a normal follow-up and preserves that turn's existing To-dos, or adds one pending plan item without replacing unrelated work.\n- **Work** (internally `agent`): ordinary multi-step execution. The first-turn toolbox includes `read`, `write`, `edit`, `file_search`, `bash`, `agent`, `workflow`, `todo_write`, and `load_skill`, plus `create_goal`, `get_goal`, `update_goal`, and `task_shell_wait` so goal and background-shell controls are available without discovery. The agent calls `create_goal` when a request is a durable objective; the host never infers a goal from wording, and the contract must not tell the model otherwise. Approval, sandbox, repository law, and managed policy decide what may execute.\n- **Operate**: Work at full strength, the \"ultra\"mode.SametoolsandauthorityasWork;thedifferenceishowtheyareused.Eachsubstantiverequestbecomesagoal(`create_goal`),theplanstaysvisible(`todo_write`),multi-partworkrunsthrough`workflow`andparallel`agent`workers,non-trivialchangesgetanindependentreviewerbeforetheyarecalleddone,longcommandskeeprunninginthebackground,andrecurringorwatch-typeworkisofferedasan`automation`(createdafterapproval).Itkeepsgoinguntilthegoalisverified,blockedonyou,orpaused.\n\n`Act`and`/modeact`remaincompatibilityaliasesforWork.Savedsettings\nstillnormalizetotheinternalvalue`agent`.\n\n###Toolavailabilitybymode\n\n|Toolfamily|Plan|Work|Operate|\n|:---|:---:|:---:|:---:|\n|`read`andpolicy-alloweddeferredresearchtools|yes|yes|yes|\n|`write`and`edit`|visiblenames;executiondenied|approval-andpolicy-gated|sameasWork|\n|`bash`|visiblename;executiondenied|approval-andpolicy-gated|sameasWork;delegationispreferredwhenparallelismorisolationhelps|\n|`agent`|yes,subjecttochild-depthauthority|yes,subjecttochild-depthauthority|yes,subjecttochild-depthauthority|\n|Deferrednative,MCP,andplugintools|discoverablethrough`tool_search`whenpolicypermits|same|same|\n|Paidorexternal-servicetools|followspermissionposture|followspermissionposture|followspermissionposture|\n|Accessoutsidetheworkspaceroot|explicittrustedpathsonly|onlythroughtrustedpathsortrustmode|sametrusted-path/trustpolicyasWork;fleetprofilesneverwidenit|\n\nOperatechangesschedulingemphasis,notauthority.Itneitheraddsa\nmode-specifictooldenialnorbypassestheactiveapproval,sandbox,shell,\nask-rule,repository-law,ormanaged-policyboundary.Planremainsthe\nmode-specificexecutionboundaryforshellandwrite-capabletools;that\nauthoritydifferencedoesnotrequireadifferentprimitivevocabulary.\n\n###Operateloop(onescreen)\n\n```text\nUseLine truncated
"content":"# Agent fleet\n\n> Baca terjemahan bahasa Indonesia: [id/FLEET.md](id/FLEET.md)\n\nAgent fleet is the local-first roster and member-selection layer for durable\nmulti-worker runs. It does not execute or authorize work. After fleet resolves\nwho should participate, the delegated coordinator launches a headless\n`codewhale exec` run and the Runtime tracks it durably. See\n[AGENT_RUNTIME.md](AGENT_RUNTIME.md) for how sub-agents, `exec`, and\nfleet-backed workers converge on one runtime. In product language, a user may\nstill \"open a sub-agent\"; in architecture language, durable nested work uses a\nfleet member identity with delegated runtime execution.\n\n## Naming and compatibility boundary\n\n**Fleet** is the public product noun. The durable ledger, saved rosters, config\ntables, and `--fleet` flag share that name:\n\n| Surface | Canonical |\n| --- | --- |\n| CLI | `codewhale fleet …` |\n| Slash command | `/fleet …` |\n\nThese shared names are load-bearing wherever changing them would break\nexisting workspaces, receipts, or scripts:\n\n- the durable ledger `.codewhale/fleet.jsonl` and the log directories\n `.codewhale/fleet/` and `.codewhale/fleet-host/`;\n- saved rosters `fleets/<name>.toml` and their `schema = \"fleet\"`header,under\n`$CODEWHALE_HOME/`ortheworkspace's`.codewhale/`(checked-inrostersatthe\nworkspaceroot's`fleets/`arestillread);\n-the`[fleet]`configtable(inline`[fleets.*]`tableswereremovedin0.9.14;namedfleetslivein`fleets/<name>.toml`files);\n-the`codewhaleworkflowrun--fleet<name>`flag;\n-wire,receipt,andcontrol-planeoperationidssuchas`fleet.status`.\n\nTherestofthisdocumentusesfleetasthepublicproductnounandretains\ntheseliteralpaths,keys,flags,andids.\n\nUseafleetrosterratherthananonymousshort-lived`agent`fanoutwhenevera\ndelegatedrunneedsstablememberidentitiesacrossretries,sleep/restart,\nremoteexecution,receipts,oraledgeredaudittrail.TheinitialCLIsurface\nis:\n\nForaguidedstart-to-monitorwalkthroughthatcombinesfleettaskspecswith\nWorkflowauthoring,see[fleet+WorkflowTutorial](FLEET_WORKFLOW_TUTORIAL.md).\n\n```sh\ncodewhalefleetinit\ncodewhalefleetruntasks.json--check#validateonly;nothingiscreatedorlaunched\ncodewhalefleetruntasks.json--max-workers4\ncodewhalefleetstatus\ncodewhalefleetinspect<worker-id>\ncodewhalefleetlogs<worker-id>\ncodewhalefleetartifacts<worker-id>\ncodewhalefleetinterrupt<worker-id>\ncodewhalefleetrestart<worker-id>\ncodewhalefleetresume<run-id>\ncodewhalefleetstop--all\n```\n\n`codewhalefleetresume<run-id>`istherestart-recoveryverb:itreplaysthe\nledger,reconcilesanyin-flightleasewhoseworkerstoppedheartbeating\n(retryingwithinthetask'sbudget,elsefailingandescalatingperthealert\npolicy),andprintsthepost-resumestatus.Itlaunchesnonewworkandis\nidempotent,soitissafetorunafteramanagerexit,laptopsleep,orruntime\nrestart.\n\nCoordinatorstateforfleet-backedrunsisstoredundertheworkspacein\n`.codewhale/fleet.jsonl`.Workerlogsandadapterlogsarestoredunder\n`.codewhale/fleet/`and`.codewhale/fleet-host/`.\n\n##Publiccontract:identity,membership,andselection\n\n**Fleet**=theuser'smodelinventory:whoisintherosterandwhichmemberisselected.\n\nApublicfleetidentityconsistsonlyof:\n\n-astablememberidandanoptionaluser-facingname;\n-asemanticrole,suchas`explore`,`implement`,or`reviewer`(thelegacy\nspellings`worker`,`scout`,`builder`,`verifier`,`consultant`,and`oracle`\narestillacceptedoninputandmapto`general`,`explore`,`implement`,\n`test`,and`advisor`);\n-anexactprovider/modelidentity,oranexplicitinheritedroute;\n-visiblerosterstateororigin.\n\nProjectorworkspacetrust,filesystemandnetworkreach,secretaccess,\napprovalmode,sandboxing,toolauthorization,andeveryotherformofruntime\nauthorityareseparatedelegated-coordinationandRuntimepolicyinputs.They\nareneverfleetidentityfields,andtheyneverselectorrerouteafleet\nmember.TheRuntimeappliesandclampsthosepoliciesonlyaftermember\nselection;iftheselectedmembercannotruninsidetheeffectiveenvelope,\nlaunchfailsclosedinsteadofchoosingsomebodyelse.\n\nNatural-languagememberselectionisdeterministic.Acallermayname:\n\n-anexactmemberid,optionallyas`member:<id>`or`id:<id>`;\n-auniqueuser-facingmembername,optionallyas`name:<name>`;\n-auniquesemanticrole,forexample`explore`or`role:explore`;\n-anexactpinnedmodelid,forexample`deepseek-v4-flash`,oritsoffline\ndisplayname,forexample`DeepSeekV4Flash`;or\n-anexact`route:<provider>/<model>`.\n\nAnunqualifiedexactmemberidwins.Everyothermatchsucceedsonlywhenit\nidentifiesonedistinctrostermember.Multiplematchesproduceanambiguity\nerroLine truncated
"content":"# Fleet + Workflow Tutorial\n\n> 阅读简体中文版:[zh_hans/FLEET_WORKFLOW_TUTORIAL.md](zh_hans/FLEET_WORKFLOW_TUTORIAL.md)。\n\nFleet and Workflow are meant to work together, but they solve different parts\nof the problem:\n\n- **Fleet** configures and manages the same sub-agents: reusable roles, model\n routes, permissions, logs, artifacts, and status/restart/stop controls.\n- **Workflow** describes orchestration: phases, branches, reducers, loops, and\n agent leaves that can dispatch through the fleet/sub-agent runtime.\n\n**Default product path:** ask in natural language. Operate handles small or\ntightly coupled work directly under the active posture. Multi-step delegation\nuses a compact Workflow plan with named steps, dependencies, bounded scopes,\nand completion checks; results and evidence pass to the steps that need them.\nOne bounded, independent task can use a direct background agent. Reuse that\nagent with `followup` for continued work. Background work keeps the composer\navailable, and ordinary multi-agent work does not require workflow files. Details:\n[Automatic Workflows](AUTOMATIC_WORKFLOWS.md).\n\nThis tutorial covers the **manual** fleet task-spec / checked-in Workflow path\nfor operators who want durable host workers and reviewable specs. A\none-sentence request should still not silently generate `tasks.json`; worker\ncards and permission posture make dispatch visible without exposing authoring\nmechanics.\n\nThe examples use `codewhale fleet` and `/fleet`.\nOn-disk paths, config keys, and the Workflow `--fleet` flag use the Fleet name.\n\n## 1. Prepare The Workspace\n\nRun fleet from the workspace you want workers to inspect or modify:\n\n```sh\ncodewhale fleet init\n```\n\nThis creates the workspace ledger at `.codewhale/fleet.jsonl`. Worker logs and\nbounded artifacts live under `.codewhale/fleet/`; host adapter logs live under\n`.codewhale/fleet-host/`.\n\nIf you want named reusable workers, open the TUI and run:\n\n```text\n/fleet setup\n```\n\nPick a role, choose whether that profile inherits the operator route or pins a\nspecific provider/model, choose where the profile lives (**This project** →\n`.codewhale/agents/<role>.toml`, or **Personal** →\n`$CODEWHALE_HOME/agents/<role>.toml`, available across repositories while a\nsame-id project profile remains the higher-priority override), then review the\nexact file, permissions/tools/route posture, and save. The save control names\nits effect (\"Save to this project\" / \"Save as Personal profile\"), and\nreplacing an existing file always asks for a second confirmation. fleet task\nspecs can reference either resolved profile with `worker.agent_profile` or the\nshorter `worker.profile` alias.\n\nThis makes the fleet definition cross-repository, not the authority of one\nrunning session. For a multi-repository operation, launch Codewhale from a\nshared parent workspace. Profile availability does not grant filesystem access;\nthe session's workspace, explicit trusted paths, trust mode, and permission\nposture remain authoritative.\n\n## 2. Write A fleet Task Spec\n\n`codewhale fleet run` accepts JSON or TOML. The checked-in\n`docs/examples/fleet-dogfood.toml` file is the realistic manual smoke example;\nthe JSON below shows the same authoring shape with one read-only reviewer and\none bounded docs-note worker. The live Runtime policy controls secrets and\ntrust; fleet identity carries neither.\n\n```json\n{\n \"name\": \"docs readiness check\",\n \"labels\": {\n \"kind\": \"tutorial\"\n },\n \"tasks\": [\n {\n \"id\": \"map-docs\",\n \"name\": \"Map current docs\",\n \"objective\": \"Find the docs that describe fleet and Workflow.\",\n \"instructions\": \"Read docs/FLEET.md and docs/WORKFLOW_AUTHORING.md. Report the command surfaces, current limitations, and any confusing gaps.\",\n \"worker\": {\n \"role\": \"reviewer\",\n \"profile\": \"reviewer\",\n \"tools\": [\"rg\", \"sed\", \"git\"],\n \"model\": \"deepseek-v4-flash\"\n },\n \"workspace\": {\n \"required_files\": [\"docs/FLEET.md\", \"docs/WORKFLOW_AUTHORING.md\"],\n \"writable_paths\": [],\n \"environment\": {\n \"required\": [],\n \"allowlist\": []\n }\n },\n \"input_files\": [\"docs/FLEET.md\", \"docs/WORKFLOW_AUTHORING.md\"],\n \"expected_artifacts\": [\"log\", \"report\"],\n \"scorer\": {\n \"kind\": \"manual\"\n },\n \"retry_policy\": {\n \"max_attempts\": 1\n }\n },\n {\n \"id\": \"draft-gap-note\",\n \"name\": \"Draft gap note\",\n \"objective\": \"Draft a short local note for any missing tutorial steps.\",\n \"instructions\": \"Write a concise Markdown note with the missing fleet + Workflow tutorial steps. Do not edit public docs unless explicitly asked.\",\n \"worker\": {\n \"role\": \"builder\",\n \"tools\": [\"rg\", \"sed\Line truncated
"content":"# Workflow Authoring\n\n> 阅读简体中文版:[zh_hans/WORKFLOW_AUTHORING.md](zh_hans/WORKFLOW_AUTHORING.md)。\n\n> **Ordinary multi-agent work does not require this file.** In Operate, send\n> normal messages. Small work stays direct; multiple delegated steps use a\n> compact Workflow plan with dependencies, bounded scopes, and completion\n> evidence. Fleet manages the same sub-agents and roles. One bounded,\n> independent task can use a direct agent, with `followup` for continued work.\n> Act/Agent may also use optional soft-auto launch. See\n> [Automatic Workflows](AUTOMATIC_WORKFLOWS.md).\n\nWorkflow has one runtime boundary: authored source lowers to typed\nRust `WorkflowSpec`, Rust validates the IR, and the scheduler/headless worker\nruntime executes leaves. Authoring languages do not get hidden authority to own\nfiles, shell, network, providers, cancellation, or TUI state.\n\nCompatibility launch paths on the `workflow` tool:\n\n| Input | When to use |\n|-------|-------------|\n| `plan` | Structured goal / phases / children (preferred agent path) |\n| `script` | Short inline JS the model owns |\n| `source_path` | Checked-in `.workflow.js` / `.workflow.ts` in the workspace |\n\nUse `agent(action=\"roster\")` to inspect the saved Fleet models and roles before\nassigning children. Native plan children accept `model` for a saved shortlist\nselector, or `role`/`profile` for a saved assignment. Named Exact Fleets keep\ntheir member routes fixed and reject per-step model overrides. Plan children\nalso accept `cwd`, a repository-relative working directory — required in\nmulti-repository workspaces so the child (and worktree isolation) resolves the\nright repository, mirroring `task({cwd})`.\n\nFor a guided walkthrough from fleet task specs to Workflow authoring and\nmonitoring, see [fleet + Workflow Tutorial](FLEET_WORKFLOW_TUTORIAL.md).\n\n\n## Access model\n\nThe Workflow script is a **coordinator only**. It has no filesystem or shell of\nits own. Real work happens in sub-agents the script launches.\n\n| Layer | What it can access |\n|-------|--------------------|\n| Workflow script (JS VM) | Script variables, branching/loops, `task()` / `parallel()` / `pipeline()`, `phase` / `log`, `budget` / `args`. **No** direct FS, shell, network, env, imports, clock, or randomness. |\n| Workflow-spawned sub-agents | Normal tool surface (read/search/edit/write, shell, web, MCP) subject to role posture, allowlists, and parent policy. File edits for write-capable roles auto-accept under Workflow; shell / web / MCP still require parent auto-approve or fail closed. |\n| Parent session | Working directory, configured tools/MCP, permission mode, sandbox/network rules. |\n\n### Scale\n\n- Up to **16 concurrent** live agents in one run (additional spawns wait for a slot).\n- No default cumulative child limit; `[workflow] max_children = 0` leaves total work uncapped.\n- Owners can set a positive `max_children` limit and configure `max_concurrent`.\n- Automatic launch is model-judged on scope; the host enforces configured owner limits and plan validation.\n- Plan the population the work needs and let the host queue and clamp it.\n These ceilings are enforcement, not a reason to pre-shrink a valid plan.\n\nSee the Workflow JS sandbox tests for the fail-closed host surface inventory.\n\n## Language Choice\n\n| Surface | Strength | Tradeoff | Stance |\n|---|---|---|---|\n| YAML / JSON IR | Simple, reviewable, no runtime | Verbose for generated workflows | Keep as interchange/debug format |\n| JavaScript | Familiar object syntax and easy agent generation | Unsafe if executed as a general runtime | First-class authoring through declarative compile-only subset |\n| TypeScript | Best editor/types story for workflow SDK | Needs stripping/typechecking if full TS is supported | Same compile-only subset for now; richer SDK later |\n\nThe default high-capability path is TypeScript/JavaScript authoring, but only as\na compile step. The compiler accepts a JSON-compatible object inside\n`workflow({...})` from `.workflow.js` or `.workflow.ts`, lowers it to\n`WorkflowSpec`, and runs the Rust validation gate. (Starlark authoring was a\nbootstrap reference and has been removed; Workflow authoring is JS-only.)\n\n## Contract\n\nAccepted source shape:\n\n```js\nexport default workflow({\n \"id\": \"issue-audit-js\",\n \"goal\": \"Audit an issue fix with parallel agents\",\n \"nodes\": [\n {\n \"branch\": {\n \"id\": \"parallel-audit\",\n \"children\": [\n { \"agent\": { \"id\": \"code-audit\", \"prompt\": \"Review code\", \"agent_type\": \"review\" } },\n { \"agent\": { \"id\": \"test-audit\", \"prompt\": \"Review tests\", \"agent_type\": \"verifier\" } }\n ]\n }\n },\n { \"reduce\": { \"id\": \"summary\", \"inputs\": [\"code-audit\", \"test-audit\"], \"prompt\": \"Summarize\"}}\n]\n});\n```\n\nSupportednodewrappers:`agent`,`branch`,`sequence`,`reduLine truncated
"content":"# Codewhale product telemetry\n\n> 阅读简体中文版:[zh_hans/TELEMETRY.md](zh_hans/TELEMETRY.md)\n\n**Usage analytics are on by default in the current source.** The first\ninteractive launch gives a localized, nonblocking disclosure naming **Codewhale\nand PostHog**, with a direct route to turn counting off. Notice version `5`\ndescribes this policy; displaying it is not recorded as human acceptance.\nMissing an older acceptance record does not turn a fresh installation off.\nExisting explicit opt-outs remain off across upgrades, and unreadable privacy\nstate fails closed. Environment and CLI kill switches still take precedence.\n\nRead the disclosure with `codewhale config telemetry`. Turn usage off in\n`/settings` or run `codewhale config set telemetry false`. Turning it back on\nin Settings or with `codewhale config set telemetry true` updates the existing\ndurable preference and privacy records for new sessions. The compatibility\ncommand `codewhale config telemetry --accept-notice 5` remains available, but\nis not required for a fresh installation. Collection never includes work content or credentials.\n\nThe PostHog sink is optional and inert until the ingest operator explicitly\nconfigures a project token and approved regional host, and records staging\nproof that the actual egress path does not forward the original client IP.\nSource readiness does not establish a deployment, an activated processor,\nan egress receipt, or a retention setting.\n\nCodewhale does not collect conversations, code, prompts, files, file/repo/branch\nnames, model content, or credentials. It sends no per-turn or per-tool timeline.\nIt does send the closed, aggregate schema below: version and platform classes,\nsession duration/outcome, feature/error counters, and a random install id that\nrotates every 90 days.\n\n**There is now a real endpoint.** An enabled session sends its batches to the\nfirst-party ingest service at `https://telemetry.codewhale.net/v1/telemetry`,\nwhich is the shipped default for `telemetry_endpoint`. What that service is,\nwhat it stores, and what it structurally cannot store is spelled out in \"What\nthe endpoint does\" below.\n\n**To send nothing anywhere, turn telemetry off** (see \"Turning it off\"). To stay\nenabled but contact nobody, set `telemetry_endpoint = \"\"`:batchesarethen\nappendedto`$CODEWHALE_HOME/telemetry/dryrun.jsonl`onyourownmachine,byte\nforbytewhattheserverwouldhavereceived,andnoHTTPclientisever\nconstructed.Thatfileishowyouauditthisdocumentagainstreality.\n\nThisdocumentistheschema.Itisnotasummaryoftheschema:atestin\n`crates/telemetry`parsesthefieldnamesoutofthisfileandassertsset\nequalityagainstthestructstheserializeractuallyuses,soafieldthatis\nhereandnotinthecode—orinthecodeandnothere—failsthebuild.\n\nLocalsavedsessionsand`codewhalemetrics`remainavailablewithtelemetry\ndisabledandrequirenoaccountorhostedreportingservice.Disablingtelemetry\ndoesnotdisablelocalusagereceipts.\n\n##Turningitoff\n\nTherearetwooffswitchesandtheydodifferentthings.Bothstopcollection\ncompletely;onlyoneofthemerasesanything.\n\n```sh\ncodewhaleconfigsettelemetryfalse#optout:stopscollectionanderasesstate\nCODEWHALE_TELEMETRY=0codewhale#killswitch:stopscollection,erasesnothing\ncodewhale--telemetryfalse#thesamekillswitch,foronecommand\n```\n\n**`telemetry=false`intheconfigfileistheopt-out.**Itisafloor:\n`--telemetrytrue`and`CODEWHALE_TELEMETRY=1`bothlosetoit,becausea\nsettingyoucanundobyaccidentfromawrapperscriptisnotasetting.It\ndeletestherandominstallid,truncateseverybufferedeventandeverydry-run\nrecord,andwritesatombstone.Appends,identity/statewrites,anddeliveryall\nsharethewipe'sorderinglock,soonceopt-outreturnsnopre-opt-outwriteor\nPOSTremainsinflight.Ifanypartofthatwipefails,thetombstoneisstill\nthereandthebufferisundrainable—afailedwipefailsclosed.Everylater\nrunre-assertsthesametombstoneforaslongasthesettingstands,soit\nsurvives.Turningtelemetrybackonin`/settings`recordsthatexplicitpreference\ninbothexistingprivacyregisters;afreshlaunchcanthenclearthetombstone.Nothingbufferedbeforethatpointis\neversent.\n\n**Theenvironmentvariableandtheflagarekillswitches,notopt-outs.**\nTelemetryisofffortherun,nothingiswritten,nothingissent—and\nnothingondiskistouchedordeleted.Thatisdeliberate:aharnessoragent\nthatsets`CODEWHALE_TELEMETRY=0`foronecommandmustnotsilentlydiscardthe\ninstallidandthedry-runrecordsofthepersonwhoownsthemachine.Ifyou\nwanttheerasingkind,usetheconfigfile.\n\n`CODEWHALE_TELEMETRY`(andits`DEEPSEEK_TELEMETRY`alias)accepts\n`0`,`1`,`true`,`false`,`yes`,`no`Line truncated
"content":"# `/preview-request` — see the outbound request without sending it\n\n`/preview-request` renders a **request manifest**: a typed, redacted\ndescription of the request the next **primary agent turn** would send. It never\nsends that request, never appends to the conversation, and never writes engine,\nsession, or Work state. `/dryrun` and `/preview_request` are compatibility\naliases.\n\nIt is a human command. There is deliberately no model-visible tool for it.\n\n```\n/preview-request # session facts; route/body reported unavailable\n/preview-request json # same manifest as JSON\n/preview-request --prompt <text> # the next turn for that prompt\n/preview-request json --prompt <text> # both\n/preview-request base-prompt # exact base layer only; no runtime additions\n```\n\n### Argument grammar\n\n```\nargs := flag* [ \"--prompt\" <whitespace> prompt ]\nflag := json | --json | manifest | --manifest | prompt | base-prompt | --base-prompt\nprompt := every remaining byte, verbatim\n```\n\n**Flags come before `--prompt`, which is terminal.** Everything after it is\nprompt text — including a trailing `json`. `/preview-request --prompt fix it\njson` previews the prompt *\"fix it json\"* as a human table; the JSON spelling\nis `/preview-request json --prompt fix it`. There is exactly one reading of any\ninput, and an unknown argument before `--prompt` is rejected rather than\nguessed at.\n\n**The prompt keeps your bytes.** Repeated whitespace and newlines inside it\nsurvive; exactly one whitespace codepoint separates `--prompt` from its text\nand is consumed as syntax. Any additional leading whitespace, plus all\ntrailing whitespace and newlines, remains prompt data. The prompt is hashed\ninto the previewed body, so normalizing it would describe a request that\ndiffers from the real one in the one field you typed.\n\n`prompt` remains a compatibility alias for the ordinary protected manifest.\n`base-prompt` / `--base-prompt` are an explicit, human-only disclosure mode:\nthey print exactly the effective base-prompt bytes and nothing else. They\ncannot be combined with JSON or `--prompt`. Effective system text is never\nprinted because it can contain project instructions, skills, and memory.\n\n## `--prompt` is necessary for an exact manifest, and not always sufficient\n\nThe next user message is **part of the request**. Without it there is no\nnext-turn body to describe, and under auto model routing there is not even a\nroute: the route is decided by the text you have not typed yet.\n\nSo the manifest is sectioned, and each section is either exact or typed-absent:\n\n| Section | Exact when |\n| --- | --- |\n| `session` | always — posture, gates, base-prompt provenance, requested model/reasoning |\n| `route` | `--prompt` was supplied, an active goal has not exhausted its token budget, a fixed route is selected, no `message_submit` hooks are configured, and the shared planner resolved that route |\n| `tools` | the route is exact **and** the MCP tool state can be snapshotted without connecting |\n| `body` | the tool surface and authoritative Work snapshot are exact **and** no runtime transform would rewrite the request first |\n\n### Why a section stops being exact\n\n| Typed reason | What a real turn would do that an inspection may not |\n| --- | --- |\n| `auto-route-unresolved-until-next-prompt` | decide the route from text you have not typed |\n| `auto-route-classification-not-executed` | call the provider-backed Auto classifier; preview is strictly offline, so production must resolve it |\n| `no-hypothetical-prompt-supplied` | send a message the manifest does not have |\n| `message-submit-hooks-not-executed` | run mutable hooks that can rewrite or block the text — and therefore the route, tool policy, and body derived from it |\n| `prompt-resolution-failed` | fail the same way on skill authority or file mentions |\n| `route-plan-failed` | fail route resolution or preflight |\n| `mcp-state-not-snapshottable` | connect MCP servers and discover tools this catalog does not contain |\n| `runtime-transforms-before-send` | auto-compact, run context-overflow recovery, inject a background-shell completion, allow a running or terminal-undelivered child to complete, or flush pending LSP diagnostics before the first request |\n| `work-state-not-snapshottable` | read the current graph-backed Work projection; preview never substitutes an asynchronously published, potentially stale To-do view |\n| `goal-token-budget-exhausted` | stop the active goal before dispatch because durable token usage has reached the configured budget |\n| `goal-state-not-snapshottable` | decide whether the active goal's terminal budget gate permits another request |\n| `request-preparation-failed` | build no body at all |\n\n**`mcp-state-not-snapshottable` takes the body with it.** A catalog missing its\nMCP contribution is not \"the same request with no MCP tools\"—aLine truncated
"content":"# Hooks\n> 阅读简体中文版:[zh_hans/HOOKS.md](zh_hans/HOOKS.md)\n\nHooks run a shell command when the Codewhale **TUI** reaches a lifecycle\npoint. They are plain processes: they receive context through environment\nvariables, some receive a JSON payload on stdin, and three of them can steer\nwhat Codewhale does next.\n\nThis page is the authoritative reference for what is implemented today.\nConfiguration syntax that overlaps with the rest of `config.toml` lives in\n[CONFIGURATION.md](CONFIGURATION.md); this file is the event-by-event\ncontract.\n\n## Scope\n\nHooks fire in the interactive TUI and in the engine turn loop, which the\nRuntime API threads behind the desktop app and web drive as well.\n\n| Surface | Fires hooks |\n| --- | --- |\n| `codewhale` / `codew` interactive TUI | yes |\n| `codewhale exec` (headless one-shot) | opt-in: `--hooks` fires `tool_call_before` and `shell_env` |\n| Runtime API threads (desktop app, web) | yes: `tool_call_before`, `shell_env`, `tool_call_after`, `on_error`; `GET /v1/hooks` lists the set |\n| the `codewhale` CLI dispatcher and its subcommands | no |\n| app-server / ACP | no |\n| the `workflow` tool and sub-agent *internals* | no — but the TUI fires `subagent_spawn` / `subagent_complete` around them |\n| public API | there is none |\n\nThe `crates/hooks` event-sink crate in this repository is an unrelated\ninternal mechanism. It shares no configuration, no event names, and no\ncontract with the hooks described here.\n\n### `codewhale exec --hooks`\n\nHeadless runs fire no hooks by default — a CI job should not start paging an\non-call rotation merely because a config exists. `codewhale exec --hooks`\nopts the run in. The engine-side events are `tool_call_before` (exit code 2\nstill denies the call; `ask` resolves fail-closed because nothing can prompt\nheadlessly) and `shell_env`. UI-driven events such as `session_start`,\n`message_submit`, and `turn_end` do not fire — they live in the interactive\nshell, not the turn loop. Fleet worker subprocesses never fire operator\nhooks. Independently of this flag, `permissions.toml` typed rules already\napply to `exec` — the run drives the same turn loop, and a `deny` blocks in\nevery mode.\n\n## Quick start\n\n```toml\n# ~/.codewhale/config.toml\n[hooks]\nenabled = true\n\n[[hooks.hooks]]\nname = \"announce\"\nevent = \"session_start\"\ncommand = \"echo 'Codewhale session started'\"\n```\n\nRun `/hooks` in the TUI to list what is configured, whether the global switch\nis on, and any entry that was rejected at load. Run `/hooks events` for the\nevent names.\n\n## Configuration\n\n```toml\n[hooks]\nenabled = true # global switch; false suppresses every hook\ndefault_timeout_secs = 30 # see the timeout note below\nworking_dir = \"/path/to/dir\" # default: the session workspace\n\n[[hooks.hooks]]\nevent = \"tool_call_before\" # required; one of the 15 names below\ncommand = \"~/.codewhale/hooks/gate.sh\" # required; `sh -c` on Unix, `cmd /C` on Windows\nname = \"gate\" # optional label for /hooks and log lines\ntimeout_secs = 30 # optional, default 30\nbackground = false # optional; foreground inside the hook worker\ncontinue_on_error = true # optional, default true\ncondition = { type = \"tool_name\", name = \"bash\" } # optional\n```\n\n`timeout_secs` note, stated as implemented: when `[hooks].default_timeout_secs`\nis set it **overrides** every hook's own `timeout_secs`, it does not merely\nsupply a default for hooks that omit one. Leave it unset if you want per-hook\ntimeouts to apply. `/hooks list` shows the timeout the runtime will actually\napply, and names the override when one is in force.\n\n`default_timeout_secs = 0` is **rejected at load**. Because the value replaces\nevery hook's own `timeout_secs`, a zero there would expire every hook in the\nconfig immediately — including a `tool_call_before` gate, which then denies\nevery matching tool call. The override is ignored, per-hook `timeout_secs`\napplies, the hooks themselves still load, and the rejection is reported by\n`/hooks list` under *configuration problems*. Per-hook `timeout_secs = 0` is\nrejected too, but that only drops the one hook that wrote it.\n\nHooks run with the workspace (or `working_dir`) as the current directory.\n\n### Timeouts\n\nThe timeout applies to **foreground and background hooks alike**. When it\nexpires:\n\n- the hook's whole process group is killed — Unix process groups, Windows Job\n Objects — so a hook that spawns children does not outlive its budget;\n- the child is then reaped, so nothing is normally left detached or zombied;\n- a foreground hook's result is `success = false`, `exit_code = None`, empty\n `stdout`/`stderr`, and `error = \"Hook timed out after Ns\"`;\n-abackgroundhook'stimeoutisloggedat`warn`underthe`hooks`target.\nNothingisreportedtothecaller,becausethecallerstoppedwaitinLine truncated
"content":"# Configuration\n\ncodewhale reads configuration from a TOML file plus environment variables.\nAt process startup it may also load literal built-in-provider credentials from\na workspace-local `.env` file. Use the tracked `.env.example` as the template;\ncopy it to `.env`, then add only credential values.\n\nA workspace is not configuration authority. Codewhale therefore ignores\nconfig/profile/home paths, provider/model/base-URL routing, MCP/plugin state,\napproval/sandbox/shell posture, executable paths, runtime settings, and every\nother non-credential `.env` entry. Variable expansion is rejected so a\nrepository cannot substitute an ambient secret into a credential value. Use\n`config.toml`, CLI flags, or values exported by the launching shell for those\nexplicit control-plane settings. `.env` is read from a stable regular-file\nhandle, is capped at 1 MiB, and symbolic links, reparse points, and multiply\nlinked files are rejected.\n\n## Reading and checking configuration from the CLI\n\n`codewhale config get <key>` reads scalar keys, whole tables such as `tools`,\nand nested paths such as `tools.user_input_timeout_seconds`. Displayed tables\nand nested values apply the same recursive credential redaction as `config dump`.\n\n`config set` supports its named scalar keys and the provider, route, and\nnotification commands. It refuses a key that nothing reads and suggests the\nnearest real key (`config set calm_mod on` names `calm_mode`). A settings.toml\nkey such as `calm_mode` or `tool_collapse` is validated and written to the\nuser-global settings.toml, not config.toml; `config get` reads it back from\nthere even when an old config.toml copy (which nothing reads) is still present,\nand names that copy so you can `config unset` it. Settings keys have no project\nscope, so `--project` refuses them. A config.toml key is checked against the\ntype its reader expects and stored as a TOML boolean or number where the\nreader needs one (`yolo = true`, `max_subagents = 4`); `reasoning_effort`\naccepts the same aliases as `/effort` (#6563). Other dotted writes fail before\nmodifying the file and name the TOML table to edit. For example, set a tools\ntimeout in the file as:\n\n```toml\n[tools]\nuser_input_timeout_seconds = 0\n```\n\n`codewhale config doctor` checks credential presence and endpoint shape, and\nwarns about each config.toml root key that nothing reads: a settings.toml key\nleft in config.toml is named as misplaced, and any other unread key gets a\ndid-you-mean. Keys read by any runtime reader are not reported. The warnings do\nnot fail the check, and a clean result does not validate every runtime setting's\nvalue (#6083, #6563).\n\n## Constitution, project instructions, and repo authority\n\nCodewhale has several instruction surfaces. They are deliberately separate so a\npersonal constitution, repo policy, project instructions, and runtime security\ncontrols do not blur together.\n\n- **Bundled global Constitution** — the compiled base law in the binary. It is\n the default floor for every session.\n- **User-global constitution** — the normal guided setup output. Manage it with\n `/constitution` or `/setup`; Codewhale stores structured data at\n `$CODEWHALE_HOME/constitution.json` (default `~/.codewhale/constitution.json`)\n and renders it into a separate `<codewhale_user_constitution>` prose block.\n This can express preferences and stop conditions, but it does not change\n runtime approval policy, sandbox, shell, network, trust, or MCP permissions.\n- **Repo-local constitution** — optional project policy in\n `.codewhale/constitution.json`, described below.\n- **`AGENTS.md`** — cross-agent **project instructions** (prose). This is the\n canonical file for \"how should an agent work in this repo.\" Run `/init` to\n scaffold one. Instruction files written for other tools (`CLAUDE.md`,\n `.claude/instructions.md`, `.cursorrules`, `.github/copilot-instructions.md`\n and similar) are read only when you opt in with the top-level\n `project_instruction_imports` key, for example `[\"claude\"]`, `[\"cursor\"]` or\n `[\"all\"]`(see`config.example.toml`);Codewhalewarnswhenitfindsoneit\ndidnotload.\n-**Memoryandhandoffs**—recalledstate.Useful,butlowerauthoritythan\nconstitutionsandprojectinstructions.\n\n###Managingtheuser-globalconstitution(`/setup`and`/constitution`)\n\nThebundled**workingagreement**isthesafedefaultandnolongeraddsa\nrequiredfirst-runscreen.Customizeitlaterthrough`/constitution`orthe\nprogressive`/setup`guide.Provider/modelreadiness,workspacetrust,and\nruntimeposturestayseparatefromthisguidance.\n\nOnthe**Workingagreement**(constitution)step:\n\n-**`1`–`6`**tunetheguideddraft.**`G`**previewsit,and**`G`**again\nratifiesandsavesafreshstructured`constitution.json`.\n-**`A`**(shownonlywhenaproviderisconfigured)asksyourfirstconfigured\nmodeltoLine truncated
"content":"# Claude Plugin Compatibility\n\nCodewhale treats Claude Code skill folders as instruction bundles when they are\nplain `SKILL.md` directories. It does not run Claude Code plugin runtimes.\n\n## Supported\n\n- Workspace or global `.claude/skills/<name>/SKILL.md` directories discovered by\n the normal skill registry.\n- GitHub or tarball installs that contain one selected skill directory such as\n `skills/<name>/SKILL.md`, `.agents/skills/<name>/SKILL.md`,\n `.claude/skills/<name>/SKILL.md`, or a nested package layout ending in\n `skills/<name>/SKILL.md`.\n- Companion files inside the selected skill directory, such as `references/`,\n `examples/`, or scripts that are only used after the skill is explicitly\n loaded and trusted.\n\n## Compatible plugin bundles\n\nSince v0.9.13, `/plugin install` accepts the declarative subset described in\n[Installing plugins](PLUGINS.md): nested `.claude-plugin/plugin.json` metadata,\nroot skills, commands and agent profiles, plus inline MCP declarations or\n`.mcp.json`. Local sources in Claude marketplace catalogs resolve from the\nrepository root outside `.claude-plugin`.\n\nExact `${ENV_NAME}` MCP header references map to Codewhale's environment-backed\ncredentials, including `Authorization: Bearer ${ENV_NAME}`. Literal credential\nvalues and compound header templates are rejected; importing never reads the\nreferenced environment variables.\n\nThe importer uses Codewhale's existing adapters, review hashes and enablement.\nA Claude marketplace's labels or defaults grant no authority. Bundles are not\nscanned from another application's plugin roots or activated automatically.\n\n## Unsupported runtime features\n\nThe importer rejects hooks, LSP declarations, custom MCP file paths, and\n`${CLAUDE_PLUGIN_ROOT}` expansion. It does not run plugin build steps,\nTypeScript agents, dashboard servers, shared plugin state, or token-gated\nservice processes. Claude-specific frontmatter behavior such as `model: inherit`\nis not an additional runtime contract. Remote MCP authentication still follows\nCodewhale's existing plugin credential boundary; installation does not complete\nOAuth or borrow another application's credentials.\n\n`/skill install` remains a separate one-skill operation: it rejects multi-skill\nplugin archives rather than silently selecting one directory and dropping other\ncomponents. Use `/plugin install` for supported bundles, or migrate one explicit\nskill directory when the repository depends on unsupported runtime behavior.\n\nSee [PLUGIN_BUNDLES.md](PLUGIN_BUNDLES.md) for discovery, review and activation.\n"
"content":"# Claude Plugin Compatibility\n\nCodewhale treats Claude Code skill folders as instruction bundles when they are\nplain `SKILL.md` directories. It does not run Claude Code plugin runtimes.\n\n## Supported\n\n- Workspace or global `.claude/skills/<name>/SKILL.md` directories discovered by\n the normal skill registry.\n- GitHub or tarball installs that contain one selected skill directory such as\n `skills/<name>/SKILL.md`, `.agents/skills/<name>/SKILL.md`,\n `.claude/skills/<name>/SKILL.md`, or a nested package layout ending in\n `skills/<name>/SKILL.md`.\n- Companion files inside the selected skill directory, such as `references/`,\n `examples/`, or scripts that are only used after the skill is explicitly\n loaded and trusted.\n\n## Compatible plugin bundles\n\nSince v0.9.13, `/plugin install` accepts the declarative subset described in\n[Installing plugins](PLUGINS.md): nested `.claude-plugin/plugin.json` metadata,\nroot skills, commands and agent profiles, plus inline MCP declarations or\n`.mcp.json`. Local sources in Claude marketplace catalogs resolve from the\nrepository root outside `.claude-plugin`.\n\nExact `${ENV_NAME}` MCP header references map to Codewhale's environment-backed\ncredentials, including `Authorization: Bearer ${ENV_NAME}`. Literal credential\nvalues and compound header templates are rejected; importing never reads the\nreferenced environment variables.\n\nThe importer uses Codewhale's existing adapters, review hashes and enablement.\nA Claude marketplace's labels or defaults grant no authority. Bundles are not\nscanned from another application's plugin roots or activated automatically.\n\n## Unsupported runtime features\n\nThe importer rejects hooks, LSP declarations, custom MCP file paths, and\n`${CLAUDE_PLUGIN_ROOT}` expansion. It does not run plugin build steps,\nTypeScript agents, dashboard servers, shared plugin state, or token-gated\nservice processes. Claude-specific frontmatter behavior such as `model: inherit`\nis not an additional runtime contract. Remote MCP authentication still follows\nCodewhale's existing plugin credential boundary; installation does not complete\nOAuth or borrow another application's credentials.\n\n`/skill install` remains a separate one-skill operation: it rejects multi-skill\nplugin archives rather than silently selecting one directory and dropping other\ncomponents. Use `/plugin install` for supported bundles, or migrate one explicit\nskill directory when the repository depends on unsupported runtime behavior.\n\nSee [PLUGIN_BUNDLES.md](PLUGIN_BUNDLES.md) for discovery, review and activation.\n"
"content":"# Configuration\n\ncodewhale reads configuration from a TOML file plus environment variables.\nAt process startup it may also load literal built-in-provider credentials from\na workspace-local `.env` file. Use the tracked `.env.example` as the template;\ncopy it to `.env`, then add only credential values.\n\nA workspace is not configuration authority. Codewhale therefore ignores\nconfig/profile/home paths, provider/model/base-URL routing, MCP/plugin state,\napproval/sandbox/shell posture, executable paths, runtime settings, and every\nother non-credential `.env` entry. Variable expansion is rejected so a\nrepository cannot substitute an ambient secret into a credential value. Use\n`config.toml`, CLI flags, or values exported by the launching shell for those\nexplicit control-plane settings. `.env` is read from a stable regular-file\nhandle, is capped at 1 MiB, and symbolic links, reparse points, and multiply\nlinked files are rejected.\n\n## Reading and checking configuration from the CLI\n\n`codewhale config get <key>` reads scalar keys, whole tables such as `tools`,\nand nested paths such as `tools.user_input_timeout_seconds`. Displayed tables\nand nested values apply the same recursive credential redaction as `config dump`.\n\n`config set` supports its named scalar keys and the provider, route, and\nnotification commands. It refuses a key that nothing reads and suggests the\nnearest real key (`config set calm_mod on` names `calm_mode`). A settings.toml\nkey such as `calm_mode` or `tool_collapse` is validated and written to the\nuser-global settings.toml, not config.toml; `config get` reads it back from\nthere even when an old config.toml copy (which nothing reads) is still present,\nand names that copy so you can `config unset` it. Settings keys have no project\nscope, so `--project` refuses them. A config.toml key is checked against the\ntype its reader expects and stored as a TOML boolean or number where the\nreader needs one (`yolo = true`, `max_subagents = 4`); `reasoning_effort`\naccepts the same aliases as `/effort` (#6563). Other dotted writes fail before\nmodifying the file and name the TOML table to edit. For example, set a tools\ntimeout in the file as:\n\n```toml\n[tools]\nuser_input_timeout_seconds = 0\n```\n\n`codewhale config doctor` checks credential presence and endpoint shape, and\nwarns about each config.toml root key that nothing reads: a settings.toml key\nleft in config.toml is named as misplaced, and any other unread key gets a\ndid-you-mean. Keys read by any runtime reader are not reported. The warnings do\nnot fail the check, and a clean result does not validate every runtime setting's\nvalue (#6083, #6563).\n\n## Constitution, project instructions, and repo authority\n\nCodewhale has several instruction surfaces. They are deliberately separate so a\npersonal constitution, repo policy, project instructions, and runtime security\ncontrols do not blur together.\n\n- **Bundled global Constitution** — the compiled base law in the binary. It is\n the default floor for every session.\n- **User-global constitution** — the normal guided setup output. Manage it with\n `/constitution` or `/setup`; Codewhale stores structured data at\n `$CODEWHALE_HOME/constitution.json` (default `~/.codewhale/constitution.json`)\n and renders it into a separate `<codewhale_user_constitution>` prose block.\n This can express preferences and stop conditions, but it does not change\n runtime approval policy, sandbox, shell, network, trust, or MCP permissions.\n- **Repo-local constitution** — optional project policy in\n `.codewhale/constitution.json`, described below.\n- **`AGENTS.md`** — cross-agent **project instructions** (prose). This is the\n canonical file for \"how should an agent work in this repo.\" Run `/init` to\n scaffold one. Instruction files written for other tools (`CLAUDE.md`,\n `.claude/instructions.md`, `.cursorrules`, `.github/copilot-instructions.md`\n and similar) are read only when you opt in with the top-level\n `project_instruction_imports` key, for example `[\"claude\"]`, `[\"cursor\"]` or\n `[\"all\"]`(see`config.example.toml`);Codewhalewarnswhenitfindsoneit\ndidnotload.\n-**Memoryandhandoffs**—recalledstate.Useful,butlowerauthoritythan\nconstitutionsandprojectinstructions.\n\n###Managingtheuser-globalconstitution(`/setup`and`/constitution`)\n\nThebundled**workingagreement**isthesafedefaultandnolongeraddsa\nrequiredfirst-runscreen.Customizeitlaterthrough`/constitution`orthe\nprogressive`/setup`guide.Provider/modelreadiness,workspacetrust,and\nruntimeposturestayseparatefromthisguidance.\n\nOnthe**Workingagreement**(constitution)step:\n\n-**`1`–`6`**tunetheguideddraft.**`G`**previewsit,and**`G`**again\nratifiesandsavesafreshstructured`constitution.json`.\n-**`A`**(shownonlywhenaproviderisconfigured)asksyourfirstconfigured\nmodeltoLine truncated
"content":"# Installing plugins\n\n> 阅读简体中文版:[zh_hans/PLUGINS.md](zh_hans/PLUGINS.md)。\n\nTo create a bundle, start with [Write your first Codewhale plugin](PLUGIN_AUTHORING.md)\nand its runnable Skills example.\n\nThis is the walkthrough for the `/plugin install` on-ramp (v0.9.4, #5182).\n[PLUGIN_BUNDLES.md](PLUGIN_BUNDLES.md) remains the contract for the bundle\nformat (`plugin.json`, compatible `kimi.plugin.json` or\n`.claude-plugin/plugin.json`, or legacy `plugin.toml`), discovery, validation, and\nthe trust/enable lifecycle — this document covers how bits get onto disk in\nthe first place.\n\n`/plugin suggest <task>` is a local, read-only companion: it ranks already\ninstalled bundles (name, keywords, description, bundled skill names, declared\nhosts) and any marketplace catalogs you have added with `/plugin marketplace add`.\nIt explains the match and gives the next review, enable, or catalog-install\nstep, but never installs, trusts, or enables a bundle on its own.\n\n## How Codewhale offers plugins\n\nCodewhale is helpful about plugins, not pushy. The rules:\n\n- **One proactive surface.** Sending a task can show one quiet toast when the\n prompt matches an installed-but-idle plugin or a catalog candidate you do\n not have yet, for example `/plugin trust supabase` or\n `/plugin marketplace install <catalog> supabase`. Nothing appears while you\n type, and nothing is appended to your message to advertise plugins.\n- **One switch.** With `contextual_tips` off, no plugin guidance appears\n anywhere. Required notices and your own `/plugin` commands still work.\n- **One budget.** Plugin offers share the per-session guidance budget with\n other tips. In the interactive TUI the model can call\n `request_plugin_install` once per session to ask you to review a plugin the\n task needs; a second call fails. Exec, ACP, and runtime-API sessions do not\n get the tool.\n- **Built-ins are never advertised.** Bundled plugins such as Computer Use\n appear only in `/plugin list` and Extensions.\n- **Specific terms only.** Generic words (accessibility, browser, chrome,\n docs, screenshot, web, wiki, …) and Codewhale's own vocabulary (agent, code,\n data, MCP, model, session, tool) never trigger an offer, so asking why an\n MCP server is stuck does not suggest a vendor's plugin. Distinctive names,\n phrases and domains still do. The matcher and the marketplace's\n `check-marketplace.mjs` share one stoplist.\n- **Only what runs here.** Plugins whose `when.os` excludes this OS are not\n offered.\n- **Review before acting.** A model-requested row offers Review. Its button\n opens the existing Extensions inventory: Plugins for an installed bundle,\n Marketplace for a suggestion that is not installed. Nothing is installed,\n trusted, or enabled by that click. Choose the inventory's explicit action;\n trust still reviews the exact installed content before activation.\n- **Reversible dismissal.** Esc clears a non-empty draft first, then hides the\n row for this session only. \"Don't suggest again\"istheexplicit,\npersistedchoice.`/plugindismissals`listsbothkinds,and\n`/plugindismissalsreset[<name>]`letssuggestionsofferapluginagain.\n-**Discoveryispassive.**Findnewpluginsinthesedocs,\n`/pluginmarketplacelist`,Extensions,andthebrowserguidebelow.\n\nCodewhaledoesnotinventaremotepluginURL;missingpluginsaresuggested\nonlyfromcatalogsyouadded.On-diskbundlechangesstilltoast\n`/pluginreload`onsendandbetweenturns.\n\n##Browser:pickone\n\nSeveraloptionscandriveabrowser.Theydifferinwhosebrowseritisand\nwhatitcansee.\n\n|Option|Whosebrowser|Goodfor|\n|---|---|---|\n|`chrome-devtools`MCP(`/mcprecommendations`)|AChromeitdrives,whichcanincludesigned-inpages|DevTools-levelinspectionandperformancework|\n|PlaywrightMCP(`/mcprecommendations`)|Afresh,isolatedprofilewith`--isolated`|Scriptedflowsandtestingwithoutyouridentity|\n|ComputerUse`browser_*`tools(bundled,offuntilreviewed)|Oneitlaunches,inaprofileofitsown|Browserstepsinsideawiderdesktoptask|\n|Chromewhale(developerpreview,`codewhale-hq/codewhale-plugin-marketplace`)|Yours,alreadyopen,inyourownChromeprofile;loadunpacked|Readingoractingonthetabyouarelookingat,onegrantedsiteatatime|\n\nNoneoftheseisofferedtoyouproactively.Addtheonethatfitsthejob.\n\n##Sources\n\n`codewhaleinstall<spec>`and`/plugininstall<spec>`usethesameinstaller.\nTheshellentrydoesnotstartamodelorrequiremodelcredentials.Bothkeep\nbundlesdisabledanduntrusteduntilyouexplicitlyreviewandenablethem.\n\n```sh\ncodewhaleinstallgit:github.com/example/plugin@v1.0.0\ncodewhaleinstallnpm:@example/plugin@1.0.0\ncodewhaleinstall./local-plugin\n```\n\nGitshorthandsupportsGitHubarchivesonly;refsmustbeasinglesafesegment.\nULine truncated
"content":"# Plugin bundles\n\n> 阅读简体中文版:[zh_hans/PLUGIN_BUNDLES.md](zh_hans/PLUGIN_BUNDLES.md)。\n\nCodewhale supports a deliberately small plugin-bundle boundary. The boundary\nwas drawn in v0.9.1 and is extended deliberately in v0.9.10: a bundle may\ncontribute declarative Skills, MCP configuration, Commands, Agent profiles,\nand Hooks through Codewhale's existing engines. Unsupported declarations stay\ninventoried instead of disabling a mixed bundle. Discovery alone never\nexecutes, enables, trusts, downloads, updates, or installs anything.\n\nThis document owns the bundle formats, discovery,\nvalidation, and the trust/enable/runtime contract. [PLUGINS.md](PLUGINS.md)\nowns how bits get onto and off disk — the `/plugin install`, `update`,\n`uninstall`, and `suggest` on-ramp added in v0.9.4 (#5182). Compatible Claude Code bundles use the same native adapters; their supported subset is\n[CLAUDE_PLUGIN_COMPAT.md](CLAUDE_PLUGIN_COMPAT.md).\nFor a runnable native example and explicit OpenCode/DSH data conversion, see\n[Write your first plugin](PLUGIN_AUTHORING.md).\n\n## Discovery and precedence\n\nCodewhale scans only its own roots, looking in each `<name>/` directory for a\nmanifest named `plugin.json` (the native Agent Plugins v1.0.0 format, since\nv0.9.4), `kimi.plugin.json` (the compatible Kimi Skills/MCP subset, since\nv0.9.8), `plugin.toml` (the legacy Codewhale format, still fully readable), or\n`.claude-plugin/plugin.json` (the compatible Claude subset, since v0.9.13):\n\n- User: `~/.codewhale/plugins/<name>/`\n- Workspace: `<workspace>/.codewhale/plugins/<name>/`\n\nA bundle that publishes multiple formats is read through `plugin.json` first,\nthen `kimi.plugin.json`, then the legacy `plugin.toml`, then\n`.claude-plugin/plugin.json`.\nComputer Use ships as a built-in bundle; it still requires review and\nenablement before activation. The internal precedence order is\nbuilt-in, user, then workspace; the first bundle with a given name wins. This\nprevents a repository from shadowing an explicitly installed user bundle.\nSymbolic-link roots, manifests, component paths, and nested component files\nfail closed.\n\nThe embedded Computer Use files are materialized under\n`$CODEWHALE_HOME/builtin-plugins/snapshots/computer-use-<bundle-digest>/computer-use`.\nEach process captures only its own embedded digest's discovery root. Concurrent\nbuilds therefore keep separate, complete source trees; publishers never delete\nor replace an existing snapshot. Reuse checks every embedded byte, directory\nentry, file type, executable flag, and the stamp. A partial or altered snapshot\nis rejected without repair. Interrupted private staging directories are not\ndiscovered or reused.\n\nThe existing path-bound plugin identity and trust rules apply: identical embedded\nbytes at the same home reuse the same identity; changed bytes require a fresh\nreview and enablement. Moving from the older mutable\n`builtin-plugins/computer-use` layout also requires one fresh review. Legacy\nbundles and receipts remain intact for running older binaries; no trust is\nmigrated. Diagnostics do not create a missing Codewhale home.\nThe selected home may itself be a symlink: its resolved directory is pinned\nbefore creating any built-in paths. Links in the owned built-in cache paths\nstill fail closed.\n\nNew user and workspace bundles are always untrusted and disabled. Discovery is\nread-only and does not inspect any other application's extension or credential\ndirectories: ambient roots such as `.claude/plugins` or `.cursor/plugins` are\nnever scanned.\n\nPre-v0.9.1 `overrides.json` enablement was intentionally not imported as\ntrust; every bundle activates only through the content-hash and\n`codewhale-plugin-capabilities-v3` activation-policy review below.\n\n## Manifest\n\nAll supported encodings parse into the same internal manifest, so validation, hashing,\nreview, and runtime behavior are identical downstream. On-disk auto-migration\nbetween them is deliberately not performed; `/plugin export <name>\n<target-dir>` publishes a loaded bundle as a spec-valid Agent Plugins v1.0.0\ndirectory without modifying the installed one.\n\n### `plugin.json` (Agent Plugins v1.0.0)\n\nThe standard's manifest root is closed: `$schema`, `name`, and the optional\nwell-known fields (`version`, `description`, `author`, `homepage`,\n`repository`, `license`, `keywords`, `extensions`). An unknown root key is a\nparse error. Client-specific data lives under `extensions`, keyed by\nreverse-domain namespace: unknown vendor namespaces are ignored, never\nrejected — that is what lets a bundle authored for another client load here —\nwhile unknown keys inside Codewhale's own `extensions[\"net.codewhale\"]`\nnamespacearerejectedratherthansilentlydropped.\n\nNamesfollowthestandard'srule:1–64lowercaseASCIIletters,digits,or\ninternalsingle`-`/`.`,startingandendingalphanumeric,never`--`or`..`.\nA`skills/`dLine truncated
"content":"# First-party plugin marketplace\n\nCodewhale includes an offline snapshot of the `codewhale` catalog in the same\nmarketplace store consumed by the terminal, Extensions, recommendations, and\nRuntime API. It lists Computer Use, WhaleWiki, Whalesong, Cloudflare Docs, the\nCodewhale skill bundle, and Codewhale for Chrome (Chromewhale). Browsing does\nnot fetch or execute anything.\n\n```text\n/plugin marketplace list\n/plugin marketplace show codewhale\n/plugin marketplace install codewhale whalewiki\n/plugin show whalewiki\n/plugin trust whalewiki\n/plugin enable whalewiki\n/plugin update whalewiki\n```\n\nReview the manifest and capabilities before trust and enablement. Installation\nuses the existing size-limited, traversal-safe installer and starts disabled\nand untrusted. An update with changed bytes requires review again. Official\ncatalog provenance grants no execution or network permission. Removing the\ncatalog persists the choice and leaves installed plugins untouched; a locally\nadded catalog named `codewhale` takes precedence over the bundled snapshot.\n\nThe bundle source uses a gzip tarball URL with `#path=plugins/whalewiki` (or\n`#path=skills`). The fragment selects exactly one bundle inside the shared\nrepository archive. Only that subtree is installed. Empty paths, traversal,\nambiguous roots, links, oversized archives, and changed plugin identities are\nrejected. The install receipt preserves the source, including its selector,\nso `/plugin update` retains the same bundle selector and reviewed revision.\n\n## Built-in Computer Use across upgrades\n\nComputer Use also ships inside the binary as a built-in bundle. Each build\nwrites its own copy under `$CODEWHALE_HOME/builtin-plugins`, so an upgrade\npresents it as a new bundle. Your review carries over when the capability\nhash is unchanged: a bundle you trusted and enabled stays trusted and enabled\non the new build. When the capabilities changed, it shows\n`capabilities-changed` and stays off until you review it again with\n`/plugin show computer-use` and `/plugin trust computer-use`. If you revoked\ntrust after your most recent review, nothing carries and the new build waits\nfor a fresh review; once you review a build again, later upgrades carry that\nreview. User and workspace plugins never carry trust: changed bytes\nalways need review.\n\n## Chromewhale\n\nChromewhale (listed as \"Codewhale for Chrome\")isinthecatalogbundledwith\nCorefrommarketplacerevision`ae3dd22`on,whereitscheckspassonmacOS,\nLinuxandWindows.Itisadeveloperpreview:installingtheplugindoesnot\ninstallthebrowserside,soyouloaditsbundledChromeextensionunpacked\nyourself.Itthenreadsandactsonthetabyouarelookingat,onegranted\nsiteatatime.Likeeverycatalogentry,itinstallsdisabledanduntrusted\nuntilyoureviewit.\n\n##Keepingtherepositoriescurrent\n\n|Content|Authoritativesource|Copiestocheck|\n|---|---|---|\n|Catalog,WhaleWiki,Whalesong,CloudflareDocs|`codewhale-hq/codewhale-plugin-marketplace`|Corecatalogsnapshot|\n|Bundledskills|Coreactivecatalogand`crates/tui/assets/skills`|Marketplace`skills`and`skills/upstream.json`|\n|ComputerUse|`Hmbown/codewhale-cu-plugin`|MarketplacepluginandCorebundledruntime|\n\nThe`Marketplaceconnection`workflowvalidatestheexactcatalogrevisionon\ncatalogchanges.Itsweeklyandmanualrunscomparethecurrentpublic\nmarketplace,bundledsnapshot,skills,andComputerUseruntime.Driftfails\nthecheckwithamaintenanceinstruction;itdoesnotrewriteuserinstalls\norgrantnewpermissions.Itusesread-onlyrepositoryaccess.\n\nForeachintentionalupdate,reviewtheupstreamdiff,synchronizethe\nsource-ownedcopies,andrunthemarketplacechecks:\n\n```sh\n#Fromcodewhale-plugin-marketplace,withsiblingsourcecheckouts:\n#Aftercommittingcanonicalskillchanges,whenintentionallyupdatingskills:\nnpmrunsync:skills\nnpmruncheck----core../codewhale\nnpmruncheck:cu-sync\nnpmtest&&npmruncheck:web\n```\n\nCommitthereviewedmarketplacechanges,thenupdateCorefromthatcommitted\nrevision(thegeneratornevercopiesanuncommittedmarketplacedocument):\n\n```sh\n#Fromcodewhale:\npython3scripts/sync-marketplace.py--marketplace../codewhale-plugin-marketplace\npython3scripts/sync-marketplace.py--marketplace../codewhale-plugin-marketplace--check\nnpmtest&&npmruncheck:web\n```\n\nReviewthegeneratedsnapshotandrebuildCore.Itsprovenancerecordsthe\nexactmarketplacecommit,andeachgeneratedbundleURLpinsthatimmutable\nrevision.AlatermarketplacechangerequiresrefreshingtheCoresnapshotand\nrebuilding;anexistingpinnedinstalldoesnotsilentlyfollow`main`.Pushthe\nreviewedmarketplacerevisionbeforepublishingaCorereleasethatreferencesit.HostedCImustbegreenforthe\nactualpublishedrevisions;localchecksdonotproveLine truncated
"content":"# Writing an extension tool or command\n\nThe experimental TypeScript extension host runs reviewed plugin code in a\nshared Node process (or, as an opt-in, Bun). Rust still owns sessions, tool admission, approval and\nexecution. Extensions currently contribute tools and slash commands; they\ncannot provide an approval service, run a second agent loop or replace\nbuilt-in tools or commands.\n\n## Quick start\n\nStart with [hello-extension](examples/plugins/hello-extension/hello.mts) and its\n[plugin.json](examples/plugins/hello-extension/plugin.json). The example is\nexecuted unchanged by the host tests. It returns a greeting and does not read\nfiles or use the network.\n\n1. Explicitly enable `[features] extension_host = true` in your configuration,\n or start Codewhale with `codewhale --enable extension_host` (the flag goes\n before any subcommand).\n2. Install the example's directory using `/plugin install <local-directory>`.\n3. Run `/plugin validate hello-extension`, then `/plugin show hello-extension`.\n4. Run `/plugin enable hello-extension` to open its content/capability review.\n Read the review and personally run its exact `/plugin trust <token>` command,\n then enable the plugin. Trust alone does not execute the code.\n5. Ask Codewhale to use `hello_greet`. The tool follows the normal approval gate.\n6. Run `/hello-greet Codewhale`. The command runs when you type it, and shows\n its answer in the transcript with its origin, `extension:hello-extension`.\n\nAn authoring agent should stop after installation and validation and present\nthe review to the person. Do not trust or enable a bundle automatically.\n\n## Locations, installation and trust\n\nUser bundles live under `~/.codewhale/plugins/`; workspace bundles live under\n`<workspace>/.codewhale/plugins/`. New bundles use Agent Plugins v1.0.0\n`plugin.json`. Declare `extensions[\"net.codewhale\"].native.path` as one regular\n`.mjs`, `.js` or `.mts` file inside the bundle. Directories and other extensions\nfail validation when the host is enabled. With the flag off, native entries\nremain inventory-only.\n\nA plugin may split its code across several entries with `native.paths` (up to\n64, alone or beside `path`). They are activated in the order declared, as the\nfibers of one plugin: they share one owner, so one disable, review change or\ncrash tears all of them down together. Each entry is a separate module with its\nown `apply`, and tool and command names must be unique across them. If an entry\nfails to activate (throws, requires a service the host does not provide, or has\na registration refused), only that entry is withdrawn: entries that already\nactivated stay registered, later entries still activate, and the plugin fails\nonly when no entry activates. Listing the same file twice is one entry.\n\n## Runtime\n\n`[extension_host] runtime` selects what runs the host. Node is the default.\nBun is an opt-in: it is qualified on macOS only, and becomes the default only\nafter an explicit, recorded cutover.\n\n```toml\n[extension_host]\nruntime = \"node\" # default: Node only\n# runtime = \"bun\" # Bun only; fails rather than falling back to Node\n# runtime = \"auto\" # Bun >= 1.4.0 if one is found and starts, otherwise Node\n# bun = \"~/.bun/bin/bun\" # the only Bun tried when set\n# node = \"/opt/homebrew/bin/node\" # the only Node tried when set\n# mcp_backend = \"host\" # experimental SDK backend; default is \"rust\"\n```\n\n`mcp_backend = \"host\"` selects the pinned MCP SDK independently of\n`[features] extension_host`. That feature controls optional Native extensions;\nselecting Host MCP does not activate them. The SDK for existing stdio, HTTP and legacy SSE server connections in a separate\nbuiltin host process. Rust retains process/network authority, credential\nresolution, catalog admission, tool approval and cancellation. HTTP/SSE uses\nRust's guarded HTTP client; the SDK receives opaque session selectors and\nexact operation grants. Request IDs remain the Rust client's strings through\nan adapter bound to each grant. Explicit Host selection never falls back to\nRust or replays an uncertain operation. The default remains `\"rust\"`while\nrecordedtransportparityisqualified.TheHostpathusesthesameRust\nGETsessionpreflightandboundedreactiveOAuthrefreshasthedefault\nbackend.AnexplicitincompatibleHTTPresponsecannegotiatetherealSDK\nSSEtransportonlywithafreshexactRustoperationgrant;astale-session\nrefusalremainsatypedRustrecoverydecision.Credentials,configuredURLs,\nOAuthbrowser/loginoperationsandComputerUsedecisionkeysstayinRust.\nBothbackendsrunagainstthesamerecordedMCPgoldens.Passingthatsuite\nandtheactualbrokeracceptancetestsisrequiredbeforeanydefaultcutover.\nThedefaultalsorequirestheplatformisolationandmeasuredPhase3gates\ninOpsCURRENT_DECISIONS§26.ThenativeRustprotocoladaptersremainforoneLine truncated
"content":"# Local browser client\n\n> 阅读简体中文版:[zh_hans/WEB.md](zh_hans/WEB.md)。\n\n`codewhale web` opens Codewhale's embedded browser client over the canonical\nRuntime API. It is a local surface: the server always binds to\n`127.0.0.1`, cannot be rebound to a LAN address, and cannot run with Runtime\nauthentication disabled.\n\n## Start it\n\nFrom the workspace Codewhale should operate in, run:\n\n```bash\ncodewhale web\n```\n\nThe default address is `http://127.0.0.1:7878`. To avoid a local port\ncollision, choose another loopback port:\n\n```bash\ncodewhale web --port 8788\n```\n\nCodewhale starts the Runtime API, serves the dependency-free client embedded\nin the installed binary, prints a single-use launch URL, and asks the operating\nsystem to open that URL in the default browser. If the browser does not open,\nuse the printed URL within ten minutes. Stop the process with `Ctrl+C`; the\nbrowser session ends with it.\n\n## What the browser can do\n\nThe embedded client provides a responsive thread and search rail, Runtime-owned\nsession facts, transcript and tool receipts, and a composer. It can create,\nselect, rename, and archive threads; start or steer turns; interrupt work;\nresolve approvals; and answer Runtime user-input requests.\n\nThe browser is another view of the same local Runtime. It does not create a\nsecond cloud account, copy provider credentials into browser storage, or\nweaken the configured approval and sandbox policies.\n\n## Authentication boundary\n\nThe browser-launch URL contains a random, short-lived, one-time bootstrap\ncapability. It never contains the Runtime bearer token. A loopback request\nexchanges the capability for an `HttpOnly`, `SameSite=Strict`, process-local\nsession cookie and immediately invalidates the capability.\n\nReused, expired, malformed, and non-loopback bootstrap attempts fail closed.\nThe Runtime token is not placed in rendered HTML, browser storage, URL\nqueries or fragments, or browser-launch arguments. The one-time bootstrap\nvalue is printed in the local terminal and briefly passes through the operating\nsystem's browser launcher. It is single-use and expires after ten minutes, but\na hostile process already running as the same OS user remains inside the local\ntrust boundary.\n\nCookie-authenticated state-changing requests must also present the exact\nlocal web origin. Cross-origin browser requests are rejected. Existing\nexplicit bearer and Runtime-token-header clients retain their normal Runtime\nAPI behavior.\n\n## Local means local\n\n`codewhale web` accepts only `--port`; there is no `--host` or insecure-auth\noption on this command. Do not treat it as a public website or expose its port\ndirectly through router forwarding, a public reverse proxy, or a tunnel.\n\nThe separate `codewhale app-server --mobile` and `--http` modes have different\ndeployment and authentication contracts. Read [RUNTIME_API.md](RUNTIME_API.md)\nbefore operating either one, especially before selecting a non-loopback bind.\n\n## Troubleshooting\n\n- If port `7878` is occupied, pass an unused `--port` value.\n- If the browser does not open, copy the printed single-use bootstrap URL into\n a browser on the same machine within ten minutes. Start `codewhale web` again\n if that URL has already been used or expired.\n- If the page loads but a provider is unavailable, inspect `codewhale doctor`\n and `/provider`; the web command does not configure or move provider\n credentials.\n- If a session expired, stop and restart `codewhale web` to mint a new\n process-local session. Reusing an old bootstrap URL is expected to fail.\n- A new tab opened by typing the address is not signed in: the session's\n request proof stays in the tab that opened it. Use that tab, duplicate it,\n or restart `codewhale web`.\n\nFor integration endpoints, headers, events, and the complete web-session\ncontract, see [RUNTIME_API.md](RUNTIME_API.md).\n"
"content":"# Installing Codewhale\n\n> 阅读简体中文版:[zh_hans/INSTALL.md](zh_hans/INSTALL.md)\n\nCodewhale is an open-source coding agent that runs in your terminal. You give\nit a task (\"fix the failing test\", \"add a CLI flag\"). It reads your repository,\nedits files and runs commands. In the default **Ask** posture it applies file\nedits inside the workspace immediately (and shows you the diff), but asks before\nrunning shell commands, so commit or stash anything you care about first. It\nworks with many model providers. **DeepSeek** is the default.\n\nThe command is `codewhale`. `codew` is a shorter alias for the same program.\n\nThe installation paths were first checked with **v0.10.0** (released 2026-09-22) on a\nfresh **Ubuntu 24.04 x86_64** machine. The dated\n[install receipts](https://github.com/codewhale-hq/Codewhale/blob/37ecdfcc49bc68a9b0d058b97c3946e62c34bd31/docs/install-report/v0.10.0-2026-09-23/RECEIPTS.md) record the commands and output checked at that time. Steps that\ncould not be run on that machine are marked **(untested on this VM: reason)**.\nmacOS, Windows and Android are out of scope, apart from a few notes. A second\npass re-ran the installer, manual-download, archive and npm paths, the no-key\nchecks and zsh completion on **macOS 26.1 (Apple silicon)**; see\n[macOS notes](#macos-notes). Steps that need a model call were not re-run\nthere.\n\nInstall commands that use `latest` resolve to the latest **published** GitHub\nRelease or package. Between releases, `main` may already describe the next\nversion (for example a v0.10.2 source candidate before its tag). A\nprebuilt candidate is available through the official installer only after its\ntag, checksums and release assets exist. Contributors can build `main` now.\n\n## No account needed\n\nCodewhale needs no account. Keys you save with `/provider` or\n`codewhale auth set` stay on your machine; see\n[API keys and providers](#8-api-keys-and-providers). New Codewhale accounts\nare not open, so this guide does not cover account key storage.\n\n## Test and contribute to current source\n\n`latest` selects the latest published release. To test unreleased repairs,\nbuild canonical GitHub `main`, or CNB `main` after verifying its commit matches.\nThe source version number alone does not establish a published package.\n\n```bash\ngit clone --branch main https://github.com/codewhale-hq/CodeWhale.git\ncd CodeWhale\ncargo build --release --locked -p codewhale-cli\ntarget/release/codewhale --version\n```\n\nSee the Cargo section for build prerequisites; use the repository's stable Rust\nselection (declared minimum 1.89). Windows uses `target/release/codewhale.exe`\nand needs MSVC. A development build is separate from a signed release package.\nFor mainland-China downloads and source validation, see [CNB mirror](CNB_MIRROR.md).\nThe [Tencent Lighthouse bootstrap](../scripts/tencent-lighthouse/README.md) is\nbilingual. An npm registry mirror serves the wrapper; native binary downloads\nstill need an available release asset source.\n\n---\n\n## 60-second quickstart (Linux or macOS)\n\n```bash\n# 1. Install. Downloads two checksum-verified binaries into ~/.local/bin (no sudo).\ncurl -fsSL https://codewhale.net/install.sh | sh\n\n# 2. Make sure ~/.local/bin is on your PATH, now and in future terminals.\necho 'export PATH=\"$HOME/.local/bin:$PATH\"' >> ~/.bashrc # zsh: use ~/.zshrc\nexport PATH=\"$HOME/.local/bin:$PATH\"\ncodewhale --version # -> codewhale 0.10.0 (1be1a703b975)\n\n# 3. Give it a DeepSeek API key (from https://platform.deepseek.com/api_keys).\ncodewhale auth set --provider deepseek # prompts for the key; nothing is echoed\ncodewhale auth status --provider deepseek # \"active source: secret store\"\n\n#4.Runyourfirsttaskinsideagitrepository.\ncd~/your-project\ncodewhale\n```\n\nIntheTUI,typesomethingconcrete:\n\n```text\ncreateaPythonfileprimes.pythatprintsthefirst10primes,runit,andshowmetheoutput\n```\n\nCodewhalewritesthefile,showsyouadiff,thenasks**APPROVAL:bash\npython3primes.py–Doyouwanttoproceed?**Press`y`toallowitonce.It\nrunsthecommandandreportstheoutput.Press`Ctrl-D`(withanemptyinput\nbox)toquit.Itprintsthecommandtoresumethesessionlater.\n\n>**Ifnothinghappensafteryousendyourfirstmessage,**youhavenokey\n>configured.v0.10.0doesn'twarnyouinthatcase.Press**F3**.IfDeepSeek\n>shows`missingkey`,pressEnter,pastethekey,thenpickamodeland\n>confirm.\n\n---\n\n##Contents\n\n1.[Beforeyoustart](#1-before-you-start)\n2.[Install:recommendedinstaller](#2-recommended-installer-curl--sh)\n3.[Install:manualdownloadfromGitHubReleases](#3-manual-download-from-github-releases)\n4.[Install:npm](#4-npm)\n5.[Install:Cargo/buildfromsource](#5-cargo-and-building-from-source)\n6.[Install:Homebrew(Linux)andNix](#6-homebrew-on-linux-and-nix)\n7.[Updatingandrollingback]Line truncated
"content":"# codewhale Operations Runbook\n\n> 阅读简体中文版:[zh_hans/OPERATIONS_RUNBOOK.md](zh_hans/OPERATIONS_RUNBOOK.md)。\n\nThis runbook covers practical debugging and incident response for the local CLI/TUI runtime.\n\n## Quick Triage\n\n1. Confirm binary + config:\n - `cargo run -- --version`\n - `cat ~/.codewhale/config.toml` (or inspect configured profile)\n2. Enable verbose logs:\n - `RUST_LOG=codewhale_tui=debug cargo run`\n - For HTTP retries/reconnects: `RUST_LOG=codewhale_tui::client=debug cargo run`\n3. Capture current state:\n - `ls ~/.codewhale/sessions`\n - `ls ~/.codewhale/sessions/checkpoints`\n - `ls ~/.codewhale/tasks`\n\n## Incident: Turn Hangs or Stream Stops\n\nSymptoms:\n- TUI remains in loading state\n- partial assistant output with no completion\n\nChecks:\n1. Inspect retry/health logs (`codewhale_tui::client`)\n2. Verify endpoint connectivity:\n - `curl -sS https://api.deepseek.com/beta/models -H \"Authorization: Bearer $DEEPSEEK_API_KEY\"`\n3. Confirm no local sandbox/permission deadlock in tool output\n\nActions:\n1. If a foreground shell command is running, press `Ctrl+B` to move it to the background (the turn keeps running and the command becomes a background job under `/jobs`); use `Ctrl+C` instead if you want to cancel the turn.\n2. If the command was started in the background, ask the assistant to use `Bash` with `action: \"cancel\"` and the returned process id.\n3. Use `Esc` or `Ctrl+C` to interrupt the current turn when you want to stop the request itself.\n4. Retry prompt; if still failing, restart TUI.\n5. On restart, verify the previous queued/in-flight runtime turn is shown as interrupted rather than left in a running state.\n\n## Incident: Network Outage / Offline Behavior\n\nExpected behavior:\n- New prompts are queued while offline mode is active\n- Queue state persists per session to\n `~/.codewhale/sessions/checkpoints/<session-id>.offline_queue.json`; a legacy\n global `offline_queue.json` is adopted once on upgrade\n\nChecks:\n1. Open queue in TUI: `/queue list`\n2. Confirm persisted queue file exists and updates timestamp\n\nActions:\n1. Restore connectivity\n2. Re-send queued entries (from `/queue edit <n>` + Enter, or normal input flow)\n3. Ensure queue file clears when queue is empty\n\n## Incident: Crash Recovery Needed\n\nExpected behavior:\n- Each session checkpoints to `~/.codewhale/sessions/checkpoints/<session-id>.json`;\n a legacy `latest.json` is still read for recovery but is no longer written\n- Startup begins a fresh session unless `--resume`/`--continue` is supplied\n\nActions:\n1. Resume prior work explicitly via `codewhale --resume <id>` (alias\n `codewhale resume <id>`; `codewhale --continue` recovers the newest\n interrupted checkpoint for the workspace) or `Ctrl+R` in TUI\n2. If checkpoint inspection is needed, inspect `checkpoints/<session-id>.json` (or a leftover legacy\n `latest.json`) for schema mismatch/details\n3. If schema is newer than binary supports, upgrade binary or remove stale checkpoint\n\n## Incident: Persistent State Schema Errors\n\nSymptoms:\n- Errors like `schema vX is newer than supported vY`\n\nAffected stores:\n- sessions (`~/.codewhale/sessions/*.json`)\n- runtime thread/turn/item records\n- tasks (`~/.codewhale/tasks/tasks/*.json`)\n\nActions:\n1. Confirm binary version and migration expectations\n2. Back up the state directory before editing\n3. Either:\n - run with a newer compatible binary, or\n - archive incompatible records and regenerate state\n\n## Incident: MCP/Tool Execution Failures\n\nChecks:\n1. Validate `~/.codewhale/mcp.json` schema and server command paths\n2. Confirm server process can start manually\n3. Check sandbox denials in TUI history / logs\n\nActions:\n1. Retry with required approvals (or YOLO only when appropriate)\n2. Temporarily disable failing MCP server and isolate issue\n3. Re-enable after verification with `/mcp` diagnostics\n\n## Post-Incident Checklist\n\n1. Preserve logs and relevant state files\n2. Record trigger, impact, and mitigation\n3. Add or update regression tests (retry/recovery/schema)\n4. Update this runbook and architecture docs if behavior changed\n"
"content":"# Docker\n\n> 阅读简体中文版:[zh_hans/DOCKER.md](zh_hans/DOCKER.md)。\n\nCodewhale publishes a multi-arch Linux image to GitHub Container Registry\nfor each release.\n\nThe latest published image is 0.10.0 at `ghcr.io/hmbown/codewhale`.\nTo test the current `main` source, use [Building locally](#building-locally)\nin a checkout of [`codewhale-hq/Codewhale`](https://github.com/codewhale-hq/Codewhale).\n\n```bash\ndocker pull ghcr.io/hmbown/codewhale:latest\n```\n\n## Quick start\n\nRun the published image with a Docker-managed data volume:\n\n```bash\ndocker volume create codewhale-home\n\ndocker run --rm -it \\\n -e DEEPSEEK_API_KEY=\"$DEEPSEEK_API_KEY\" \\\n -v codewhale-home:/home/codewhale/.codewhale \\\n -v \"$PWD:/workspace\" \\\n -w /workspace \\\n ghcr.io/hmbown/codewhale:latest\n```\n\nUse a pinned release tag for reproducible installs:\n\n```bash\ndocker run --rm -it \\\n -e DEEPSEEK_API_KEY=\"$DEEPSEEK_API_KEY\" \\\n -v codewhale-home:/home/codewhale/.codewhale \\\n -v \"$PWD:/workspace\" \\\n -w /workspace \\\n ghcr.io/hmbown/codewhale:v0.10.0\n```\n\nThe pinned example uses the [0.10.0 release](https://github.com/codewhale-hq/Codewhale/releases/tag/v0.10.0).\n\n## Default image contract\n\n`ghcr.io/hmbown/codewhale:latest` and the semver tags are conservative runtime\nimages:\n\n- the container runs as the non-root `codewhale` user with UID/GID `1000:1000`\n- the image does not grant passwordless `sudo`\n- the image is meant to run Codewhale against mounted workspaces, not to mutate\n the base operating system at runtime\n- user state belongs in a volume mounted at `/home/codewhale/.codewhale`\n\nThat default is intentional. Keep using it for the smallest trust boundary. If a\nproject needs `apt-get`, compiler toolchains, Node/Python package managers,\ncustom CA certificates, or other host-like setup inside Docker, build an\nexplicit toolbox image instead of changing the default image contract.\n\n## Opt-in toolbox/custom image\n\nThe repository includes an example\n[`docs/examples/Dockerfile.toolbox`](examples/Dockerfile.toolbox) that extends\nthe official image with passwordless `sudo` and common development packages.\nBuild it with a pinned Codewhale tag when you want repeatable project\nenvironments:\n\n```bash\ndocker build -f docs/examples/Dockerfile.toolbox \\\n --build-arg CODEWHALE_IMAGE=ghcr.io/hmbown/codewhale:v0.10.0 \\\n --build-arg TOOLBOX_PACKAGES=\"git openssh-client curl build-essential pkg-config python3 python3-pip nodejs npm\" \\\n -t codewhale-toolbox:my-project .\n```\n\nUse `latest` only for throwaway testing. For shared projects, keep the\n`CODEWHALE_IMAGE` value pinned and review package additions like any other\ndevelopment-environment change.\n\nRun the toolbox image with the same workspace and state mounts:\n\n```bash\ndocker volume create codewhale-my-project-home\n\ndocker run --rm -it \\\n -e DEEPSEEK_API_KEY=\"$DEEPSEEK_API_KEY\" \\\n -v codewhale-my-project-home:/home/codewhale/.codewhale \\\n -v \"$PWD:/workspace\" \\\n -w /workspace \\\n codewhale-toolbox:my-project\n```\n\nInside this opt-in image, Codewhale can use commands such as\n`sudo apt-get update` and `sudo apt-get install -y <package>`. For repeatable\ncontainers, prefer baking those packages into the toolbox Dockerfile instead of\nletting a long-lived container drift.\n\nDo not bake API keys, SSH private keys, or other secrets into custom images.\nPass API keys at runtime and mount any SSH material deliberately, preferably\nread-only and only for projects that need it.\n\n### Compose toolbox template\n\nIf you prefer a repeatable `docker compose` entry point, use\n[`docs/examples/compose.toolbox.yml`](examples/compose.toolbox.yml). It builds\nthe toolbox image from [`docs/examples/Dockerfile.toolbox`](examples/Dockerfile.toolbox)\nand keeps the project state volume explicit:\n\n```bash\nCODEWHALE_IMAGE=ghcr.io/hmbown/codewhale:v0.10.0 \\\nCODEWHALE_TOOLBOX_IMAGE=codewhale-toolbox:my-project \\\nCODEWHALE_HOME_VOLUME=codewhale-my-project-home \\\nCODEWHALE_WORKSPACE=\"$PWD\" \\\ndocker compose -f docs/examples/compose.toolbox.yml run --rm codewhale\n```\n\nUse a different `CODEWHALE_TOOLBOX_IMAGE` and `CODEWHALE_HOME_VOLUME` for each\nproject that needs an independent toolchain or independent `.codewhale` state.\nThe Compose file also shows opt-in, read-only mounts for SSH material and local\nCA certificates; keep those commented out unless the project needs them.\n\n## Multiple independent projects\n\nUse one named state volume per project so sessions, config, skills, memory, and\nthe offline queue do not bleed across workspaces:\n\n```bash\nproject=\"$(basename \"$PWD\")\"\nimage=\"codewhale-toolbox:${project}\"\ndocker volume create \"codewhale-${project}-home\"\n\ndocker run --rm -it \\\n --name \"codewhale-${project}\" \\\n -e DEEPSEEK_API_KEY=\"$DEEPSEEK_API_KEY\" \\\n -v \"codewhale-${project}-home:/home/codewhale/.codewhale\" \\\n -v \"$PLine truncated
"content":"# Contributing to codewhale\n\nThank you for your interest in contributing to codewhale! This document provides guidelines and instructions for contributing.\n\n## Getting Started\n\n### Prerequisites\n\n- Rust 1.89 or later (edition 2024)\n- Cargo package manager\n- Git\n\n### Setting Up Development Environment\n\n1. Fork and clone the repository:\n ```bash\n git clone https://github.com/YOUR_USERNAME/CodeWhale.git\n cd CodeWhale\n ```\n\n2. Build the current engine and terminal:\n ```bash\n CODEWHALE_BUILD_SHA=\"$(git rev-parse HEAD)\" cargo build --locked -p codewhale-cli\n ```\n\n3. Run the tests near your change (see [Fast local loop](#fast-local-loop)):\n ```bash\n scripts/dev-test.sh tui your_test_filter\n ```\n\n4. Run with development settings:\n ```bash\n ./target/debug/codewhale --version\n ./target/debug/codewhale\n ```\n\n### Testing the latest source\n\nThe canonical source is [`codewhale-hq/Codewhale`'s `main` branch](https://github.com/codewhale-hq/Codewhale/tree/main).\nRelease candidates land there after their CI gates pass, so contributors can\ntest and build on the same source. Tagged downloads remain the latest published\nrelease; their version can lag the development version on `main`.\n\nFor a fresh checkout of the current source:\n\n```bash\ngit clone --branch main https://github.com/codewhale-hq/Codewhale.git\ncd Codewhale\nCODEWHALE_BUILD_SHA=\"$(git rev-parse HEAD)\"cargobuild--release--locked-pcodewhale-cli\n./target/release/codewhale--version\n./target/release/codewhale\n```\n\nIncludethecommitshownby`--version`whenreportingaproblem.Ifyouwork\nfromafork,addthecanonicalrepositoryas`upstream`andfetch`upstream/main`\nbeforestartingachange;preserveanylocalworkwhenupdatingyourbranch.\n\n##DevelopmentWorkflow\n\n###CodeStyle\n\n-Run`cargofmt`beforecommittingtoensureconsistentformatting\n-Run`cargoclippy`andaddressallwarnings\n-FollowRustnamingconventions(snake_caseforfunctions/variables,CamelCasefortypes)\n-AdddocumentationcommentsforpublicAPIs\n\n###Testing\n\n-Writetestsfornewfunctionality\n-Runthetestsnearyourchange(see[Fastlocalloop](#fast-local-loop));\nyourpullrequestrunstheLinuxworkspacesuiteandaWindowsbuildfor\nyou(see[Whathappensafteryouopenapullrequest](#what-happens-after-you-open-a-pull-request))\n-Colocateunittestsbesidethecodetheycover(standardRust`#[cfg(test)]`\nmodules),andaddintegrationtestsundertheowningcrate's`tests/`\ndirectory(forexample`crates/tui/tests/`or`crates/state/tests/`).The\nrepositoryroot`tests/`directoryisnotused\n\n###Pre-pushverification\n\nThesearetheRustcommandsyourpullrequestrunsonLinux(the`Lint`and\n`Test(ubuntu-latest)`checks).Youdonotneedallofthembeforeevery\npush:run`cargofmt`,thencheckandtestthecratesyoutouched(see\n[Fastlocalloop](#fast-local-loop)).Runthefullsetlocallywhenachange\nspansmanycrates,orletthepullrequestrunitforyou:\n\n```bash\ncargofmt--all----check\ncargoclippy--workspace--all-targets--all-features--locked--\\\n-Dwarnings\\\n-Aclippy::uninlined_format_args\\\n-Aclippy::too_many_arguments\\\n-Aclippy::unnecessary_map_or\ncargotest--workspace--all-features--locked\n```\n\nClippylintstest,benchandexampletargetstoo(`--all-targets`),andthe\nthreeallowancesabovearetheonlyonesCIandthereleaselaneaccept.\n\n####Fastlocalloop\n\nThefullgateaboveiswhatCIenforces,butyoudonotneeditforevery\nedit.`crates/tui`isa~750k-linecrate,sotheloopthatstaysfastis\ntheonethatavoidsrebuildingitmorethannecessary(numbersandthe\nreasoningarein[`docs/BUILD_PERFORMANCE.md`](docs/BUILD_PERFORMANCE.md)):\n\n```bash\n#1.Type-checkfirst(secondsafterthefirstbuild;nocodegen,nolink).\nscripts/dev-cargo.shcheck-pcodewhale-tui\n\n#2.Runonlythetestsnearyourchange(onecrate,onefilter).\nscripts/dev-test.shtuifleet_setup\n#or:scripts/dev-test.shcrates/command-contract/src/elapsed.rs\n\n#3.Runawholecrate'sunitsuite.scripts/dev-test.shusesnextestwhen\n#itisinstalled(oneprocesspertest,allcoresbusy,slowtests\n#named;~100sherevs~270swithlibtest).\ncargoinstallcargo-nextest--locked#once\nscripts/dev-test.shtui\nscripts/dev-cargo.shnextestrun--workspace--all-features--locked\n\n#4.TheLinuxsuiteyourPRruns:\ncargotest--workspace--all-features--locked\n```\n\n`.config/nextest.toml`alreadyserializesthePTYsuiteandboundsthe\nintegrationteststhatspawntherealbinary,so`cargonextestrun`is\nsafetouseonthewholeworkspace(nextestdoesnotrundoctests;the\nauthoritative`cargotest`gatedoes).Testsmustnotdependonrunningin\nthesameprocessasanothertest(nextestgiveseverytestitsown\nprocess);ifatesLine truncated
"content":"# Agent Ethos\n\n> 阅读简体中文版:[zh_hans/AGENT_ETHOS.md](zh_hans/AGENT_ETHOS.md)。\n\nCodewhale is maintained with agents, but it is not maintained by automation\nalone. Treat community reports and patches as real collaboration: people are\nbringing us machines, providers, regions, shells, packages, and edge cases we\ncould not cover by ourselves.\n\n## Stewardship\n\n- Verify live truth before acting. Check the current branch, release state,\n registry state, CI, and linked issues instead of trusting a handoff.\n- Issues are intake, not a privilege boundary. Do not auto-close good-faith\n issues because the reporter is not allowlisted. Ask for missing reproduction\n detail and leave room for maintainer triage.\n- PR gates exist for code review, CI load, and trust-boundary safety. They are\n not a quality judgment on the contributor. Keep dry-run mode unless a\n maintainer deliberately enables enforcement, and use warm copy when the gate\n comments.\n- Be generous with recurring contributors. When someone repeatedly brings\n useful reports or patches, use `/lgtmi` for issue access or `/lgtm` for PR\n access so the automation gets out of their way.\n- Preserve contributor credit. When harvesting work, inspect the PR and linked\n issues, keep author/co-author attribution where possible, add\n `Harvested from PR #N by @handle`, and credit the contributor in the\n changelog or release notes.\n- Make credit machine-readable. If a harvested commit cannot preserve the\n contributor as the author, add a `Co-authored-by` trailer with the GitHub\n numeric noreply address from `.github/AUTHOR_MAP` or\n `gh api users/<login> --jq '\"\\(.id)+\\(.login)@users.noreply.github.com\"'`.\n Do not use `.local`, placeholder, bot/tool, or raw third-party emails for\n human contributor credit.\n- Deferral is a maintainer action, not a dismissal. If a PR or issue is not\n ready, say what is blocked, what evidence would change the decision, and\n which part of the work remains valuable.\n\n## Agent Workflow\n\n- Use sub-agents for exploration, review, and verification, but keep a human\n maintainer posture in the parent session. Sub-agent output is evidence; the\n parent is responsible for the final decision.\n- Personally review community PRs before merging, harvesting, closing, or\n deferring them. Do not close work based only on title, labels, or an agent's\n summary.\n- Prefer narrow, reversible changes that match the existing codebase. Avoid\n drive-by refactors while harvesting community work.\n- Run the smallest meaningful validation first, then broaden tests when a\n change touches shared behavior, release plumbing, auth, sandboxing,\n providers, or UI workflows.\n- Do not tag, publish, push release artifacts, or create GitHub releases\n without explicit maintainer approval.\n\n## Product Tone\n\nCodewhale should feel like a capable coding harness with a public community,\nnot a closed queue. Automation should reduce maintainer load while making\ncontributors feel seen, credited, and able to keep helping.\n"
"content":"# Release Checklist\n\nFollow this sequence from the canonical `/Volumes/VIXinSSD/CW/codewhale`\ncheckout on `main`. Agent-owned preparation lands as verified slices on `main`;\nit requires no release branch, worktree, nested clone or preparation PR.\nContributor PRs still land as PRs. Treat any unchecked required gate as a release\nblocker.\n\nFor deeper context on the underlying tools (preflight scripts, npm smoke,\npublish-crates), see [`RELEASE_RUNBOOK.md`](RELEASE_RUNBOOK.md).\nFor larger milestone releases, add any version-specific acceptance matrix to\nthe existing release packet before tagging; use it for provider routes, feature\ngates, GUI/runtime smoke, remote-workbench decisions, and credit hygiene that the\ngeneric checklist does not enumerate. Record its candidate SHA, owners, local\ncheck results, hosted run URLs, artifact inventory, install/provider receipts,\nknown limitations and publication approval. These proofs are separate.\n\n## 0. Reconcile the canonical checkout\n\n- [ ] Inspect the branch, status, upstream and alternate checkouts before editing:\n ```bash\n git branch --show-current\n git status --short --branch\n git log -5 --oneline\n git worktree list\n git fetch origin main\n git log --left-right --oneline main...origin/main\n ```\n- [ ] The canonical checkout is on `main`. Recover stranded work only after\n preserving dirty/untracked files and comparing commits with current\n `origin/main`. Keep a recoverable backup and unresolved inventory; do not\n reset away work, stash it indefinitely or create another development lane.\n- [ ] Partition files among active writers. Check status before staging and\n stage only owned paths. Historical branches/checkouts are inventoried;\n deleting them requires explicit Hunter approval even if a helper calls\n their tips safe to delete.\n\n## 1. CHANGELOG entry exists for the version\n\n- [ ] `CHANGELOG.md` has a `## [X.Y.Z] - YYYY-MM-DD` heading at the top\n- [ ] The entry credits every external contributor, harvested PR author,\n linked issue reporter, reproduction/log provider, reviewer, and\n verification helper whose work materially shaped this version. Get the\n commit list with:\n ```\n git log vPREV..HEAD --no-merges --format=\"%h %an <%ae> %s\"\\\n|grep-v'<your-email@…>'\n```\nForeachcontributor,linkboththeirdisplaynameand(whenknown)\n`@github-handle`.TheninspectlinkedissuesandharvestedPRsso\nreporters/helpersarenotlostjustbecausetheydidnotauthorcommits.\nKeep`docs/CONTRIBUTORS.md`and`web/lib/release-credits.ts`consistent.\n-[]TheentryusestheKeepaChangelogheaders—`Added`,`Changed`,\n`Fixed`,`Security`,`Removed`,`Deprecated`.Add`Knownissues`only\nifthereissomethingmaterialtheusermustworkaround.\n-[]Theentrymentionsallreferencedissue/PRnumbersas`#NNNN`sothe\nauto-linkeronGitHubpicksthemup.\n-[]Run`scripts/sync-changelog.sh`toregenerate`crates/tui/CHANGELOG.md`\n(therecent-releasessliceembeddedinthebinaryfor`/change`).Do\nnoteditthatfilebyhand,anddonotcopythefullrootchangelog\nintoit—olderentrieslivein`docs/CHANGELOG_ARCHIVE.md`.\n-[]Run`scripts/release/check-feature-release-notes.shvPREVHEAD`.Every\nissue-linked`feat`commitmustleaveareceiptin`CHANGELOG.md`orthe\narchive;theVersiondriftCIgaterunsthesamecheckwithfullhistory.\n\n##2.Versionpinsareinsync\n\n-[]Use`./scripts/release/prepare-release.shX.Y.Z`whenversionpinsor\ngeneratedsourcesneedpreparation.Reuseavalidpreparationreceipt\nwhenthosesourcesarealreadycurrent.Thehelperbumpsthe\nworkspaceversion,everyper-cratedependencypin,thenpmwrapper\n(`version`+`codewhaleBinaryVersion`),RuntimeSDK,VSCodeextension\nandlock,remote-smokedefault,publicsource-candidatefacts,andREADME\ninstall-tagexamples;itrefreshestheCargo/npmlocks,regenerates\n`crates/tui/CHANGELOG.md`and`web/lib/facts.generated.ts`,andends\nbyrunningtheversionandOHOSgates.WritetheCHANGELOGentry\n**before**runningit.Thehelperissafetorerunattherequested\nworkspaceversion;itskipsreplacementsbutrefreshesbothgenerated\nfilesandrerunsthegates.\n-[]`npm/deepseek-tui/package.json`remainsprivate/compatibility-onlyand\nis**not**bumpedorpublished.\n-[]`./scripts/release/check-versions.sh`reports\n`VersionstateOK:workspace=X.Y.Z,npm=X.Y.Z,npm-binary=X.Y.Z,lockfileinsync.`\n-[]`./scripts/release/check-ohos-deps.sh`reportsthattheOpenHarmony\nWindowslinkerkeepsthetarget/sysrootflags,thetargetenablesthe\n`rquickjs-sys`bindgenedge,anditsgraphdoesnotpulltheunsuppLine truncated
"content":"# Fleet + Workflow Tutorial\n\n> 阅读简体中文版:[zh_hans/FLEET_WORKFLOW_TUTORIAL.md](zh_hans/FLEET_WORKFLOW_TUTORIAL.md)。\n\nFleet and Workflow are meant to work together, but they solve different parts\nof the problem:\n\n- **Fleet** configures and manages the same sub-agents: reusable roles, model\n routes, permissions, logs, artifacts, and status/restart/stop controls.\n- **Workflow** describes orchestration: phases, branches, reducers, loops, and\n agent leaves that can dispatch through the fleet/sub-agent runtime.\n\n**Default product path:** ask in natural language. Operate handles small or\ntightly coupled work directly under the active posture. Multi-step delegation\nuses a compact Workflow plan with named steps, dependencies, bounded scopes,\nand completion checks; results and evidence pass to the steps that need them.\nOne bounded, independent task can use a direct background agent. Reuse that\nagent with `followup` for continued work. Background work keeps the composer\navailable, and ordinary multi-agent work does not require workflow files. Details:\n[Automatic Workflows](AUTOMATIC_WORKFLOWS.md).\n\nThis tutorial covers the **manual** fleet task-spec / checked-in Workflow path\nfor operators who want durable host workers and reviewable specs. A\none-sentence request should still not silently generate `tasks.json`; worker\ncards and permission posture make dispatch visible without exposing authoring\nmechanics.\n\nThe examples use `codewhale fleet` and `/fleet`.\nOn-disk paths, config keys, and the Workflow `--fleet` flag use the Fleet name.\n\n## 1. Prepare The Workspace\n\nRun fleet from the workspace you want workers to inspect or modify:\n\n```sh\ncodewhale fleet init\n```\n\nThis creates the workspace ledger at `.codewhale/fleet.jsonl`. Worker logs and\nbounded artifacts live under `.codewhale/fleet/`; host adapter logs live under\n`.codewhale/fleet-host/`.\n\nIf you want named reusable workers, open the TUI and run:\n\n```text\n/fleet setup\n```\n\nPick a role, choose whether that profile inherits the operator route or pins a\nspecific provider/model, choose where the profile lives (**This project** →\n`.codewhale/agents/<role>.toml`, or **Personal** →\n`$CODEWHALE_HOME/agents/<role>.toml`, available across repositories while a\nsame-id project profile remains the higher-priority override), then review the\nexact file, permissions/tools/route posture, and save. The save control names\nits effect (\"Save to this project\" / \"Save as Personal profile\"), and\nreplacing an existing file always asks for a second confirmation. fleet task\nspecs can reference either resolved profile with `worker.agent_profile` or the\nshorter `worker.profile` alias.\n\nThis makes the fleet definition cross-repository, not the authority of one\nrunning session. For a multi-repository operation, launch Codewhale from a\nshared parent workspace. Profile availability does not grant filesystem access;\nthe session's workspace, explicit trusted paths, trust mode, and permission\nposture remain authoritative.\n\n## 2. Write A fleet Task Spec\n\n`codewhale fleet run` accepts JSON or TOML. The checked-in\n`docs/examples/fleet-dogfood.toml` file is the realistic manual smoke example;\nthe JSON below shows the same authoring shape with one read-only reviewer and\none bounded docs-note worker. The live Runtime policy controls secrets and\ntrust; fleet identity carries neither.\n\n```json\n{\n \"name\": \"docs readiness check\",\n \"labels\": {\n \"kind\": \"tutorial\"\n },\n \"tasks\": [\n {\n \"id\": \"map-docs\",\n \"name\": \"Map current docs\",\n \"objective\": \"Find the docs that describe fleet and Workflow.\",\n \"instructions\": \"Read docs/FLEET.md and docs/WORKFLOW_AUTHORING.md. Report the command surfaces, current limitations, and any confusing gaps.\",\n \"worker\": {\n \"role\": \"reviewer\",\n \"profile\": \"reviewer\",\n \"tools\": [\"rg\", \"sed\", \"git\"],\n \"model\": \"deepseek-v4-flash\"\n },\n \"workspace\": {\n \"required_files\": [\"docs/FLEET.md\", \"docs/WORKFLOW_AUTHORING.md\"],\n \"writable_paths\": [],\n \"environment\": {\n \"required\": [],\n \"allowlist\": []\n }\n },\n \"input_files\": [\"docs/FLEET.md\", \"docs/WORKFLOW_AUTHORING.md\"],\n \"expected_artifacts\": [\"log\", \"report\"],\n \"scorer\": {\n \"kind\": \"manual\"\n },\n \"retry_policy\": {\n \"max_attempts\": 1\n }\n },\n {\n \"id\": \"draft-gap-note\",\n \"name\": \"Draft gap note\",\n \"objective\": \"Draft a short local note for any missing tutorial steps.\",\n \"instructions\": \"Write a concise Markdown note with the missing fleet + Workflow tutorial steps. Do not edit public docs unless explicitly asked.\",\n \"worker\": {\n \"role\": \"builder\",\n \"tools\": [\"rg\", \"sed\Line truncated
"content":"# Write your first Codewhale plugin\n\n> 阅读简体中文版:[zh_hans/PLUGIN_AUTHORING.md](zh_hans/PLUGIN_AUTHORING.md)\n\nStart with a skill: a Markdown instruction file inside a small plugin bundle.\nThe [hello-codewhale example](examples/plugins/hello-codewhale/plugin.json)\ncontains two files, declares no server or hook, and asks for no tool use.\nThis walkthrough takes it from source files to a reviewed, enabled skill.\n\nFor a custom AI provider with host-owned OAuth, see [Plugin providers](PLUGIN_PROVIDERS.md)\nand the [provider example](examples/plugins/oauth-provider/plugin.json).\n\n## 1. Create the bundle\n\nUse the checked-in example, or create this directory outside an installed\nplugins directory:\n\n```text\nhello-codewhale/\n├── plugin.json\n└── skills/\n └── hello/\n └── SKILL.md\n```\n\n`plugin.json`:\n\n```json\n{\n \"$schema\": \"https://agent-plugins.org/schemas/plugin.json\",\n \"name\": \"hello-codewhale\",\n \"version\": \"0.1.0\",\n \"description\": \"A minimal, explicitly invoked greeting skill.\"\n}\n```\n\n`skills/hello/SKILL.md`:\n\n```markdown\n---\nname:hello\ndescription:Greettheuserwhentheyexplicitlytrythehello-codewhaleexample.\ninvocation:explicit-only\n---\n\nRespondwithoneshortgreetingintheuser'slanguage.Includetheexacttext\n`hello-codewhale:hello`sotheycanidentifytheexampletheyinvoked.\n\nUseonlytheconversation.Donotcalltools,runcommands,readorwritefiles,\norcontactexternalservices.\n```\n\nCodewhalefinds`skills/`automatically.`explicit-only`keepsthisexampleout\nofthemodel'sautomaticskillcatalogue;youloaditbyname.See\n[Skills](SKILLS.md#invocation-and-alias-metadata)forinvocationmetadataand\n[Pluginbundles](PLUGIN_BUNDLES.md#manifest)fortheauthoritativemanifest\ncontract.Keepnewnativebundlesin`plugin.json`;nosecondmanifestis\nneeded.\n\n##2.Install,inspect,andtrust\n\nStartCodewhaleintherepositoryroot.Enterthesecommands**insidethe\nCodewhalesession**,oneatatime:\n\n```text\n/plugininstall./docs/examples/plugins/hello-codewhale\n/pluginvalidatehello-codewhale\n/pluginshowhello-codewhale\n```\n\nForyourownbundle,replacetheinstallpathwithitsdirectory.Installation\ncopiesitto`~/.codewhale/plugins/hello-codewhale/`,disabledanduntrusted.\nReviewtheinstalledsource,skillinventory,andpermissions.Thisexample\nshoulddeclareonlySkills,withnoMCPserver,hook,orrequestednetworkhost.\n\nTheinstallreviewprintsacommandcontainingtwofullhashes:\n\n```text\n/plugintrusthello-codewhale<full-content-sha256>.<full-capability-sha256>\n```\n\nRuntheexactcommandprintedbyyourreview;theangle-brackettextaboveis\naplaceholder.Ifyouneedafreshreview,use`/plugintrusthello-codewhale`\nwithoutatoken.Trustrecordsthereviewedcontentandcapabilityhashesand\ncreatesaruntimesnapshot.Itdoesnotenabletheplugin.\n\n```text\n/pluginenablehello-codewhale\n/skillshello-codewhale:\n/skillsinspect\n```\n\nTheskillisnamed`hello-codewhale:hello`:thebundlenamequalifiestheskill\nname.`/skillsinspect`identifiesitsreviewedpluginsnapshot.\n\n##3.Invokeitandturnitoff\n\n```text\n/skillhello-codewhale:hello\n```\n\nCodewhaleconfirmsactivation.Thensend`Sayhello.`asanormalmessage.\nThereplyshouldbeashortgreetingcontaining`hello-codewhale:hello`.\nTheexamplecontributesinstructionsonly;thereplystillusesyourselected\nmodelanditsnormalproviderconnection.Thelocalinstall,review,and\nactivationstepsdonotneedamodelcall.\n\n```text\n/plugindisablehello-codewhale\n```\n\nDisablingremovestheplugin'scontributionswhilepreservingitstrust\nreceipt.Asubsequent`/skillhello-codewhale:hello`mustnotactivateit.\nEnableitagainwhenneeded,provideditsreviewedhashesstillmatch.\n\n##4.Iterateandreviewchanges\n\nTheinstalledbundleisacopy.Editingtheexample'soriginalsourcedoes\nnotupdatethatcopy.Totryachangedlocalsource,disableanduninstallthe\ninstalledexample,theninstallthesourcedirectoryagain:\n\n```text\n/plugindisablehello-codewhale\n/pluginuninstallhello-codewhale\n/plugininstall./docs/examples/plugins/hello-codewhale\n/pluginvalidatehello-codewhale\n```\n\nUninstallremovestheinstalledcopy;itleavestheoriginalexamplesource\nalone.Reviewthenewtoken,trustit,andenableitagain.Forbundles\ninstalledfromaremotesource,use`/pluginupdate<name>`;see\n[Installingplugins](PLUGINS.md#update-and-uninstall).\n\nWhenfilesinadiscoveredbundlechangedirectly,`/pluginreload`refreshes\ntheregistry.Changedcontentinvalidatestheoldreceipt,evenifyouleave\ntheversionunchanged.Reloaddoesnotgranttrust.Use`/pluginrevoke<name>`\ntoremovetrustexplicitly.\n\n##Addonlythecomponentsyouneed\n\nAllcomponentsusethLine truncated
"content":"# Agent fleet\n\n> Baca terjemahan bahasa Indonesia: [id/FLEET.md](id/FLEET.md)\n\nAgent fleet is the local-first roster and member-selection layer for durable\nmulti-worker runs. It does not execute or authorize work. After fleet resolves\nwho should participate, the delegated coordinator launches a headless\n`codewhale exec` run and the Runtime tracks it durably. See\n[AGENT_RUNTIME.md](AGENT_RUNTIME.md) for how sub-agents, `exec`, and\nfleet-backed workers converge on one runtime. In product language, a user may\nstill \"open a sub-agent\"; in architecture language, durable nested work uses a\nfleet member identity with delegated runtime execution.\n\n## Naming and compatibility boundary\n\n**Fleet** is the public product noun. The durable ledger, saved rosters, config\ntables, and `--fleet` flag share that name:\n\n| Surface | Canonical |\n| --- | --- |\n| CLI | `codewhale fleet …` |\n| Slash command | `/fleet …` |\n\nThese shared names are load-bearing wherever changing them would break\nexisting workspaces, receipts, or scripts:\n\n- the durable ledger `.codewhale/fleet.jsonl` and the log directories\n `.codewhale/fleet/` and `.codewhale/fleet-host/`;\n- saved rosters `fleets/<name>.toml` and their `schema = \"fleet\"`header,under\n`$CODEWHALE_HOME/`ortheworkspace's`.codewhale/`(checked-inrostersatthe\nworkspaceroot's`fleets/`arestillread);\n-the`[fleet]`configtable(inline`[fleets.*]`tableswereremovedin0.9.14;namedfleetslivein`fleets/<name>.toml`files);\n-the`codewhaleworkflowrun--fleet<name>`flag;\n-wire,receipt,andcontrol-planeoperationidssuchas`fleet.status`.\n\nTherestofthisdocumentusesfleetasthepublicproductnounandretains\ntheseliteralpaths,keys,flags,andids.\n\nUseafleetrosterratherthananonymousshort-lived`agent`fanoutwhenevera\ndelegatedrunneedsstablememberidentitiesacrossretries,sleep/restart,\nremoteexecution,receipts,oraledgeredaudittrail.TheinitialCLIsurface\nis:\n\nForaguidedstart-to-monitorwalkthroughthatcombinesfleettaskspecswith\nWorkflowauthoring,see[fleet+WorkflowTutorial](FLEET_WORKFLOW_TUTORIAL.md).\n\n```sh\ncodewhalefleetinit\ncodewhalefleetruntasks.json--check#validateonly;nothingiscreatedorlaunched\ncodewhalefleetruntasks.json--max-workers4\ncodewhalefleetstatus\ncodewhalefleetinspect<worker-id>\ncodewhalefleetlogs<worker-id>\ncodewhalefleetartifacts<worker-id>\ncodewhalefleetinterrupt<worker-id>\ncodewhalefleetrestart<worker-id>\ncodewhalefleetresume<run-id>\ncodewhalefleetstop--all\n```\n\n`codewhalefleetresume<run-id>`istherestart-recoveryverb:itreplaysthe\nledger,reconcilesanyin-flightleasewhoseworkerstoppedheartbeating\n(retryingwithinthetask'sbudget,elsefailingandescalatingperthealert\npolicy),andprintsthepost-resumestatus.Itlaunchesnonewworkandis\nidempotent,soitissafetorunafteramanagerexit,laptopsleep,orruntime\nrestart.\n\nCoordinatorstateforfleet-backedrunsisstoredundertheworkspacein\n`.codewhale/fleet.jsonl`.Workerlogsandadapterlogsarestoredunder\n`.codewhale/fleet/`and`.codewhale/fleet-host/`.\n\n##Publiccontract:identity,membership,andselection\n\n**Fleet**=theuser'smodelinventory:whoisintherosterandwhichmemberisselected.\n\nApublicfleetidentityconsistsonlyof:\n\n-astablememberidandanoptionaluser-facingname;\n-asemanticrole,suchas`explore`,`implement`,or`reviewer`(thelegacy\nspellings`worker`,`scout`,`builder`,`verifier`,`consultant`,and`oracle`\narestillacceptedoninputandmapto`general`,`explore`,`implement`,\n`test`,and`advisor`);\n-anexactprovider/modelidentity,oranexplicitinheritedroute;\n-visiblerosterstateororigin.\n\nProjectorworkspacetrust,filesystemandnetworkreach,secretaccess,\napprovalmode,sandboxing,toolauthorization,andeveryotherformofruntime\nauthorityareseparatedelegated-coordinationandRuntimepolicyinputs.They\nareneverfleetidentityfields,andtheyneverselectorrerouteafleet\nmember.TheRuntimeappliesandclampsthosepoliciesonlyaftermember\nselection;iftheselectedmembercannotruninsidetheeffectiveenvelope,\nlaunchfailsclosedinsteadofchoosingsomebodyelse.\n\nNatural-languagememberselectionisdeterministic.Acallermayname:\n\n-anexactmemberid,optionallyas`member:<id>`or`id:<id>`;\n-auniqueuser-facingmembername,optionallyas`name:<name>`;\n-auniquesemanticrole,forexample`explore`or`role:explore`;\n-anexactpinnedmodelid,forexample`deepseek-v4-flash`,oritsoffline\ndisplayname,forexample`DeepSeekV4Flash`;or\n-anexact`route:<provider>/<model>`.\n\nAnunqualifiedexactmemberidwins.Everyothermatchsucceedsonlywhenit\nidentifiesonedistinctrostermember.Multiplematchesproduceanambiguity\nerroLine truncated
"content":"# Modes and Permission Postures\n\n> 阅读简体中文版:[zh_hans/MODES.md](zh_hans/MODES.md)\n\nCodewhale has three related concepts:\n\n- **TUI mode**: what kind of visible interaction you're in (Plan/Work/Operate).\n- **Permission posture**: how aggressively the UI asks before executing tools.\n- **Workflow**: named steps that coordinate sub-agents, with dependencies and\n results, available in any TUI mode.\n\nModel selection is separate. `--model auto` and `/model auto` route each turn to\na concrete model and thinking level; they are not TUI modes and are not part of\nthe `Tab` cycle.\n\nWorkflow runs named steps through the same sub-agent runtime; Fleet manages\ntheir saved roles and model assignments. A workflow adds ordering, result\nhandoffs, gates and a progress view. The active mode and permission posture\nstill control what each step may execute.\n\nBefore assigning steps, query `agent(action=\"roster\")` for the saved Fleet\nmodels and roles. A plan child can select a listed model with `model`, or use\nthe saved `role`/`profile` assignment. An Exact Fleet fixes each member's route.\n\n## TUI Modes\n\nPress `Tab` to complete composer menus or cycle through the visible modes\nwhen the composer is empty: **Plan → Work → Operate → Plan**. `Tab` never sends\nor queues composer text; use `Enter` to send or queue it.\nPress `Shift+Tab` to cycle permission posture (Ask → Auto-Review → Full Access).\nEvery change of effective permission writes a `Permissions changed to …` line\nin the transcript.\nPress `Ctrl+T` to cycle reasoning effort.\nRun `/mode` to open the mode picker, or switch directly with `/mode work`,\n`/mode plan`, or `/mode operate`.\n\n- **Plan**: design-first prompting. The stable primitive names remain familiar, but the runtime centrally refuses file mutation and shell execution. Read-only inspection and policy-allowed research, including deferred Web search/fetch, remain available. When a Plan turn successfully finishes a nonempty response, including a prose-only plan, the TUI asks how to continue: Work (Ask), Work (Auto-Review), or keep planning. Work sends the exact approved response as a normal follow-up and preserves that turn's existing To-dos, or adds one pending plan item without replacing unrelated work.\n- **Work** (internally `agent`): ordinary multi-step execution. The first-turn toolbox includes `read`, `write`, `edit`, `file_search`, `bash`, `agent`, `workflow`, `todo_write`, and `load_skill`, plus `create_goal`, `get_goal`, `update_goal`, and `task_shell_wait` so goal and background-shell controls are available without discovery. The agent calls `create_goal` when a request is a durable objective; the host never infers a goal from wording, and the contract must not tell the model otherwise. Approval, sandbox, repository law, and managed policy decide what may execute.\n- **Operate**: Work at full strength, the \"ultra\"mode.SametoolsandauthorityasWork;thedifferenceishowtheyareused.Eachsubstantiverequestbecomesagoal(`create_goal`),theplanstaysvisible(`todo_write`),multi-partworkrunsthrough`workflow`andparallel`agent`workers,non-trivialchangesgetanindependentreviewerbeforetheyarecalleddone,longcommandskeeprunninginthebackground,andrecurringorwatch-typeworkisofferedasan`automation`(createdafterapproval).Itkeepsgoinguntilthegoalisverified,blockedonyou,orpaused.\n\n`Act`and`/modeact`remaincompatibilityaliasesforWork.Savedsettings\nstillnormalizetotheinternalvalue`agent`.\n\n###Toolavailabilitybymode\n\n|Toolfamily|Plan|Work|Operate|\n|:---|:---:|:---:|:---:|\n|`read`andpolicy-alloweddeferredresearchtools|yes|yes|yes|\n|`write`and`edit`|visiblenames;executiondenied|approval-andpolicy-gated|sameasWork|\n|`bash`|visiblename;executiondenied|approval-andpolicy-gated|sameasWork;delegationispreferredwhenparallelismorisolationhelps|\n|`agent`|yes,subjecttochild-depthauthority|yes,subjecttochild-depthauthority|yes,subjecttochild-depthauthority|\n|Deferrednative,MCP,andplugintools|discoverablethrough`tool_search`whenpolicypermits|same|same|\n|Paidorexternal-servicetools|followspermissionposture|followspermissionposture|followspermissionposture|\n|Accessoutsidetheworkspaceroot|explicittrustedpathsonly|onlythroughtrustedpathsortrustmode|sametrusted-path/trustpolicyasWork;fleetprofilesneverwidenit|\n\nOperatechangesschedulingemphasis,notauthority.Itneitheraddsa\nmode-specifictooldenialnorbypassestheactiveapproval,sandbox,shell,\nask-rule,repository-law,ormanaged-policyboundary.Planremainsthe\nmode-specificexecutionboundaryforshellandwrite-capabletools;that\nauthoritydifferencedoesnotrequireadifferentprimitivevocabulary.\n\n###Operateloop(onescreen)\n\n```text\nUseLine truncated
"content":"# Codewhale Architecture\n\n> 阅读简体中文版:[zh_hans/ARCHITECTURE.md](zh_hans/ARCHITECTURE.md)。\n\nCodewhale Engine is the existing Rust execution runtime. The\n[Runtime API](RUNTIME_API.md) is its client interface, and\n[TypeScript mods](EXTENSIONS.md) contribute reviewed extensions.\n\nCurrent boundary note (read the workspace version from `Cargo.toml`; this\nboundary has held since v0.9.1):\n- `crates/tui` is still the live end-user runtime for the TUI, runtime API, task manager, and tool execution loop.\n- Other workspace crates are being split out incrementally, but they are not yet the sole runtime source of truth.\n\n<img src=\"assets/architecture.webp\" alt=\"Codewhale architecture: clients reach the one turn loop, Engine::run_turn, through the CLI or the Runtime API; the engine uses crates/core to build requests, runs tools under approval and sandbox policy, routes to any provider through crates/config, and logs everything model-visible to the session store.\" width=\"1000\">\n\nThe diagram draws the Runtime API and the task manager as their own boxes; today\nboth are hosted inside `crates/tui`, as the note above says. The GPUI desktop\napp and the VS Code extension live outside this repository. The arrow from the\nengine to the session log follows the \"model-visible means logged\"contract\nratherthanatracedwritepath.\n-`crates/tui`isbeingsplitintocratessizedasagentworkpartitions:\ncontracts,utilities,services,providers,runtime,leaffeaturecrates,\nTUIviewsandhosts,intheorder`docs/design/TUI_DECONSTRUCTION.md`\nrecords.TheTUIstaystheonlycratethatwritestotheterminal.Untila\nmodulehasmoved,itspathunder`crates/tui/src`isstillwhereitlives.\n-TheLSPsubsystem(`crates/tui/src/lsp/`)isfullywiredintotheengine's\npost-tool-executionpath(`core/engine/lsp_hooks.rs`),providinginline\ndiagnosticsafter`File`write,edit,andpatchactions.\n-Theswarmagentsystemwasremovedinv0.8.5.Theactivesub-agentsurfaceis\nthesingle`agent`tool;persistentRLMsessionsareavailablethroughthe\ndeferred`rlm`actionfamily.\nNomodel-visibleswarmtoolremainsintheactivecodebase.\n\n##High-LevelOverview\n\n```\n┌─────────────────────────────────────────────────────────────────┐\n│UserInterface│\n│┌─────────────────┐┌─────────────────┐┌────────────────┐│\n││TUI(ratatui)││One-shotMode││Config/CLI││\n│└────────┬────────┘└────────┬────────┘└────────┬───────┘│\n└───────────┼─────────────────────┼────────────────────┼──────────┘\n│││\n▼▼▼\n┌─────────────────────────────────────────────────────────────────┐\n│CoreEngine│\n│┌─────────────────────────────────────────────────────────┐│\n││AgentLoop(core/engine.rs)││\n││┌─────────┐┌─────────────┐┌──────────────────────┐││\n│││Session││TurnMgmt││ToolOrchestration│││\n││└─────────┘└─────────────┘└──────────────────────┘││\n│└─────────────────────────────────────────────────────────┘│\n└─────────────────────────────────────────────────────────────────┘\n│││\n▼▼▼\n┌─────────────────────────────────────────────────────Line truncated
"content":"# Tool surface\n\n> 阅读简体中文版:[zh_hans/TOOL_SURFACE.md](zh_hans/TOOL_SURFACE.md)。\n\nThis document describes the current model-facing tool contract. The v0.9.1\ncutover that produced it is recorded in `docs/RUNTIME_SIMPLIFICATION_DESIGN.md`;\nread the workspace version from `Cargo.toml`, not from this line. The registry\nremains larger than the first-turn catalog so\nsaved transcripts can replay and uncommon capabilities can be loaded on demand.\nThe model should learn one canonical name for each common operation.\n\nImplementation sources:\n\n- `crates/tui/src/core/engine/tool_catalog.rs` owns the eager/deferred catalog.\n- `crates/tui/src/tools/registry.rs` registers canonical tools and hidden aliases.\n- `crates/tui/src/tools/{file,file_tool,shell}.rs` own the small foreground\n primitive behavior and schemas; the other native tools remain searchable.\n- `docs/RUNTIME_SIMPLIFICATION_DESIGN.md` records the v0.9.1 cutover and receipt.\n\n## Default-active contract\n\nNew turns start with thirteen eager native names plus synthetic `tool_search`:\n\n1. `read`\n2. `write`\n3. `edit`\n4. `file_search`\n5. `bash`\n6. `agent`\n7. `workflow`\n8. `todo_write`\n9. `create_goal`\n10. `get_goal`\n11. `update_goal`\n12. `task_shell_wait`\n13. `load_skill`\n14. `tool_search` (synthetic, always active)\n\nThe thirteen native names are `DEFAULT_ACTIVE_NATIVE_TOOLS` in\n`crates/tui/src/core/engine/tool_catalog.rs`, pinned by\n`default_active_contract_keeps_discovery_and_core_tools_eager`. An authority\nboundary may remove `agent` at the maximum child depth, and Plan registers only\nthe foreground shell surface (so no `task_shell_wait`), but route size alone\nmust not change this core vocabulary.\n\nThe direct schemas deliberately stay small:\n\n| Tool | Input | Purpose |\n|---|---|---|\n| `read` | `path`, optional `offset`, optional `limit` | Read a bounded file window with explicit continuation or truncation notices. |\n| `write` | `path`, `content` | Create or replace a file. |\n| `edit` | `path`, `edits` | Apply one or more unambiguous text replacements against one original snapshot. |\n| `file_search` | `query`, optional `path`, `limit`, `extensions`, `exclude` | Find files by name, path fragment or glob and return bounded, ranked matches. Respects ignore files; `exclude` replaces default exclusions. |\n| `bash` | `command`, optional `timeout`, optional `description` | Run one cancellable foreground shell command and return a bounded tail. The optional description is display-only. |\n| `agent` | delegated task and optional scope/context controls | Start or inspect focused child work. |\n| `workflow` | plan/script/source_path plus run controls | Coordinate multi-agent phases with dependencies and completion checks. |\n| `todo_write` | complete replacement list of `{content, status}` items | Keep optional, agent-owned progress notes for genuinely multi-step work. |\n| `create_goal` | objective plus optional budget | Start the session goal the turn works toward. |\n| `get_goal` | none | Read the active goal and its progress. |\n| `update_goal` | terminal status | Mark the goal complete or blocked. |\n| `task_shell_wait` | `task_id`, optional `wait`, `timeout_ms`, `cancel`, `gate` | Poll or stop a background shell task. Completion still arrives as a runtime event, so use it for early output, true barriers, or gate evidence. |\n| `load_skill` | optional `name`, `query` | Load a named skill's `SKILL.md` body and companion-file list; omit `name` or use `name=\"list\"`tolisteligibleskills,oruse`query`tosearchnamesanddescriptions.|\n|`tool_search`|`query`,optionalmatchingcontrols|Discoverpolicy-alloweddeferredtoolsandaddselectedschemastothisconversation'stoolbox.|\n\nModeisanauthoritydecision,notasynonymsystem.Plan,Work,andOperate\nusethesameprimitiveidentities.Plancentrallyrefuses`write`,`edit`,and\n`bash`;WorkandOperatestillpassthosecallsthroughapproval,sandbox,\ntrusted-path,repository-law,andmanaged-policygates.FullAccesschanges\nordinaryapprovalbehaviorbutdoesnotbypasshardsafetyorrepositorylaw.\n\n`update_plan`remainsregisteredonlyforsaved-artifactcompatibilityandis\nnotmodel-visible.`tasks`,`Git`,`Run`,`Web`,`remember`,andother\nspecializedcapabilitiesaresearchableratherthanfirst-turnceremony.\n\nOntheTUI'sdefault(calm)shellcardtheBashdescriptionistheheadline,\nshownaswrittenapartfromstrippedterminalescapes,collapsedwhitespaceand\nalengthbound,andthecommandsitsonitsown`$`rowdirectlyunderitin\neverystateandateverywidth,includingautomaticruns.Acardwithouta\ndescriptionleadswiththe`$`commandinstead.Thelabeledcard\n(`show_tool_details=true`with`calm_mode=false`)keepsthecommandinits\nheaderandthedescriptionasamuted`note:`row.Thedescriptionnever\nreplacesthecommandandisexcludedfromexecution,exactLine truncated
"content":"# Codewhale Runtime Simplification Design\n\n**Status:** Pre-implementation design record for the v0.9.1 cutover, kept for its\n\"Rejected alternatives\" provenance. It is **not** current runtime documentation\nand it shipped differently in two ways:\n\n- Goal 3 below (\"keeping every legacy tool name registered but hidden\")was\nreversed.Theper-actionfile/git/run/weband`exec_shell*`nameswere\n**removed**,nothidden—`crates/tui/src/tools/registry.rs`assertsthey\nmuststayunregistered.Only`apply_patch`andthe\n`task_*`/`github_*`/`automation_*`/`rlm_*`/`checklist_*`families\nsurviveashiddenaliases.\n-Thedefault-activepolicyisninenames,notten.`update_plan`and`Web`are\nnotin`DEFAULT_ACTIVE_NATIVE_TOOLS`\n(`crates/tui/src/core/engine/tool_catalog.rs:44-58`).\n\nForthecurrentcontractsee[`TOOL_SURFACE.md`](TOOL_SURFACE.md).\n\n##Goal\n\nMakethemodel-facingruntimesmaller,calmer,andeasierformodelstouseby:\n\n1.Collapsingthelongtailofsingle-purposefile,git,run,andwebtoolsinto\nafewcanonicalaction-basedtools.\n2.Shrinkingthesystemprompttodurablebehavioralinvariantsandper-turn\npermissiondeltas.\n3.Keepingeverylegacytoolnameregisteredbuthiddensooldtranscripts,\nsavedsessions,andrecordedautomationreplaywithoutmigration.\n\n##Targetmodel-facingsurface(defaultactive)\n\n|Tool|Actions/Niche|\n|---|---|\n|`Bash`|`run`,`wait`,`interact`,`cancel`(existing)|\n|`File`|`read`,`list`,`search_name`,`search_content`,`write`,`edit`,`patch`|\n|`Git`|`status`,`diff`,`log`,`show`,`blame`|\n|`Run`|`tests`,`verifiers`|\n|`Web`|`search`,`fetch`,`wait`(deferredunlessnetworkisenabled;hiddenaliasesforlegacynames)|\n|`tasks`|durabletaskfamily(existingaction-basedsurface)|\n|`github`|durableGitHubfamily(existing;deferredbydefault)|\n|`automation`|durableautomationfamily(existing;deferredbydefault)|\n|`rlm`|durableRLMfamily(existing;deferredbydefault)|\n|`agent`|sub-agentdispatch|\n|`remember`|opt-indurableuser-memorycapture;eagerwheneverregistered|\n|`todo_write`|progress/plan-of-workupdates|\n|`update_plan`|planartifactupdates|\n|`tool_search`|on-demanddiscoveryofdeferredtools|\n\nDefault-activepolicy:**10names**(vs.~18beforethesimplification),with\n`remember`registeredonlyforbuilt-in-memoryusersandthedurablefamilies\nand`Web`discoverablevia`tool_search`whenneeded.`tool_search`itselfisa\nsyntheticalways-activecatalogentry.\n\n##Rejectedalternatives\n\n-**Keepeverytoolbutdefertherareones.**Thisonlychangeswhatis\nadvertised,nothowmanydistinctschemasthemodelmustlearn.Italso\nleavesduplicatedguidanceintheprompt.\n-**Routesearchandgitthrough`Bash`.**`grep_files`,`file_search`,andthe\ngittoolsreturnstructured,workspace-awareoutputandrespectsandbox,\n`.gitignore`,andnetworkpolicy.Shellwouldforcethemodeltore-parse\nfree-formtextandlosethoseguarantees,sodedicatedtoolswin.\n-**Onemega`File`toolplusaseparate`Edit`tool.**Asingle`File`toolis\nonlyslightlylargerthanaread/editpairandkeepstheboundarythemodel\nalreadyunderstands(`read`ischeap,`edit`requirespriorread).Splitting\nwouldre-introduceatwo-toolaliasforthesameunderlyingoperations.\n-**Deletelegacytools.**Savedtranscriptsandreplaytestsrelyontheold\nnames.Removingthemwouldrequireaconfigmigrationandbreakreproducibility.\nHiddenaliasesavoidboth.\n\n##Compatibility\n\n-Legacynames(`read_file`,`write_file`,`edit_file`,`list_dir`,`file_search`,\n`grep_files`,`apply_patch`,`git_status`,`git_diff`,`git_log`,`git_show`,\n`git_blame`,`run_tests`,`run_verifiers`,`web_search`,`fetch_url`,\n`wait_for_dev_server`)stayregisteredwith`model_visible=false`.\n-Theengineresolvescallsbyname,sooldtranscriptsreplaywithoutchanges.\n-`DEFAULT_ACTIVE_NATIVE_TOOLS`isupdatedtolistthenewcanonicalnamesonly;\nhiddenlegacytoolsareignoredbycatalogconstruction.\n\n##Promptsimplification\n\n-Replacethetool-callingrecipesectionsin`AGENT_MODE`and\n`SUBAGENT_OUTPUT_FORMAT`withshortreferencestothecanonicaltools.\n-Reducemodedeltastopermissionstatements(Act=writerequiresapproval,\nPlan=nowritesorshell,FullAccess=auto-approved,Operate=coordinatefrom\nordinarymessages).\n-Keepthe`BASE_PROMPT`behavioralinvariants,`LANGUAGE_PROMPT`,and\n`OUTPUT_PROMPT`intact.\n-Movedetailedtemplates(`COMPACT_TEMPLATE`,sub-agentbriefformat,planning\nartifacttemplate)outofthestableprefixandintotoolschemasor\nconditionalblocks.\n\n##Validation\n\n-Provider-free:`scripts/measure-runtime-contract.py`reportsactivetoolcount\nandpromptbytesbeforeandLine truncated
"content":"# LSP: PHP Support & Custom Language Server Extension\n\n> 阅读简体中文版:[zh_hans/LSP_PHP_CUSTOM.md](zh_hans/LSP_PHP_CUSTOM.md)\n>\n> v0.8.65+ | `codex/lsp-php-custom-servers`\n\n## Overview\n\nThis feature adds **PHP** to the built-in LSP language registry and introduces a\n`[lsp.custom]` config section so users can register arbitrary LSP servers by\nfile extension — covering languages absent from the built-in `Language` enum\n(Ruby, C#, Swift, Lua, etc.).\n\n## Changes\n\n### 1. PHP built-in support\n\n- `Language::Php` variant added to the enum in `crates/tui/src/lsp/registry.rs`\n- `.php` files are detected and routed to `intelephense --stdio` by default\n- User can override via `[lsp.servers].php`\n\n### 2. Custom LSP server extension\n\nNew struct `CustomLspDef` (defined in `crates/tui/src/lsp/mod.rs` and mirrored\nin `crates/config/src/lib.rs`):\n\n```rust\npub struct CustomLspDef {\n pub language_id: String, // LSP languageId for textDocument/didOpen\n pub command: String, // executable to spawn\n pub args: Vec<String>, // arguments (default empty)\n}\n```\n\nNew config field `LspConfig.custom: HashMap<String, CustomLspDef>` — keyed by\nfile extension (without dot), e.g. `\"rb\"`, `\"cs\"`, `\"swift\"`.\n\nIn `LspManager::diagnostics_for`, when the built-in registry returns\n`Language::Other`, the manager checks the user's custom table before giving up.\nCustom servers get their own lazy-spawn transport map and once-per-extension\nmissing-binary warnings (no log spam).\n\n### 3. Transport generalization\n\n`StdioLspTransport::spawn` now accepts `&str language_id` instead of\n`Language`, so both built-in and custom servers share the same transport\nimplementation. The old `Language` import is removed from `client.rs`.\n\n### 4. Polling pipeline extracted\n\n`poll_diagnostics` is a new private method that both built-in and custom\ndiagnostics paths share — eliminating duplicated call/wait/filter/sort/truncate\nlogic.\n\n## Configuration\n\n### Built-in PHP (enabled by default if `intelephense` is on PATH)\n\n```toml\n# No config needed — PHP .php files are detected automatically.\n# Override the server if desired:\n[lsp.servers]\nphp = [\"phpactor\", \"language-server\"]\n```\n\n### Custom language servers\n\n```toml\n[lsp.custom.rb]\ncommand = \"ruby-lsp\"\nargs = [\"--stdio\"]\nlanguage_id = \"ruby\"\n\n[lsp.custom.cs]\ncommand = \"csharp-ls\"\nlanguage_id = \"csharp\"\n\n[lsp.custom.swift]\ncommand = \"sourcekit-lsp\"\nlanguage_id = \"swift\"\n```\n\nKeys are file extensions (no leading dot). The `args` field defaults to empty.\n`language_id` must match what the LSP server expects in `textDocument/didOpen`.\n\n## Architecture\n\n```\nedit_file / write_file / apply_patch success\n │\n ▼\n LspManager.diagnostics_for(file)\n │\n ├── custom_for_extension(file) ── found? ──► transport_for_custom(ext, def)\n │ │\n ├── detect_language(file) ── Other? ──► return None (skip)\n │\n └── transport_for(lang)\n │\n ▼\n poll_diagnostics(file, text, transport)\n │\n ▼\n DiagnosticBlock → injected into session message stream\n```\n\n## Verification\n\n```\ncargo test -p codewhale-tui --lib lsp::\n# 32 tests passed (3 new: detects_php_extension, language_ids_for_php,\n# server_for_php_is_intelephense)\ncargo clippy -p codewhale-tui --lib\n# lsp module: zero new warnings\n```\n\n## Files touched\n\n| File | Change |\n|------|--------|\n| `crates/tui/src/lsp/registry.rs` | +Php variant, detection, server mapping, tests |\n| `crates/tui/src/lsp/mod.rs` | +CustomLspDef, LspConfig.custom, LspManager custom fallback |\n| `crates/tui/src/lsp/client.rs` | spawn accepts &str language_id |\n| `crates/tui/src/config.rs` | LspConfigToml.custom + into_runtime |\n| `crates/config/src/lib.rs` | LspConfigToml.custom + CustomLspDef |\n| `config.example.toml` | PHP docs + custom extension examples |\n"
"What Codewhale is and what a person gains: their own models, capable agents, and control on their own machine — with availability stated per surface.",
},
{
path:"/app",
title:"Desktop and browser apps",
description:
"The desktop app is a development build and the browser app is not open yet; the terminal and the local browser client are released.",
"Explore Codewhale’s reusable Ratatui components. Compare terminal themes and widths, inspect real rendered previews, and use the Rust examples in your app.",
},
{
path:"/signin",
title:"Sign in",
description:
"Sign in to Codewhale with GitHub, Google or an email code. Cloud computers are invite-only; the terminal needs no account.",
return`Facts generated ${facts.generatedAt.slice(0,10)}${facts.sourceRevision?` from source revision ${facts.sourceRevision}`:""}${read?`; published release read ${read.slice(0,10)}`:""}`;
}
functionfactLines(facts: RepoFacts):string[]{
constrelease=facts.latestPublishedRelease;
constlines=[
release
?`- Latest published release: v${release.version}, published ${release.publishedAt.slice(0,10)}${release.readAt?`, read ${release.readAt.slice(0,10)}`:""} (${release.url}). The install commands above install this release.`
:"- Latest published release: unknown at build time; see https://github.com/codewhale-hq/CodeWhale/releases.",
];
constlines: string[]=[];
if(release)lines.push(`- Published release: ${release.tag} (${release.url}).`);
if(facts.license)lines.push(`- License: ${facts.license} (${REPO_URL}/blob/main/LICENSE). Free to use; you pay your model provider directly, and local models need no key.`);
if(facts.defaultModel)lines.push(`- Default model id: \`${facts.defaultModel}\` (DeepSeek). Every provider and model is selectable with /provider and /model.`);
constreleased=releaseView(facts);
@@ -142,34 +201,41 @@ function factLines(facts: RepoFacts): string[] {
lines.push(`- Built-in provider routes in ${release?.tag??"the published release"} (${released.counts.providerRoutes}): ${released.providers.map((provider)=>provider.label).join(", ")}. Any OpenAI-compatible endpoint also works.`);
}
if(facts.sandboxBackends.length>0)lines.push(`- OS command sandbox backends: ${facts.sandboxBackends.join("; ")}.`);
"- Windows x64: `winget install HunterBown.CodeWhale`",
`- Then run \`codewhale\` from the root of a git repository. Full guide: ${SITE_URL}/en/install`,"",
...installLines(facts),
`## ${home.mapHeading}`,"",
home.sectionDirectionBody,"",
...mapLines(facts,surface),"",
"## Main pages","",
sitePage("install"),
sitePage("guide"),
sitePage("providers"),
`- [Changelog](${SITE_URL}/en/changelog)`,
`- [${home.readTheCode}](${REPO_URL})`,"",
"## Facts","",
...factLines(facts),"",
];
if(headlines.length>0){
lines.push(
`## Unreleased on main${release?` (after v${release.version})`:""}`,"",
`Merged on main and not in a published release yet; the install commands do not include these. Changelog headlines, not the full list: ${SITE_URL}/en/changelog`,"",
...headlines.map((headline)=>`- ${headline}`),"",
);
}
lines.push(
"## Agent entry points","",
`- [Feature catalogue](${SITE_URL}/en/features): Source capabilities, status, surfaces and documentation.`,
`- [Feature JSON](${SITE_URL}/features.json): The generated feature registry, including its source hash.`,
@@ -182,14 +248,15 @@ export function buildLlmsTxt(): string {
`- [Computer Use](${SITE_URL}/en/computer-use): Mac helper setup, CLI-version limitations and its separate open-source repository.`,
`- [Public plugin catalog](https://github.com/codewhale-hq/codewhale-plugin-marketplace): Installed CLI releases bundle a dated snapshot; current catalog entries are not proof of installed versions or end-to-end qualification.`,
lines.push(`- [${topic.label.en} on the site](${topic.hasPage?`${SITE_URL}${docTopicHref(topic,"en")}`:`${REPO_DOCS_BASE}/${Array.isArray(topic.repoSource)?topic.repoSource[0]:topic.repoSource}`}): ${topic.description.en}`);
lines.push(`- [${topic.label.en} on the site](${topic.hasPage?`${SITE_URL}${docTopicHref(topic,"en")}`:`${REPO_DOCS_BASE}/${Array.isArray(topic.repoSource)?topic.repoSource[0]:topic.repoSource}`}): ${topicDescription(topic)}`);
`The document${many?"s":""} below ${many?"are":"is"} the repository's file${many?"s":""} as ${many?"they were":"it was"} at the published release ${tag}.`,"",
...document.missing.flatMap((path)=>[`${path} was added after ${tag} and is not part of this release; the document below is what ${tag} has.`,""]),
"check":"npm run check:facts && npm run check:latest-release && npm run prebuild && npm run check:docs && npm run check:tokens && npm run lint && tsc --noEmit && npm run build",
return["data/agent-docs.release.json is missing or malformed; run npm run prebuild in a checkout that has the tag"];
}
if(snapshot.tag!==release.tag){
problems.push(`data/agent-docs.release.json is for ${snapshot.tag}, the published release is ${release.tag}; run npm run prebuild in a checkout that has the tag`);
}
if(snapshot.version!==release.version)problems.push(`snapshot version ${snapshot.version} differs from the published release ${release.version}`);
for(consttopicofsnapshot.topics){
for(constsourceoftopic.sources){
if(createHash("sha256").update(source.content).digest("hex")!==source.sha256)problems.push(`${topic.slug}: ${source.path} does not match its recorded hash`);
problems.push(`data/agent-docs.release.json differs from the documents at ${release.tag}; run npm run prebuild`);
}
returnproblems;
}
exportfunctioncurrentRelease(){
returnderiveLatestPublishedRelease();
}
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.