fix(mcp): qualify the local 0.10.3 rebuild and workspace routing

Keep required credential failures explicit before connection, propagate stored
OAuth initialization failures, and keep transient desktop browser servers
with their admitted workspace. Forward the shared runtime map to the existing
Core and child adapters, schedule late registrations through the existing
supervisor, and include them in the live manager snapshot. Preserve static
precedence, private-network admission, redaction and credential authority.

Avoid an unnecessary coordination transaction when a validated session owns
no agent or worker records. Preserve owned/read-only/failed-admission fences.
Supply the plugin reload workspace translation argument. Heap-pin eight
existing interactive awaits: the real plugin confirmation overflowed the
32 MiB owner stack; measured heavy frames shrink by 7,339,536 bytes without
changing the stack limit, confirmation or execution ownership.

Prepare consistent 0.10.3 development metadata with the incumbent release
preparer: 49 local lock records updated, all 698 external registry records
and the latest-published-release fact unchanged. This does not publish a
release or freeze a final candidate.

Verification: ordinary production CLI build PASS (56.31s, 3025 frozen inputs);
actual --version codewhale 0.10.3 (dev), binary e5fc64ca47e3. Nine scoped
Engine/MCP observations PASS through seven retained and two fresh controls
on the unchanged Engine cohort; all eight ordinary dependency fingerprints
match this TUI-only rebuild. Native plugin journey PASS 46/46 plus 20 public
renderer comparisons: exact two-step trust, literal argv, stale/prefix refusal,
trust remains disabled, no plugin execution, exit 0, actual terminal EOF and
owned cleanup. One owned catalog GET, zero inference POSTs. Earlier failed
runs are preserved. Modified Rust formatting and JSON parsing pass.

Receipts: CW/artifacts/engine-0103-execution-20261102/local-0103-rebuild/
LOCAL-QUALIFICATION-V5.json (4601029555d3) and the retained ordinary Native
receipt (368289757940). Full fresh-session ACK, actual Child/RLM, paired
Desktop, broader Ubuntu and native-platform/final release gates remain open.
This commit is contained in:
CodeWhale Bot committed 2026-11-02 18:21:26 -08:00
1 parent 13fc7a254f
commit 97e073ac79
71 files changed
+955 -1153

No files matched your search

+13 -1
View File
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [0.10.3] - Unreleased candidate
### Added
- Goal continuations send the objective once per live conversation window. Later passes show the current goal state, estimated conversation input and the applicable compaction setting. Full notes stay available through `get_goal`. Thanks [@7jrxt42BxFZo4iAnN4CX](https://github.com/7jrxt42BxFZo4iAnN4CX) for measuring the repeated text and identifying the misleading token figure ([#7172](https://github.com/codewhale-hq/Codewhale/issues/7172)).
@@ -160,6 +162,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- **Interactive command confirmations keep their stack headroom:** the existing interactive startup chain heap-pins its large futures instead of embedding their debug state machines in each caller.
- **Live MCP status includes runtime servers:** the existing pool snapshot now includes workspace-scoped dynamic servers, retaining static-name precedence and the same permission and connection checks.
- **Plugin reload confirms its workspace:** the status translation receives the required workspace value instead of producing an empty acknowledgement.
- **MCP credentials fail clearly:** missing or invalid required credential configuration stops before connection, stored OAuth discovery failures retain their cause, and reviewed-plugin errors redact credential details. Explicit reconnect still reloads saved credentials.
- **Desktop browser MCP stays with its workspace:** the paired desktop host registers its authenticated loopback server at runtime, late registrations refresh the tool catalog, and another workspace cannot inherit the connection. Recognizable obsolete global browser entries are ignored without rewriting user configuration.
- **Closing an empty delegated fleet avoids an unnecessary writer lock:** a session with no locally owned agent or worker records can close while another Codewhale process coordinates the workspace. Read-only or failed-admission state and sessions with owned records retain the existing coordination checks.
- Child, review and Auto usage keeps its captured session and route through late completion and persistence retries. Unreported usage stays unknown, cache writes and reasoning remain distinct, and cost calculations use the quote captured for the request.
- Interrupted children persist their continuation checkpoint and complete their workflow task instead of leaving it waiting on a worker whose execution already ended. Live approval waits retain their existing behavior.
- Session publication compares the document and crash checkpoint captured before work was queued. A delayed save refuses changed records without replacing newer history or clearing its checkpoint. Ordered completion retains real checkpoint acknowledgments, and native turn/control identity follows restores, undo and terminal saves. Legacy checkpoints require explicit recovery; canonical history recovery retains its original source and exact prepared generation.
@@ -13219,7 +13230,8 @@ overflow report and `/theme` picker edge-wrapping patch in #1814.
Older releases (v0.8.39 and earlier) are archived in [docs/CHANGELOG_ARCHIVE.md](docs/CHANGELOG_ARCHIVE.md).
[Unreleased]: https://github.com/codewhale-hq/CodeWhale/compare/v0.10.2...HEAD
[Unreleased]: https://github.com/codewhale-hq/CodeWhale/compare/v0.10.3...HEAD
[0.10.3]: https://github.com/codewhale-hq/CodeWhale/compare/v0.10.2...HEAD
[0.10.2]: https://github.com/codewhale-hq/CodeWhale/compare/v0.10.1...v0.10.2
[0.10.1]: https://github.com/codewhale-hq/CodeWhale/compare/v0.10.0...v0.10.1
[0.10.0]: https://github.com/codewhale-hq/CodeWhale/compare/v0.9.13...v0.10.0
Generated
+54 -54
View File
@@ -833,7 +833,7 @@ dependencies = [
[[package]]
name = "codewhale-account-cli"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"anyhow",
"clap",
@@ -853,7 +853,7 @@ dependencies = [
[[package]]
name = "codewhale-agent"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"codewhale-config",
"serde",
@@ -861,7 +861,7 @@ dependencies = [
[[package]]
name = "codewhale-agent-contract"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"codewhale-execpolicy",
"codewhale-protocol",
@@ -870,13 +870,13 @@ dependencies = [
[[package]]
name = "codewhale-app-server"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"codewhale-app-server-protocol",
"anyhow",
"axum",
"chrono",
"codewhale-agent",
"codewhale-app-server-protocol",
"codewhale-config",
"codewhale-core",
"codewhale-hooks",
@@ -903,7 +903,7 @@ dependencies = [
[[package]]
name = "codewhale-app-server-protocol"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"schemars",
"serde",
@@ -912,14 +912,14 @@ dependencies = [
[[package]]
name = "codewhale-build-support"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"tempfile",
]
[[package]]
name = "codewhale-cli"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"anyhow",
"chrono",
@@ -961,7 +961,7 @@ dependencies = [
[[package]]
name = "codewhale-cloud-facts"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"codewhale-config",
"codewhale-release",
@@ -978,7 +978,7 @@ dependencies = [
[[package]]
name = "codewhale-command-contract"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"chrono",
"codewhale-protocol",
@@ -988,7 +988,7 @@ dependencies = [
[[package]]
name = "codewhale-commands"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"codewhale-command-contract",
"serde_json",
@@ -996,7 +996,7 @@ dependencies = [
[[package]]
name = "codewhale-config"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"anyhow",
"base64 0.23.1",
@@ -1024,7 +1024,7 @@ dependencies = [
[[package]]
name = "codewhale-core"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"codewhale-config",
"codewhale-hooks",
@@ -1042,7 +1042,7 @@ dependencies = [
[[package]]
name = "codewhale-dependencies"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"anyhow",
"codewhale-config",
@@ -1060,9 +1060,8 @@ dependencies = [
[[package]]
name = "codewhale-engine"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"codewhale-app-server-protocol",
"anyhow",
"arboard",
"async-stream",
@@ -1073,6 +1072,7 @@ dependencies = [
"clap",
"codewhale-agent",
"codewhale-agent-contract",
"codewhale-app-server-protocol",
"codewhale-build-support",
"codewhale-command-contract",
"codewhale-commands",
@@ -1172,7 +1172,7 @@ dependencies = [
[[package]]
name = "codewhale-error-taxonomy"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"codewhale-tools",
"serde",
@@ -1180,7 +1180,7 @@ dependencies = [
[[package]]
name = "codewhale-execpolicy"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"anyhow",
"codewhale-protocol",
@@ -1193,12 +1193,12 @@ dependencies = [
[[package]]
name = "codewhale-hooks"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"codewhale-app-server-protocol",
"anyhow",
"async-trait",
"chrono",
"codewhale-app-server-protocol",
"codewhale-paths",
"codewhale-protocol",
"codewhale-release",
@@ -1213,7 +1213,7 @@ dependencies = [
[[package]]
name = "codewhale-lane"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"anyhow",
"chrono",
@@ -1228,7 +1228,7 @@ dependencies = [
[[package]]
name = "codewhale-localization"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"codewhale-build-support",
"ratatui",
@@ -1241,7 +1241,7 @@ dependencies = [
[[package]]
name = "codewhale-lsp"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"anyhow",
"async-trait",
@@ -1259,7 +1259,7 @@ dependencies = [
[[package]]
name = "codewhale-mcp"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"serde",
"serde_json",
@@ -1268,7 +1268,7 @@ dependencies = [
[[package]]
name = "codewhale-memory"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"clap",
"regex",
@@ -1283,7 +1283,7 @@ dependencies = [
[[package]]
name = "codewhale-models"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"chrono",
"codewhale-config",
@@ -1296,7 +1296,7 @@ dependencies = [
[[package]]
name = "codewhale-net"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"anyhow",
"chrono",
@@ -1319,7 +1319,7 @@ dependencies = [
[[package]]
name = "codewhale-notify"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"codewhale-config",
"codewhale-runtime",
@@ -1333,7 +1333,7 @@ dependencies = [
[[package]]
name = "codewhale-palette"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"codewhale-config",
"libc",
@@ -1345,7 +1345,7 @@ dependencies = [
[[package]]
name = "codewhale-paths"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"anyhow",
"dirs 7.0.0",
@@ -1355,7 +1355,7 @@ dependencies = [
[[package]]
name = "codewhale-plugins"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"anyhow",
"base64 0.23.1",
@@ -1393,7 +1393,7 @@ dependencies = [
[[package]]
name = "codewhale-portable-config"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"codewhale-command-contract",
"codewhale-protocol",
@@ -1403,14 +1403,14 @@ dependencies = [
[[package]]
name = "codewhale-portable-core-navigation"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"codewhale-command-contract",
]
[[package]]
name = "codewhale-portable-session"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"chrono",
"codewhale-command-contract",
@@ -1422,10 +1422,10 @@ dependencies = [
[[package]]
name = "codewhale-protocol"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"codewhale-app-server-protocol",
"chrono",
"codewhale-app-server-protocol",
"serde",
"serde_json",
"uuid",
@@ -1448,7 +1448,7 @@ dependencies = [
[[package]]
name = "codewhale-release"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"anyhow",
"base64 0.23.1",
@@ -1467,7 +1467,7 @@ dependencies = [
[[package]]
name = "codewhale-repl"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"codewhale-dependencies",
"codewhale-runtime",
@@ -1479,7 +1479,7 @@ dependencies = [
[[package]]
name = "codewhale-runtime"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"ahash",
"anyhow",
@@ -1536,7 +1536,7 @@ dependencies = [
[[package]]
name = "codewhale-sandbox"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"anyhow",
"async-trait",
@@ -1561,7 +1561,7 @@ dependencies = [
[[package]]
name = "codewhale-sanitize"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"icu_properties",
"regex",
@@ -1571,7 +1571,7 @@ dependencies = [
[[package]]
name = "codewhale-secrets"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"chrono",
"codewhale-paths",
@@ -1589,7 +1589,7 @@ dependencies = [
[[package]]
name = "codewhale-skill-install"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"anyhow",
"codewhale-net",
@@ -1611,7 +1611,7 @@ dependencies = [
[[package]]
name = "codewhale-skills"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"anyhow",
"codewhale-config",
@@ -1633,7 +1633,7 @@ dependencies = [
[[package]]
name = "codewhale-snapshot"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"codewhale-config",
"codewhale-dependencies",
@@ -1652,7 +1652,7 @@ dependencies = [
[[package]]
name = "codewhale-state"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"anyhow",
"chrono",
@@ -1667,7 +1667,7 @@ dependencies = [
[[package]]
name = "codewhale-telemetry"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"anyhow",
"chrono",
@@ -1687,7 +1687,7 @@ dependencies = [
[[package]]
name = "codewhale-tools"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"anyhow",
"async-trait",
@@ -1701,9 +1701,8 @@ dependencies = [
[[package]]
name = "codewhale-tui"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"codewhale-app-server-protocol",
"ahash",
"ansi-to-tui",
"anyhow",
@@ -1717,6 +1716,7 @@ dependencies = [
"codewhale-agent",
"codewhale-agent-contract",
"codewhale-app-server",
"codewhale-app-server-protocol",
"codewhale-build-support",
"codewhale-cloud-facts",
"codewhale-command-contract",
@@ -1828,7 +1828,7 @@ dependencies = [
[[package]]
name = "codewhale-tui-kit"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"codewhale-palette",
"codewhale-ratatui",
@@ -1843,7 +1843,7 @@ dependencies = [
[[package]]
name = "codewhale-util"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"anyhow",
"chrono",
@@ -1869,7 +1869,7 @@ dependencies = [
[[package]]
name = "codewhale-workflow"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"codewhale-protocol",
"serde",
@@ -1882,7 +1882,7 @@ dependencies = [
[[package]]
name = "codewhale-workflow-js"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"async-trait",
"jsonschema",
+1 -1
View File
@@ -55,7 +55,7 @@ default-members = ["crates/cli"]
resolver = "2"
[workspace.package]
version = "0.10.2"
version = "0.10.3"
edition = "2024"
# The floor is what CI's MSRV job builds (1.89): Rust 1.88 stabilized the
# `let_chains` the codebase relies on, but a locked dependency
+4 -4
View File
@@ -13,10 +13,10 @@ workspace = true
[dependencies]
anyhow.workspace = true
clap.workspace = true
codewhale-config = { path = "../config", version = "0.10.2" }
codewhale-release = { path = "../release", version = "0.10.2" }
codewhale-runtime = { path = "../runtime", version = "0.10.2" }
codewhale-secrets = { path = "../secrets", version = "0.10.2" }
codewhale-config = { path = "../config", version = "0.10.3" }
codewhale-release = { path = "../release", version = "0.10.3" }
codewhale-runtime = { path = "../runtime", version = "0.10.3" }
codewhale-secrets = { path = "../secrets", version = "0.10.3" }
console = "0.16.3"
reqwest = { workspace = true, features = ["blocking"] }
serde.workspace = true
+2 -2
View File
@@ -14,6 +14,6 @@ workspace = true
test-support = []
[dependencies]
codewhale-execpolicy = { path = "../execpolicy", version = "0.10.2" }
codewhale-protocol = { path = "../protocol", version = "0.10.2" }
codewhale-execpolicy = { path = "../execpolicy", version = "0.10.3" }
codewhale-protocol = { path = "../protocol", version = "0.10.3" }
serde = { workspace = true, features = ["derive"] }
+1 -1
View File
@@ -11,5 +11,5 @@ description = "Model/provider registry and fallback strategy for Codewhale"
workspace = true
[dependencies]
codewhale-config = { path = "../config", version = "0.10.2" }
codewhale-config = { path = "../config", version = "0.10.3" }
serde.workspace = true
+10 -10
View File
@@ -13,19 +13,19 @@ autobins = false
workspace = true
[dependencies]
codewhale-app-server-protocol = { path = "../app-server-protocol", version = "0.10.2" }
codewhale-app-server-protocol = { path = "../app-server-protocol", version = "0.10.3" }
anyhow.workspace = true
axum.workspace = true
chrono.workspace = true
codewhale-agent = { path = "../agent", version = "0.10.2" }
codewhale-config = { path = "../config", version = "0.10.2" }
codewhale-core = { path = "../core", version = "0.10.2" }
codewhale-hooks = { path = "../hooks", version = "0.10.2" }
codewhale-paths = { path = "../paths", version = "0.10.2" }
codewhale-protocol = { path = "../protocol", version = "0.10.2" }
codewhale-release = { path = "../release", version = "0.10.2" }
codewhale-runtime = { path = "../runtime", version = "0.10.2" }
codewhale-state = { path = "../state", version = "0.10.2" }
codewhale-agent = { path = "../agent", version = "0.10.3" }
codewhale-config = { path = "../config", version = "0.10.3" }
codewhale-core = { path = "../core", version = "0.10.3" }
codewhale-hooks = { path = "../hooks", version = "0.10.3" }
codewhale-paths = { path = "../paths", version = "0.10.3" }
codewhale-protocol = { path = "../protocol", version = "0.10.3" }
codewhale-release = { path = "../release", version = "0.10.3" }
codewhale-runtime = { path = "../runtime", version = "0.10.3" }
codewhale-state = { path = "../state", version = "0.10.3" }
serde.workspace = true
serde_json.workspace = true
rustls.workspace = true
+15 -15
View File
@@ -24,23 +24,23 @@ name = "codewhale"
path = "src/main.rs"
[dependencies]
codewhale-account-cli = { path = "../account-cli", version = "0.10.2" }
codewhale-runtime = { path = "../runtime", version = "0.10.2" }
codewhale-account-cli = { path = "../account-cli", version = "0.10.3" }
codewhale-runtime = { path = "../runtime", version = "0.10.3" }
anyhow.workspace = true
clap.workspace = true
clap_complete.workspace = true
codewhale-tui = { path = "../tui", version = "0.10.2", default-features = false }
codewhale-engine = { path = "../engine", version = "0.10.2" }
codewhale-app-server = { path = "../app-server", version = "0.10.2" }
codewhale-config = { path = "../config", version = "0.10.2" }
codewhale-lane = { path = "../lane", version = "0.10.2" }
codewhale-workflow = { path = "../workflow", version = "0.10.2" }
codewhale-execpolicy = { path = "../execpolicy", version = "0.10.2" }
codewhale-paths = { path = "../paths", version = "0.10.2" }
codewhale-release = { path = "../release", version = "0.10.2" }
codewhale-secrets = { path = "../secrets", version = "0.10.2" }
codewhale-state = { path = "../state", version = "0.10.2" }
codewhale-telemetry = { path = "../telemetry", version = "0.10.2" }
codewhale-tui = { path = "../tui", version = "0.10.3", default-features = false }
codewhale-engine = { path = "../engine", version = "0.10.3" }
codewhale-app-server = { path = "../app-server", version = "0.10.3" }
codewhale-config = { path = "../config", version = "0.10.3" }
codewhale-lane = { path = "../lane", version = "0.10.3" }
codewhale-workflow = { path = "../workflow", version = "0.10.3" }
codewhale-execpolicy = { path = "../execpolicy", version = "0.10.3" }
codewhale-paths = { path = "../paths", version = "0.10.3" }
codewhale-release = { path = "../release", version = "0.10.3" }
codewhale-secrets = { path = "../secrets", version = "0.10.3" }
codewhale-state = { path = "../state", version = "0.10.3" }
codewhale-telemetry = { path = "../telemetry", version = "0.10.3" }
chrono.workspace = true
console = "0.16.3"
dirs.workspace = true
@@ -59,7 +59,7 @@ tracing.workspace = true
zeroize = "1.8.2"
[build-dependencies]
codewhale-build-support = { path = "../build-support", version = "0.10.2" }
codewhale-build-support = { path = "../build-support", version = "0.10.3" }
# Parent-death cleanup for delegated server children (#3259): on Linux the
# dispatcher sets PR_SET_PDEATHSIG so the child is signalled if the dispatcher
+2 -2
View File
@@ -11,8 +11,8 @@ description = "Fetch + verified disk cache for the Codewhale cloud facts channel
workspace = true
[dependencies]
codewhale-config = { path = "../config", version = "0.10.2" }
codewhale-release = { path = "../release", version = "0.10.2" }
codewhale-config = { path = "../config", version = "0.10.3" }
codewhale-release = { path = "../release", version = "0.10.3" }
reqwest.workspace = true
libc.workspace = true
semver.workspace = true
+1 -1
View File
@@ -11,7 +11,7 @@ description = "Prototype command capability and dispatch shapes for the staged T
workspace = true
[dependencies]
codewhale-protocol = { path = "../protocol", version = "0.10.2" }
codewhale-protocol = { path = "../protocol", version = "0.10.3" }
# FEAT-025 export projections carry JSON tool payloads. The workspace already
# pins serde_json with `preserve_order` (crates/core and crates/tui), so this
# adds no new external dependency to the graph.
+1 -1
View File
@@ -8,7 +8,7 @@ license.workspace = true
repository.workspace = true
[dependencies]
codewhale-command-contract = { path = "../command-contract", version = "0.10.2" }
codewhale-command-contract = { path = "../command-contract", version = "0.10.3" }
serde_json.workspace = true
[lints]
+4 -4
View File
@@ -15,7 +15,7 @@ schema = ["dep:schemars"]
test-support = []
[dependencies]
codewhale-protocol = { path = "../protocol", version = "0.10.2" }
codewhale-protocol = { path = "../protocol", version = "0.10.3" }
anyhow.workspace = true
base64 = "0.23.1"
chrono.workspace = true
@@ -23,9 +23,9 @@ ring = "0.17.14"
rust_decimal = { version = "1.43.0", default-features = false, features = ["std"] }
semver.workspace = true
schemars = { version = "1.2.1", optional = true, features = ["derive", "preserve_order"] }
codewhale-execpolicy = { path = "../execpolicy", version = "0.10.2" }
codewhale-paths = { path = "../paths", version = "0.10.2" }
codewhale-secrets = { path = "../secrets", version = "0.10.2" }
codewhale-execpolicy = { path = "../execpolicy", version = "0.10.3" }
codewhale-paths = { path = "../paths", version = "0.10.3" }
codewhale-secrets = { path = "../secrets", version = "0.10.3" }
fd-lock = "4.0.4"
libc = "0.2"
serde.workspace = true
@@ -1,6 +1,6 @@
{
"schemaVersion": 1,
"runtimeVersion": "0.10.2",
"runtimeVersion": "0.10.3",
"routes": [
{
"id": "anthropic",
+4 -4
View File
@@ -13,10 +13,10 @@ workspace = true
[dependencies]
serde.workspace = true
thiserror.workspace = true
codewhale-config = { path = "../config", version = "0.10.2" }
codewhale-hooks = { path = "../hooks", version = "0.10.2" }
codewhale-protocol = { path = "../protocol", version = "0.10.2" }
codewhale-state = { path = "../state", version = "0.10.2" }
codewhale-config = { path = "../config", version = "0.10.3" }
codewhale-hooks = { path = "../hooks", version = "0.10.3" }
codewhale-protocol = { path = "../protocol", version = "0.10.3" }
codewhale-state = { path = "../state", version = "0.10.3" }
regex = "1.11"
sha2.workspace = true
serde_json = { workspace = true, features = ["preserve_order"] }
+5 -5
View File
@@ -15,10 +15,10 @@ test-support = []
[dependencies]
anyhow.workspace = true
codewhale-config = { path = "../config", version = "0.10.2" }
codewhale-paths = { path = "../paths", version = "0.10.2" }
codewhale-runtime = { path = "../runtime", version = "0.10.2" }
codewhale-util = { path = "../util", version = "0.10.2" }
codewhale-config = { path = "../config", version = "0.10.3" }
codewhale-paths = { path = "../paths", version = "0.10.3" }
codewhale-runtime = { path = "../runtime", version = "0.10.3" }
codewhale-util = { path = "../util", version = "0.10.3" }
serde.workspace = true
serde_json.workspace = true
tempfile.workspace = true
@@ -28,4 +28,4 @@ uuid.workspace = true
wait-timeout = "0.2"
[dev-dependencies]
codewhale-runtime = { path = "../runtime", version = "0.10.2", features = ["test-support"] }
codewhale-runtime = { path = "../runtime", version = "0.10.3", features = ["test-support"] }
+50 -50
View File
@@ -73,41 +73,41 @@ path = "src/lib.rs"
doctest = false
[dependencies]
codewhale-app-server-protocol = { path = "../app-server-protocol", version = "0.10.2" }
codewhale-sanitize = { path = "../sanitize", version = "0.10.2" }
codewhale-memory = { path = "../memory", version = "0.10.2" }
codewhale-app-server-protocol = { path = "../app-server-protocol", version = "0.10.3" }
codewhale-sanitize = { path = "../sanitize", version = "0.10.3" }
codewhale-memory = { path = "../memory", version = "0.10.3" }
anyhow.workspace = true
codewhale-agent = { path = "../agent", version = "0.10.2" }
codewhale-agent-contract = { path = "../agent-contract", version = "0.10.2" }
codewhale-config = { path = "../config", version = "0.10.2" }
codewhale-command-contract = { path = "../command-contract", version = "0.10.2" }
codewhale-commands = { path = "../commands", version = "0.10.2" }
codewhale-core = { path = "../core", version = "0.10.2" }
codewhale-execpolicy = { path = "../execpolicy", version = "0.10.2" }
codewhale-lane = { path = "../lane", version = "0.10.2" }
codewhale-mcp = { path = "../mcp", version = "0.10.2" }
codewhale-models = { path = "../models", version = "0.10.2" }
codewhale-net = { path = "../net", version = "0.10.2" }
codewhale-palette = { path = "../palette", version = "0.10.2" }
codewhale-paths = { path = "../paths", version = "0.10.2" }
codewhale-protocol = { path = "../protocol", version = "0.10.2" }
codewhale-release = { path = "../release", version = "0.10.2" }
codewhale-runtime = { path = "../runtime", version = "0.10.2" }
codewhale-sandbox = { path = "../sandbox", version = "0.10.2" }
codewhale-secrets = { path = "../secrets", version = "0.10.2" }
codewhale-telemetry = { path = "../telemetry", version = "0.10.2" }
codewhale-dependencies = { path = "../dependencies", version = "0.10.2" }
codewhale-lsp = { path = "../lsp", version = "0.10.2" }
codewhale-repl = { path = "../repl", version = "0.10.2" }
codewhale-snapshot = { path = "../snapshot", version = "0.10.2" }
codewhale-plugins = { path = "../plugins", version = "0.10.2" }
codewhale-skill-install = { path = "../skill-install", version = "0.10.2" }
codewhale-skills = { path = "../skills", version = "0.10.2" }
codewhale-util = { path = "../util", version = "0.10.2" }
codewhale-error-taxonomy = { path = "../error-taxonomy", version = "0.10.2" }
codewhale-tools = { path = "../tools", version = "0.10.2" }
codewhale-workflow = { path = "../workflow", version = "0.10.2" }
codewhale-workflow-js = { path = "../workflow-js", version = "0.10.2" }
codewhale-agent = { path = "../agent", version = "0.10.3" }
codewhale-agent-contract = { path = "../agent-contract", version = "0.10.3" }
codewhale-config = { path = "../config", version = "0.10.3" }
codewhale-command-contract = { path = "../command-contract", version = "0.10.3" }
codewhale-commands = { path = "../commands", version = "0.10.3" }
codewhale-core = { path = "../core", version = "0.10.3" }
codewhale-execpolicy = { path = "../execpolicy", version = "0.10.3" }
codewhale-lane = { path = "../lane", version = "0.10.3" }
codewhale-mcp = { path = "../mcp", version = "0.10.3" }
codewhale-models = { path = "../models", version = "0.10.3" }
codewhale-net = { path = "../net", version = "0.10.3" }
codewhale-palette = { path = "../palette", version = "0.10.3" }
codewhale-paths = { path = "../paths", version = "0.10.3" }
codewhale-protocol = { path = "../protocol", version = "0.10.3" }
codewhale-release = { path = "../release", version = "0.10.3" }
codewhale-runtime = { path = "../runtime", version = "0.10.3" }
codewhale-sandbox = { path = "../sandbox", version = "0.10.3" }
codewhale-secrets = { path = "../secrets", version = "0.10.3" }
codewhale-telemetry = { path = "../telemetry", version = "0.10.3" }
codewhale-dependencies = { path = "../dependencies", version = "0.10.3" }
codewhale-lsp = { path = "../lsp", version = "0.10.3" }
codewhale-repl = { path = "../repl", version = "0.10.3" }
codewhale-snapshot = { path = "../snapshot", version = "0.10.3" }
codewhale-plugins = { path = "../plugins", version = "0.10.3" }
codewhale-skill-install = { path = "../skill-install", version = "0.10.3" }
codewhale-skills = { path = "../skills", version = "0.10.3" }
codewhale-util = { path = "../util", version = "0.10.3" }
codewhale-error-taxonomy = { path = "../error-taxonomy", version = "0.10.3" }
codewhale-tools = { path = "../tools", version = "0.10.3" }
codewhale-workflow = { path = "../workflow", version = "0.10.3" }
codewhale-workflow-js = { path = "../workflow-js", version = "0.10.3" }
async-stream = "0.3.6"
async-trait.workspace = true
base64 = "0.23.1"
@@ -179,34 +179,34 @@ sha2.workspace = true
# Reuse the locked cryptographic implementation for exact-call user decisions.
ring = "0.17"
semver.workspace = true
codewhale-localization = { path = "../localization", version = "0.10.2" }
codewhale-localization = { path = "../localization", version = "0.10.3" }
shell-words = "1.1.1"
wiremock = { version = "0.6", optional = true }
[build-dependencies]
codewhale-build-support = { path = "../build-support", version = "0.10.2" }
codewhale-build-support = { path = "../build-support", version = "0.10.3" }
[dev-dependencies]
codewhale-app-server-protocol = { path = "../app-server-protocol", version = "0.10.2", features = ["schema"] }
codewhale-skills = { path = "../skills", version = "0.10.2", features = ["test-support"] }
codewhale-dependencies = { path = "../dependencies", version = "0.10.2", features = ["test-support"] }
codewhale-error-taxonomy = { path = "../error-taxonomy", version = "0.10.2", features = ["test-support"] }
codewhale-lsp = { path = "../lsp", version = "0.10.2", features = ["test-support"] }
codewhale-snapshot = { path = "../snapshot", version = "0.10.2", features = ["test-support"] }
codewhale-config = { path = "../config", version = "0.10.2", features = ["schema", "test-support"] }
codewhale-agent-contract = { path = "../agent-contract", version = "0.10.2", features = [
codewhale-app-server-protocol = { path = "../app-server-protocol", version = "0.10.3", features = ["schema"] }
codewhale-skills = { path = "../skills", version = "0.10.3", features = ["test-support"] }
codewhale-dependencies = { path = "../dependencies", version = "0.10.3", features = ["test-support"] }
codewhale-error-taxonomy = { path = "../error-taxonomy", version = "0.10.3", features = ["test-support"] }
codewhale-lsp = { path = "../lsp", version = "0.10.3", features = ["test-support"] }
codewhale-snapshot = { path = "../snapshot", version = "0.10.3", features = ["test-support"] }
codewhale-config = { path = "../config", version = "0.10.3", features = ["schema", "test-support"] }
codewhale-agent-contract = { path = "../agent-contract", version = "0.10.3", features = [
"test-support",
] }
codewhale-runtime = { path = "../runtime", version = "0.10.2", features = [
codewhale-runtime = { path = "../runtime", version = "0.10.3", features = [
"test-support",
"test-thread-scoped-state",
] }
codewhale-sandbox = { path = "../sandbox", version = "0.10.2", features = ["test-support"] }
codewhale-plugins = { path = "../plugins", version = "0.10.2", features = ["test-support"] }
codewhale-skill-install = { path = "../skill-install", version = "0.10.2", features = ["test-support"] }
codewhale-util = { path = "../util", version = "0.10.2", features = ["test-support"] }
codewhale-models = { path = "../models", version = "0.10.2", features = [
codewhale-sandbox = { path = "../sandbox", version = "0.10.3", features = ["test-support"] }
codewhale-plugins = { path = "../plugins", version = "0.10.3", features = ["test-support"] }
codewhale-skill-install = { path = "../skill-install", version = "0.10.3", features = ["test-support"] }
codewhale-util = { path = "../util", version = "0.10.3", features = ["test-support"] }
codewhale-models = { path = "../models", version = "0.10.3", features = [
"test-support",
] }
cucumber = "0.23.0"
+51 -27
View File
@@ -2546,7 +2546,16 @@ impl Engine {
None => EngineHostProfile::Normal,
};
let mcp_pool = host.as_ref().and_then(|host| match host {
EngineHostSetup::Child(child) => child.authority.runtime.mcp_pool.clone(),
EngineHostSetup::Child(child) => {
child.authority.runtime.mcp_pool.clone().filter(|_| {
child.authority.runtime.context.workspace == config.workspace
&& config
.runtime_services
.runtime_mcp_servers
.as_ref()
.is_some_and(|(workspace, _)| workspace == &config.workspace)
})
}
EngineHostSetup::Rlm(_) => None,
});
let (child_host, rlm_host) = match host {
@@ -6038,23 +6047,14 @@ impl Engine {
let plan_state = self.config.plan_state.clone();
let mut tool_context = self.build_tool_context_for_turn(input_policy, &route);
if wiring.is_live() {
if let Some(owner) = self.config.compaction.runtime_cost_owner.as_deref() {
tool_context.accounting_origin = crate::cost_status::NativeUsageOrigin::runtime(
crate::cost_status::scope_token(),
owner,
)
.ok();
}
tool_context.rlm_caller = rlm_host::CapturedRlmCaller::capture(
self,
input_policy,
&route,
&tool_context,
turn_id,
if wiring.is_live()
&& let Some(owner) = self.config.compaction.runtime_cost_owner.as_deref()
{
tool_context.accounting_origin = crate::cost_status::NativeUsageOrigin::runtime(
crate::cost_status::scope_token(),
owner,
)
.ok()
.map(Arc::new);
.ok();
}
// Ensure MCP pool is initialized before building the tool registry,
// so start_mcp_server can be registered when Feature::Mcp is enabled.
@@ -6065,6 +6065,29 @@ impl Engine {
self.wait_for_explicit_mcp_boot(allowed_tools.as_deref())
.await;
}
let mcp_pool = if self.config.features.enabled(Feature::Mcp) {
if mcp_access.may_connect() {
self.ensure_mcp_pool().await.ok()
} else {
self.mcp_pool.clone()
}
} else {
None
};
tool_context.runtime.runtime_mcp_servers =
self.config.runtime_services.runtime_mcp_servers.clone();
if wiring.is_live() {
tool_context.rlm_caller = rlm_host::CapturedRlmCaller::capture(
self,
input_policy,
&route,
&tool_context,
turn_id,
)
.ok()
.map(Arc::new);
}
let builder = if let Some(child) = self.child_host.as_ref() {
let mut runtime = child.authority.nested_runtime(
tool_context.clone(),
@@ -6082,6 +6105,7 @@ impl Engine {
runtime.api_config = Some(Arc::new(*route.api_config.clone()));
runtime.reasoning_effort = route.reasoning_effort.clone();
runtime.reasoning_effort_auto = route.reasoning_effort_auto;
runtime.mcp_pool = mcp_pool.clone();
child
.authority
.tool_registry_builder(
@@ -6192,16 +6216,6 @@ impl Engine {
None
};
let mcp_pool = if self.config.features.enabled(Feature::Mcp) {
if mcp_access.may_connect() {
self.ensure_mcp_pool().await.ok()
} else {
self.mcp_pool.clone()
}
} else {
None
};
let mut subagent_runtime_model = None;
let mut tool_registry = if self.child_host.is_some() {
builder.build(tool_context)
@@ -8444,6 +8458,7 @@ impl Engine {
}
held.bind_caller_plugins(Arc::clone(&self.plugin_registry))
.map_err(|error| ToolError::not_available(error.to_string()))?;
self.config.runtime_services.runtime_mcp_servers = held.runtime_server_scope();
drop(held);
self.ensure_mcp_supervisor();
return Ok(pool);
@@ -8487,6 +8502,15 @@ impl Engine {
)))
.await;
}
pool.bind_caller_plugins(Arc::clone(&self.plugin_registry))
.map_err(|error| ToolError::not_available(error.to_string()))?;
if let Some((workspace, servers)) =
self.config.runtime_services.runtime_mcp_servers.as_ref()
&& workspace == &self.config.workspace
{
pool.dynamic_servers = Arc::clone(servers);
}
self.config.runtime_services.runtime_mcp_servers = pool.runtime_server_scope();
pool = pool.with_backend(self.api_config.mcp_backend());
pool = pool.with_disallowed_tools(self.config.disallowed_tools.clone().unwrap_or_default());
if let Some(decider) = self.current_network_decider() {
+196 -68
View File
@@ -24,6 +24,8 @@ use parking_lot::RwLock;
use serde::{Deserialize, Serialize};
use sha2::Digest as _;
pub type SharedRuntimeMcpServers = Arc<RwLock<HashMap<String, McpServerConfig>>>;
pub mod external_import;
mod headers;
mod http;
@@ -152,6 +154,38 @@ fn mcp_secret_var(
}
}
const MCP_CREDENTIAL_CONFIGURATION_ERROR_PREFIX: &str = "MCP credential configuration error:";
#[derive(Debug)]
struct McpCredentialConfigurationError(String);
impl std::fmt::Display for McpCredentialConfigurationError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{MCP_CREDENTIAL_CONFIGURATION_ERROR_PREFIX} {}", self.0)
}
}
impl std::error::Error for McpCredentialConfigurationError {}
fn required_mcp_secret_var(
reviewed_plugin: Option<&ReviewedPluginMcpSource>,
name: &str,
auth_context: &dyn AuthContext,
) -> Result<String> {
mcp_secret_var(reviewed_plugin, name, auth_context)
.filter(|value| !value.trim().is_empty())
.ok_or_else(|| {
let message = if reviewed_plugin.is_some() {
"Reviewed plugin MCP credential configuration is unavailable (details suppressed)".to_string()
} else if name == "CODEWHALE_BROWSER_TOOLS_TOKEN" {
"Codewhale desktop browser tools are available only in their owning desktop window; this process has no desktop browser credential".to_string()
} else {
format!("Required MCP environment variable {name} is missing; configure it for this process before reconnecting")
};
anyhow::Error::new(McpCredentialConfigurationError(message))
})
}
pub fn mcp_secret_available(
server: &McpServerConfig,
name: &str,
@@ -1975,61 +2009,26 @@ async fn prepare_mcp_http_client(
let oauth_runtime = if config.reviewed_plugin.is_some() {
None
} else {
match oauth::build_default_headers_with_auth_context(config, auth_context.as_ref()) {
Ok(default_headers) => {
let prepared = tokio::select! {
biased;
_ = cancel_token.cancelled() => {
anyhow::bail!(
"MCP OAuth setup cancelled after plugin authority changed"
)
}
prepared = oauth::McpOAuthRuntime::from_server_config_with_client(
name,
config,
default_headers,
client.clone(),
) => prepared,
};
match prepared {
Ok(runtime) => runtime,
Err(err) => {
if config.reviewed_plugin.is_some() {
tracing::warn!(
target: "mcp",
server = %name,
"failed to prepare reviewed plugin MCP OAuth runtime; provider details suppressed; continuing without stored OAuth token"
);
} else {
tracing::warn!(
target: "mcp",
server = %name,
error = %err,
"failed to prepare MCP OAuth runtime; continuing without stored OAuth token"
);
}
None
}
}
let default_headers =
oauth::build_default_headers_with_auth_context(config, auth_context.as_ref()).map_err(
|error| {
error.context(McpCredentialConfigurationError(
"Configured MCP request headers could not be built".to_string(),
))
},
)?;
let prepared = tokio::select! {
biased;
_ = cancel_token.cancelled() => {
anyhow::bail!("MCP OAuth setup cancelled after plugin authority changed")
}
Err(err) => {
if config.reviewed_plugin.is_some() {
tracing::warn!(
target: "mcp",
server = %name,
"failed to prepare reviewed plugin MCP OAuth headers; details suppressed; continuing without stored OAuth token"
);
} else {
tracing::warn!(
target: "mcp",
server = %name,
error = %err,
"failed to prepare MCP OAuth default headers; continuing without stored OAuth token"
);
}
None
}
}
prepared = oauth::McpOAuthRuntime::from_server_config_with_client(
name, config, default_headers, client.clone(),
) => prepared,
};
prepared.with_context(|| {
format!("preparing stored OAuth credentials for MCP server '{name}'")
})?
};
let client = client.with_mcp_auth(McpHttpAuth::from_config_with_auth_context(
name,
@@ -3353,7 +3352,7 @@ pub struct McpPool {
native_mcp_epoch: u64,
/// Dynamically added MCP servers (from tool calls at runtime).
/// These are not persisted to disk and live for the process lifetime.
pub dynamic_servers: Arc<RwLock<HashMap<String, McpServerConfig>>>,
pub dynamic_servers: SharedRuntimeMcpServers,
/// Servers whose most recent connect attempt failed auth-required (401 /
/// OAuth not logged in). Each gets a synthetic `mcp_<server>_authenticate`
/// tool in the model catalog so the model can self-serve the OAuth login
@@ -3771,6 +3770,12 @@ impl McpPool {
self.network_policy.clone()
}
pub fn runtime_server_scope(&self) -> Option<(PathBuf, SharedRuntimeMcpServers)> {
self.workspace
.clone()
.map(|workspace| (workspace, Arc::clone(&self.dynamic_servers)))
}
pub fn current_catalog_generation(&self) -> u64 {
self.catalog_generation.load(Ordering::SeqCst)
}
@@ -3948,6 +3953,9 @@ impl McpPool {
.map(|source| mcp_config_mtime(source))
.collect();
if self.workspace.as_deref() != Some(workspace.as_path()) {
self.dynamic_servers = Arc::new(RwLock::new(HashMap::new()));
}
self.drop_all_connections("config source switch");
self.config_hash = hash_mcp_config(&new_config);
self.config = new_config;
@@ -3966,12 +3974,18 @@ impl McpPool {
&mut self,
plugins: Arc<codewhale_plugins::PluginRegistry>,
) -> Result<()> {
anyhow::ensure!(
self.workspace
.as_deref()
.is_none_or(|workspace| workspace == plugins.workspace()),
"another MCP workspace must use its own selected pool fork"
);
let next = plugins.caller_selection();
let previous = self
.plugin_registry
.as_deref()
.and_then(codewhale_plugins::PluginRegistry::caller_selection);
if next == previous {
if next == previous && self.workspace.as_deref() == Some(plugins.workspace()) {
return Ok(());
}
anyhow::ensure!(
@@ -4007,7 +4021,7 @@ impl McpPool {
}
/// A selected child uses this same Rust pool factory with its own caller
/// receipt. Dynamic operator servers keep their single shared registry.
/// receipt. Dynamic operator servers retain their owning workspace registry.
pub fn fork_for_plugins(
&self,
plugins: Arc<codewhale_plugins::PluginRegistry>,
@@ -4031,7 +4045,9 @@ impl McpPool {
pool.network_policy = self.network_policy.clone();
pool.oauth_callback_port = self.oauth_callback_port;
pool.oauth_callback_url = self.oauth_callback_url.clone();
pool.dynamic_servers = Arc::clone(&self.dynamic_servers);
if self.workspace.as_deref() == Some(workspace.as_path()) {
pool.dynamic_servers = Arc::clone(&self.dynamic_servers);
}
pool.config_sources = self.config_sources.clone();
if pool.config_sources.len() > 1 {
pool.config_sources[1] = checked_workspace_mcp_config_path(&workspace)?;
@@ -4454,15 +4470,17 @@ impl McpPool {
.chain(
dynamic
.iter()
.filter(|(_, server)| server.is_enabled())
.filter(|(name, server)| {
server.is_enabled()
&& self.server_allowed(name)
&& !self.config.servers.contains_key(*name)
})
.map(|(name, server)| (name.clone(), server.clone())),
)
.collect();
drop(dynamic);
let watched: HashSet<String> = candidates.iter().map(|(name, _)| name.clone()).collect();
// Silent prune: manual retries drop connections the supervisor never
// re-spawns (on-demand reconnect owns connectionless servers), and
// removed/disabled servers leave supervision without an event.
// Removed or disabled servers leave supervision without an event.
self.supervised_dead
.retain(|name| watched.contains(name) && self.connections.contains_key(name));
self.supervised_parked.retain(|name| watched.contains(name));
@@ -4471,10 +4489,19 @@ impl McpPool {
let mut parked = Vec::new();
let now = std::time::Instant::now();
for (name, config) in candidates {
let Some(connection) = self.connections.get(&name) else {
let had_connection = self.connections.contains_key(&name);
if !had_connection
&& (config.lazy
|| self.config.servers.contains_key(&name)
|| !self.dynamic_servers.read().contains_key(&name))
{
continue;
};
if connection.is_transport_ready() {
}
if self
.connections
.get(&name)
.is_some_and(McpConnection::is_transport_ready)
{
if self.supervised_dead.remove(&name) {
self.supervised_parked.remove(&name);
recovered.push(name);
@@ -4487,7 +4514,7 @@ impl McpPool {
if self.needs_auth_servers.contains(&name) {
continue;
}
let fresh_death = self.supervised_dead.insert(name.clone());
let fresh_death = had_connection && self.supervised_dead.insert(name.clone());
if self.connecting.contains(&name) {
continue;
}
@@ -6534,6 +6561,52 @@ impl McpPool {
&self,
name: String,
config: McpServerConfig,
) -> Result<(), String> {
self.add_dynamic_server_config(name, config, true)
}
pub fn add_desktop_browser_server_config(
&self,
name: String,
config: McpServerConfig,
) -> Result<(), String> {
if !is_desktop_browser_server(&name, &config)
|| !mcp_secret_available(
&config,
"CODEWHALE_BROWSER_TOOLS_TOKEN",
self.auth_context.as_ref(),
)
{
return Err(
"Desktop browser tools require their owning desktop process and credential"
.to_string(),
);
}
self.require_server(&name)
.map_err(|error| error.to_string())?;
if self.config.servers.contains_key(&name) {
return Err("A configured MCP server already owns this name".to_string());
}
let held = self.dynamic_servers.read().get(&name).cloned();
if let Some(held) = held {
let old =
serde_json::to_value(&held).map_err(|_| "Runtime MCP configuration unavailable")?;
let new = serde_json::to_value(&config)
.map_err(|_| "Runtime MCP configuration unavailable")?;
return if old == new {
Ok(())
} else {
Err("A different runtime MCP server already owns this name".to_string())
};
}
self.add_dynamic_server_config(name, config, false)
}
fn add_dynamic_server_config(
&self,
name: String,
config: McpServerConfig,
runtime_added: bool,
) -> Result<(), String> {
self.require_server(&name)
.map_err(|error| error.to_string())?;
@@ -6554,7 +6627,7 @@ impl McpPool {
));
}
let mut config = config;
config.runtime_added = true;
config.runtime_added = runtime_added;
dynamic.insert(name, config);
self.catalog_generation.fetch_add(1, Ordering::SeqCst);
Ok(())
@@ -6856,6 +6929,12 @@ pub fn mcp_recovery_kind(
return Some(McpRecoveryKind::Enable);
}
if let Some(error) = error {
if error
.to_ascii_lowercase()
.contains(&MCP_CREDENTIAL_CONFIGURATION_ERROR_PREFIX.to_ascii_lowercase())
{
return Some(McpRecoveryKind::Diagnose);
}
// Before the OAuth classifier: an expired AWS token often surfaces as
// `UnauthorizedException`/`401`, and `/mcp login` — an OAuth flow —
// cannot renew an AWS session.
@@ -7285,6 +7364,45 @@ fn launches_computer_use_plugin(server: &McpServerConfig) -> Option<String> {
})
}
pub fn is_desktop_browser_server(name: &str, server: &McpServerConfig) -> bool {
name == "codewhale-browser"
&& server.is_enabled()
&& !server.required
&& !server.lazy
&& server.command.is_none()
&& server.args.is_empty()
&& server.env.is_empty()
&& server.cwd.is_none()
&& server.transport.is_none()
&& server.connect_timeout.is_none()
&& server.execute_timeout.is_none()
&& server.read_timeout.is_none()
&& server.enabled_tools.is_empty()
&& server.disabled_tools.is_empty()
&& server.headers.is_empty()
&& server.env_headers.is_empty()
&& server.bearer_token_env_var.as_deref() == Some("CODEWHALE_BROWSER_TOOLS_TOKEN")
&& server.scopes.is_empty()
&& server.oauth.is_none()
&& server.oauth_resource.is_none()
&& server.reviewed_plugin.is_none()
&& !server.allow_private_network
&& server
.url
.as_deref()
.and_then(|url| reqwest::Url::parse(url).ok())
.is_some_and(|url| {
url.scheme() == "http"
&& url.host_str() == Some("127.0.0.1")
&& url.port().is_some_and(|port| port != 0)
&& url.path() == "/mcp"
&& url.query().is_none()
&& url.fragment().is_none()
&& url.username().is_empty()
&& url.password().is_none()
})
}
pub fn load_config_with_workspace(global_path: &Path, workspace: &Path) -> Result<McpConfig> {
let plugins = codewhale_plugins::PluginRegistry::empty(workspace);
load_config_with_workspace_and_plugins(global_path, workspace, &plugins)
@@ -7296,6 +7414,9 @@ pub fn load_config_with_workspace_and_plugins(
plugins: &codewhale_plugins::PluginRegistry,
) -> Result<McpConfig> {
let mut merged = load_config(global_path)?;
merged
.servers
.retain(|name, server| !is_desktop_browser_server(name, server));
let workspace = checked_workspace_path(workspace)?;
let project_path = checked_workspace_mcp_config_path(&workspace)?;
if !project_path.exists() || paths_refer_to_same_config(global_path, &project_path) {
@@ -8124,11 +8245,18 @@ impl McpPool {
reload_required: bool,
errors: &HashMap<String, String>,
) -> McpManagerSnapshot {
let mut config = self.config.clone();
for (name, server) in self.dynamic_servers.read().iter() {
config
.servers
.entry(name.clone())
.or_insert_with(|| server.clone());
}
snapshot_from_config(
path,
path.exists(),
reload_required,
&self.config,
&config,
Some((self, errors)),
)
}
+39 -15
View File
@@ -111,29 +111,25 @@ impl McpHttpAuth {
}
let mut headers = self.headers.clone();
for (name, env_var) in &self.env_headers {
let value = super::mcp_secret_var(
let value = super::required_mcp_secret_var(
self.reviewed_plugin.as_ref(),
env_var,
self.auth_context.as_ref(),
);
if let Some(value) = value
&& !value.trim().is_empty()
{
headers.insert(name.clone(), value);
}
)?;
headers.insert(name.clone(), value);
}
if !mcp_headers_have_authorization(&headers)
&& let Some(env_var) = self.bearer_token_env_var.as_deref()
&& let Some(token) = super::mcp_secret_var(
{
let token = super::required_mcp_secret_var(
self.reviewed_plugin.as_ref(),
env_var,
self.auth_context.as_ref(),
)
{
let token = token.trim();
if !token.is_empty() {
headers.insert("Authorization".to_string(), format!("Bearer {token}"));
}
)?;
headers.insert(
"Authorization".to_string(),
format!("Bearer {}", token.trim()),
);
}
if !mcp_headers_have_authorization(&headers)
&& let Some(oauth) = &self.oauth
@@ -151,6 +147,18 @@ impl McpHttpAuth {
headers.insert("Authorization".to_string(), value);
}
}
let mut validated = header::HeaderMap::new();
for (name, value) in &headers {
oauth::insert_header(&mut validated, name, value).map_err(|_| {
super::McpCredentialConfigurationError(
if self.suppress_server_error_details {
"Reviewed plugin MCP HTTP headers are invalid (details suppressed)"
} else {
"Configured MCP HTTP headers are invalid; correct the selected headers or credentials before reconnecting"
}.to_string(),
)
})?;
}
Ok(headers)
}
}
@@ -1413,7 +1421,7 @@ mod tests {
"first-header",
);
let url = "https://example.invalid/mcp";
let session = client(url, false).with_mcp_auth(McpHttpAuth {
let mut session = client(url, false).with_mcp_auth(McpHttpAuth {
headers: HashMap::from([
("Accept".to_string(), "incorrect-accept".to_string()),
("Content-Type".to_string(), "incorrect-type".to_string()),
@@ -1427,6 +1435,22 @@ mod tests {
bearer_token_env_var: Some("CODEWHALE_TEST_MCP_SESSION_BEARER".to_string()),
..Default::default()
});
for selected in ["Accept", "Content-Type", "X-Unsafe"] {
let mut invalid = session.clone();
invalid.mcp_auth.headers.retain(|name, _| name == selected);
let error = invalid
.prepare_mcp_request(invalid.post(url), true)
.await
.err()
.expect("invalid selected headers must refuse request preparation");
assert!(
error
.downcast_ref::<super::super::McpCredentialConfigurationError>()
.is_some()
);
assert!(!super::super::oauth::error_looks_auth_required(&error));
}
session.mcp_auth.headers.retain(|name, _| name == "X-Live");
let cloned = session.clone();
let first = session
.prepare_mcp_request(
+12 -9
View File
@@ -348,7 +348,10 @@ impl OAuthHttpClient for RecordingOAuthHttpClient {
}
pub fn error_looks_auth_required(error: &anyhow::Error) -> bool {
error_text_looks_auth_required(&format!("{error:#}"))
error
.downcast_ref::<super::McpCredentialConfigurationError>()
.is_none()
&& error_text_looks_auth_required(&format!("{error:#}"))
}
/// Whether the error chain carries the OAuth `invalid_grant` code: the
@@ -368,8 +371,11 @@ fn error_is_invalid_grant(error: &anyhow::Error) -> bool {
/// authorization server has definitively rejected the stored grant — only a
/// fresh login recovers it.
pub fn error_text_looks_auth_required(text: &str) -> bool {
let status_401 = text_names_http_status(text, "401");
let text = text.to_ascii_lowercase();
if text.contains(&super::MCP_CREDENTIAL_CONFIGURATION_ERROR_PREFIX.to_ascii_lowercase()) {
return false;
}
let status_401 = text_names_http_status(&text, "401");
// `auth required` and `requires oauth` are anchored to the shapes this
// product and the Codex-compatible managers actually emit (`◆ auth
// required`, `requires OAuth login/authentication/reauthentication`) —
@@ -1698,17 +1704,14 @@ pub(super) fn build_default_headers_with_auth_context(
insert_header(&mut headers, name, value)?;
}
for (name, env_var) in &server.env_headers {
if let Some(value) =
super::mcp_secret_var(server.reviewed_plugin_source(), env_var, auth_context)
&& !value.trim().is_empty()
{
insert_header(&mut headers, name, &value)?;
}
let value =
super::required_mcp_secret_var(server.reviewed_plugin_source(), env_var, auth_context)?;
insert_header(&mut headers, name, &value)?;
}
Ok(headers)
}
fn insert_header(headers: &mut HeaderMap, name: &str, value: &str) -> Result<()> {
pub(super) fn insert_header(headers: &mut HeaderMap, name: &str, value: &str) -> Result<()> {
if !super::headers::is_safe_custom_header(name, value) {
bail!("unsafe MCP HTTP header '{name}'");
}
@@ -3340,6 +3340,8 @@ pub struct RuntimeThreadManager {
pub(super) projection_locks: Arc<parking_lot::Mutex<HashMap<String, Arc<Mutex<()>>>>>,
pub(super) event_tx: broadcast::Sender<RuntimeEventRecord>,
pub(super) manager_cfg: RuntimeThreadManagerConfig,
runtime_mcp_servers:
Arc<parking_lot::Mutex<HashMap<PathBuf, crate::mcp::SharedRuntimeMcpServers>>>,
pub(super) cancel_token: CancellationToken,
task_manager: Arc<parking_lot::Mutex<std::sync::Weak<crate::task_manager::TaskManager>>>,
task_execution_lease:
@@ -4256,6 +4258,7 @@ impl RuntimeThreadManager {
projection_locks: Arc::new(parking_lot::Mutex::new(HashMap::new())),
event_tx,
manager_cfg,
runtime_mcp_servers: Arc::new(parking_lot::Mutex::new(HashMap::new())),
cancel_token: CancellationToken::new(),
task_manager: Arc::new(parking_lot::Mutex::new(std::sync::Weak::new())),
task_execution_lease: Arc::new(parking_lot::Mutex::new(None)),
@@ -4284,6 +4287,18 @@ impl RuntimeThreadManager {
Ok(manager)
}
pub fn workspace(&self) -> &Path {
&self.workspace
}
pub fn mcp_runtime_servers(&self, workspace: &Path) -> crate::mcp::SharedRuntimeMcpServers {
self.runtime_mcp_servers
.lock()
.entry(workspace.to_path_buf())
.or_default()
.clone()
}
pub fn mcp_auth_context(&self) -> Arc<dyn codewhale_runtime::credentials::AuthContext> {
Arc::clone(&self.manager_cfg.mcp_auth_context.0)
}
@@ -13946,6 +13961,14 @@ source_usage_records: Default::default(),
record_restore_points: true,
lsp_config,
runtime_services: crate::tools::spec::RuntimeToolServices {
runtime_mcp_servers: (!isolated_chat && !self.host_profile.is_acp()).then(
|| {
(
thread.workspace.clone(),
self.mcp_runtime_servers(&thread.workspace),
)
},
),
accounting_origin: Some({
let store = self.store.clone();
let process_owner = self._process_owner_lock.clone();
+3
View File
@@ -98,6 +98,7 @@ pub trait DynamicToolExecutor: Send + Sync {
#[derive(Clone)]
pub struct RuntimeToolServices {
pub accounting_origin: Option<crate::cost_status::NativeUsageOrigin>,
pub runtime_mcp_servers: Option<(PathBuf, crate::mcp::SharedRuntimeMcpServers)>,
pub shell_manager: Option<SharedShellManager>,
/// True only for the real headless exec host after it has established the
/// explicit authority required to transfer `persist:true` services.
@@ -130,6 +131,7 @@ impl Default for RuntimeToolServices {
fn default() -> Self {
Self {
accounting_origin: None,
runtime_mcp_servers: None,
shell_manager: None,
persist_services_enabled: false,
task_manager: None,
@@ -151,6 +153,7 @@ impl std::fmt::Debug for RuntimeToolServices {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RuntimeToolServices")
.field("accounting_origin", &self.accounting_origin.is_some())
.field("runtime_mcp_servers", &self.runtime_mcp_servers.is_some())
.field("shell_manager", &self.shell_manager.is_some())
.field("persist_services_enabled", &self.persist_services_enabled)
.field("task_manager", &self.task_manager.is_some())
+14
View File
@@ -6690,6 +6690,20 @@ impl SubAgentManager {
}
pub fn finalize_session_close_for_session(&mut self, active_session_id: &str) -> Result<usize> {
if !self.coordination_snapshot_read_only
&& (self.state_path.is_none() || self.store_authority.is_some())
&& !self.agents.values().any(|agent| {
self.owns_store_record(&agent.id)
&& self.agent_is_owned_by_session(agent, active_session_id)
})
&& !self.worker_records.values().any(|record| {
self.owns_store_record(&record.spec.worker_id)
&& !active_session_id.is_empty()
&& record.owner_session_id == active_session_id
})
{
return Ok(0);
}
self.with_store_transaction(|held| {
held.finalize_session_close_inner(Some(active_session_id))
})
+1 -1
View File
@@ -14,5 +14,5 @@ workspace = true
test-support = []
[dependencies]
codewhale-tools = { path = "../tools", version = "0.10.2" }
codewhale-tools = { path = "../tools", version = "0.10.3" }
serde.workspace = true
+1 -1
View File
@@ -12,7 +12,7 @@ workspace = true
[dependencies]
anyhow.workspace = true
codewhale-protocol = { path = "../protocol", version = "0.10.2" }
codewhale-protocol = { path = "../protocol", version = "0.10.3" }
regex = "1.11"
serde.workspace = true
serde_json.workspace = true
+4 -4
View File
@@ -11,13 +11,13 @@ description = "Hook dispatch and notifications support for Codewhale"
workspace = true
[dependencies]
codewhale-app-server-protocol = { path = "../app-server-protocol", version = "0.10.2" }
codewhale-app-server-protocol = { path = "../app-server-protocol", version = "0.10.3" }
anyhow.workspace = true
async-trait.workspace = true
chrono.workspace = true
codewhale-paths = { path = "../paths", version = "0.10.2" }
codewhale-protocol = { path = "../protocol", version = "0.10.2" }
codewhale-release = { path = "../release", version = "0.10.2" }
codewhale-paths = { path = "../paths", version = "0.10.3" }
codewhale-protocol = { path = "../protocol", version = "0.10.3" }
codewhale-release = { path = "../release", version = "0.10.3" }
reqwest.workspace = true
serde.workspace = true
serde_json.workspace = true
+1 -1
View File
@@ -13,7 +13,7 @@ workspace = true
[dependencies]
anyhow.workspace = true
chrono.workspace = true
codewhale-config = { path = "../config", version = "0.10.2" }
codewhale-config = { path = "../config", version = "0.10.3" }
fd-lock = "4.0.4"
serde.workspace = true
serde_json.workspace = true
+1 -1
View File
@@ -17,7 +17,7 @@ unicode-width = "0.2"
[build-dependencies]
rust-i18n-support = { version = "=4.2.4", features = ["codegen"] }
codewhale-build-support = { path = "../build-support", version = "0.10.2" }
codewhale-build-support = { path = "../build-support", version = "0.10.3" }
[dev-dependencies]
serde_json = { workspace = true, features = ["preserve_order", "raw_value"] }
+4 -4
View File
@@ -16,9 +16,9 @@ test-support = ["codewhale-runtime/test-support"]
[dependencies]
anyhow.workspace = true
async-trait.workspace = true
codewhale-config = { path = "../config", version = "0.10.2" }
codewhale-runtime = { path = "../runtime", version = "0.10.2" }
codewhale-util = { path = "../util", version = "0.10.2" }
codewhale-config = { path = "../config", version = "0.10.3" }
codewhale-runtime = { path = "../runtime", version = "0.10.3" }
codewhale-util = { path = "../util", version = "0.10.3" }
reqwest.workspace = true
serde.workspace = true
serde_json.workspace = true
@@ -27,7 +27,7 @@ tokio.workspace = true
tracing.workspace = true
[dev-dependencies]
codewhale-runtime = { path = "../runtime", version = "0.10.2", features = ["test-support"] }
codewhale-runtime = { path = "../runtime", version = "0.10.3", features = ["test-support"] }
[target.'cfg(unix)'.dependencies]
libc.workspace = true
+3 -3
View File
@@ -16,9 +16,9 @@ workspace = true
[dependencies]
chrono.workspace = true
codewhale-config = { path = "../config", version = "0.10.2" }
codewhale-core = { path = "../core", version = "0.10.2" }
codewhale-sanitize = { path = "../sanitize", version = "0.10.2" }
codewhale-config = { path = "../config", version = "0.10.3" }
codewhale-core = { path = "../core", version = "0.10.3" }
codewhale-sanitize = { path = "../sanitize", version = "0.10.3" }
serde.workspace = true
serde_json = { workspace = true, features = ["preserve_order"] }
thiserror.workspace = true
+5 -5
View File
@@ -13,11 +13,11 @@ workspace = true
[dependencies]
anyhow.workspace = true
chrono.workspace = true
codewhale-command-contract = { path = "../command-contract", version = "0.10.2" }
codewhale-config = { path = "../config", version = "0.10.2" }
codewhale-release = { path = "../release", version = "0.10.2" }
codewhale-runtime = { path = "../runtime", version = "0.10.2" }
codewhale-tools = { path = "../tools", version = "0.10.2" }
codewhale-command-contract = { path = "../command-contract", version = "0.10.3" }
codewhale-config = { path = "../config", version = "0.10.3" }
codewhale-release = { path = "../release", version = "0.10.3" }
codewhale-runtime = { path = "../runtime", version = "0.10.3" }
codewhale-tools = { path = "../tools", version = "0.10.3" }
http = "1"
hyper-util = { version = "=0.1.21", features = ["client-proxy"] }
reqwest.workspace = true
+3 -3
View File
@@ -15,14 +15,14 @@ test-support = []
test-thread-scoped-state = ["test-support"]
[dependencies]
codewhale-config = { path = "../config", version = "0.10.2" }
codewhale-secrets = { path = "../secrets", version = "0.10.2" }
codewhale-config = { path = "../config", version = "0.10.3" }
codewhale-secrets = { path = "../secrets", version = "0.10.3" }
regex = "1.11"
tempfile.workspace = true
tracing.workspace = true
[dev-dependencies]
codewhale-runtime = { path = "../runtime", version = "0.10.2", features = ["test-support"] }
codewhale-runtime = { path = "../runtime", version = "0.10.3", features = ["test-support"] }
[target.'cfg(windows)'.dependencies]
dirs.workspace = true
+1 -1
View File
@@ -19,7 +19,7 @@ default = ["ratatui"]
ratatui = ["dep:ratatui"]
[dependencies]
codewhale-config = { path = "../config", version = "0.10.2" }
codewhale-config = { path = "../config", version = "0.10.3" }
ratatui = { version = "=0.30.2", features = ["unstable-rendered-line-info"], optional = true }
serde.workspace = true
serde_json = { workspace = true, features = ["preserve_order", "raw_value"] }
+12 -12
View File
@@ -17,14 +17,14 @@ test-support = ["codewhale-runtime/test-support"]
anyhow.workspace = true
base64 = "0.23.1"
chrono.workspace = true
codewhale-config = { path = "../config", version = "0.10.2" }
codewhale-mcp = { path = "../mcp", version = "0.10.2" }
codewhale-net = { path = "../net", version = "0.10.2" }
codewhale-runtime = { path = "../runtime", version = "0.10.2" }
codewhale-sanitize = { path = "../sanitize", version = "0.10.2" }
codewhale-secrets = { path = "../secrets", version = "0.10.2" }
codewhale-skill-install = { path = "../skill-install", version = "0.10.2" }
codewhale-util = { path = "../util", version = "0.10.2" }
codewhale-config = { path = "../config", version = "0.10.3" }
codewhale-mcp = { path = "../mcp", version = "0.10.3" }
codewhale-net = { path = "../net", version = "0.10.3" }
codewhale-runtime = { path = "../runtime", version = "0.10.3" }
codewhale-sanitize = { path = "../sanitize", version = "0.10.3" }
codewhale-secrets = { path = "../secrets", version = "0.10.3" }
codewhale-skill-install = { path = "../skill-install", version = "0.10.3" }
codewhale-util = { path = "../util", version = "0.10.3" }
fd-lock = "4.0.4"
flate2 = "1.1"
regex = "1.11"
@@ -52,7 +52,7 @@ windows-sys = { version = "0.61.2", features = ["Wdk_Storage_FileSystem", "Win32
windows = { version = "0.62", features = ["Win32_Foundation", "Win32_Security", "Win32_Security_Authorization", "Win32_Storage_FileSystem", "Win32_System_Console", "Win32_System_Diagnostics_ToolHelp", "Win32_System_Environment", "Win32_System_JobObjects", "Win32_System_Registry", "Win32_System_Threading", "Win32_UI_WindowsAndMessaging"] }
[dev-dependencies]
codewhale-config = { path = "../config", version = "0.10.2", features = ["schema", "test-support"] }
codewhale-runtime = { path = "../runtime", version = "0.10.2", features = ["test-support", "test-thread-scoped-state"] }
codewhale-skill-install = { path = "../skill-install", version = "0.10.2", features = ["test-support"] }
codewhale-util = { path = "../util", version = "0.10.2", features = ["test-support"] }
codewhale-config = { path = "../config", version = "0.10.3", features = ["schema", "test-support"] }
codewhale-runtime = { path = "../runtime", version = "0.10.3", features = ["test-support", "test-thread-scoped-state"] }
codewhale-skill-install = { path = "../skill-install", version = "0.10.3", features = ["test-support"] }
codewhale-util = { path = "../util", version = "0.10.3", features = ["test-support"] }
+1 -1
View File
@@ -11,7 +11,7 @@ description = "App-server protocol frames for Codewhale runtime integrations"
workspace = true
[dependencies]
codewhale-app-server-protocol = { path = "../app-server-protocol", version = "0.10.2" }
codewhale-app-server-protocol = { path = "../app-server-protocol", version = "0.10.3" }
chrono.workspace = true
serde.workspace = true
serde_json = { workspace = true, features = ["preserve_order"] }
+1 -1
View File
@@ -13,7 +13,7 @@ workspace = true
[dependencies]
anyhow.workspace = true
base64 = "0.23.1"
codewhale-sanitize = { path = "../sanitize", version = "0.10.2" }
codewhale-sanitize = { path = "../sanitize", version = "0.10.3" }
reqwest = { workspace = true, features = ["blocking"] }
ring = "0.17.14"
rustls.workspace = true
+3 -3
View File
@@ -14,12 +14,12 @@ workspace = true
test-support = []
[dependencies]
codewhale-dependencies = { path = "../dependencies", version = "0.10.2" }
codewhale-runtime = { path = "../runtime", version = "0.10.2" }
codewhale-dependencies = { path = "../dependencies", version = "0.10.3" }
codewhale-runtime = { path = "../runtime", version = "0.10.3" }
serde.workspace = true
serde_json.workspace = true
tokio.workspace = true
uuid.workspace = true
[dev-dependencies]
codewhale-runtime = { path = "../runtime", version = "0.10.2", features = ["test-support"] }
codewhale-runtime = { path = "../runtime", version = "0.10.3", features = ["test-support"] }
+17 -17
View File
@@ -35,12 +35,12 @@ path = "src/lib.rs"
# scripts/check-command-crate-boundaries.py enforces that with `cargo tree -p`.
[dependencies]
base64 = "0.23.1"
codewhale-cloud-facts = { path = "../cloud-facts", version = "0.10.2" }
codewhale-localization = { path = "../localization", version = "0.10.2" }
codewhale-protocol = { path = "../protocol", version = "0.10.2" }
codewhale-util = { path = "../util", version = "0.10.2" }
codewhale-release = { path = "../release", version = "0.10.2" }
codewhale-paths = { path = "../paths", version = "0.10.2" }
codewhale-cloud-facts = { path = "../cloud-facts", version = "0.10.3" }
codewhale-localization = { path = "../localization", version = "0.10.3" }
codewhale-protocol = { path = "../protocol", version = "0.10.3" }
codewhale-util = { path = "../util", version = "0.10.3" }
codewhale-release = { path = "../release", version = "0.10.3" }
codewhale-paths = { path = "../paths", version = "0.10.3" }
dirs.workspace = true
jsonwebtoken = { version = "11", default-features = false, features = ["aws_lc_rs"] }
parking_lot = "0.12"
@@ -51,16 +51,16 @@ toml_edit.workspace = true
ahash = "0.8"
anyhow.workspace = true
chrono.workspace = true
codewhale-command-contract = { path = "../command-contract", version = "0.10.2" }
codewhale-config = { path = "../config", version = "0.10.2" }
codewhale-error-taxonomy = { path = "../error-taxonomy", version = "0.10.2" }
codewhale-core = { path = "../core", version = "0.10.2" }
codewhale-execpolicy = { path = "../execpolicy", version = "0.10.2" }
codewhale-lane = { path = "../lane", version = "0.10.2" }
codewhale-memory = { path = "../memory", version = "0.10.2" }
codewhale-models = { path = "../models", version = "0.10.2" }
codewhale-palette = { path = "../palette", version = "0.10.2", default-features = false }
codewhale-secrets = { path = "../secrets", version = "0.10.2" }
codewhale-command-contract = { path = "../command-contract", version = "0.10.3" }
codewhale-config = { path = "../config", version = "0.10.3" }
codewhale-error-taxonomy = { path = "../error-taxonomy", version = "0.10.3" }
codewhale-core = { path = "../core", version = "0.10.3" }
codewhale-execpolicy = { path = "../execpolicy", version = "0.10.3" }
codewhale-lane = { path = "../lane", version = "0.10.3" }
codewhale-memory = { path = "../memory", version = "0.10.3" }
codewhale-models = { path = "../models", version = "0.10.3" }
codewhale-palette = { path = "../palette", version = "0.10.3", default-features = false }
codewhale-secrets = { path = "../secrets", version = "0.10.3" }
colored = "3.0.0"
fd-lock = "4.0.4"
ignore = "0.4"
@@ -83,7 +83,7 @@ wait-timeout = "0.2"
futures-util = "0.3"
ring = "0.17"
wiremock = "0.6"
codewhale-models = { path = "../models", version = "0.10.2", features = [
codewhale-models = { path = "../models", version = "0.10.3", features = [
"test-support",
] }
tempfile.workspace = true
+5 -5
View File
@@ -17,9 +17,9 @@ test-support = ["codewhale-runtime/test-support"]
anyhow.workspace = true
async-trait.workspace = true
chrono.workspace = true
codewhale-paths = { path = "../paths", version = "0.10.2" }
codewhale-release = { path = "../release", version = "0.10.2" }
codewhale-runtime = { path = "../runtime", version = "0.10.2" }
codewhale-paths = { path = "../paths", version = "0.10.3" }
codewhale-release = { path = "../release", version = "0.10.3" }
codewhale-runtime = { path = "../runtime", version = "0.10.3" }
dirs.workspace = true
reqwest.workspace = true
serde.workspace = true
@@ -28,8 +28,8 @@ tracing.workspace = true
uuid.workspace = true
[dev-dependencies]
codewhale-execpolicy = { path = "../execpolicy", version = "0.10.2" }
codewhale-runtime = { path = "../runtime", version = "0.10.2", features = ["test-support", "test-thread-scoped-state"] }
codewhale-execpolicy = { path = "../execpolicy", version = "0.10.3" }
codewhale-runtime = { path = "../runtime", version = "0.10.3", features = ["test-support", "test-thread-scoped-state"] }
tempfile.workspace = true
tokio.workspace = true
+2 -2
View File
@@ -11,8 +11,8 @@ description = "Secret storage backends for Codewhale, with OS keyring and file f
workspace = true
[dependencies]
codewhale-sanitize = { path = "../sanitize", version = "0.10.2" }
codewhale-paths = { path = "../paths", version = "0.10.2" }
codewhale-sanitize = { path = "../sanitize", version = "0.10.3" }
codewhale-paths = { path = "../paths", version = "0.10.3" }
chrono.workspace = true
fd-lock = "=4.0.4"
libc.workspace = true
+3 -3
View File
@@ -15,9 +15,9 @@ test-support = []
[dependencies]
anyhow.workspace = true
codewhale-net = { path = "../net", version = "0.10.2" }
codewhale-paths = { path = "../paths", version = "0.10.2" }
codewhale-util = { path = "../util", version = "0.10.2" }
codewhale-net = { path = "../net", version = "0.10.3" }
codewhale-paths = { path = "../paths", version = "0.10.3" }
codewhale-util = { path = "../util", version = "0.10.3" }
flate2 = "1.1"
futures-util = "0.3.31"
reqwest = { workspace = true, features = ["stream"] }
+13 -13
View File
@@ -20,14 +20,14 @@ test-support = ["codewhale-runtime/test-support", "codewhale-plugins/test-suppor
[dependencies]
anyhow.workspace = true
codewhale-config = { path = "../config", version = "0.10.2" }
codewhale-localization = { path = "../localization", version = "0.10.2" }
codewhale-net = { path = "../net", version = "0.10.2" }
codewhale-paths = { path = "../paths", version = "0.10.2" }
codewhale-plugins = { path = "../plugins", version = "0.10.2" }
codewhale-runtime = { path = "../runtime", version = "0.10.2" }
codewhale-skill-install = { path = "../skill-install", version = "0.10.2" }
codewhale-util = { path = "../util", version = "0.10.2" }
codewhale-config = { path = "../config", version = "0.10.3" }
codewhale-localization = { path = "../localization", version = "0.10.3" }
codewhale-net = { path = "../net", version = "0.10.3" }
codewhale-paths = { path = "../paths", version = "0.10.3" }
codewhale-plugins = { path = "../plugins", version = "0.10.3" }
codewhale-runtime = { path = "../runtime", version = "0.10.3" }
codewhale-skill-install = { path = "../skill-install", version = "0.10.3" }
codewhale-util = { path = "../util", version = "0.10.3" }
serde.workspace = true
serde_json = { workspace = true, features = ["preserve_order", "raw_value"] }
sha2.workspace = true
@@ -37,8 +37,8 @@ toml.workspace = true
uuid.workspace = true
[dev-dependencies]
codewhale-config = { path = "../config", version = "0.10.2", features = ["schema", "test-support"] }
codewhale-plugins = { path = "../plugins", version = "0.10.2", features = ["test-support"] }
codewhale-runtime = { path = "../runtime", version = "0.10.2", features = ["test-support", "test-thread-scoped-state"] }
codewhale-skill-install = { path = "../skill-install", version = "0.10.2", features = ["test-support"] }
codewhale-util = { path = "../util", version = "0.10.2", features = ["test-support"] }
codewhale-config = { path = "../config", version = "0.10.3", features = ["schema", "test-support"] }
codewhale-plugins = { path = "../plugins", version = "0.10.3", features = ["test-support"] }
codewhale-runtime = { path = "../runtime", version = "0.10.3", features = ["test-support", "test-thread-scoped-state"] }
codewhale-skill-install = { path = "../skill-install", version = "0.10.3", features = ["test-support"] }
codewhale-util = { path = "../util", version = "0.10.3", features = ["test-support"] }
+6 -6
View File
@@ -14,11 +14,11 @@ workspace = true
test-support = ["codewhale-runtime/test-support"]
[dependencies]
codewhale-config = { path = "../config", version = "0.10.2" }
codewhale-dependencies = { path = "../dependencies", version = "0.10.2" }
codewhale-paths = { path = "../paths", version = "0.10.2" }
codewhale-runtime = { path = "../runtime", version = "0.10.2" }
codewhale-util = { path = "../util", version = "0.10.2" }
codewhale-config = { path = "../config", version = "0.10.3" }
codewhale-dependencies = { path = "../dependencies", version = "0.10.3" }
codewhale-paths = { path = "../paths", version = "0.10.3" }
codewhale-runtime = { path = "../runtime", version = "0.10.3" }
codewhale-util = { path = "../util", version = "0.10.3" }
fd-lock = "4.0.4"
ignore = "0.4"
serde.workspace = true
@@ -28,7 +28,7 @@ tracing.workspace = true
wait-timeout = "0.2"
[dev-dependencies]
codewhale-runtime = { path = "../runtime", version = "0.10.2", features = ["test-support"] }
codewhale-runtime = { path = "../runtime", version = "0.10.3", features = ["test-support"] }
[target.'cfg(unix)'.dependencies]
libc.workspace = true
+2 -2
View File
@@ -13,8 +13,8 @@ workspace = true
[dependencies]
anyhow.workspace = true
chrono.workspace = true
codewhale-paths = { path = "../paths", version = "0.10.2" }
codewhale-protocol = { path = "../protocol", version = "0.10.2" }
codewhale-paths = { path = "../paths", version = "0.10.3" }
codewhale-protocol = { path = "../protocol", version = "0.10.3" }
fd-lock = "4.0.4"
rusqlite.workspace = true
serde.workspace = true
+5 -5
View File
@@ -13,9 +13,9 @@ workspace = true
[dependencies]
anyhow.workspace = true
chrono.workspace = true
codewhale-config = { path = "../config", version = "0.10.2" }
codewhale-paths = { path = "../paths", version = "0.10.2" }
codewhale-release = { path = "../release", version = "0.10.2" }
codewhale-config = { path = "../config", version = "0.10.3" }
codewhale-paths = { path = "../paths", version = "0.10.3" }
codewhale-release = { path = "../release", version = "0.10.3" }
fd-lock = "4.0.4"
reqwest = { workspace = true, features = ["blocking"] }
serde.workspace = true
@@ -25,9 +25,9 @@ tracing.workspace = true
uuid.workspace = true
[build-dependencies]
codewhale-build-support = { path = "../build-support", version = "0.10.2" }
codewhale-build-support = { path = "../build-support", version = "0.10.3" }
[dev-dependencies]
# Used as an *assertion*, never as a filter: every string leaf of a serialized
# batch is run through `redact_for_disclosure` and must come back unredacted.
codewhale-workflow = { path = "../workflow", version = "0.10.2" }
codewhale-workflow = { path = "../workflow", version = "0.10.3" }
+1 -1
View File
@@ -13,7 +13,7 @@ workspace = true
[dependencies]
anyhow.workspace = true
async-trait.workspace = true
codewhale-protocol = { path = "../protocol", version = "0.10.2" }
codewhale-protocol = { path = "../protocol", version = "0.10.3" }
serde.workspace = true
serde_json.workspace = true
thiserror.workspace = true
+2 -2
View File
@@ -14,9 +14,9 @@ workspace = true
test-support = []
[dependencies]
codewhale-palette = { path = "../palette", version = "0.10.2" }
codewhale-palette = { path = "../palette", version = "0.10.3" }
codewhale-ratatui = { path = "../../vendor/codewhale-ratatui", version = "0.2.0" }
codewhale-secrets = { path = "../secrets", version = "0.10.2" }
codewhale-secrets = { path = "../secrets", version = "0.10.3" }
crossterm = "0.29"
ratatui = { version = "=0.30.2", features = ["unstable-rendered-line-info"] }
similar = { version = "3", features = ["unicode"] }
+13 -539
View File
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [0.10.3] - Unreleased candidate
### Added
- Goal continuations send the objective once per live conversation window. Later passes show the current goal state, estimated conversation input and the applicable compaction setting. Full notes stay available through `get_goal`. Thanks [@7jrxt42BxFZo4iAnN4CX](https://github.com/7jrxt42BxFZo4iAnN4CX) for measuring the repeated text and identifying the misleading token figure ([#7172](https://github.com/codewhale-hq/Codewhale/issues/7172)).
@@ -160,6 +162,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- **Interactive command confirmations keep their stack headroom:** the existing interactive startup chain heap-pins its large futures instead of embedding their debug state machines in each caller.
- **Live MCP status includes runtime servers:** the existing pool snapshot now includes workspace-scoped dynamic servers, retaining static-name precedence and the same permission and connection checks.
- **Plugin reload confirms its workspace:** the status translation receives the required workspace value instead of producing an empty acknowledgement.
- **MCP credentials fail clearly:** missing or invalid required credential configuration stops before connection, stored OAuth discovery failures retain their cause, and reviewed-plugin errors redact credential details. Explicit reconnect still reloads saved credentials.
- **Desktop browser MCP stays with its workspace:** the paired desktop host registers its authenticated loopback server at runtime, late registrations refresh the tool catalog, and another workspace cannot inherit the connection. Recognizable obsolete global browser entries are ignored without rewriting user configuration.
- **Closing an empty delegated fleet avoids an unnecessary writer lock:** a session with no locally owned agent or worker records can close while another Codewhale process coordinates the workspace. Read-only or failed-admission state and sessions with owned records retain the existing coordination checks.
- Child, review and Auto usage keeps its captured session and route through late completion and persistence retries. Unreported usage stays unknown, cache writes and reasoning remain distinct, and cost calculations use the quote captured for the request.
- Interrupted children persist their continuation checkpoint and complete their workflow task instead of leaving it waiting on a worker whose execution already ended. Live approval waits retain their existing behavior.
- Session publication compares the document and crash checkpoint captured before work was queued. A delayed save refuses changed records without replacing newer history or clearing its checkpoint. Ordered completion retains real checkpoint acknowledgments, and native turn/control identity follows restores, undo and terminal saves. Legacy checkpoints require explicit recovery; canonical history recovery retains its original source and exact prepared generation.
- Damaged usage sidecars keep valid recovery data readable with incomplete coverage and no publication receipt. Known retirement, changed raw records and unsafe writes still refuse.
- Local control and ACP output serialize within the existing 8 MiB frame limit and stop a damaged or stalled output stream. Compatibility replies, events and relay stdout now share the 30-second write deadline; owner forwarding keeps its tighter 5 seconds. Standalone and attached ACP recover after complete invalid UTF-8 or oversized input frames, discard oversized tails through the real newline, and preserve cancellation, permissions and actual Core receipts.
@@ -8604,545 +8617,6 @@ stale runtime and dependency surface.
source-candidate credit for the canonical `Bash` workspace fix from PR #4673
and issue #4674.
## [0.9.2] - 2026-07-29
This is the Codewhale v0.9.2 source candidate. It is not a published release
until the matching tag, packages, checksums, and release assets exist.
### Changed — behavior
- **Legacy `model = auto` no longer elects a network classifier on its own.**
Holding a DeepSeek API key used to silently select `deepseek-v4-flash` as the
classifier for every Auto turn — a per-turn cost on a route nobody asked for,
and one provider privileged over the rest. Auto now stays local and free
unless an explicit `[auto.router]` block names a provider and model.
**If you relied on the implicit default**, restore it explicitly:
```toml
[auto.router]
provider = "deepseek"
model = "deepseek-v4-flash"
```
`[auto.router]` remains legacy `model = auto` configuration. It is unrelated
to a Fleet's Adaptive Reasoning Router, which is a saved service referenced by
name from a Fleet file and decides only how hard an already-frozen route
thinks.
Landed since v0.9.1, not yet released. A cluster of defects found by a
read-through audit of the policy engine, the MCP proxy, the session index,
and the app-server bridge — several of them cases where the wrong outcome
was reached silently, behind a response or a log line that looked fine. The
release also adds opt-in session, reasoning, localization, and inspectability
surfaces; existing defaults remain stable unless an entry below explicitly
says otherwise.
### Added
- `/permissions` now lists the active user permission-rule source, each rule's
effective matcher and global/repository scope, and whether that scope applies
in the current workspace. `/permissions remove <number>` previews deletion
and requires a snapshot-bound confirmation token, so a concurrent edit
cannot move a different rule under the confirmed index. Appends and removals
share one adjacent lock, preserve unrelated TOML formatting and comments,
atomically replace `permissions.toml`, and reload the live user ruleset
without clearing session-only approvals. `/config ask-rules` remains a
compatibility entry; rule creation, glob/directory rules, and deny
persistence remain out of scope (#1186, PR #4960 by @greyfreedom).
- `/preview-request` (aliases `/dryrun` and `/preview_request`) is a human-only,
provider-free inspection of the next primary turn. Production dispatch and
preview share one prepared-request seam across Chat Completions, Anthropic
Messages, and OpenAI Responses, so the manifest reads the final wire model,
reasoning controls, tool choice, tool schemas, and body hash from the same
value production sends. Route, tool, or body facts that require Auto's
provider classifier, an MCP connection, mutable hooks, compaction, or queued
runtime injections remain typed unavailable. The manifest reports exact
primary role/lane identity, upstream route-source provenance, requested and
effective reasoning, canonical JSON sizes, conservative offline estimates,
and provider-reported usage as unavailable because no request ran. It never
adds a model-visible tool, sends a provider call, or prints prompt, message,
credential, endpoint-path, or workspace-path content. The explicit
`/preview-request base-prompt` mode prints only the exact effective base
prompt; effective system text remains protected behind its final hash. The
exact body includes the same authoritative transient Work/To-do tail used by
production, including graph-backed state newer than the legacy projection.
Preflight preserves production's separately framed base-plus-Work estimate,
and fails closed when the authoritative projection is unavailable. An
exhausted active goal token budget also produces a typed unavailable result
before any outbound request is built.
(#1004, #3928; dry-run concept harvested from PR #1099 by @GTC2080 / TaoMu.)
- Slash commands, hotbar actions, and CLI entrypoints for the same Lane/Fleet
lifecycle operation now share one typed control-plane contract
(`codewhale-lane::control`): a stable `<domain>.<verb>` id, read-vs-write
authority, persistence scope, exact-identity target selection, retryability,
lifecycle outcome, and one bounded, sanitized receipt. `docs/COMMAND_CONTROL_PLANE.md`
documents it (#1888).
- `/lane [list|status|interrupt|restart|resume]` — durable Lane control from the
composer, backed by the same executor `codewhale lane …` calls. `codewhale
lane interrupt|restart|resume` are the matching CLI verbs; `lane stop` stays as
a compatibility spelling of `lane interrupt`. Appending `@<lifecycle-seq>` to a
lane id fences a write to the exact durable generation you observed, so a
concurrent transition is rejected as a conflict rather than acted on (#1888).
- `codewhale fleet list` and `/fleet [list|status|interrupt|resume]` — durable
Fleet run inspection and control from either surface, through shared DTOs that
carry the exact provider, provider-table id, model, effective reasoning tier,
and route source when the ledger records them, and a typed `not_recorded` /
`not_applicable` / `redacted` reason when it does not. Requested-vs-effective
reasoning is never back-filled: the ledger persists the effective tier only, so
the requested tier reports `not_recorded` (#4022).
- The bundled skill pack now ships a `help` skill (catalog generation 7). It is
`invocation: explicit-only`, so it never enters the model's ambient catalogue
and costs no prompt budget. Its body is a routing card that points at the
surfaces this build actually exposes — `/help` and `/help <command>`,
`/skills` and `/skills inspect`, `/config`, `doctor`, and the `docs/` tree
when the workspace is a Codewhale checkout — and explicitly forbids pasting a
command list or settings table into context (#4698).
- `crates/tui/assets/skills-catalog-matrix.json`: an authored, provider-free
expectation matrix covering every bundled skill (tier, invocation, aliases,
ambient-catalogue eligibility, shadowed aliases). Contract tests in
`crates/tui/src/skills/catalog_matrix.rs` assert a bijection between the
fixture and the shipped bundle, so the starter pack cannot change without an
explicit fixture update. The matrix covers positive eligibility and explicit
load, non-activation negatives, alias resolution, explicit-only exclusion,
alias-vs-canonical collision precedence, and prompt-budget invariants (no
duplicate catalogue entries, no aliases as extra entries, and the shipped
pack fitting inside the 12 000-char budget with no omitted-skills line). These
are deterministic registry/catalog/resolver assertions and make no claim about
semantic model routing (#4698).
- Locale-routing coverage for the complete bundled catalog across every shipped
locale (`en`, `ja`, `zh-Hans`, `zh-Hant`, `pt-BR`, `es-419`, `vi`, `ko`). No
bundled skill ships a localized routing description and none was invented;
the tested contract is deterministic fallback to the canonical English
description, with the rendered catalogue byte-identical across locales.
Exact-tag match, primary-subtag fallback, and English fallback are covered
against a synthetic authored fixture, and the parity test fails if a bundled
skill ever gains localized metadata without source-backed coverage (#4698).
- `docs/LIVE_SMOKE.md`: copy-pasteable, opt-in live-smoke instructions for Kimi
K3 and a second provider/model (DeepSeek). The runs are manual only — nothing
in CI, tests, or skills invokes them. They use `env -i` plus a throwaway
`CODEWHALE_HOME`; `HOME` is left unset rather than repurposed, and ambient
provider variables are not forwarded. The operator names the credential
variable explicitly, and the isolated child reads its value with echo off,
restores the prior terminal state on exit or interruption, and never persists
the value or puts it in a command argument. The page states the expected
route/model/reasoning/tool receipt fields while treating provider errors as
unclassified until provider configuration, authentication/entitlement, and
harness behavior have been corroborated independently (#4698).
- Approval cards can now remember eligible safe shell and file-write approvals
as exact `allow` rules scoped to the current repository. Remembered shell
commands use complete-command matching, validated file and patch paths remain
workspace-relative, and dangerous, critical, or repo-law-held requests stay
ineligible and continue to require review.
- `tui.header_items` (array of strings, optional, default `[]`): an opt-in
header chip showing cumulative session token usage as input / cache-hit /
output. Set `header_items = ["tokens"]` under `[tui]` to enable it. The
chip is the only elidable element of the header — the git label, context
meter, and version stamp keep their space, and narrow terminals drop the
chip rather than the baseline chrome. Unknown entries are warned about and
skipped so configs written by newer builds stay loadable by older ones
(#4520 requested by @eugenicum; PR #4610 by @XhesicaFrost, harvested with
co-authorship).
- `thinking_default_expanded` lets reasoning blocks start open while keeping
Space as the per-block toggle. The setting is persisted, available through
native and runtime configuration, and documented for SSH/tmux accessibility
(issue #4925 and PR #4928 by @M-Maciej).
- The transcript renders a conservative subset of LaTeX math as readable
Unicode without rewriting fenced/inline code, ordinary currency, escaped
dollars, or unknown commands. The contribution from PR #4973 by
@SparkofSpike was hardened and landed through PR #4974; reported by
@antarikshraya in #4957.
- Session control now includes a sessions rail, shared archive projection,
picker archive controls, and opt-in interactive auto-resume with explicit
handoff behavior. The work closes the remaining session-browsing direction
from #2934; thanks @cy2311 for the original report.
- The bundled contributor-onboarding skill can sync contribution context,
select the appropriate gate, and prepare a digest without inflating the
ambient skill catalog. It follows the contributor-navigation request in
#4227 by @JayBeest.
- Bahasa Indonesia now has a complete repository documentation suite and a
registered website dictionary alongside the shipped TUI locale (PRs #4962
and #4972 by @atmosuwiryo, closing #4789).
- Reasoning content can keep its rail, italics, cursor, and expansion controls
while disabling only the warm background highlight. The independent setting
is persisted and localized (#4089; reported by @elijahchan2019).
- StepFun setup now asks whether a key belongs to PAYG or Step Plan, keeps the
two endpoint/billing routes distinct, and localizes the choice across the
complete packs (#4526; reported by @whp233).
- OpenCode Zen is a separate model-aware API-key provider. Its curated catalog
selects Responses, Anthropic Messages, or Chat Completions per model;
unsupported Gemini and unknown models fail closed, and missing Zen
credentials never fall through to ChatGPT/Codex OAuth guidance. The
implementation from closed PR #4467 by @snail-vs (snailoniu) is preserved in
the candidate.
- Markdown exports correlate prompts with stable workspace restore-point ids
and say when correlation is unavailable or ambiguous, completing the
remaining restoration/export direction from #2494 by @wywsoor.
### Fixed
- Permission setup now consistently presents the product postures Ask,
Auto-Review, and Full Access instead of leaking the internal `never` token.
The same resolved sandbox policy now drives execution and UI receipts: Plan
stays read-only, Ask and Auto-Review stay workspace-scoped, and Full Access
is actually unsandboxed unless a stricter effective configuration wins.
- Fleet setup no longer stalls when a user explicitly selects a configured
Codex or Grok external-consent route. The selected route is activated and
validated before saving, roster roles open directly on their Model step,
Review saves on the first Enter, and new profiles default to the personal
profile directory that the roster loads on the next session.
- Provider credential dialogs now share one wrapping, secret-safe API-key
surface across every non-OAuth provider. A key already present in durable
storage is reported as configured without rendering it, typing or pasting is
clearly framed as replacement, narrow help text remains visible, and Codex
and Grok OAuth flows remain token-free in this modal.
- Ctrl+O again opens the complete recorded reasoning detail for the selected,
active, or latest reasoning block. The whole-turn Turn Inspector moved to
Ctrl+Alt+O and `/turn inspect`, removing the shortcut collision while keeping
raw leaf detail and post-flush reasoning discoverable.
- Failed child agents now deliver a distinct high-priority failure receipt to
their owning parent with a sanitized failure class, elapsed work, and a full
transcript handle. Parent-to-child message, follow-up, and interrupt tools
now use one hierarchy-checked mailbox path, and persisted nested completion
envelopes remain safely restorable across instruction-text revisions.
- Background-shell completion events now carry only bounded tails plus a
retrievable exact-evidence handle. Terminal foreground Bash results are
acknowledged at the direct tool-result boundary and are no longer emitted a
second time as background completion artifacts.
- Providerless Fleet and child-agent fixed-model routes now reject only
high-confidence foreign-provider model ids before creating a worktree, while
explicit provider/model pairs, custom and local endpoints, unknown ids, and
aggregator wire-id resolution retain their intended behavior.
- Manual compaction now preserves and reports the supplied provider failure
class instead of replacing it with an opaque generic error. This does not
infer quota exhaustion when the recorded failure does not prove it.
- The ambient jellyfish keep complete, readable silhouettes while animating,
and the website favicon now uses the Signal Current desktop tile instead of
the legacy whale mark.
- ACP JSON-RPC responses preserve numeric request ids for avante.nvim while
retaining the negotiated string-id exception for Zed (PR #4929 by
@atmosuwiryo).
- Restored shell cells whose job no longer exists stop displaying live
spinners and settle into a truthful stale/no-output state across transcript,
phase strip, and sidebar (PR #4937 by @LI-Jialu, closing #4547).
- Interrupted checkpoints and timed recovery snapshots remain checkpoints
instead of being promoted into orphan session files, preventing duplicate
`/resume` entries (PR #4963 by @SparkofSpike).
- Every shipped locale is admitted by the typed settings schema and native
chooser, with complete/partial status kept independent and tested (PR #4856
by @nightt5879, closing #4786). Context-menu hover hit-testing also accounts
for its title row (PR #4897 by @XhesicaFrost; reported by @SparkofSpike in
#4803).
- OSC 52 and SSH/tmux clipboard transport run on one bounded background worker
rather than blocking input and rendering on the TUI loop; late transport
failures still surface through the status path (PR #4896 by @nightt5879,
closing #4159).
- Non-streaming model calls receive a generation-length response budget rather
than the SSE header-open timeout, while actual SSE opens share the bounded
cross-provider transport seam. The equivalent fix direction came from
closed PR #4743 by @vibecoding-skills.
- Resumed sessions diagnose a deleted inherited workspace before shell launch
instead of failing as an opaque Windows process error (report #4100 by
@redjade75723). DeepSeek native tool-call wrapper tokens are also scrubbed
from visible streaming and completed output as a grounded fail-soft follow-up
to report #3880 by @hardy922; that report's exact emitted marker remained
unconfirmed.
- Auto model routing now preserves the user's requested reasoning effort
through startup, provider/model changes, session restore, the picker,
Ctrl+T, and Hotbar actions. The tier is normalized only after the concrete
provider route is known instead of being silently replaced by Auto
(#4941, PR #4961 by @nightt5879).
- Auto-compaction now defaults on for every known model context window,
including Kimi K3's 1,048,576-token routes. Persisting only an
`auto_compact_threshold` or `auto_compact_threshold_percent` now counts as
opt-in intent, while an explicit `auto_compact = false` remains authoritative.
`/config` reports the effective state, percentage, and computed token trigger.
- Per-provider `context_window` overrides are now documented and visible in
`/config`, provider setup help, diagnostics, and the example configuration.
The effective override consistently drives preflight budgeting, the context
meter, and compaction; this lets a user cap a 1M Kimi route to 256K when their
Coding Plan tier has the smaller window.
- Agent Details now projects status, model, elapsed time, and step counts from
the same row snapshot as the primary agents list, eliminating contradictory
worker state between the two surfaces.
- Composer submission and its hints now share one state machine. Portable
terminals use Enter to queue during a running turn and Enter again to steer;
Ctrl/Cmd+Enter is accepted only when an enhanced terminal reports it and is
no longer advertised as universally available.
- `edit_file` now matches LF-only model search text against CRLF files,
preserves the file's line-ending style for replacement text, and still
rejects newline-normalized duplicate matches as non-unique (#4764).
Implemented in PR #4942 by @nightt5879; reported and root-caused by
@LmeSzinc.
- `/fleet status` read the current TUI session's sub-agents while `codewhale
fleet status` read the durable `.codewhale/fleet.jsonl` ledger — two different
things wearing one name, so a run started by `codewhale fleet run` never
appeared in the TUI. `/fleet status` now reads the durable ledger through the
same code path as the CLI; the session view keeps its own name as
`/fleet workers` (`/subagents` and `n` still work). When a workspace has no
ledger, both surfaces report a typed `no_fleet_ledger` reason instead of an
empty-looking "all clear", and neither creates the ledger as a side effect of
reading it (#4022).
- `codewhale fleet status` (and `list`/`interrupt`/`resume`) created
`.codewhale/fleet.jsonl` as a side effect of opening the manager, then
reported `no_fleet_ledger` for the file it had just made — so the second
invocation showed an empty Fleet where none existed. The CLI now refuses
those verbs before the manager is constructed, matching `/fleet` (#4022).
- `fleet resume <run-id>` accepted any string. An id absent from the ledger
reconciled nothing but still wrote a run-status record keyed by whatever was
typed, and reported `no_change`. Unknown ids are now refused as `not_found`
before any durable write (#4022).
- `lane interrupt` reported `transitioned` even when it changed nothing —
another process's stop looked like our own. The Runtime backend now reports
whether *this* call performed the transition, and a no-op is `no_change`.
The `@<lifecycle-seq>` fence is also evaluated inside the registry's per-Lane
lock rather than before it, so a stale fence refuses without running Runtime
teardown instead of racing between the check and the stop (#1888).
- `/lane` no longer runs Runtime teardown on the TUI composer thread. Reads on
the slash surface skip reconciliation (which probes tmux and takes a lock)
and say so on the receipt instead of implying freshness; `lane interrupt` is
CLI-only until that work runs off-thread, and reports
`surface_not_supported` naming `codewhale lane interrupt` (#4022).
- The hotbar is no longer modelled as a third control surface. A slot binds a
slash command and fires it with no argument, so it runs *as* the slash
surface; the contract now declares which verb a bare press actually reaches
(`hotbar_bare_dispatch`, true only for `lane.list`) instead of advertising
target-taking verbs as hotbar-reachable (#1888).
- `codewhale lane list --json` and `lane status --json` keep emitting the
`LaneRecord` shape they always have — the receipt did not replace it. The
human `lane status` output also regained `branch`, `session`, `socket`,
`attach`, and `log`, which the first cut of the shared DTO had dropped
(#1888).
- No surface advertises a backend it does not have. `lane restart` and
`lane resume` have no implementation — a Lane is re-created by
`codewhale lane start`, and a stopped Lane's Runtime session is gone — so all
three surfaces refuse them with `backend_not_implemented` and say why.
`fleet restart` drives the manager loop to completion, which only the CLI
runs, so `/fleet restart` reports `surface_not_supported` and names the CLI
command rather than quietly doing a smaller thing (#1888).
- Deny rules in `permissions.toml` no longer miss a command because of an
intervening flag: deny matching is token-based with flag-skipping and
backtracking, so a `git push` rule still catches
`git -c foo=bar push`. Path matching folds case only on platforms whose
filesystems are case-insensitive, and the default approval branch no
longer proposes the working directory as a network host.
- MCP tool calls run once. A failed call is no longer retried as if it
were a failed lookup, qualified-name resolution collects every match and
reports an ambiguity instead of taking whichever the hash map yielded
first, and registering a server whose name collides with an existing one
after sanitization is now an error rather than a silent overwrite.
The equivalent call-once fix direction came from closed PR #4756 by
@adity982.
- The session index survives a torn line: an unparseable entry is skipped
rather than aborting the whole read, appends carry their data through to
disk, and appends and compaction share a lock so a compaction can no
longer race an append into a lost record.
- `Edit` counts as a write tool for workflow elevation, and the TUI's
write/shell classification now delegates to one shared allowlist rather
than keeping a second copy that could drift.
- A rejected `app/config/set` stays a no-op. Previously an invalid value
still tore down the cached runtime bridge, killing the child runtime and
orphaning every other in-flight stdio thread behind a response that
correctly reported failure.
- A malformed project `config.toml` is no longer indistinguishable from
having no project config. Because a project config may only *tighten*
approval and sandbox policy, silently discarding a broken one dropped a
repository's restrictions back to the looser user defaults; the setup
wizard now says so, naming the file but never quoting its contents.
- An expired lane worktree no longer leaves its branch behind, which made
reusing the same lane name fail with "branch already exists". A branch
still carrying unmerged commits is kept — a TTL lapsing is not consent
to delete someone's work.
- An in-flight `thread/message` turn can be stopped. The stdio loop keeps
reading while a turn streams, so the new `thread/interrupt` request (and
`shutdown`) can reach a runaway turn instead of waiting on the very turn
they were meant to stop.
- Precedence is stated only in the constitution's "Whose word wins" section.
Memory hygiene no longer ships an inverted Tier list that put the
constitution above the user's current request; approval, compaction, and
personality overlays describe behavior without rank vocabulary; and the
authority recap points at the single source rather than restating a second
ladder.
- `<turn_meta>` carries facts (mode, posture, model, workspace), not mode
doctrine or permission-question essays re-asserted every user message.
- The project context pack (pretty-printed workspace tree) is off by default
and opt-in via `[context] project_pack = true`. Language law is compressed
while keeping the English-constitution / user-language-reply contract.
- Modal lists and config pickers wrap selection at both ends (Down past the
last row returns to the top). Home-directory resolution prefers
`HOME`/`USERPROFILE` via `effective_home_dir` across remaining call sites so
Windows tests that fake the home env vars match production paths. The
equivalent home-directory sweep came from closed PR #4760 by
@EvanProgramming.
### Changed
- Prefix-cache tool catalog entries store only the SHA-256 digest, not the
joined catalog string. Unused plan-transition validation helpers are removed.
- Settings sections now hold only what they claim (#4751). Fleet keeps
Fleet/member concerns; `/goal` moved to a **Session** section and Workflow
orchestration to its own **Workflow** section. The inert DeepSeek-only
`default_model` fallback moved out of Model settings into an explicit
**Legacy** section — exact-Fleet users switch Fleets, not fallback models;
the config field is retained because the runtime still reads it. This is
presentation only: the persisted keys (`goal_command`, `workflow`,
`default_model`), their values, scopes, and runtime behavior are unchanged.
- Auto model routing is scoped to the active provider. The classifier
inventory no longer discloses other providers' runnable routes (or the fact
that their credentials exist), a classifier reply naming another provider is
refused, the local heuristic no longer falls back to a different provider
when the active one is unusable, and the implicit DeepSeek-flash classifier
is skipped for non-DeepSeek sessions. Auto receipts and the model picker
hint report the active-provider-only scope instead of "runnable providers".
Cross-provider Auto is available only through the persisted `[auto]
cross_provider = true` opt-in (an explicit `[auto.router]` route remains its
own opt-in for the classifier call). Same-provider strong/fast selection and
`[auto] cost_saving` are unchanged.
- The QA pseudo-terminal acceptance harness now parses frames with `rio-vt`
behind its existing neutral frame/color surface, retaining the assertions
while removing the `vt100` dependency (PR #4931 by @raphamorim).
- Anthropic Messages and OpenAI Responses stream opening now share the
`client/stream_entry.rs` seam already used by Chat Completions: one bounded
response-header wait, shared dual/HTTP-1.1 policy selection, at most one
HTTP/1.1 fallback on a classified HTTP/2 header stall, and common idle-timeout
diagnostics. Wire-specific authentication, headers, endpoints, decoding, and
rate-limit behavior remain at each adapter edge. The timeout-placement
diagnosis and fix direction came from closed PR #4743 by
@vibecoding-skills.
### Security
- Release containers now publish an SBOM attestation and pin maximum-mode
provenance explicitly so supply-chain metadata cannot silently weaken with a
builder-default change (PR #4958 by @kobihikri).
### Contributors
Thank you to the contributors whose code, reports, and reviews shaped v0.9.2:
- [@greyfreedom](https://github.com/greyfreedom) — exact repository-scoped
allow grants and cross-platform path semantics (PR #4761), plus safe
permission-rule listing and snapshot-bound removal (PR #4960).
- [@nightt5879](https://github.com/nightt5879) — off-event-loop clipboard
writes (PR #4896), complete locale exposure in settings (PR #4856), CRLF-safe
edits (PR #4942), and reasoning-effort preservation across automatic model
routing (PR #4961).
- [@XhesicaFrost](https://github.com/XhesicaFrost) — the configurable
session-token header (PR #4610) and context-menu hover alignment (PR #4897).
- [@cyq1017](https://github.com/cyq1017) — the hooks configuration/executor
split from PR #4087.
- [@snail-vs](https://github.com/snail-vs) (snailoniu) — OpenCode Zen's
model-aware routes, authentication, documentation, and test isolation from
closed PR #4467, whose contributor commits are preserved in the candidate.
- [@SparkofSpike](https://github.com/SparkofSpike) — the zh-Hans translation
quality review harvested from PR #4908, duplicate-session fix in PR #4963,
LaTeX implementation from PR #4973 landed through #4974, and the context-menu
reproduction in #4803.
- [@GTC2080](https://github.com/GTC2080) — the request-preview concept from
PR #1099.
- [@h3c-hexin](https://github.com/h3c-hexin) — non-UTF-8 `fetch_url`
decoding direction from PR #4909.
- [@fleitz](https://github.com/fleitz) — required source-candidate credit for
the canonical `Bash` no-`cwd` workspace fix and regression in PR #4673
(issue #4674).
- [@LmeSzinc](https://github.com/LmeSzinc) — the Windows CRLF `edit_file`
reproduction, root-cause analysis, and affected-code anchors in issue #4764.
- [@atmosuwiryo](https://github.com/atmosuwiryo) — ACP numeric-id compatibility
(PR #4929) and the Indonesian documentation and website locale (PRs #4962
and #4972).
- [@M-Maciej](https://github.com/M-Maciej) — the expanded-by-default reasoning
setting and its original report (PR #4928, issue #4925).
- [@raphamorim](https://github.com/raphamorim) — migration of the QA PTY frame
parser to `rio-vt` (PR #4931).
- [@LI-Jialu](https://github.com/LI-Jialu) — truthful finalization of restored
stale shell cells (PR #4937).
- [@kobihikri](https://github.com/kobihikri) — release-container SBOM and
explicit provenance mode (PR #4958).
- [@EvanProgramming](https://github.com/EvanProgramming),
[@adity982](https://github.com/adity982), and
[@vibecoding-skills](https://github.com/vibecoding-skills) — equivalent fix
direction for the effective-home sweep (#4760), MCP call-once behavior
(#4756), and streaming/non-streaming timeout split (#4743).
- [@antarikshraya](https://github.com/antarikshraya) — the LaTeX transcript
rendering report in #4957.
- [@eugenicum](https://github.com/eugenicum) — the token-header request and
output-presentation measurements in #4520 and #4468.
- [@whp233](https://github.com/whp233) — the StepFun/OpenCode subscription-route
request in #4526.
- [@redjade75723](https://github.com/redjade75723),
[@hardy922](https://github.com/hardy922),
[@JayBeest](https://github.com/JayBeest),
[@elijahchan2019](https://github.com/elijahchan2019),
[@cy2311](https://github.com/cy2311), and
[@wywsoor](https://github.com/wywsoor) — reports and product direction behind
the stale-workspace diagnosis (#4100), native-tool-token filtering (#3880),
contributor onboarding (#4227), optional reasoning highlight (#4089),
session control (#2934), and export/restore correlation (#2494).
---
Older releases: [CHANGELOG.md](https://github.com/codewhale-hq/CodeWhale/blob/main/CHANGELOG.md) and [docs/CHANGELOG_ARCHIVE.md](https://github.com/codewhale-hq/CodeWhale/blob/main/docs/CHANGELOG_ARCHIVE.md).
+58 -58
View File
@@ -80,50 +80,50 @@ path = "src/lib.rs"
doctest = false
[dependencies]
codewhale-app-server-protocol = { path = "../app-server-protocol", version = "0.10.2" }
codewhale-app-server-protocol = { path = "../app-server-protocol", version = "0.10.3" }
# Independent terminal kit, pinned and vendored for reproducible binary builds.
codewhale-ratatui = { path = "../../vendor/codewhale-ratatui", version = "0.2.0" }
codewhale-sanitize = { path = "../sanitize", version = "0.10.2" }
codewhale-memory = { path = "../memory", version = "0.10.2" }
codewhale-sanitize = { path = "../sanitize", version = "0.10.3" }
codewhale-memory = { path = "../memory", version = "0.10.3" }
ahash = "0.8"
anyhow.workspace = true
codewhale-agent = { path = "../agent", version = "0.10.2" }
codewhale-agent-contract = { path = "../agent-contract", version = "0.10.2" }
codewhale-app-server = { path = "../app-server", version = "0.10.2" }
codewhale-engine = { path = "../engine", version = "0.10.2" }
codewhale-config = { path = "../config", version = "0.10.2" }
codewhale-cloud-facts = { path = "../cloud-facts", version = "0.10.2" }
codewhale-command-contract = { path = "../command-contract", version = "0.10.2" }
codewhale-commands = { path = "../commands", version = "0.10.2" }
codewhale-core = { path = "../core", version = "0.10.2" }
codewhale-execpolicy = { path = "../execpolicy", version = "0.10.2" }
codewhale-hooks = { path = "../hooks", version = "0.10.2" }
codewhale-lane = { path = "../lane", version = "0.10.2" }
codewhale-mcp = { path = "../mcp", version = "0.10.2" }
codewhale-models = { path = "../models", version = "0.10.2" }
codewhale-net = { path = "../net", version = "0.10.2" }
codewhale-notify = { path = "../notify", version = "0.10.2" }
codewhale-palette = { path = "../palette", version = "0.10.2" }
codewhale-paths = { path = "../paths", version = "0.10.2" }
codewhale-protocol = { path = "../protocol", version = "0.10.2" }
codewhale-release = { path = "../release", version = "0.10.2" }
codewhale-runtime = { path = "../runtime", version = "0.10.2" }
codewhale-sandbox = { path = "../sandbox", version = "0.10.2" }
codewhale-secrets = { path = "../secrets", version = "0.10.2" }
codewhale-telemetry = { path = "../telemetry", version = "0.10.2" }
codewhale-dependencies = { path = "../dependencies", version = "0.10.2" }
codewhale-lsp = { path = "../lsp", version = "0.10.2" }
codewhale-repl = { path = "../repl", version = "0.10.2" }
codewhale-snapshot = { path = "../snapshot", version = "0.10.2" }
codewhale-plugins = { path = "../plugins", version = "0.10.2" }
codewhale-skill-install = { path = "../skill-install", version = "0.10.2" }
codewhale-skills = { path = "../skills", version = "0.10.2" }
codewhale-util = { path = "../util", version = "0.10.2" }
codewhale-tui-kit = { path = "../tui-kit", version = "0.10.2" }
codewhale-error-taxonomy = { path = "../error-taxonomy", version = "0.10.2" }
codewhale-tools = { path = "../tools", version = "0.10.2" }
codewhale-workflow = { path = "../workflow", version = "0.10.2" }
codewhale-workflow-js = { path = "../workflow-js", version = "0.10.2" }
codewhale-agent = { path = "../agent", version = "0.10.3" }
codewhale-agent-contract = { path = "../agent-contract", version = "0.10.3" }
codewhale-app-server = { path = "../app-server", version = "0.10.3" }
codewhale-engine = { path = "../engine", version = "0.10.3" }
codewhale-config = { path = "../config", version = "0.10.3" }
codewhale-cloud-facts = { path = "../cloud-facts", version = "0.10.3" }
codewhale-command-contract = { path = "../command-contract", version = "0.10.3" }
codewhale-commands = { path = "../commands", version = "0.10.3" }
codewhale-core = { path = "../core", version = "0.10.3" }
codewhale-execpolicy = { path = "../execpolicy", version = "0.10.3" }
codewhale-hooks = { path = "../hooks", version = "0.10.3" }
codewhale-lane = { path = "../lane", version = "0.10.3" }
codewhale-mcp = { path = "../mcp", version = "0.10.3" }
codewhale-models = { path = "../models", version = "0.10.3" }
codewhale-net = { path = "../net", version = "0.10.3" }
codewhale-notify = { path = "../notify", version = "0.10.3" }
codewhale-palette = { path = "../palette", version = "0.10.3" }
codewhale-paths = { path = "../paths", version = "0.10.3" }
codewhale-protocol = { path = "../protocol", version = "0.10.3" }
codewhale-release = { path = "../release", version = "0.10.3" }
codewhale-runtime = { path = "../runtime", version = "0.10.3" }
codewhale-sandbox = { path = "../sandbox", version = "0.10.3" }
codewhale-secrets = { path = "../secrets", version = "0.10.3" }
codewhale-telemetry = { path = "../telemetry", version = "0.10.3" }
codewhale-dependencies = { path = "../dependencies", version = "0.10.3" }
codewhale-lsp = { path = "../lsp", version = "0.10.3" }
codewhale-repl = { path = "../repl", version = "0.10.3" }
codewhale-snapshot = { path = "../snapshot", version = "0.10.3" }
codewhale-plugins = { path = "../plugins", version = "0.10.3" }
codewhale-skill-install = { path = "../skill-install", version = "0.10.3" }
codewhale-skills = { path = "../skills", version = "0.10.3" }
codewhale-util = { path = "../util", version = "0.10.3" }
codewhale-tui-kit = { path = "../tui-kit", version = "0.10.3" }
codewhale-error-taxonomy = { path = "../error-taxonomy", version = "0.10.3" }
codewhale-tools = { path = "../tools", version = "0.10.3" }
codewhale-workflow = { path = "../workflow", version = "0.10.3" }
codewhale-workflow-js = { path = "../workflow-js", version = "0.10.3" }
async-stream = "0.3.6"
async-trait.workspace = true
base64 = "0.23.1"
@@ -202,34 +202,34 @@ sha2.workspace = true
# Reuse the locked cryptographic implementation for exact-call user decisions.
ring = "0.17"
semver.workspace = true
codewhale-localization = { path = "../localization", version = "0.10.2" }
codewhale-localization = { path = "../localization", version = "0.10.3" }
shell-words = "1.1.1"
[build-dependencies]
codewhale-build-support = { path = "../build-support", version = "0.10.2" }
codewhale-build-support = { path = "../build-support", version = "0.10.3" }
[dev-dependencies]
codewhale-engine = { path = "../engine", version = "0.10.2", features = ["test-support"] }
codewhale-skills = { path = "../skills", version = "0.10.2", features = ["test-support"] }
codewhale-tui-kit = { path = "../tui-kit", version = "0.10.2", features = ["test-support"] }
codewhale-dependencies = { path = "../dependencies", version = "0.10.2", features = ["test-support"] }
codewhale-error-taxonomy = { path = "../error-taxonomy", version = "0.10.2", features = ["test-support"] }
codewhale-lsp = { path = "../lsp", version = "0.10.2", features = ["test-support"] }
codewhale-snapshot = { path = "../snapshot", version = "0.10.2", features = ["test-support"] }
codewhale-notify = { path = "../notify", version = "0.10.2", features = ["test-thread-scoped-state"] }
codewhale-config = { path = "../config", version = "0.10.2", features = ["schema", "test-support"] }
codewhale-agent-contract = { path = "../agent-contract", version = "0.10.2", features = [
codewhale-engine = { path = "../engine", version = "0.10.3", features = ["test-support"] }
codewhale-skills = { path = "../skills", version = "0.10.3", features = ["test-support"] }
codewhale-tui-kit = { path = "../tui-kit", version = "0.10.3", features = ["test-support"] }
codewhale-dependencies = { path = "../dependencies", version = "0.10.3", features = ["test-support"] }
codewhale-error-taxonomy = { path = "../error-taxonomy", version = "0.10.3", features = ["test-support"] }
codewhale-lsp = { path = "../lsp", version = "0.10.3", features = ["test-support"] }
codewhale-snapshot = { path = "../snapshot", version = "0.10.3", features = ["test-support"] }
codewhale-notify = { path = "../notify", version = "0.10.3", features = ["test-thread-scoped-state"] }
codewhale-config = { path = "../config", version = "0.10.3", features = ["schema", "test-support"] }
codewhale-agent-contract = { path = "../agent-contract", version = "0.10.3", features = [
"test-support",
] }
codewhale-runtime = { path = "../runtime", version = "0.10.2", features = [
codewhale-runtime = { path = "../runtime", version = "0.10.3", features = [
"test-support",
"test-thread-scoped-state",
] }
codewhale-sandbox = { path = "../sandbox", version = "0.10.2", features = ["test-support"] }
codewhale-plugins = { path = "../plugins", version = "0.10.2", features = ["test-support"] }
codewhale-skill-install = { path = "../skill-install", version = "0.10.2", features = ["test-support"] }
codewhale-util = { path = "../util", version = "0.10.2", features = ["test-support"] }
codewhale-models = { path = "../models", version = "0.10.2", features = [
codewhale-sandbox = { path = "../sandbox", version = "0.10.3", features = ["test-support"] }
codewhale-plugins = { path = "../plugins", version = "0.10.3", features = ["test-support"] }
codewhale-skill-install = { path = "../skill-install", version = "0.10.3", features = ["test-support"] }
codewhale-util = { path = "../util", version = "0.10.3", features = ["test-support"] }
codewhale-models = { path = "../models", version = "0.10.3", features = [
"test-support",
] }
cucumber = "0.23.0"
@@ -1,5 +1,5 @@
{
"message": "codewhale 0.10.2\n\n Route: deepseek · baseline-{provider}-model · reasoning auto\n Directory: <WORKSPACE>\n Project docs: AGENTS.md\n Mode: Work · approvals ask · shell off · workspace\n Safety: no OS sandbox on this platform (workspace-write requested, not enforced), network requested off, not enforced\n MCP: 0 configured\n\n Context window: 0.0% used (48 / 1000000 tokens)\n Window source: catalog · Cloud facts: off\n Window override: [providers.deepseek] context_window in config.toml\n Session: baseline-{model}-session · 0 cells · 0 API messages\n Session tokens: 0 in · 0 out · 1234 total · cache not reported\n Session cost: unknown\n Session metrics: 4 turns\n Tool outputs: no large outputs tracked\n\n Per-turn tokens: /tokens · Footer items: /statusline\n",
"message": "codewhale 0.10.3\n\n Route: deepseek · baseline-{provider}-model · reasoning auto\n Directory: <WORKSPACE>\n Project docs: AGENTS.md\n Mode: Work · approvals ask · shell off · workspace\n Safety: no OS sandbox on this platform (workspace-write requested, not enforced), network requested off, not enforced\n MCP: 0 configured\n\n Context window: 0.0% used (48 / 1000000 tokens)\n Window source: catalog · Cloud facts: off\n Window override: [providers.deepseek] context_window in config.toml\n Session: baseline-{model}-session · 0 cells · 0 API messages\n Session tokens: 0 in · 0 out · 1234 total · cache not reported\n Session cost: unknown\n Session metrics: 4 turns\n Tool outputs: no large outputs tracked\n\n Per-turn tokens: /tokens · Footer items: /statusline\n",
"error": false,
"action": "None"
}
@@ -192,7 +192,7 @@ pub(super) fn plugins(
["enable", selector] => mutate_bundle(presentation, plugin, selector, Mutation::Enable),
["disable", selector] => mutate_bundle(presentation, plugin, selector, Mutation::Disable),
["revoke", selector] => mutate_bundle(presentation, plugin, selector, Mutation::Revoke),
["reload"] => reload(presentation, plugin),
["reload"] => reload(workspace, presentation, plugin),
["doctor"] => doctor(presentation, plugin, false),
["doctor", "--fix"] => doctor(presentation, plugin, true),
["doctor", ..] => CommandResult::error(DOCTOR_USAGE),
@@ -280,6 +280,7 @@ fn translate(presentation: &mut dyn CommandPresentationContext, key: &str) -> St
}
fn reload(
workspace: &Path,
presentation: &mut dyn CommandPresentationContext,
plugin: &mut dyn CommandPluginContext,
) -> CommandResult {
@@ -288,7 +289,13 @@ fn reload(
let message = presentation
.translate(
"cmd_plugin_bundle_reloaded",
&[("count", &count.to_string())],
&[
("count", &count.to_string()),
(
"workspace",
&escape_review_path(workspace, ReviewTextContext::Prose),
),
],
)
.unwrap_or_default();
CommandResult::with_message_and_action(message, AppAction::PluginRegistryChanged)
+16 -16
View File
@@ -2459,13 +2459,13 @@ async fn run_async_main_inner(
if should_arm_before_dispatch {
arm_telemetry(&cli, command.as_ref());
}
let outcome = run_async_main_dispatch(
let outcome = Box::pin(run_async_main_dispatch(
cli,
command,
plugin_discovery,
plugin_registry,
pending_telemetry_notice,
)
))
.await;
finish_telemetry(&outcome, surface).await;
outcome
@@ -3033,14 +3033,14 @@ async fn run_async_main_dispatch(
let config = load_config_from_cli(&cli)?;
let workspace = resolve_workspace(&cli);
let resume_id = resolve_session_id(session_id, last, &workspace)?;
run_interactive(
Box::pin(run_interactive(
&cli,
&config,
Some(resume_id),
None,
pending_telemetry_notice.take(),
std::sync::Arc::clone(&plugin_registry),
)
))
.await
}
Commands::Fork { session_id, last } => {
@@ -3060,7 +3060,7 @@ async fn run_async_main_dispatch(
std::sync::Arc::clone(&plugin_registry),
)
.await?;
run_interactive_prepared(
Box::pin(run_interactive_prepared(
&cli,
prepared,
Some(new_session_id),
@@ -3068,7 +3068,7 @@ async fn run_async_main_dispatch(
None,
pending_telemetry_notice.take(),
std::sync::Arc::clone(&plugin_registry),
)
))
.await
}
};
@@ -3081,14 +3081,14 @@ async fn run_async_main_dispatch(
// one-shot behavior (#2370).
let config = load_config_from_cli(&cli)?;
if let Some(initial_input) = top_level_prompt_initial_input(&cli.prompt) {
return run_interactive(
return Box::pin(run_interactive(
&cli,
&config,
None,
Some(initial_input),
pending_telemetry_notice.take(),
std::sync::Arc::clone(&plugin_registry),
)
))
.await;
}
@@ -3120,7 +3120,7 @@ async fn run_async_main_dispatch(
// Default: Interactive TUI
// --yolo starts in YOLO mode (auto-approve; shell enabled)
run_interactive_with_notice(
Box::pin(run_interactive_with_notice(
&cli,
&config,
resume_session_id,
@@ -3128,7 +3128,7 @@ async fn run_async_main_dispatch(
startup_notice,
pending_telemetry_notice.take(),
plugin_registry,
)
))
.await
}
@@ -13927,7 +13927,7 @@ async fn run_interactive(
pending_telemetry_notice: Option<codewhale_engine::telemetry_notice::PendingTelemetryNotice>,
plugin_registry: std::sync::Arc<codewhale_plugins::PluginRegistry>,
) -> Result<()> {
run_interactive_with_notice(
Box::pin(run_interactive_with_notice(
cli,
config,
resume_session_id,
@@ -13935,7 +13935,7 @@ async fn run_interactive(
None,
pending_telemetry_notice,
plugin_registry,
)
))
.await
}
@@ -13967,7 +13967,7 @@ async fn run_interactive_with_notice(
} else {
(prepared, None)
};
run_interactive_prepared(
Box::pin(run_interactive_prepared(
cli,
prepared,
resume_session_id,
@@ -13975,7 +13975,7 @@ async fn run_interactive_with_notice(
startup_notice,
pending_telemetry_notice,
plugin_registry,
)
))
.await
}
@@ -14157,7 +14157,7 @@ async fn run_interactive_prepared(
// env var (config.yolo), not as a CLI flag. Honour either.
let yolo = cli.yolo || config.yolo.unwrap_or(false);
tui::run_tui(
Box::pin(tui::run_tui(
config,
tui::TuiOptions {
model,
@@ -14184,7 +14184,7 @@ async fn run_interactive_prepared(
},
plugin_registry,
pending_telemetry_notice,
)
))
.await
}
+92 -17
View File
@@ -1,6 +1,6 @@
//! Runtime HTTP/SSE API for local Codewhale automation.
use std::collections::{BTreeMap, BTreeSet, HashMap};
use std::collections::{BTreeMap, BTreeSet};
use std::convert::Infallible;
use std::fs;
use std::net::{IpAddr, SocketAddr};
@@ -253,6 +253,7 @@ struct RuntimeWorkspaceScope {
canonical: PathBuf,
directory: Arc<std::fs::File>,
mcp: Mutex<Option<(u64, Arc<Mutex<McpPool>>)>>,
dynamic_servers: codewhale_engine::mcp::SharedRuntimeMcpServers,
mcp_policy: parking_lot::Mutex<Value>,
lsp: std::sync::OnceLock<Arc<codewhale_lsp::LspManager>>,
owner: SharedRuntimeThreadManager,
@@ -307,8 +308,6 @@ impl Drop for RuntimeWorkspaceScope {
struct RuntimeWorkspaceScopes {
scopes: parking_lot::Mutex<BTreeMap<PathBuf, Arc<RuntimeWorkspaceScope>>>,
mcp_generation: std::sync::atomic::AtomicU64,
dynamic_servers:
Arc<parking_lot::RwLock<HashMap<String, codewhale_mcp::config::McpServerConfig>>>,
owner: SharedRuntimeThreadManager,
workers: SharedSubAgentManager,
cleanup_runtime: tokio::runtime::Handle,
@@ -366,7 +365,6 @@ impl RuntimeWorkspaceScopes {
Arc::new(Self {
scopes: parking_lot::Mutex::new(BTreeMap::new()),
mcp_generation: std::sync::atomic::AtomicU64::new(0),
dynamic_servers: Arc::new(parking_lot::RwLock::new(HashMap::new())),
owner,
workers,
cleanup_runtime: tokio::runtime::Handle::current(),
@@ -455,6 +453,7 @@ impl RuntimeWorkspaceScopes {
canonical,
directory,
mcp: Mutex::new(None),
dynamic_servers: owner.owner.mcp_runtime_servers(&lexical),
mcp_policy: parking_lot::Mutex::new(Value::Null),
lsp: std::sync::OnceLock::new(),
owner: owner.owner.clone(),
@@ -6994,7 +6993,7 @@ fn load_mcp_management_pool(state: &RuntimeApiState) -> Result<(McpPool, Value)>
),
);
}
pool.dynamic_servers = state.workspace_scopes.dynamic_servers.clone();
pool.dynamic_servers = state.workspace_scope.dynamic_servers.clone();
Ok((pool, mcp_pool_policy(&config)))
}
@@ -7051,6 +7050,28 @@ async fn list_mcp_servers(
disabled_tools: server_cfg.disabled_tools.clone(),
});
}
for (name, server_cfg) in state.workspace_scope.dynamic_servers.read().iter() {
if servers.iter().any(|entry| entry.name == *name) {
continue;
}
servers.push(McpServerEntry {
name: name.clone(),
origin: "runtime",
writable: false,
auth_required: pool
.as_ref()
.is_some_and(|pool| pool.server_needs_auth(name)),
enabled: server_cfg.is_enabled(),
required: server_cfg.required,
command: server_cfg.command.clone(),
url: server_cfg.url.clone(),
connected: pool
.as_ref()
.is_some_and(|pool| pool.connected_servers().contains(&name.as_str())),
enabled_tools: server_cfg.enabled_tools.clone(),
disabled_tools: server_cfg.disabled_tools.clone(),
});
}
servers.sort_by(|a, b| a.name.cmp(&b.name));
Ok(Json(McpServersResponse { servers, revision }))
}
@@ -7166,9 +7187,16 @@ async fn get_mcp_server(
Path(name): Path<String>,
) -> Result<Json<McpServerDetail>, ApiError> {
let ((config, origins), revision) = mcp_management_snapshot(state.clone()).await?;
let runtime_cfg = state
.workspace_scope
.dynamic_servers
.read()
.get(&name)
.cloned();
let server_cfg = config
.servers
.get(&name)
.or(runtime_cfg.as_ref())
.ok_or_else(|| ApiError::not_found(format!("MCP server '{name}' not found")))?;
let handle = mcp_pool_handle(&state, false).await?;
let pool = match handle.as_ref() {
@@ -7184,7 +7212,14 @@ async fn get_mcp_server(
.as_ref()
.is_some_and(|pool| pool.connected_servers().contains(&name.as_str()));
let mut detail = McpServerDetail::from_config(&name, server_cfg, connected, revision);
detail.origin = origins.get(&name).copied().unwrap_or("unknown");
detail.origin = origins
.get(&name)
.copied()
.unwrap_or(if runtime_cfg.is_some() {
"runtime"
} else {
"unknown"
});
detail.writable = detail.origin == "global";
detail.auth_required = pool
.as_ref()
@@ -7192,7 +7227,7 @@ async fn get_mcp_server(
Ok(Json(detail))
}
/// `POST /v1/apps/mcp/servers` — add a new server to the persistent config.
/// `POST /v1/apps/mcp/servers` — add a server to its selected config source.
///
/// Body: JSON object with all `McpServerWriteRequest` fields **plus** a
/// required top-level `"name"` string that will be the server key.
@@ -7211,6 +7246,11 @@ async fn create_mcp_server(
return Err(ApiError::bad_request("'name' must not be empty"));
}
let runtime = match body.get("scope") {
None => false,
Some(value) if value.as_str() == Some("runtime") => true,
Some(_) => return Err(ApiError::bad_request("scope must be runtime when supplied")),
};
let req: McpServerWriteRequest = serde_json::from_value(body)
.map_err(|e| ApiError::bad_request(format!("Invalid request body: {e}")))?;
@@ -7228,13 +7268,42 @@ async fn create_mcp_server(
}
let new_cfg = mcp_server_config_from_write_request(req, None);
if runtime {
if state.workspace.as_path() != state.runtime_threads.workspace() {
return Err(ApiError::conflict(
"Desktop browser tools belong to their original desktop workspace",
));
}
let ((effective, _), _) = mcp_management_snapshot(state.clone()).await?;
let handle = mcp_pool_handle(&state, true)
.await?
.ok_or_else(|| ApiError::internal("MCP pool unavailable"))?;
let pool = handle.lock().await;
state
.workspace_scope
.validate()
.await
.map_err(|_| ApiError::conflict("selected workspace identity changed"))?;
if !pool.config_matches(&effective) {
return Err(ApiError::conflict(
"MCP configuration changed; reload it before registering desktop tools",
));
}
pool.add_desktop_browser_server_config(name.clone(), new_cfg.clone())
.map_err(ApiError::bad_request)?;
let mut detail = McpServerDetail::from_config(&name, &new_cfg, false, String::new());
detail.origin = "runtime";
detail.writable = false;
return Ok((StatusCode::CREATED, Json(detail)));
}
let target_name = name.clone();
let (new_cfg, revision) =
mutate_mcp_management(state.clone(), headers, move |state, config| {
if mcp_management_config(state)?
.0
.servers
.contains_key(&target_name)
if config.servers.contains_key(&target_name)
|| mcp_management_config(state)?
.0
.servers
.contains_key(&target_name)
{
return Err(ApiError {
status: StatusCode::CONFLICT,
@@ -7412,11 +7481,17 @@ async fn reconnect_mcp_server(
Path(name): Path<String>,
) -> Result<Json<McpServerActionReceipt>, ApiError> {
let (config, _) = mcp_management_config(&state)?;
if !config.servers.contains_key(&name) {
return Err(ApiError::not_found(format!(
"MCP server '{name}' not found"
)));
}
let runtime_cfg = state
.workspace_scope
.dynamic_servers
.read()
.get(&name)
.cloned();
let server_cfg = config
.servers
.get(&name)
.or(runtime_cfg.as_ref())
.ok_or_else(|| ApiError::not_found(format!("MCP server '{name}' not found")))?;
let handle = mcp_pool_handle(&state, true)
.await?
.ok_or_else(|| ApiError::internal("MCP pool unavailable"))?;
@@ -7426,7 +7501,7 @@ async fn reconnect_mcp_server(
.validate()
.await
.map_err(|_| ApiError::conflict("selected workspace identity changed"))?;
let error = if !config.servers[&name].is_enabled() {
let error = if !server_cfg.is_enabled() {
Some(anyhow::anyhow!("MCP server '{name}' is disabled"))
} else if !pool.config_matches(&config) {
Some(anyhow::anyhow!(
@@ -1113,7 +1113,7 @@ async fn real_owner_two_workspace_services_share_scope_caches_and_settle_global_
let original_pool = original_scope.mcp.lock().await.as_ref().unwrap().1.clone();
let selected_pool = selected_scope.mcp.lock().await.as_ref().unwrap().1.clone();
assert!(!Arc::ptr_eq(&original_pool, &selected_pool));
assert!(Arc::ptr_eq(
assert!(!Arc::ptr_eq(
&original_pool.lock().await.dynamic_servers,
&selected_pool.lock().await.dynamic_servers
));
+1
View File
@@ -1302,6 +1302,7 @@ pub async fn run_tui(
}
app.runtime_services = RuntimeToolServices {
accounting_origin: None,
runtime_mcp_servers: app.runtime_services.runtime_mcp_servers.clone(),
shell_manager: Some(shell_manager),
persist_services_enabled: false,
+6 -6
View File
@@ -16,11 +16,11 @@ test-support = []
[dependencies]
anyhow.workspace = true
chrono.workspace = true
codewhale-config = { path = "../config", version = "0.10.2" }
codewhale-models = { path = "../models", version = "0.10.2" }
codewhale-paths = { path = "../paths", version = "0.10.2" }
codewhale-protocol = { path = "../protocol", version = "0.10.2" }
codewhale-telemetry = { path = "../telemetry", version = "0.10.2" }
codewhale-config = { path = "../config", version = "0.10.3" }
codewhale-models = { path = "../models", version = "0.10.3" }
codewhale-paths = { path = "../paths", version = "0.10.3" }
codewhale-protocol = { path = "../protocol", version = "0.10.3" }
codewhale-telemetry = { path = "../telemetry", version = "0.10.3" }
futures-util = "0.3.31"
ignore = "0.4"
reqwest.workspace = true
@@ -32,7 +32,7 @@ tracing.workspace = true
uuid.workspace = true
[dev-dependencies]
codewhale-runtime = { path = "../runtime", version = "0.10.2", features = ["test-support"] }
codewhale-runtime = { path = "../runtime", version = "0.10.3", features = ["test-support"] }
[target.'cfg(unix)'.dependencies]
libc.workspace = true
+1 -1
View File
@@ -11,7 +11,7 @@ description = "Typed Workflow IR and validation for Codewhale"
workspace = true
[dependencies]
codewhale-protocol = { path = "../protocol", version = "0.10.2" }
codewhale-protocol = { path = "../protocol", version = "0.10.3" }
serde.workspace = true
serde_json.workspace = true
sha2.workspace = true
+1 -1
View File
@@ -23,7 +23,7 @@ there.
Install commands that use `latest` resolve to the latest **published** GitHub
Release or package. Between releases, `main` may already describe the next
version (for example a v0.10.2 source candidate before its tag). A
version (for example a v0.10.3 source candidate before its tag). A
prebuilt candidate is available through the official installer only after its
tag, checksums and release assets exist. Contributors can build `main` now.
+2 -2
View File
@@ -20,7 +20,7 @@
]
},
"sourceCandidate": {
"version": "0.10.2",
"version": "0.10.3",
"providerCount": 50,
"toolCount": 81,
"sandboxBackends": [
@@ -186,7 +186,7 @@
"localRuntime": "runtime, workspace state, and audit log stay on the user's machine",
"hostedProviderBoundary": "the selected hosted provider receives turn context required for inference",
"localInference": "a loopback local-model route can keep inference local",
"telemetry": "Codewhale 0.10.2 counts anonymous usage by default and discloses it at first launch (policy notice version 5, naming Codewhale and PostHog as processors), while the earlier 0.9.11 release asked first; turning it off is a saved choice that later versions keep, an opt-out recorded under the earlier opt-in policy stays off, and showing the notice never records any acceptance on the user's behalf; Codewhale does not collect conversations, code, prompts, files, file/repo/branch names, model content, credentials, or per-turn/per-tool timelines; while on, sessions post aggregate counts and closed enums to the first-party endpoint https://telemetry.codewhale.net/v1/telemetry, whose storage has no IP, country, or geo column and a fixed three-month retention; optional PostHog forwarding requires separate operator configuration and verified IP-safe egress, and its retention is a separate project setting; telemetry_endpoint = \"\" writes only to a local dry-run file; no mandatory hosted relay",
"telemetry": "Codewhale 0.10.3 counts anonymous usage by default and discloses it at first launch (policy notice version 5, naming Codewhale and PostHog as processors), while the earlier 0.9.11 release asked first; turning it off is a saved choice that later versions keep, an opt-out recorded under the earlier opt-in policy stays off, and showing the notice never records any acceptance on the user's behalf; Codewhale does not collect conversations, code, prompts, files, file/repo/branch names, model content, credentials, or per-turn/per-tool timelines; while on, sessions post aggregate counts and closed enums to the first-party endpoint https://telemetry.codewhale.net/v1/telemetry, whose storage has no IP, country, or geo column and a fixed three-month retention; optional PostHog forwarding requires separate operator configuration and verified IP-safe egress, and its retention is a separate project setting; telemetry_endpoint = \"\" writes only to a local dry-run file; no mandatory hosted relay",
"account": "no account required for the local runtime",
"plan": "Plan centrally refuses file mutation and shell execution; policy-allowed research may contact external services and local session state can still be persisted.",
"sandbox": "Seatbelt is used on macOS when available; Linux bubblewrap is on by default once installed and verified working, and prefer_bwrap = false opts out; Windows currently reports no OS sandbox",
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "codewhale-vscode",
"version": "0.10.2",
"version": "0.10.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "codewhale-vscode",
"version": "0.10.2",
"version": "0.10.3",
"license": "MIT",
"devDependencies": {
"@types/node": "^20.19.27",
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "codewhale-vscode",
"displayName": "Codewhale",
"description": "Official Codewhale VS Code extension: agentic chat in the secondary sidebar over the local Engine Runtime API, with editor context, streaming turns, approvals, and terminal parity.",
"version": "0.10.2",
"version": "0.10.3",
"publisher": "codewhale",
"license": "MIT",
"preview": true,
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "codewhale",
"version": "0.10.2",
"codewhaleBinaryVersion": "0.10.2",
"version": "0.10.3",
"codewhaleBinaryVersion": "0.10.3",
"description": "Terminal coding agent for supported hosted and local models. One runtime on your machine. Rust, MIT.",
"author": "Hmbown",
"license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@codewhale/runtime-sdk",
"version": "0.10.2",
"version": "0.10.3",
"description": "JavaScript helpers for Codewhale Runtime Fleet operations and read-only thread events.",
"license": "MIT",
"type": "module",
+2 -2
View File
@@ -38,7 +38,7 @@
}
},
"npm/codewhale": {
"version": "0.10.2",
"version": "0.10.3",
"funding": [
{
"type": "github",
@@ -79,7 +79,7 @@
},
"npm/runtime-sdk": {
"name": "@codewhale/runtime-sdk",
"version": "0.10.2",
"version": "0.10.3",
"license": "MIT",
"devDependencies": {
"typescript": "5.9.3"
+1 -1
View File
@@ -180,7 +180,7 @@ dependencies = [
[[package]]
name = "codewhale-protocol"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"codewhale-app-server-protocol",
"chrono",
+1 -1
View File
@@ -20,7 +20,7 @@
# Uses prebuilt release binaries instead of a Rust build.
set -euo pipefail
RELEASE_TAG="${RELEASE_TAG:-v0.10.2}"
RELEASE_TAG="${RELEASE_TAG:-v0.10.3}"
REPO_URL="${REPO_URL:-https://github.com/codewhale-hq/CodeWhale.git}"
REPO_BRANCH="${REPO_BRANCH:-main}"
SECRETS_FILE="${SECRETS_FILE:-/tmp/cw-secrets.env}"
+1 -1
View File
@@ -108,7 +108,7 @@ dependencies = [
[[package]]
name = "codewhale-protocol"
version = "0.10.2"
version = "0.10.3"
dependencies = [
"codewhale-app-server-protocol",
"chrono",
+1 -1
View File
@@ -36,7 +36,7 @@ include = [
]
[dependencies]
codewhale-protocol = { path = "../../crates/protocol", version = "0.10.2" }
codewhale-protocol = { path = "../../crates/protocol", version = "0.10.3" }
ratatui = { version = "0.30.2", default-features = false, features = ["std", "layout-cache", "unstable-rendered-line-info"] }
# Enable style data directly without resolving optional facade backend packages.
ratatui-core = { version = "0.1.2", default-features = false, features = ["underline-color"] }
+30 -30
View File
@@ -5,8 +5,8 @@
"sources": [
{
"path": "docs/INSTALL.md",
"sha256": "f5ce752143e9672e01d2b6d64a6993a7b538ef3ec27ccf47331853e8cc1256d0",
"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.\nprintf '%s\\n' '# >>> codewhale PATH >>>' 'export PATH=\"$HOME/.local/bin:$PATH\"' '# <<< codewhale 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. Run your first task inside a git repository.\ncd ~/your-project\ncodewhale\n```\n\nIn the TUI, type something concrete:\n\n```text\ncreate a Python file primes.py that prints the first 10 primes, run it, and show me the output\n```\n\nCodewhale writes the file, shows you a diff, then asks **APPROVAL: bash\npython3 primes.py – Do you want to proceed?** Press `y` to allow it once. It\nruns the command and reports the output. Press `Ctrl-D` (with an empty input\nbox) to quit. It prints the command to resume the session later.\n\n> **If nothing happens after you send your first message,** you have no key\n> configured. v0.10.0 doesn't warn you in that case. Press **F3**. If DeepSeek\n> shows `missing key`, press Enter, paste the key, then pick a model and\n> confirm.\n\n---\n\n## Contents\n\n1. [Before you start](#1-before-you-start)\n2. [Install: recommended installer](#2-recommended-installer-curl--sh)\n3. [Install: manual download from GitHub Releases](#3-manual-download-from-github-releases)\n4. [Install: npm](#4-npm)\n5. [Install: Cargo / build from source](#5-cargo-and-building-from-source)\n6. [Install: Homebrew (Linux) and Nix]Line truncated
"sha256": "f94b2ba02a9393c08eae288b844f31e2fe2e736e852f2276d89613811543f40e",
"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.3 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.\nprintf '%s\\n' '# >>> codewhale PATH >>>' 'export PATH=\"$HOME/.local/bin:$PATH\"' '# <<< codewhale 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. Run your first task inside a git repository.\ncd ~/your-project\ncodewhale\n```\n\nIn the TUI, type something concrete:\n\n```text\ncreate a Python file primes.py that prints the first 10 primes, run it, and show me the output\n```\n\nCodewhale writes the file, shows you a diff, then asks **APPROVAL: bash\npython3 primes.py – Do you want to proceed?** Press `y` to allow it once. It\nruns the command and reports the output. Press `Ctrl-D` (with an empty input\nbox) to quit. It prints the command to resume the session later.\n\n> **If nothing happens after you send your first message,** you have no key\n> configured. v0.10.0 doesn't warn you in that case. Press **F3**. If DeepSeek\n> shows `missing key`, press Enter, paste the key, then pick a model and\n> confirm.\n\n---\n\n## Contents\n\n1. [Before you start](#1-before-you-start)\n2. [Install: recommended installer](#2-recommended-installer-curl--sh)\n3. [Install: manual download from GitHub Releases](#3-manual-download-from-github-releases)\n4. [Install: npm](#4-npm)\n5. [Install: Cargo / build from source](#5-cargo-and-building-from-source)\n6. [Install: Homebrew (Linux) and Nix]Line truncated
}
]
},
@@ -16,8 +16,8 @@
"sources": [
{
"path": "docs/GUIDE.md",
"sha256": "25ac37c777ef97d328013af83f742e50c28ae8d07e479f76dfed712d6d7cd1d0",
"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\nNew Codewhale config is stored under `~/.codewhale/config.toml`. Legacy\n`~/.deepseek/config.toml` files are still supported for users migrating from\nthe old name.\n\nUse `/constitution` to review or change standing guidance. After setup, run a\ndoctor check:\n\n```bash\ncodewhale doctor\n```\n\nUse the JSON form when you need a machine-readable report for an issue:\n\n```bash\ncodewhale doctor --json\n```\n\nBoth forms are offline by default. They report structural configuration and\nliteral unknown/not-probed credential states without loading workspace `.env`\ncredentials, openiLine truncated
"sha256": "6838818bff8fb301624104db11c7e46793717e2aecce39d49524e6597e48c8d4",
"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\nNew Codewhale config is stored under `~/.codewhale/config.toml`. Legacy\n`~/.deepseek/config.toml` files are still supported for users migrating from\nthe old name.\n\nUse `/constitution` to review or change standing guidance. After setup, run a\ndoctor check:\n\n```bash\ncodewhale doctor\n```\n\nUse the JSON form when you need a machine-readable report for an issue:\n\n```bash\ncodewhale doctor --json\n```\n\nBoth forms are offline by default. They report structural configuration and\nliteral unknown/not-probed credential states without loading workspace `.env`\ncredentials, openiLine truncated
},
{
"path": "docs/KEYBINDINGS.md",
@@ -32,8 +32,8 @@
"sources": [
{
"path": "docs/CONFIGURATION.md",
"sha256": "e98c48c12a1706233342bb483aea239dabce283fc3851adf10ea0038fd8f93b6",
"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`); Codewhale warns when it finds one it\n did not load.\n\n Codewhale reads one instruction file per directory, from the repository\n root down to the workspace, and puts your global instruction file (such as\n `~/.codewhale/AGENTS.md`) in front of them. The nearest scope reads last\n and wins: the workspace file overrides its parents, and project files\n override the global file. Global and project instructions are budgeted\n separately in estimated tokens (see [Instruction budget](#instruction-budget-instruction_budget-6526)\n below): a scope over its budget drops whole files from the broadest end\n first, and a single file larger than the remaining budget keeps its head\n with a marker that names the bytes and tokens dropped. A file keeps at most\n its first 100 KiB, and a file larger than `file_max_bytesLine truncated
"sha256": "2daa322f15c098aa82593f6ac38429e87a58e64693351ace88497d432080e1ac",
"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`); Codewhale warns when it finds one it\n did not load.\n\n Codewhale reads one instruction file per directory, from the repository\n root down to the workspace, and puts your global instruction file (such as\n `~/.codewhale/AGENTS.md`) in front of them. The nearest scope reads last\n and wins: the workspace file overrides its parents, and project files\n override the global file. Global and project instructions are budgeted\n separately in estimated tokens (see [Instruction budget](#instruction-budget-instruction_budget-6526)\n below): a scope over its budget drops whole files from the broadest end\n first, and a single file larger than the remaining budget keeps its head\n with a marker that names the bytes and tokens dropped. A file keeps at most\n its first 100 KiB, and a file larger than `file_max_bytesLine truncated
},
{
"path": "docs/CODEWHALE_AGENT.md",
@@ -64,8 +64,8 @@
"sources": [
{
"path": "docs/CONFIGURATION.md",
"sha256": "e98c48c12a1706233342bb483aea239dabce283fc3851adf10ea0038fd8f93b6",
"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`); Codewhale warns when it finds one it\n did not load.\n\n Codewhale reads one instruction file per directory, from the repository\n root down to the workspace, and puts your global instruction file (such as\n `~/.codewhale/AGENTS.md`) in front of them. The nearest scope reads last\n and wins: the workspace file overrides its parents, and project files\n override the global file. Global and project instructions are budgeted\n separately in estimated tokens (see [Instruction budget](#instruction-budget-instruction_budget-6526)\n below): a scope over its budget drops whole files from the broadest end\n first, and a single file larger than the remaining budget keeps its head\n with a marker that names the bytes and tokens dropped. A file keeps at most\n its first 100 KiB, and a file larger than `file_max_bytesLine truncated
"sha256": "2daa322f15c098aa82593f6ac38429e87a58e64693351ace88497d432080e1ac",
"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`); Codewhale warns when it finds one it\n did not load.\n\n Codewhale reads one instruction file per directory, from the repository\n root down to the workspace, and puts your global instruction file (such as\n `~/.codewhale/AGENTS.md`) in front of them. The nearest scope reads last\n and wins: the workspace file overrides its parents, and project files\n override the global file. Global and project instructions are budgeted\n separately in estimated tokens (see [Instruction budget](#instruction-budget-instruction_budget-6526)\n below): a scope over its budget drops whole files from the broadest end\n first, and a single file larger than the remaining budget keeps its head\n with a marker that names the bytes and tokens dropped. A file keeps at most\n its first 100 KiB, and a file larger than `file_max_bytesLine truncated
},
{
"path": "docs/LEGACY_PATHS.md",
@@ -80,8 +80,8 @@
"sources": [
{
"path": "docs/MODES.md",
"sha256": "e327b2042c9b782660cb0d6de9f4208364537ef1052a456a45332a61b1ee2468",
"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. Same tools and authority as Work; the difference is how they are used. Each substantive request becomes a goal (`create_goal`), the plan stays visible (`todo_write`), multi-part work runs through `workflow` and parallel `agent` workers, non-trivial changes get an independent reviewer before they are called done, long commands keep running in the background, and recurring or watch-type work is offered as an `automation` (created after approval). It keeps going until the goal is verified, blocked on you, or paused.\n\n`Act` and `/mode act` remain compatibility aliases for Work. Saved settings\nstill normalize to the internal value `agent`.\n\n### Tool availability by mode\n\n| Tool family | Plan | Work | Operate |\n|:---|:---:|:---:|:---:|\n| `read` and policy-allowed deferred research tools | yes | yes | yes |\n| `write` and `edit` | visible names; execution denied | approval- and policy-gated | same as Work |\n| `bash` | visible name; execution denied | approval- and policy-gated | same as Work; delegation is preferred when parallelism or isolation helps |\n| `agent` | yes, subject to child-depth authority | yes, subject to child-depth authority | yes, subject to child-depth authority |\n| Deferred native, MCP, and plugin tools | discoverable through `tool_search` when policy permits | same | same |\n| Paid or external-service tools | follows permission posture | follows permission posture | follows permission posture |\n| Access outside the workspace root | explicit trusted paths only | only through trusted paths or trust mode | same trusted-path/trust policy as Work; fleet profiles never widen it |\n\nOperate changes scheduling emphasis, not authority. It neither adds a\nmode-specific tool denial nor bypasses the active approval, sandbox, shell,\nask-rule, repository-law, or managed-policy boundary. Plan remains the\nmode-specific execution boundary for shell and write-capable tools; that\nauthority difference does not require a different primitive vocabulary.\n\n### Operate loop (one screen)\n\n```text\nUser Line truncated
"sha256": "f07a2fce8d2ef02692fdc9298911ebc99dbd7940af6a3b88d396c039dc7e4608",
"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. Same tools and authority as Work; the difference is how they are used. Each substantive request becomes a goal (`create_goal`), the plan stays visible (`todo_write`), multi-part work runs through `workflow` and parallel `agent` workers, non-trivial changes get an independent reviewer before they are called done, long commands keep running in the background, and recurring or watch-type work is offered as an `automation` (created after approval). It keeps going until the goal is verified, blocked on you, or paused.\n\n`Act` and `/mode act` remain compatibility aliases for Work. Saved settings\nstill normalize to the internal value `agent`.\n\n### Tool availability by mode\n\n| Tool family | Plan | Work | Operate |\n|:---|:---:|:---:|:---:|\n| `read` and policy-allowed deferred research tools | yes | yes | yes |\n| `write` and `edit` | visible names; execution denied | approval- and policy-gated | same as Work |\n| `bash` | visible name; execution denied | approval- and policy-gated | same as Work; delegation is preferred when parallelism or isolation helps |\n| `agent` | yes, subject to child-depth authority | yes, subject to child-depth authority | yes, subject to child-depth authority |\n| Deferred native, MCP, and plugin tools | discoverable through `tool_search` when policy permits | same | same |\n| Paid or external-service tools | follows permission posture | follows permission posture | follows permission posture |\n| Access outside the workspace root | explicit trusted paths only | only through trusted paths or trust mode | same trusted-path/trust policy as Work; fleet profiles never widen it |\n\nOperate changes scheduling emphasis, not authority. It neither adds a\nmode-specific tool denial nor bypasses the active approval, sandbox, shell,\nask-rule, repository-law, or managed-policy boundary. Plan remains the\nmode-specific execution boundary for shell and write-capable tools; that\nauthority difference does not require a different primitive vocabulary.\n\n### Operate loop (one screen)\n\n```text\nUser Line truncated
}
]
},
@@ -96,8 +96,8 @@
},
{
"path": "docs/CONFIGURATION.md",
"sha256": "e98c48c12a1706233342bb483aea239dabce283fc3851adf10ea0038fd8f93b6",
"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`); Codewhale warns when it finds one it\n did not load.\n\n Codewhale reads one instruction file per directory, from the repository\n root down to the workspace, and puts your global instruction file (such as\n `~/.codewhale/AGENTS.md`) in front of them. The nearest scope reads last\n and wins: the workspace file overrides its parents, and project files\n override the global file. Global and project instructions are budgeted\n separately in estimated tokens (see [Instruction budget](#instruction-budget-instruction_budget-6526)\n below): a scope over its budget drops whole files from the broadest end\n first, and a single file larger than the remaining budget keeps its head\n with a marker that names the bytes and tokens dropped. A file keeps at most\n its first 100 KiB, and a file larger than `file_max_bytesLine truncated
"sha256": "2daa322f15c098aa82593f6ac38429e87a58e64693351ace88497d432080e1ac",
"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`); Codewhale warns when it finds one it\n did not load.\n\n Codewhale reads one instruction file per directory, from the repository\n root down to the workspace, and puts your global instruction file (such as\n `~/.codewhale/AGENTS.md`) in front of them. The nearest scope reads last\n and wins: the workspace file overrides its parents, and project files\n override the global file. Global and project instructions are budgeted\n separately in estimated tokens (see [Instruction budget](#instruction-budget-instruction_budget-6526)\n below): a scope over its budget drops whole files from the broadest end\n first, and a single file larger than the remaining budget keeps its head\n with a marker that names the bytes and tokens dropped. A file keeps at most\n its first 100 KiB, and a file larger than `file_max_bytesLine truncated
}
]
},
@@ -134,8 +134,8 @@
"sources": [
{
"path": "docs/SUBAGENTS.md",
"sha256": "0518a808fc4e154edb6f8c9d6eb346bc0654a5c3a3cda53ad4c0db1ea2fb8e36",
"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/engine/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` | analyse and produce a strategy | nLine truncated
"sha256": "8fdaef0ecf37457e4b6e3a0d84f64ed21963ef7c6c8a17bd2fb5efb3d7004acd",
"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/engine/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` | analyse and produce a strategy | nLine truncated
}
]
},
@@ -187,13 +187,13 @@
},
{
"path": "docs/TELEMETRY.md",
"sha256": "0d337a0d48171a399db56bbb6a3eb38b8329484aa34791cb9ef230e26d415291",
"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 = \"\"`: batches are then\nappended to `$CODEWHALE_HOME/telemetry/dryrun.jsonl` on your own machine, byte\nfor byte what the server would have received, and no HTTP client is ever\nconstructed. That file is how you audit this document against reality.\n\nThis document is the schema. It is not a summary of the schema: a test in\n`crates/telemetry` parses the field names out of this file and asserts set\nequality against the structs the serializer actually uses, so a field that is\nhere and not in the code — or in the code and not here — fails the build.\n\nLocal saved sessions and `codewhale metrics` remain available with telemetry\ndisabled and require no account or hosted reporting service. Disabling telemetry\ndoes not disable local usage receipts.\n\n## Turning it off\n\nThere are two off switches and they do different things. Both stop collection\ncompletely; only one of them erases anything.\n\n```sh\ncodewhale config set telemetry false # opt out: stops collection and erases state\nCODEWHALE_TELEMETRY=0 codewhale # kill switch: stops collection, erases nothing\nDO_NOT_TRACK=1 codewhale # the same kill switch, by the common convention\ncodewhale --telemetry false # the same kill switch, for one command\n```\n\n**`telemetry = false` in the config file is the opt-out.** It is a floor:\n`--telemetry true` and `CODEWHALE_TELEMETRY=1` both lose to it, because a\nsetting you can undo by accident from a wrapper script is not a setting. It\ndeletes the random install id, truncates every buffered event and every dry-run\nrecord, and writes a tombstone. Appends, identity/state writes, and delivery all\nshare the wipe's ordering lock, so once opt-out returns no pre-opt-out write or\nPOST remains in flight. If any part of that wipe fails, the tombstone is still\nthere and the buffer is undrainable — a failed wipe fails closed. Every later\nrun re-asserts the same tombstone for as long as the setting stands, so it\nsurvives. Turning telemetry back on in `/settings` records that explicit preference\nin both existing privacy registers; a fresh launch can then clear the tombstone. Nothing buffered before that point is\never sent.\n\n**The environment variable and the flag are kill switches, not opt-outs.**\nTelemetry is off for the run, nothing is written, nothing is sent — and\nnothing on disk is touched or deleted. That is deliberate: a harness or agent\nthat sets `CODEWHALE_TELEMETRY=0` for one command must not silently discard the\ninstall id and the dry-run records of the person who owns the machine. If you\nwant the erasing kind, use the config file.\n\n`CODEWHALE_TELEMELine truncated
"sha256": "1f7e39e5d776fe8c08b28b10cd5bd797c863c0901b4806393aa3f4876b022d2a",
"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 = \"\"`: batches are then\nappended to `$CODEWHALE_HOME/telemetry/dryrun.jsonl` on your own machine, byte\nfor byte what the server would have received, and no HTTP client is ever\nconstructed. That file is how you audit this document against reality.\n\nThis document is the schema. It is not a summary of the schema: a test in\n`crates/telemetry` parses the field names out of this file and asserts set\nequality against the structs the serializer actually uses, so a field that is\nhere and not in the code — or in the code and not here — fails the build.\n\nLocal saved sessions and `codewhale metrics` remain available with telemetry\ndisabled and require no account or hosted reporting service. Disabling telemetry\ndoes not disable local usage receipts.\n\n## Turning it off\n\nThere are two off switches and they do different things. Both stop collection\ncompletely; only one of them erases anything.\n\n```sh\ncodewhale config set telemetry false # opt out: stops collection and erases state\nCODEWHALE_TELEMETRY=0 codewhale # kill switch: stops collection, erases nothing\nDO_NOT_TRACK=1 codewhale # the same kill switch, by the common convention\ncodewhale --telemetry false # the same kill switch, for one command\n```\n\n**`telemetry = false` in the config file is the opt-out.** It is a floor:\n`--telemetry true` and `CODEWHALE_TELEMETRY=1` both lose to it, because a\nsetting you can undo by accident from a wrapper script is not a setting. It\ndeletes the random install id, truncates every buffered event and every dry-run\nrecord, and writes a tombstone. Appends, identity/state writes, and delivery all\nshare the wipe's ordering lock, so once opt-out returns no pre-opt-out write or\nPOST remains in flight. If any part of that wipe fails, the tombstone is still\nthere and the buffer is undrainable — a failed wipe fails closed. Every later\nrun re-asserts the same tombstone for as long as the setting stands, so it\nsurvives. Turning telemetry back on in `/settings` records that explicit preference\nin both existing privacy registers; a fresh launch can then clear the tombstone. Nothing buffered before that point is\never sent.\n\n**The environment variable and the flag are kill switches, not opt-outs.**\nTelemetry is off for the run, nothing is written, nothing is sent — and\nnothing on disk is touched or deleted. That is deliberate: a harness or agent\nthat sets `CODEWHALE_TELEMETRY=0` for one command must not silently discard the\ninstall id and the dry-run records of the person who owns the machine. If you\nwant the erasing kind, use the config file.\n\n`CODEWHALE_TELEMELine truncated
},
{
"path": "docs/public-surface-facts.json",
"sha256": "77046f5692985d812fa2816d2c6e50be0da81fdd5fea1b72a3dff9c56425e70d",
"content": "{\n \"schemaVersion\": 2,\n \"product\": {\n \"name\": \"Codewhale\",\n \"description\": \"Terminal coding agent for supported hosted and local models. One runtime on your machine. Rust, MIT.\",\n \"positioning\": \"Terminal-native, model/provider-neutral, local-first coding agent\",\n \"license\": \"MIT\",\n \"terminology\": {\n \"Fleet\": \"the user's model inventory: who is in the roster and which member is selected\",\n \"Workflow\": \"what order the work follows\",\n \"Lane\": \"one running Workflow instance\",\n \"Runtime\": \"where, how, and with what authority selected work executes\"\n },\n \"sources\": [\n \"Cargo.toml\",\n \"LICENSE\",\n \"README.md\",\n \"npm/codewhale/package.json\",\n \"docs/FLEET.md\"\n ]\n },\n \"sourceCandidate\": {\n \"version\": \"0.10.2\",\n \"providerCount\": 50,\n \"toolCount\": 81,\n \"sandboxBackends\": [\n \"seatbelt (macOS, when available)\",\n \"bubblewrap (Linux, default when installed and working)\"\n ],\n \"sources\": [\n \"Cargo.toml\",\n \"crates/runtime/src/config.rs\",\n \"crates/sandbox/src/lib.rs\",\n \"web/scripts/derive-facts.mjs\"\n ],\n \"providerCountDefinition\": \"Website descriptor identities in this source snapshot, excluding Custom and retired identities; includes protocol/plan variants. Not a count of distinct vendors, ProviderKind identities, live catalogs, or providers in any published release.\"\n },\n \"latestPublishedRelease\": {\n \"tag\": \"v0.10.2\",\n \"version\": \"0.10.2\",\n \"publishedAt\": \"2026-10-24T17:31:14Z\",\n \"url\": \"https://github.com/codewhale-hq/CodeWhale/releases/tag/v0.10.2\",\n \"sources\": [\n \"web/data/latest-published-release.json\"\n ]\n },\n \"install\": {\n \"recommended\": \"curl -fsSL https://codewhale.net/install.sh | sh\",\n \"binaries\": [\n \"codewhale\",\n \"codew\"\n ],\n \"channels\": {\n \"npm\": \"published releases only\",\n \"cargo\": \"published crates only\",\n \"prebuiltArchives\": \"published GitHub Releases only\",\n \"cnbMirror\": \"documented targets only\"\n },\n \"androidTermux\": {\n \"status\": \"preview\",\n \"npm\": \"preview\",\n \"requiresMatchingPublishedAssets\": true,\n \"sourceBuild\": true\n },\n \"sources\": [\n \"README.md\",\n \"npm/codewhale/package.json\",\n \"npm/codewhale/README.md\",\n \"npm/codewhale/scripts/artifacts.js\",\n \"docs/INSTALL.md\",\n \"docs/CNB_MIRROR.md\"\n ]\n },\n \"control\": {\n \"modes\": [\n \"Plan\",\n \"Work\",\n \"Operate\"\n ],\n \"permissionPostures\": [\n \"Ask\",\n \"Auto-Review\",\n \"Full Access\"\n ],\n \"shortcuts\": {\n \"mode\": {\n \"chord\": \"Tab\",\n \"when\": \"composer empty\"\n },\n \"permissionPosture\": {\n \"chord\": \"Shift+Tab\",\n \"when\": \"always (suppressed only under a non-Config modal)\"\n }\n },\n \"sources\": [\n \"docs/MODES.md\",\n \"docs/KEYBINDINGS.md\",\n \"crates/runtime/src/settings.rs\"\n ]\n },\n \"toolSurface\": {\n \"defaultActive\": [\n \"read\",\n \"write\",\n \"edit\",\n \"file_search\",\n \"bash\",\n \"agent\",\n \"workflow\",\n \"todo_write\",\n \"create_goal\",\n \"get_goal\",\n \"update_goal\",\n \"task_shell_wait\",\n \"load_skill\",\n \"context_archive_get\",\n \"context_archive_search\"\n ],\n \"schemas\": {\n \"read\": [\n \"path\",\n \"offset?\",\n \"limit?\"\n ],\n \"write\": [\n \"path\",\n \"content\"\n ],\n \"edit\": [\n \"path\",\n \"edits\"\n ],\n \"bash\": [\n \"command\",\n \"timeout?\"\n ]\n },\n \"deferred\": {\n \"Web\": [\n \"search\",\n \"fetch\",\n \"wait\"\n ]\n },\n \"compatibility\": {\n \"legacyAliases\": \"hidden-exact\",\n \"modelVisible\": false,\n \"toolSearchDiscoverable\": false\n },\n \"activationCache\": {\n \"maximumNames\": 8,\n \"maximumSchemaBytes\": 16384,\n \"scope\": \"per conversation; each subagent owns an independent policy-filtered cache\"\n },\n \"agentConcurrency\": {\n \"defaultConfigured\": 64,\n \"maximumConfigured\": 128,\n \"maximumAdmitted\": 1024\n },\n \"sources\": [\n \"docs/TOOL_SURFACE.md\",\n \"docs/RUNTIME_SIMPLIFICATION_DESIGN.md\",\n \"crates/engine/src/core/engine/tool_catalog.rs\",\n \"crates/engine/src/tools/registry.rs\",\n \"crates/runtime/src/config/subagent_limits.rs\"\n ]\n },\n \"surfaces\": {\n \"availableInSourceCandidate\": [\n \"TUI\",\n \"codewhale exec\",\n \"Web client\",\n \"Runtime ALine truncated
"sha256": "17575482f66e4fd9bb0302fe33bdd7aa2c6bfeca81b14d4b169c93aed6b9e4c3",
"content": "{\n \"schemaVersion\": 2,\n \"product\": {\n \"name\": \"Codewhale\",\n \"description\": \"Terminal coding agent for supported hosted and local models. One runtime on your machine. Rust, MIT.\",\n \"positioning\": \"Terminal-native, model/provider-neutral, local-first coding agent\",\n \"license\": \"MIT\",\n \"terminology\": {\n \"Fleet\": \"the user's model inventory: who is in the roster and which member is selected\",\n \"Workflow\": \"what order the work follows\",\n \"Lane\": \"one running Workflow instance\",\n \"Runtime\": \"where, how, and with what authority selected work executes\"\n },\n \"sources\": [\n \"Cargo.toml\",\n \"LICENSE\",\n \"README.md\",\n \"npm/codewhale/package.json\",\n \"docs/FLEET.md\"\n ]\n },\n \"sourceCandidate\": {\n \"version\": \"0.10.3\",\n \"providerCount\": 50,\n \"toolCount\": 81,\n \"sandboxBackends\": [\n \"seatbelt (macOS, when available)\",\n \"bubblewrap (Linux, default when installed and working)\"\n ],\n \"sources\": [\n \"Cargo.toml\",\n \"crates/runtime/src/config.rs\",\n \"crates/sandbox/src/lib.rs\",\n \"web/scripts/derive-facts.mjs\"\n ],\n \"providerCountDefinition\": \"Website descriptor identities in this source snapshot, excluding Custom and retired identities; includes protocol/plan variants. Not a count of distinct vendors, ProviderKind identities, live catalogs, or providers in any published release.\"\n },\n \"latestPublishedRelease\": {\n \"tag\": \"v0.10.2\",\n \"version\": \"0.10.2\",\n \"publishedAt\": \"2026-10-24T17:31:14Z\",\n \"url\": \"https://github.com/codewhale-hq/CodeWhale/releases/tag/v0.10.2\",\n \"sources\": [\n \"web/data/latest-published-release.json\"\n ]\n },\n \"install\": {\n \"recommended\": \"curl -fsSL https://codewhale.net/install.sh | sh\",\n \"binaries\": [\n \"codewhale\",\n \"codew\"\n ],\n \"channels\": {\n \"npm\": \"published releases only\",\n \"cargo\": \"published crates only\",\n \"prebuiltArchives\": \"published GitHub Releases only\",\n \"cnbMirror\": \"documented targets only\"\n },\n \"androidTermux\": {\n \"status\": \"preview\",\n \"npm\": \"preview\",\n \"requiresMatchingPublishedAssets\": true,\n \"sourceBuild\": true\n },\n \"sources\": [\n \"README.md\",\n \"npm/codewhale/package.json\",\n \"npm/codewhale/README.md\",\n \"npm/codewhale/scripts/artifacts.js\",\n \"docs/INSTALL.md\",\n \"docs/CNB_MIRROR.md\"\n ]\n },\n \"control\": {\n \"modes\": [\n \"Plan\",\n \"Work\",\n \"Operate\"\n ],\n \"permissionPostures\": [\n \"Ask\",\n \"Auto-Review\",\n \"Full Access\"\n ],\n \"shortcuts\": {\n \"mode\": {\n \"chord\": \"Tab\",\n \"when\": \"composer empty\"\n },\n \"permissionPosture\": {\n \"chord\": \"Shift+Tab\",\n \"when\": \"always (suppressed only under a non-Config modal)\"\n }\n },\n \"sources\": [\n \"docs/MODES.md\",\n \"docs/KEYBINDINGS.md\",\n \"crates/runtime/src/settings.rs\"\n ]\n },\n \"toolSurface\": {\n \"defaultActive\": [\n \"read\",\n \"write\",\n \"edit\",\n \"file_search\",\n \"bash\",\n \"agent\",\n \"workflow\",\n \"todo_write\",\n \"create_goal\",\n \"get_goal\",\n \"update_goal\",\n \"task_shell_wait\",\n \"load_skill\",\n \"context_archive_get\",\n \"context_archive_search\"\n ],\n \"schemas\": {\n \"read\": [\n \"path\",\n \"offset?\",\n \"limit?\"\n ],\n \"write\": [\n \"path\",\n \"content\"\n ],\n \"edit\": [\n \"path\",\n \"edits\"\n ],\n \"bash\": [\n \"command\",\n \"timeout?\"\n ]\n },\n \"deferred\": {\n \"Web\": [\n \"search\",\n \"fetch\",\n \"wait\"\n ]\n },\n \"compatibility\": {\n \"legacyAliases\": \"hidden-exact\",\n \"modelVisible\": false,\n \"toolSearchDiscoverable\": false\n },\n \"activationCache\": {\n \"maximumNames\": 8,\n \"maximumSchemaBytes\": 16384,\n \"scope\": \"per conversation; each subagent owns an independent policy-filtered cache\"\n },\n \"agentConcurrency\": {\n \"defaultConfigured\": 64,\n \"maximumConfigured\": 128,\n \"maximumAdmitted\": 1024\n },\n \"sources\": [\n \"docs/TOOL_SURFACE.md\",\n \"docs/RUNTIME_SIMPLIFICATION_DESIGN.md\",\n \"crates/engine/src/core/engine/tool_catalog.rs\",\n \"crates/engine/src/tools/registry.rs\",\n \"crates/runtime/src/config/subagent_limits.rs\"\n ]\n },\n \"surfaces\": {\n \"availableInSourceCandidate\": [\n \"TUI\",\n \"codewhale exec\",\n \"Web client\",\n \"Runtime ALine truncated
},
{
"path": "docs/PREVIEW_REQUEST.md",
@@ -224,8 +224,8 @@
},
{
"path": "docs/CONFIGURATION.md",
"sha256": "e98c48c12a1706233342bb483aea239dabce283fc3851adf10ea0038fd8f93b6",
"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`); Codewhale warns when it finds one it\n did not load.\n\n Codewhale reads one instruction file per directory, from the repository\n root down to the workspace, and puts your global instruction file (such as\n `~/.codewhale/AGENTS.md`) in front of them. The nearest scope reads last\n and wins: the workspace file overrides its parents, and project files\n override the global file. Global and project instructions are budgeted\n separately in estimated tokens (see [Instruction budget](#instruction-budget-instruction_budget-6526)\n below): a scope over its budget drops whole files from the broadest end\n first, and a single file larger than the remaining budget keeps its head\n with a marker that names the bytes and tokens dropped. A file keeps at most\n its first 100 KiB, and a file larger than `file_max_bytesLine truncated
"sha256": "2daa322f15c098aa82593f6ac38429e87a58e64693351ace88497d432080e1ac",
"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`); Codewhale warns when it finds one it\n did not load.\n\n Codewhale reads one instruction file per directory, from the repository\n root down to the workspace, and puts your global instruction file (such as\n `~/.codewhale/AGENTS.md`) in front of them. The nearest scope reads last\n and wins: the workspace file overrides its parents, and project files\n override the global file. Global and project instructions are budgeted\n separately in estimated tokens (see [Instruction budget](#instruction-budget-instruction_budget-6526)\n below): a scope over its budget drops whole files from the broadest end\n first, and a single file larger than the remaining budget keeps its head\n with a marker that names the bytes and tokens dropped. A file keeps at most\n its first 100 KiB, and a file larger than `file_max_bytesLine truncated
}
]
},
@@ -256,8 +256,8 @@
},
{
"path": "docs/CONFIGURATION.md",
"sha256": "e98c48c12a1706233342bb483aea239dabce283fc3851adf10ea0038fd8f93b6",
"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`); Codewhale warns when it finds one it\n did not load.\n\n Codewhale reads one instruction file per directory, from the repository\n root down to the workspace, and puts your global instruction file (such as\n `~/.codewhale/AGENTS.md`) in front of them. The nearest scope reads last\n and wins: the workspace file overrides its parents, and project files\n override the global file. Global and project instructions are budgeted\n separately in estimated tokens (see [Instruction budget](#instruction-budget-instruction_budget-6526)\n below): a scope over its budget drops whole files from the broadest end\n first, and a single file larger than the remaining budget keeps its head\n with a marker that names the bytes and tokens dropped. A file keeps at most\n its first 100 KiB, and a file larger than `file_max_bytesLine truncated
"sha256": "2daa322f15c098aa82593f6ac38429e87a58e64693351ace88497d432080e1ac",
"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`); Codewhale warns when it finds one it\n did not load.\n\n Codewhale reads one instruction file per directory, from the repository\n root down to the workspace, and puts your global instruction file (such as\n `~/.codewhale/AGENTS.md`) in front of them. The nearest scope reads last\n and wins: the workspace file overrides its parents, and project files\n override the global file. Global and project instructions are budgeted\n separately in estimated tokens (see [Instruction budget](#instruction-budget-instruction_budget-6526)\n below): a scope over its budget drops whole files from the broadest end\n first, and a single file larger than the remaining budget keeps its head\n with a marker that names the bytes and tokens dropped. A file keeps at most\n its first 100 KiB, and a file larger than `file_max_bytesLine truncated
}
]
},
@@ -288,8 +288,8 @@
"sources": [
{
"path": "docs/RUNTIME_API.md",
"sha256": "7f8459a670b0966689882d189201580a23381fb69ec75930748a83720e9da1e2",
"content": "# Codewhale Engine Runtime API & Integration Contract\n\n> 阅读简体中文版:[zh_hans/RUNTIME_API.md](zh_hans/RUNTIME_API.md)。\n\n`codewhale app-server --http` exposes the [Codewhale Engine](ARCHITECTURE.md) through\nits canonical local Runtime API and control plane.\nLocal SDKs, mobile/remote-control clients, and editor integrations talk to it\ninstead of screen-scraping terminal output. It serves the full HTTP/SSE runtime\nAPI (`/v1/*`), a JSON-RPC control transport over stdio, and the phone-friendly\nmobile page. `codewhale doctor --json` provides machine-readable health, and\n`codewhale serve --acp` speaks the Agent Client Protocol over stdio for editors\nsuch as Zed.\n\n`codewhale serve --http` / `serve --mobile` remain as **compatibility aliases**\nfor `codewhale app-server --http` / `--mobile`; both launch the identical\nserver. Desktop, web, SDK and custom integrations should target\n`app-server --http`. Bare `app-server`, `--stdio` and `--socket` select smaller\ncompatibility interfaces, not the full Engine API.\n\n`codewhale exec` is the separate one-shot headless worker path (stream-json,\nfleet worker subprocess, CI primitive). It is not part of this API, but it\nshares the same runtime, provider/model resolution, permission profiles, and\nevent vocabulary.\n\nThis document is the stable integration contract for native workbench\napplications (and other local supervisors) that embed the Codewhale Engine.\n\n## Architecture\n\n```\nlocal supervisor / SDK / automation harness\n │\n ├─ codewhale app-server --http → HTTP/SSE runtime API (/v1/*) [canonical]\n ├─ codewhale app-server --mobile → runtime API + mobile control page\n ├─ codewhale app-server --stdio → JSON-RPC control transport over stdio\n ├─ codewhale app-server --socket → same JSON-RPC over a unix domain socket (desktop daemon)\n ├─ codewhale doctor --json → machine-readable health & capability\n ├─ codewhale serve --acp → ACP stdio agent for editors such as Zed\n ├─ codewhale serve --mcp → MCP stdio server\n ├─ codewhale serve --http/--mobile → legacy aliases for `app-server --http/--mobile`\n └─ codewhale exec [args] → one-shot headless worker (stream-json)\n```\n\nThe engine runs as a local-only process. All APIs bind to `localhost` by\ndefault. No hosted relay, no provider-token custody, no secret leakage.\n\nFor the read-only record of what a thread or turn did, see\n[`docs/RECEIPTS.md`](RECEIPTS.md): `codewhale receipts` on the CLI and the\n`/receipt` routes under **Threads** below.\n\n## Runtime API entrypoints\n\n| Entry | Transport | Use |\n|---|---|---|\n| `codewhale web [--port 7878]` | HTTP/SSE on `127.0.0.1:7878` + embedded client | First-class loopback-only browser client; opens the default browser |\n| `codewhale app-server --http` | HTTP/SSE on `127.0.0.1:7878` | Full `/v1/*` runtime API (canonical) |\n| `codewhale app-server --mobile` | HTTP/SSE on loopback + `/mobile` | Runtime API + local mobile control page |\n| `codewhale app-server --stdio` | JSON-RPC 2.0 over stdio | Compatibility controls and explicit tool-free turns; no full Engine event or approval/input channel. |\n| `codewhale app-server --socket [--socket-path P]` | JSON-RPC 2.0 over a `0600` Unix domain socket (macOS/Linux) or an owner-only named pipe (Windows) | Desktop daemon: multi-client, kernel peer identity checked, `daemon/attach` claim handshake. Windows native-app qualification remains open. |\n| `codewhale app-server` | HTTP on `127.0.0.1:8787` | Compatibility `/healthz`, `/thread`, `/app`, `/prompt`. Turn requests require explicit `allowed_tools: []`; interactive execution fails before a turn starts. Use `app-server --http` for tools, approvals, clarification, goal execution, steering and the full event journal. |\n| `codewhale serve --http` / `--mobile` | same server as `app-server --http`/`--mobile` | Compatibility aliases |\n\n`app-server --http` and `--mobile` launch the same mature runtime API server\nhistorically reached through `serve --http` — no routes or behavior changed, so\nevery endpoint documented below is identical across both entrypoints. The\nruntime API token is read from `--auth-token`, then `CODEWHALE_RUNTIME_TOKEN`,\nthen `DEEPSEEK_RUNTIME_TOKEN`; use `--insecure-no-auth` only with a loopback\nbind. The `serve` compatibility aliases keep their `--insecure` flag.\nThe legacy in-process `codewhale app-server` also requires an explicit\n`--auth-token` or `CODEWHALE_APP_SERVER_TOKEN` before binding a non-loopback\nhost; its generated one-time `cwapp_*` token is loopback-only.\n\nDevice tokens minted by the master through `POST /v1/auth/client-tokens`\nhave an immutable `intent`: `watch` (the default when omitted) or explicit\n`drive`. Labels do not grant authority. Watch permits ordinary GET/HEAD reads,\nComputer displayLine truncated
"sha256": "72d25df954654f13643c20a48ec5429489d7cf1311bb259035187602c9b62fb6",
"content": "# Codewhale Engine Runtime API & Integration Contract\n\n> 阅读简体中文版:[zh_hans/RUNTIME_API.md](zh_hans/RUNTIME_API.md)。\n\n`codewhale app-server --http` exposes the [Codewhale Engine](ARCHITECTURE.md) through\nits canonical local Runtime API and control plane.\nLocal SDKs, mobile/remote-control clients, and editor integrations talk to it\ninstead of screen-scraping terminal output. It serves the full HTTP/SSE runtime\nAPI (`/v1/*`), a JSON-RPC control transport over stdio, and the phone-friendly\nmobile page. `codewhale doctor --json` provides machine-readable health, and\n`codewhale serve --acp` speaks the Agent Client Protocol over stdio for editors\nsuch as Zed.\n\n`codewhale serve --http` / `serve --mobile` remain as **compatibility aliases**\nfor `codewhale app-server --http` / `--mobile`; both launch the identical\nserver. Desktop, web, SDK and custom integrations should target\n`app-server --http`. `--stdio` and `--socket` expose compatibility controls.\nThe current bare `app-server` HTTP listener serves compatibility routes and\nthe full `/v1/*` router through the same Runtime owner.\n\n`codewhale exec` is the separate one-shot headless worker path (stream-json,\nfleet worker subprocess, CI primitive). It is not part of this API, but it\nshares the same runtime, provider/model resolution, permission profiles, and\nevent vocabulary.\n\nThis document is the stable integration contract for native workbench\napplications (and other local supervisors) that embed the Codewhale Engine.\n\n## Architecture\n\n```\nlocal supervisor / SDK / automation harness\n │\n ├─ codewhale app-server --http → HTTP/SSE runtime API (/v1/*) [canonical]\n ├─ codewhale app-server --mobile → runtime API + mobile control page\n ├─ codewhale app-server --stdio → JSON-RPC control transport over stdio\n ├─ codewhale app-server --socket → same JSON-RPC over a unix domain socket (desktop daemon)\n ├─ codewhale doctor --json → machine-readable health & capability\n ├─ codewhale serve --acp → ACP stdio agent for editors such as Zed\n ├─ codewhale serve --mcp → MCP stdio server\n ├─ codewhale serve --http/--mobile → legacy aliases for `app-server --http/--mobile`\n └─ codewhale exec [args] → one-shot headless worker (stream-json)\n```\n\nThe engine runs as a local-only process. All APIs bind to `localhost` by\ndefault. No hosted relay, no provider-token custody, no secret leakage.\n\nFor the read-only record of what a thread or turn did, see\n[`docs/RECEIPTS.md`](RECEIPTS.md): `codewhale receipts` on the CLI and the\n`/receipt` routes under **Threads** below.\n\n## Runtime API entrypoints\n\n| Entry | Transport | Use |\n|---|---|---|\n| `codewhale web [--port 7878]` | HTTP/SSE on `127.0.0.1:7878` + embedded client | First-class loopback-only browser client; opens the default browser |\n| `codewhale app-server --http` | HTTP/SSE on `127.0.0.1:7878` | Full `/v1/*` runtime API (canonical) |\n| `codewhale app-server --mobile` | HTTP/SSE on loopback + `/mobile` | Runtime API + local mobile control page |\n| `codewhale app-server --stdio` | JSON-RPC 2.0 over stdio | Compatibility controls and explicit tool-free turns; no full Engine event or approval/input channel. |\n| `codewhale app-server --socket [--socket-path P]` | JSON-RPC 2.0 over a `0600` Unix domain socket (macOS/Linux) or an owner-only named pipe (Windows) | Desktop daemon: multi-client, kernel peer identity checked, `daemon/attach` claim handshake. Windows native-app qualification remains open. |\n| `codewhale app-server` | HTTP on `127.0.0.1:8787` | Compatibility `/healthz`, `/thread`, `/app`, `/prompt`, plus the full `/v1/*` router. Compatibility turn requests require explicit `allowed_tools: []`; those routes refuse interactive execution before a turn starts. New integrations should use `app-server --http`. |\n| `codewhale serve --http` / `--mobile` | same server as `app-server --http`/`--mobile` | Compatibility aliases |\n\n`app-server --http` and `--mobile` launch the same mature runtime API server\nhistorically reached through `serve --http` — no routes or behavior changed, so\nevery endpoint documented below is identical across both entrypoints. The\nruntime API token is read from `--auth-token`, then `CODEWHALE_RUNTIME_TOKEN`,\nthen `DEEPSEEK_RUNTIME_TOKEN`; use `--insecure-no-auth` only with a loopback\nbind. The `serve` compatibility aliases keep their `--insecure` flag.\nThe legacy in-process `codewhale app-server` also requires an explicit\n`--auth-token` or `CODEWHALE_APP_SERVER_TOKEN` before binding a non-loopback\nhost; its generated one-time `cwapp_*` token is loopback-only.\n\nDevice tokens minted by the master through `POST /v1/auth/client-tokens`\nhave an immutable `intent`: `watch` (the default when omitted) or explicit\n`drive`. Labels do not gLine truncated
}
]
},
@@ -336,8 +336,8 @@
},
{
"path": "docs/CONTRIBUTORS.md",
"sha256": "7dff9eefd6266de5537398f58350a88a5de41798fbbfb377c7ea23f84ab22a9b",
"content": "# Contributors\n\nCodewhale is built in the open with a growing community of contributors. Every\nissue report and pull request is real project work — welcome at any experience\nlevel. This is the full per-PR contributor record, grouped into release/time\nbands with the newest band first so it stays scannable. Expand any band to see\neveryone.\n\nFor the live list, see the\n[GitHub contributors page](https://github.com/codewhale-hq/CodeWhale/graphs/contributors),\n[`AUTHOR_MAP`](https://github.com/codewhale-hq/CodeWhale/blob/main/.github/AUTHOR_MAP),\nand [CHANGELOG.md](../CHANGELOG.md).\n\n<!-- contributor-wall:start -->\n## Everyone\n\n[![421 people built Codewhale](assets/contributors.png)](#everyone)\n\n421 people have code, co-authored commits, carried-forward pull requests or credited reports in Codewhale. GitHub's contributor list shows only commit authors whose email is linked to their account, so it counts 203 of them. If your commits are missing there, add your commit email to your GitHub account and they will link.\n\n[0gl20shk0sbt36](https://github.com/0gl20shk0sbt36) · [1Git2Clone](https://github.com/1Git2Clone) · [20bytes](https://github.com/20bytes) · [47Cid](https://github.com/47Cid) · [7jrxt42BxFZo4iAnN4CX](https://github.com/7jrxt42BxFZo4iAnN4CX) · [8sero](https://github.com/8sero) · [A-Corner](https://github.com/A-Corner) · [aboimpinto](https://github.com/aboimpinto) · [adity982](https://github.com/adity982) · [AdityaVG13](https://github.com/AdityaVG13) · [AGSaturn](https://github.com/AGSaturn) · [ai-dengwsh](https://github.com/ai-dengwsh) · [aiapienthusiast](https://github.com/aiapienthusiast) · [Aitensa](https://github.com/Aitensa) · [AiurArtanis](https://github.com/AiurArtanis) · [alitvak69](https://github.com/alitvak69) · [all-lopezg](https://github.com/all-lopezg) · [alvin1](https://github.com/alvin1) · [amuthantamil](https://github.com/amuthantamil) · [AnaheimEX](https://github.com/AnaheimEX) · [Andrea-Bruno](https://github.com/Andrea-Bruno) · [Angel-Hair](https://github.com/Angel-Hair) · [angus-guo](https://github.com/angus-guo) · [angziii](https://github.com/angziii) · [AnonymousUser443](https://github.com/AnonymousUser443) · [antarikshraya](https://github.com/antarikshraya) · [Anyexyz](https://github.com/Anyexyz) · [Apeiron0w0](https://github.com/Apeiron0w0) · [aqilaziz](https://github.com/aqilaziz) · [AresNing](https://github.com/AresNing) · [ArronAI007](https://github.com/ArronAI007) · [Artenx](https://github.com/Artenx) · [asdfg314284230](https://github.com/asdfg314284230) · [Asteria1028](https://github.com/Asteria1028) · [asto18089](https://github.com/asto18089) · [atmosuwiryo](https://github.com/atmosuwiryo) · [axobase001](https://github.com/axobase001) · [aznikline](https://github.com/aznikline) · [baendlorel](https://github.com/baendlorel) · [banqii](https://github.com/banqii) · [Beltran12138](https://github.com/Beltran12138) · [bevis-wong](https://github.com/bevis-wong) · [BH8GCJ](https://github.com/BH8GCJ) · [BigBenLabs](https://github.com/BigBenLabs) · [Biilow](https://github.com/Biilow) · [bistack](https://github.com/bistack) · [BrathonBai](https://github.com/BrathonBai) · [bruce6135](https://github.com/bruce6135) · [BryonGo](https://github.com/BryonGo) · [buiducnhat](https://github.com/buiducnhat) · [buko](https://github.com/buko) · [BX166](https://github.com/BX166) · [c020627](https://github.com/c020627) · [cacdcaecawae](https://github.com/cacdcaecawae) · [caeserchen](https://github.com/caeserchen) · [caiyilian](https://github.com/caiyilian) · [CCChisato](https://github.com/CCChisato) · [ccomma](https://github.com/ccomma) · [cenab](https://github.com/cenab) · [ChaceLyee2101](https://github.com/ChaceLyee2101) · [chatman-media](https://github.com/chatman-media) · [chennest](https://github.com/chennest) · [chinaqy110](https://github.com/chinaqy110) · [chnjames](https://github.com/chnjames) · [chuntseevolving](https://github.com/chuntseevolving) · [ci4ic4](https://github.com/ci4ic4) · [cian-lien](https://github.com/cian-lien) · [cmdcorp6534](https://github.com/cmdcorp6534) · [cmyyy](https://github.com/cmyyy) · [codepgq](https://github.com/codepgq) · [Colorful-glassblock](https://github.com/Colorful-glassblock) · [ComeFromTheMars](https://github.com/ComeFromTheMars) · [CrepuscularIRIS](https://github.com/CrepuscularIRIS) · [ct-jaryn](https://github.com/ct-jaryn) · [ctxyao](https://github.com/ctxyao) · [cy2311](https://github.com/cy2311) · [cyq1017](https://github.com/cyq1017) · [czf0718](https://github.com/czf0718) · [dajiaohuang](https://github.com/dajiaohuang) · [DarrellThomas](https://github.com/DarrellThomas) · [demian-welt](https://github.com/demian-welt) · [Dev-next-gen](https://github.com/Dev-next-gen) · [dfwqdyl-ui](https://github.com/dfwqdyl-ui) · [DI-HUO-MING-YI](https://github.com/DI-HUO-MING-YI) · [DieMoe233](https://github.com/DieMoe233) · [DingYong4223Line truncated
"sha256": "2daeb1c2436aa586c2bdb03d4381d0d10bd9401cae6b6baa1dbe8bd9a04aaa7b",
"content": "# Contributors\n\nCodewhale is built in the open with a growing community of contributors. Every\nissue report and pull request is real project work — welcome at any experience\nlevel. This is the full per-PR contributor record, grouped into release/time\nbands with the newest band first so it stays scannable. Expand any band to see\neveryone.\n\nFor the live list, see the\n[GitHub contributors page](https://github.com/codewhale-hq/CodeWhale/graphs/contributors),\n[`AUTHOR_MAP`](https://github.com/codewhale-hq/CodeWhale/blob/main/.github/AUTHOR_MAP),\nand [CHANGELOG.md](../CHANGELOG.md).\n\n<!-- contributor-wall:start -->\n## Everyone\n\n[![421 people built Codewhale](assets/contributors.png)](#everyone)\n\n421 people have code, co-authored commits, carried-forward pull requests or credited reports in Codewhale. GitHub's contributor list shows only commit authors whose email is linked to their account, so it counts 203 of them. If your commits are missing there, add your commit email to your GitHub account and they will link.\n\n[0gl20shk0sbt36](https://github.com/0gl20shk0sbt36) · [1Git2Clone](https://github.com/1Git2Clone) · [20bytes](https://github.com/20bytes) · [47Cid](https://github.com/47Cid) · [7jrxt42BxFZo4iAnN4CX](https://github.com/7jrxt42BxFZo4iAnN4CX) · [8sero](https://github.com/8sero) · [A-Corner](https://github.com/A-Corner) · [aboimpinto](https://github.com/aboimpinto) · [adity982](https://github.com/adity982) · [AdityaVG13](https://github.com/AdityaVG13) · [AGSaturn](https://github.com/AGSaturn) · [ai-dengwsh](https://github.com/ai-dengwsh) · [aiapienthusiast](https://github.com/aiapienthusiast) · [Aitensa](https://github.com/Aitensa) · [AiurArtanis](https://github.com/AiurArtanis) · [alitvak69](https://github.com/alitvak69) · [all-lopezg](https://github.com/all-lopezg) · [alvin1](https://github.com/alvin1) · [amuthantamil](https://github.com/amuthantamil) · [AnaheimEX](https://github.com/AnaheimEX) · [Andrea-Bruno](https://github.com/Andrea-Bruno) · [Angel-Hair](https://github.com/Angel-Hair) · [angus-guo](https://github.com/angus-guo) · [angziii](https://github.com/angziii) · [AnonymousUser443](https://github.com/AnonymousUser443) · [antarikshraya](https://github.com/antarikshraya) · [Anyexyz](https://github.com/Anyexyz) · [Apeiron0w0](https://github.com/Apeiron0w0) · [aqilaziz](https://github.com/aqilaziz) · [AresNing](https://github.com/AresNing) · [ArronAI007](https://github.com/ArronAI007) · [Artenx](https://github.com/Artenx) · [asdfg314284230](https://github.com/asdfg314284230) · [Asteria1028](https://github.com/Asteria1028) · [asto18089](https://github.com/asto18089) · [atmosuwiryo](https://github.com/atmosuwiryo) · [axobase001](https://github.com/axobase001) · [aznikline](https://github.com/aznikline) · [baendlorel](https://github.com/baendlorel) · [banqii](https://github.com/banqii) · [Beltran12138](https://github.com/Beltran12138) · [bevis-wong](https://github.com/bevis-wong) · [BH8GCJ](https://github.com/BH8GCJ) · [BigBenLabs](https://github.com/BigBenLabs) · [Biilow](https://github.com/Biilow) · [bistack](https://github.com/bistack) · [BrathonBai](https://github.com/BrathonBai) · [bruce6135](https://github.com/bruce6135) · [BryonGo](https://github.com/BryonGo) · [buiducnhat](https://github.com/buiducnhat) · [buko](https://github.com/buko) · [BX166](https://github.com/BX166) · [c020627](https://github.com/c020627) · [cacdcaecawae](https://github.com/cacdcaecawae) · [caeserchen](https://github.com/caeserchen) · [caiyilian](https://github.com/caiyilian) · [CCChisato](https://github.com/CCChisato) · [ccomma](https://github.com/ccomma) · [cenab](https://github.com/cenab) · [ChaceLyee2101](https://github.com/ChaceLyee2101) · [chatman-media](https://github.com/chatman-media) · [chennest](https://github.com/chennest) · [chinaqy110](https://github.com/chinaqy110) · [chnjames](https://github.com/chnjames) · [chuntseevolving](https://github.com/chuntseevolving) · [ci4ic4](https://github.com/ci4ic4) · [cian-lien](https://github.com/cian-lien) · [cmdcorp6534](https://github.com/cmdcorp6534) · [cmyyy](https://github.com/cmyyy) · [codepgq](https://github.com/codepgq) · [Colorful-glassblock](https://github.com/Colorful-glassblock) · [ComeFromTheMars](https://github.com/ComeFromTheMars) · [CrepuscularIRIS](https://github.com/CrepuscularIRIS) · [ct-jaryn](https://github.com/ct-jaryn) · [ctxyao](https://github.com/ctxyao) · [cy2311](https://github.com/cy2311) · [cyq1017](https://github.com/cyq1017) · [czf0718](https://github.com/czf0718) · [dajiaohuang](https://github.com/dajiaohuang) · [DarrellThomas](https://github.com/DarrellThomas) · [demian-welt](https://github.com/demian-welt) · [Dev-next-gen](https://github.com/Dev-next-gen) · [dfwqdyl-ui](https://github.com/dfwqdyl-ui) · [DI-HUO-MING-YI](https://github.com/DI-HUO-MING-YI) · [DieMoe233](https://github.com/DieMoe233) · [DingYong4223Line truncated
},
{
"path": "docs/RELEASE_CHECKLIST.md",
@@ -357,13 +357,13 @@
},
{
"path": "docs/MODES.md",
"sha256": "e327b2042c9b782660cb0d6de9f4208364537ef1052a456a45332a61b1ee2468",
"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. Same tools and authority as Work; the difference is how they are used. Each substantive request becomes a goal (`create_goal`), the plan stays visible (`todo_write`), multi-part work runs through `workflow` and parallel `agent` workers, non-trivial changes get an independent reviewer before they are called done, long commands keep running in the background, and recurring or watch-type work is offered as an `automation` (created after approval). It keeps going until the goal is verified, blocked on you, or paused.\n\n`Act` and `/mode act` remain compatibility aliases for Work. Saved settings\nstill normalize to the internal value `agent`.\n\n### Tool availability by mode\n\n| Tool family | Plan | Work | Operate |\n|:---|:---:|:---:|:---:|\n| `read` and policy-allowed deferred research tools | yes | yes | yes |\n| `write` and `edit` | visible names; execution denied | approval- and policy-gated | same as Work |\n| `bash` | visible name; execution denied | approval- and policy-gated | same as Work; delegation is preferred when parallelism or isolation helps |\n| `agent` | yes, subject to child-depth authority | yes, subject to child-depth authority | yes, subject to child-depth authority |\n| Deferred native, MCP, and plugin tools | discoverable through `tool_search` when policy permits | same | same |\n| Paid or external-service tools | follows permission posture | follows permission posture | follows permission posture |\n| Access outside the workspace root | explicit trusted paths only | only through trusted paths or trust mode | same trusted-path/trust policy as Work; fleet profiles never widen it |\n\nOperate changes scheduling emphasis, not authority. It neither adds a\nmode-specific tool denial nor bypasses the active approval, sandbox, shell,\nask-rule, repository-law, or managed-policy boundary. Plan remains the\nmode-specific execution boundary for shell and write-capable tools; that\nauthority difference does not require a different primitive vocabulary.\n\n### Operate loop (one screen)\n\n```text\nUser Line truncated
"sha256": "f07a2fce8d2ef02692fdc9298911ebc99dbd7940af6a3b88d396c039dc7e4608",
"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. Same tools and authority as Work; the difference is how they are used. Each substantive request becomes a goal (`create_goal`), the plan stays visible (`todo_write`), multi-part work runs through `workflow` and parallel `agent` workers, non-trivial changes get an independent reviewer before they are called done, long commands keep running in the background, and recurring or watch-type work is offered as an `automation` (created after approval). It keeps going until the goal is verified, blocked on you, or paused.\n\n`Act` and `/mode act` remain compatibility aliases for Work. Saved settings\nstill normalize to the internal value `agent`.\n\n### Tool availability by mode\n\n| Tool family | Plan | Work | Operate |\n|:---|:---:|:---:|:---:|\n| `read` and policy-allowed deferred research tools | yes | yes | yes |\n| `write` and `edit` | visible names; execution denied | approval- and policy-gated | same as Work |\n| `bash` | visible name; execution denied | approval- and policy-gated | same as Work; delegation is preferred when parallelism or isolation helps |\n| `agent` | yes, subject to child-depth authority | yes, subject to child-depth authority | yes, subject to child-depth authority |\n| Deferred native, MCP, and plugin tools | discoverable through `tool_search` when policy permits | same | same |\n| Paid or external-service tools | follows permission posture | follows permission posture | follows permission posture |\n| Access outside the workspace root | explicit trusted paths only | only through trusted paths or trust mode | same trusted-path/trust policy as Work; fleet profiles never widen it |\n\nOperate changes scheduling emphasis, not authority. It neither adds a\nmode-specific tool denial nor bypasses the active approval, sandbox, shell,\nask-rule, repository-law, or managed-policy boundary. Plan remains the\nmode-specific execution boundary for shell and write-capable tools; that\nauthority difference does not require a different primitive vocabulary.\n\n### Operate loop (one screen)\n\n```text\nUser Line truncated
},
{
"path": "docs/public-surface-facts.json",
"sha256": "77046f5692985d812fa2816d2c6e50be0da81fdd5fea1b72a3dff9c56425e70d",
"content": "{\n \"schemaVersion\": 2,\n \"product\": {\n \"name\": \"Codewhale\",\n \"description\": \"Terminal coding agent for supported hosted and local models. One runtime on your machine. Rust, MIT.\",\n \"positioning\": \"Terminal-native, model/provider-neutral, local-first coding agent\",\n \"license\": \"MIT\",\n \"terminology\": {\n \"Fleet\": \"the user's model inventory: who is in the roster and which member is selected\",\n \"Workflow\": \"what order the work follows\",\n \"Lane\": \"one running Workflow instance\",\n \"Runtime\": \"where, how, and with what authority selected work executes\"\n },\n \"sources\": [\n \"Cargo.toml\",\n \"LICENSE\",\n \"README.md\",\n \"npm/codewhale/package.json\",\n \"docs/FLEET.md\"\n ]\n },\n \"sourceCandidate\": {\n \"version\": \"0.10.2\",\n \"providerCount\": 50,\n \"toolCount\": 81,\n \"sandboxBackends\": [\n \"seatbelt (macOS, when available)\",\n \"bubblewrap (Linux, default when installed and working)\"\n ],\n \"sources\": [\n \"Cargo.toml\",\n \"crates/runtime/src/config.rs\",\n \"crates/sandbox/src/lib.rs\",\n \"web/scripts/derive-facts.mjs\"\n ],\n \"providerCountDefinition\": \"Website descriptor identities in this source snapshot, excluding Custom and retired identities; includes protocol/plan variants. Not a count of distinct vendors, ProviderKind identities, live catalogs, or providers in any published release.\"\n },\n \"latestPublishedRelease\": {\n \"tag\": \"v0.10.2\",\n \"version\": \"0.10.2\",\n \"publishedAt\": \"2026-10-24T17:31:14Z\",\n \"url\": \"https://github.com/codewhale-hq/CodeWhale/releases/tag/v0.10.2\",\n \"sources\": [\n \"web/data/latest-published-release.json\"\n ]\n },\n \"install\": {\n \"recommended\": \"curl -fsSL https://codewhale.net/install.sh | sh\",\n \"binaries\": [\n \"codewhale\",\n \"codew\"\n ],\n \"channels\": {\n \"npm\": \"published releases only\",\n \"cargo\": \"published crates only\",\n \"prebuiltArchives\": \"published GitHub Releases only\",\n \"cnbMirror\": \"documented targets only\"\n },\n \"androidTermux\": {\n \"status\": \"preview\",\n \"npm\": \"preview\",\n \"requiresMatchingPublishedAssets\": true,\n \"sourceBuild\": true\n },\n \"sources\": [\n \"README.md\",\n \"npm/codewhale/package.json\",\n \"npm/codewhale/README.md\",\n \"npm/codewhale/scripts/artifacts.js\",\n \"docs/INSTALL.md\",\n \"docs/CNB_MIRROR.md\"\n ]\n },\n \"control\": {\n \"modes\": [\n \"Plan\",\n \"Work\",\n \"Operate\"\n ],\n \"permissionPostures\": [\n \"Ask\",\n \"Auto-Review\",\n \"Full Access\"\n ],\n \"shortcuts\": {\n \"mode\": {\n \"chord\": \"Tab\",\n \"when\": \"composer empty\"\n },\n \"permissionPosture\": {\n \"chord\": \"Shift+Tab\",\n \"when\": \"always (suppressed only under a non-Config modal)\"\n }\n },\n \"sources\": [\n \"docs/MODES.md\",\n \"docs/KEYBINDINGS.md\",\n \"crates/runtime/src/settings.rs\"\n ]\n },\n \"toolSurface\": {\n \"defaultActive\": [\n \"read\",\n \"write\",\n \"edit\",\n \"file_search\",\n \"bash\",\n \"agent\",\n \"workflow\",\n \"todo_write\",\n \"create_goal\",\n \"get_goal\",\n \"update_goal\",\n \"task_shell_wait\",\n \"load_skill\",\n \"context_archive_get\",\n \"context_archive_search\"\n ],\n \"schemas\": {\n \"read\": [\n \"path\",\n \"offset?\",\n \"limit?\"\n ],\n \"write\": [\n \"path\",\n \"content\"\n ],\n \"edit\": [\n \"path\",\n \"edits\"\n ],\n \"bash\": [\n \"command\",\n \"timeout?\"\n ]\n },\n \"deferred\": {\n \"Web\": [\n \"search\",\n \"fetch\",\n \"wait\"\n ]\n },\n \"compatibility\": {\n \"legacyAliases\": \"hidden-exact\",\n \"modelVisible\": false,\n \"toolSearchDiscoverable\": false\n },\n \"activationCache\": {\n \"maximumNames\": 8,\n \"maximumSchemaBytes\": 16384,\n \"scope\": \"per conversation; each subagent owns an independent policy-filtered cache\"\n },\n \"agentConcurrency\": {\n \"defaultConfigured\": 64,\n \"maximumConfigured\": 128,\n \"maximumAdmitted\": 1024\n },\n \"sources\": [\n \"docs/TOOL_SURFACE.md\",\n \"docs/RUNTIME_SIMPLIFICATION_DESIGN.md\",\n \"crates/engine/src/core/engine/tool_catalog.rs\",\n \"crates/engine/src/tools/registry.rs\",\n \"crates/runtime/src/config/subagent_limits.rs\"\n ]\n },\n \"surfaces\": {\n \"availableInSourceCandidate\": [\n \"TUI\",\n \"codewhale exec\",\n \"Web client\",\n \"Runtime ALine truncated
"sha256": "17575482f66e4fd9bb0302fe33bdd7aa2c6bfeca81b14d4b169c93aed6b9e4c3",
"content": "{\n \"schemaVersion\": 2,\n \"product\": {\n \"name\": \"Codewhale\",\n \"description\": \"Terminal coding agent for supported hosted and local models. One runtime on your machine. Rust, MIT.\",\n \"positioning\": \"Terminal-native, model/provider-neutral, local-first coding agent\",\n \"license\": \"MIT\",\n \"terminology\": {\n \"Fleet\": \"the user's model inventory: who is in the roster and which member is selected\",\n \"Workflow\": \"what order the work follows\",\n \"Lane\": \"one running Workflow instance\",\n \"Runtime\": \"where, how, and with what authority selected work executes\"\n },\n \"sources\": [\n \"Cargo.toml\",\n \"LICENSE\",\n \"README.md\",\n \"npm/codewhale/package.json\",\n \"docs/FLEET.md\"\n ]\n },\n \"sourceCandidate\": {\n \"version\": \"0.10.3\",\n \"providerCount\": 50,\n \"toolCount\": 81,\n \"sandboxBackends\": [\n \"seatbelt (macOS, when available)\",\n \"bubblewrap (Linux, default when installed and working)\"\n ],\n \"sources\": [\n \"Cargo.toml\",\n \"crates/runtime/src/config.rs\",\n \"crates/sandbox/src/lib.rs\",\n \"web/scripts/derive-facts.mjs\"\n ],\n \"providerCountDefinition\": \"Website descriptor identities in this source snapshot, excluding Custom and retired identities; includes protocol/plan variants. Not a count of distinct vendors, ProviderKind identities, live catalogs, or providers in any published release.\"\n },\n \"latestPublishedRelease\": {\n \"tag\": \"v0.10.2\",\n \"version\": \"0.10.2\",\n \"publishedAt\": \"2026-10-24T17:31:14Z\",\n \"url\": \"https://github.com/codewhale-hq/CodeWhale/releases/tag/v0.10.2\",\n \"sources\": [\n \"web/data/latest-published-release.json\"\n ]\n },\n \"install\": {\n \"recommended\": \"curl -fsSL https://codewhale.net/install.sh | sh\",\n \"binaries\": [\n \"codewhale\",\n \"codew\"\n ],\n \"channels\": {\n \"npm\": \"published releases only\",\n \"cargo\": \"published crates only\",\n \"prebuiltArchives\": \"published GitHub Releases only\",\n \"cnbMirror\": \"documented targets only\"\n },\n \"androidTermux\": {\n \"status\": \"preview\",\n \"npm\": \"preview\",\n \"requiresMatchingPublishedAssets\": true,\n \"sourceBuild\": true\n },\n \"sources\": [\n \"README.md\",\n \"npm/codewhale/package.json\",\n \"npm/codewhale/README.md\",\n \"npm/codewhale/scripts/artifacts.js\",\n \"docs/INSTALL.md\",\n \"docs/CNB_MIRROR.md\"\n ]\n },\n \"control\": {\n \"modes\": [\n \"Plan\",\n \"Work\",\n \"Operate\"\n ],\n \"permissionPostures\": [\n \"Ask\",\n \"Auto-Review\",\n \"Full Access\"\n ],\n \"shortcuts\": {\n \"mode\": {\n \"chord\": \"Tab\",\n \"when\": \"composer empty\"\n },\n \"permissionPosture\": {\n \"chord\": \"Shift+Tab\",\n \"when\": \"always (suppressed only under a non-Config modal)\"\n }\n },\n \"sources\": [\n \"docs/MODES.md\",\n \"docs/KEYBINDINGS.md\",\n \"crates/runtime/src/settings.rs\"\n ]\n },\n \"toolSurface\": {\n \"defaultActive\": [\n \"read\",\n \"write\",\n \"edit\",\n \"file_search\",\n \"bash\",\n \"agent\",\n \"workflow\",\n \"todo_write\",\n \"create_goal\",\n \"get_goal\",\n \"update_goal\",\n \"task_shell_wait\",\n \"load_skill\",\n \"context_archive_get\",\n \"context_archive_search\"\n ],\n \"schemas\": {\n \"read\": [\n \"path\",\n \"offset?\",\n \"limit?\"\n ],\n \"write\": [\n \"path\",\n \"content\"\n ],\n \"edit\": [\n \"path\",\n \"edits\"\n ],\n \"bash\": [\n \"command\",\n \"timeout?\"\n ]\n },\n \"deferred\": {\n \"Web\": [\n \"search\",\n \"fetch\",\n \"wait\"\n ]\n },\n \"compatibility\": {\n \"legacyAliases\": \"hidden-exact\",\n \"modelVisible\": false,\n \"toolSearchDiscoverable\": false\n },\n \"activationCache\": {\n \"maximumNames\": 8,\n \"maximumSchemaBytes\": 16384,\n \"scope\": \"per conversation; each subagent owns an independent policy-filtered cache\"\n },\n \"agentConcurrency\": {\n \"defaultConfigured\": 64,\n \"maximumConfigured\": 128,\n \"maximumAdmitted\": 1024\n },\n \"sources\": [\n \"docs/TOOL_SURFACE.md\",\n \"docs/RUNTIME_SIMPLIFICATION_DESIGN.md\",\n \"crates/engine/src/core/engine/tool_catalog.rs\",\n \"crates/engine/src/tools/registry.rs\",\n \"crates/runtime/src/config/subagent_limits.rs\"\n ]\n },\n \"surfaces\": {\n \"availableInSourceCandidate\": [\n \"TUI\",\n \"codewhale exec\",\n \"Web client\",\n \"Runtime ALine truncated
}
]
},
+106 -93
View File
@@ -2,9 +2,22 @@
"format": "codewhale.features.v1",
"source": {
"path": "docs/features.toml",
"sha256": "5f0bcaad963c6b03183029c81dd99495ecff9ee40e4de730c392860b84494564"
"sha256": "44779cf81b94c861cfb89119df6652803f94537b6095d590bf36108b83b8e86c"
},
"features": [
{
"id": "undo-range",
"name": "Request range undo",
"summary": "Select a request with /undo select to restore its file changes and later requests with visible progress.",
"status": "experimental",
"surfaces": {
"tui": "preview"
},
"since": "unreleased",
"flag": null,
"docs": "docs/GUIDE.md",
"registryLine": 40
},
{
"id": "unix-uninstall",
"name": "Receipt-owned uninstall",
@@ -16,7 +29,7 @@
"since": "unreleased",
"flag": null,
"docs": "docs/INSTALL.md",
"registryLine": 40
"registryLine": 50
},
{
"id": "install-cleanup",
@@ -29,7 +42,7 @@
"since": "unreleased",
"flag": null,
"docs": "docs/INSTALL.md#clean-up-old-copies",
"registryLine": 50
"registryLine": 60
},
{
"id": "auto-update",
@@ -42,7 +55,7 @@
"since": "unreleased",
"flag": null,
"docs": "docs/INSTALL.md#automatic-updates",
"registryLine": 60
"registryLine": 70
},
{
"id": "cloud-computer-dispatch",
@@ -55,7 +68,7 @@
"since": "unreleased",
"flag": null,
"docs": "docs/CLOUD_DISPATCH.md",
"registryLine": 70
"registryLine": 80
},
{
"id": "shell-tool",
@@ -68,7 +81,7 @@
"since": "0.1.0",
"flag": "shell_tool",
"docs": "docs/TOOL_SURFACE.md",
"registryLine": 80
"registryLine": 90
},
{
"id": "local-code-execution",
@@ -81,7 +94,7 @@
"since": null,
"flag": null,
"docs": "docs/TOOL_SURFACE.md#local-code-execution",
"registryLine": 91
"registryLine": 101
},
{
"id": "subagents",
@@ -95,7 +108,7 @@
"since": "0.1.0",
"flag": "subagents",
"docs": "docs/SUBAGENTS.md",
"registryLine": 100
"registryLine": 110
},
{
"id": "web-search",
@@ -108,7 +121,7 @@
"since": "0.1.0",
"flag": "web_search",
"docs": "docs/TOOL_SURFACE.md",
"registryLine": 111
"registryLine": 121
},
{
"id": "apply-patch",
@@ -121,7 +134,7 @@
"since": "0.1.0",
"flag": "apply_patch",
"docs": "docs/TOOL_SURFACE.md",
"registryLine": 122
"registryLine": 132
},
{
"id": "mcp-client",
@@ -135,7 +148,7 @@
"since": "0.1.0",
"flag": "mcp",
"docs": "docs/MCP.md",
"registryLine": 133
"registryLine": 143
},
{
"id": "exec-policy",
@@ -148,7 +161,7 @@
"since": "0.1.0",
"flag": "exec_policy",
"docs": "docs/AUTHORIZATION_ORDER.md",
"registryLine": 144
"registryLine": 154
},
{
"id": "vision-model",
@@ -161,7 +174,7 @@
"since": "0.8.32",
"flag": "vision_model",
"docs": "docs/CONFIGURATION.md",
"registryLine": 155
"registryLine": 165
},
{
"id": "verify-tool",
@@ -174,7 +187,7 @@
"since": "0.9.2",
"flag": "verify_tool",
"docs": "docs/CONFIGURATION.md#feature-flags",
"registryLine": 166
"registryLine": 176
},
{
"id": "code-mode",
@@ -187,7 +200,7 @@
"since": "0.10.0",
"flag": "code_mode",
"docs": "docs/TOOL_SURFACE.md#code-mode-execute_tools",
"registryLine": 177
"registryLine": 187
},
{
"id": "extension-host",
@@ -202,7 +215,7 @@
"since": "0.10.1",
"flag": "extension_host",
"docs": "docs/EXTENSIONS.md",
"registryLine": 188
"registryLine": 198
},
{
"id": "finance-host",
@@ -216,7 +229,7 @@
"since": "0.10.1",
"flag": "finance_host",
"docs": "docs/TOOL_SURFACE.md",
"registryLine": 199
"registryLine": 209
},
{
"id": "data-host",
@@ -230,7 +243,7 @@
"since": "0.10.1",
"flag": "data_host",
"docs": "docs/TOOL_SURFACE.md",
"registryLine": 210
"registryLine": 220
},
{
"id": "speech-host",
@@ -244,7 +257,7 @@
"since": "0.10.1",
"flag": "speech_host",
"docs": "docs/TOOL_SURFACE.md",
"registryLine": 221
"registryLine": 231
},
{
"id": "pdf-host",
@@ -258,7 +271,7 @@
"since": "0.10.1",
"flag": "pdf_host",
"docs": "docs/TOOL_SURFACE.md",
"registryLine": 232
"registryLine": 242
},
{
"id": "ocr-host",
@@ -272,7 +285,7 @@
"since": "0.10.1",
"flag": "ocr_host",
"docs": "docs/TOOL_SURFACE.md",
"registryLine": 243
"registryLine": 253
},
{
"id": "github-host",
@@ -286,7 +299,7 @@
"since": "0.10.1",
"flag": "github_host",
"docs": "docs/TOOL_SURFACE.md",
"registryLine": 254
"registryLine": 264
},
{
"id": "web-search-host",
@@ -300,7 +313,7 @@
"since": "0.10.1",
"flag": "web_search_host",
"docs": "docs/TOOL_SURFACE.md",
"registryLine": 265
"registryLine": 275
},
{
"id": "web-extract-host",
@@ -314,7 +327,7 @@
"since": "0.10.1",
"flag": "web_extract_host",
"docs": "docs/TOOL_SURFACE.md",
"registryLine": 276
"registryLine": 286
},
{
"id": "review-host",
@@ -328,7 +341,7 @@
"since": "0.10.1",
"flag": "review_host",
"docs": "docs/TOOL_SURFACE.md",
"registryLine": 287
"registryLine": 297
},
{
"id": "modes",
@@ -342,7 +355,7 @@
"since": null,
"flag": null,
"docs": "docs/MODES.md",
"registryLine": 300
"registryLine": 310
},
{
"id": "permission-postures",
@@ -356,7 +369,7 @@
"since": null,
"flag": null,
"docs": "docs/MODES.md#permission-posture",
"registryLine": 309
"registryLine": 319
},
{
"id": "sandbox",
@@ -369,7 +382,7 @@
"since": null,
"flag": null,
"docs": "docs/SANDBOX.md",
"registryLine": 318
"registryLine": 328
},
{
"id": "providers",
@@ -383,7 +396,7 @@
"since": null,
"flag": null,
"docs": "docs/PROVIDERS.md",
"registryLine": 327
"registryLine": 337
},
{
"id": "model-routing",
@@ -396,7 +409,7 @@
"since": null,
"flag": null,
"docs": "docs/CONFIGURATION.md#auto-model-routing-auto-autorouter",
"registryLine": 336
"registryLine": 346
},
{
"id": "output-limit-answer-recovery",
@@ -410,7 +423,7 @@
"since": "unreleased",
"flag": null,
"docs": "docs/CONFIGURATION.md#reasoning-only-recovery-reasoning_only",
"registryLine": 345
"registryLine": 355
},
{
"id": "fleet",
@@ -424,7 +437,7 @@
"since": null,
"flag": null,
"docs": "docs/FLEET.md",
"registryLine": 355
"registryLine": 365
},
{
"id": "environment-placement",
@@ -438,7 +451,7 @@
"since": null,
"flag": null,
"docs": "docs/ENVIRONMENTS.md#environment-aware-fleet-placement-preview",
"registryLine": 364
"registryLine": 374
},
{
"id": "workflows",
@@ -451,7 +464,7 @@
"since": null,
"flag": null,
"docs": "docs/WORKFLOW_AUTHORING.md",
"registryLine": 373
"registryLine": 383
},
{
"id": "automatic-workflows",
@@ -464,7 +477,7 @@
"since": null,
"flag": null,
"docs": "docs/AUTOMATIC_WORKFLOWS.md",
"registryLine": 382
"registryLine": 392
},
{
"id": "goals",
@@ -478,7 +491,7 @@
"since": null,
"flag": null,
"docs": "docs/CONFIGURATION.md#goal-loop-goal",
"registryLine": 391
"registryLine": 401
},
{
"id": "thread-scheduled-turns",
@@ -491,7 +504,7 @@
"since": "unreleased",
"flag": null,
"docs": "docs/RUNTIME_API.md",
"registryLine": 400
"registryLine": 410
},
{
"id": "task-suggestions",
@@ -504,7 +517,7 @@
"since": "unreleased",
"flag": null,
"docs": null,
"registryLine": 410
"registryLine": 420
},
{
"id": "todos",
@@ -518,7 +531,7 @@
"since": null,
"flag": null,
"docs": "docs/TOOL_SURFACE.md",
"registryLine": 419
"registryLine": 429
},
{
"id": "composer-queue-steer",
@@ -532,7 +545,7 @@
"since": null,
"flag": null,
"docs": "docs/KEYBINDINGS.md",
"registryLine": 428
"registryLine": 438
},
{
"id": "receipts",
@@ -546,7 +559,7 @@
"since": "0.10.1",
"flag": null,
"docs": "docs/RECEIPTS.md",
"registryLine": 437
"registryLine": 447
},
{
"id": "preview-request",
@@ -559,7 +572,7 @@
"since": null,
"flag": null,
"docs": "docs/PREVIEW_REQUEST.md",
"registryLine": 447
"registryLine": 457
},
{
"id": "read-media",
@@ -572,7 +585,7 @@
"since": null,
"flag": null,
"docs": "docs/READ_MEDIA.md",
"registryLine": 456
"registryLine": 466
},
{
"id": "memory",
@@ -586,7 +599,7 @@
"since": null,
"flag": null,
"docs": "docs/MEMORY.md",
"registryLine": 465
"registryLine": 475
},
{
"id": "skills",
@@ -600,7 +613,7 @@
"since": null,
"flag": null,
"docs": "docs/SKILLS.md",
"registryLine": 476
"registryLine": 486
},
{
"id": "plugins",
@@ -614,7 +627,7 @@
"since": null,
"flag": null,
"docs": "docs/PLUGINS.md",
"registryLine": 485
"registryLine": 495
},
{
"id": "computer-use",
@@ -628,7 +641,7 @@
"since": null,
"flag": null,
"docs": "docs/PLUGIN_BUNDLES.md",
"registryLine": 494
"registryLine": 504
},
{
"id": "script-tools",
@@ -641,7 +654,7 @@
"since": null,
"flag": null,
"docs": "docs/CONFIGURATION.md#script-tools-and-overrides",
"registryLine": 503
"registryLine": 513
},
{
"id": "claude-plugin-compat",
@@ -654,7 +667,7 @@
"since": null,
"flag": null,
"docs": "docs/CLAUDE_PLUGIN_COMPAT.md",
"registryLine": 512
"registryLine": 522
},
{
"id": "claude-settings-import",
@@ -667,7 +680,7 @@
"since": null,
"flag": null,
"docs": null,
"registryLine": 521
"registryLine": 531
},
{
"id": "hooks",
@@ -681,7 +694,7 @@
"since": null,
"flag": null,
"docs": "docs/HOOKS.md",
"registryLine": 529
"registryLine": 539
},
{
"id": "lsp",
@@ -695,7 +708,7 @@
"since": null,
"flag": null,
"docs": "docs/LSP_PHP_CUSTOM.md",
"registryLine": 538
"registryLine": 548
},
{
"id": "runtime-api",
@@ -708,7 +721,7 @@
"since": null,
"flag": null,
"docs": "docs/RUNTIME_API.md",
"registryLine": 547
"registryLine": 557
},
{
"id": "compatibility-tool-free-turns",
@@ -721,7 +734,7 @@
"since": "0.10.2",
"flag": null,
"docs": "docs/RUNTIME_API.md#compatibility-execution-limits",
"registryLine": 556
"registryLine": 566
},
{
"id": "runtime-provider-keys",
@@ -734,7 +747,7 @@
"since": "0.10.2",
"flag": null,
"docs": "docs/RUNTIME_API.md#put-v1providersidkey--write-only-credential",
"registryLine": 566
"registryLine": 576
},
{
"id": "headless-exec",
@@ -747,7 +760,7 @@
"since": null,
"flag": null,
"docs": "docs/RUNTIME_API.md",
"registryLine": 576
"registryLine": 586
},
{
"id": "github-review",
@@ -760,7 +773,7 @@
"since": null,
"flag": null,
"docs": "docs/GITHUB_APP.md",
"registryLine": 585
"registryLine": 595
},
{
"id": "web-client",
@@ -773,7 +786,7 @@
"since": null,
"flag": null,
"docs": "docs/WEB.md",
"registryLine": 596
"registryLine": 606
},
{
"id": "keybindings",
@@ -786,7 +799,7 @@
"since": null,
"flag": null,
"docs": "docs/KEYBINDINGS.md",
"registryLine": 605
"registryLine": 615
},
{
"id": "settings",
@@ -800,7 +813,7 @@
"since": null,
"flag": null,
"docs": "docs/CONFIGURATION.md",
"registryLine": 614
"registryLine": 624
},
{
"id": "whats-new-notice",
@@ -813,7 +826,7 @@
"since": "0.10.2",
"flag": null,
"docs": "docs/CONFIGURATION.md#whats-new-after-an-update",
"registryLine": 623
"registryLine": 633
},
{
"id": "themes",
@@ -826,7 +839,7 @@
"since": null,
"flag": null,
"docs": "docs/schemas/user-theme.schema.json",
"registryLine": 633
"registryLine": 643
},
{
"id": "chat-color",
@@ -839,7 +852,7 @@
"since": "0.10.2",
"flag": null,
"docs": "docs/GUIDE.md",
"registryLine": 642
"registryLine": 652
},
{
"id": "localization",
@@ -852,7 +865,7 @@
"since": null,
"flag": null,
"docs": "docs/LOCALIZATION.md",
"registryLine": 652
"registryLine": 662
},
{
"id": "voice",
@@ -866,7 +879,7 @@
"since": null,
"flag": null,
"docs": null,
"registryLine": 661
"registryLine": 671
},
{
"id": "pet",
@@ -880,7 +893,7 @@
"since": null,
"flag": null,
"docs": "docs/PET_MODE.md",
"registryLine": 669
"registryLine": 679
},
{
"id": "overview",
@@ -893,7 +906,7 @@
"since": null,
"flag": null,
"docs": "docs/PROVIDERS.md",
"registryLine": 678
"registryLine": 688
},
{
"id": "account-login",
@@ -906,7 +919,7 @@
"since": null,
"flag": null,
"docs": null,
"registryLine": 687
"registryLine": 697
},
{
"id": "account-computer-files",
@@ -919,7 +932,7 @@
"since": "0.10.2",
"flag": null,
"docs": null,
"registryLine": 695
"registryLine": 705
},
{
"id": "context-anchors",
@@ -932,7 +945,7 @@
"since": null,
"flag": null,
"docs": null,
"registryLine": 704
"registryLine": 714
},
{
"id": "window-pin",
@@ -945,7 +958,7 @@
"since": null,
"flag": null,
"docs": null,
"registryLine": 712
"registryLine": 722
},
{
"id": "structured-copy",
@@ -958,7 +971,7 @@
"since": null,
"flag": null,
"docs": null,
"registryLine": 720
"registryLine": 730
},
{
"id": "live-thinking",
@@ -971,7 +984,7 @@
"since": null,
"flag": null,
"docs": null,
"registryLine": 728
"registryLine": 738
},
{
"id": "local-model-reasoning",
@@ -986,7 +999,7 @@
"since": "unreleased",
"flag": null,
"docs": "docs/LOCAL_MODEL_REASONING.md",
"registryLine": 736
"registryLine": 746
},
{
"id": "workbar",
@@ -999,7 +1012,7 @@
"since": null,
"flag": null,
"docs": null,
"registryLine": 746
"registryLine": 756
},
{
"id": "terminal-dock",
@@ -1012,7 +1025,7 @@
"since": "0.10.2",
"flag": null,
"docs": null,
"registryLine": 754
"registryLine": 764
},
{
"id": "telemetry",
@@ -1025,7 +1038,7 @@
"since": null,
"flag": null,
"docs": "docs/TELEMETRY.md",
"registryLine": 765
"registryLine": 775
},
{
"id": "docker",
@@ -1038,7 +1051,7 @@
"since": null,
"flag": null,
"docs": "docs/DOCKER.md",
"registryLine": 774
"registryLine": 784
},
{
"id": "termux",
@@ -1051,7 +1064,7 @@
"since": null,
"flag": null,
"docs": "docs/TERMUX.md",
"registryLine": 783
"registryLine": 793
},
{
"id": "harmonyos-native",
@@ -1064,7 +1077,7 @@
"since": null,
"flag": null,
"docs": "docs/HarmonyOS.md",
"registryLine": 792
"registryLine": 802
},
{
"id": "chatgpt-plan-sign-in",
@@ -1079,7 +1092,7 @@
"since": "0.10.1",
"flag": null,
"docs": "docs/PROVIDERS.md#sign-in-with-chatgpt",
"registryLine": 801
"registryLine": 811
},
{
"id": "claude-plan-sign-in",
@@ -1094,7 +1107,7 @@
"since": "0.10.2",
"flag": null,
"docs": "docs/PROVIDERS.md#sign-in-with-claude",
"registryLine": 811
"registryLine": 821
},
{
"id": "plugin-oauth-providers",
@@ -1109,7 +1122,7 @@
"since": "0.10.1",
"flag": null,
"docs": "docs/PLUGIN_PROVIDERS.md",
"registryLine": 821
"registryLine": 831
},
{
"id": "orcarouter-sign-in",
@@ -1124,7 +1137,7 @@
"since": "0.10.1",
"flag": null,
"docs": "docs/PROVIDERS.md",
"registryLine": 831
"registryLine": 841
},
{
"id": "merch-interest",
@@ -1139,7 +1152,7 @@
"since": "unreleased",
"flag": null,
"docs": "web/lib/merch/README.md",
"registryLine": 842
"registryLine": 852
},
{
"id": "profile-constitution",
@@ -1153,7 +1166,7 @@
"since": "0.10.1",
"flag": null,
"docs": "docs/RUNTIME_API.md#profile-constitution",
"registryLine": 852
"registryLine": 862
},
{
"id": "plugin-shell-install",
@@ -1166,7 +1179,7 @@
"since": "0.10.2",
"flag": null,
"docs": "docs/PLUGINS.md",
"registryLine": 862
"registryLine": 872
},
{
"id": "herdr-agent-state",
@@ -1179,7 +1192,7 @@
"since": "0.10.2",
"flag": null,
"docs": null,
"registryLine": 872
"registryLine": 882
},
{
"id": "instruction-budgets",
@@ -1192,7 +1205,7 @@
"since": "unreleased",
"flag": null,
"docs": "docs/CONFIGURATION.md#instruction-budget-instruction_budget-6526",
"registryLine": 881
"registryLine": 891
},
{
"id": "github-action-review",
@@ -1205,7 +1218,7 @@
"since": "unreleased",
"flag": null,
"docs": "docs/GITHUB_ACTION.md",
"registryLine": 891
"registryLine": 901
},
{
"id": "telegram-agent",
@@ -1218,7 +1231,7 @@
"since": "unreleased",
"flag": null,
"docs": "integrations/telegram-bridge/README.md",
"registryLine": 901
"registryLine": 911
},
{
"id": "dingtalk-agent",
@@ -1231,7 +1244,7 @@
"since": "unreleased",
"flag": null,
"docs": "integrations/dingtalk-bridge/README.md",
"registryLine": 911
"registryLine": 921
},
{
"id": "compaction-archive-recall",
@@ -1244,7 +1257,7 @@
"since": "unreleased",
"flag": null,
"docs": null,
"registryLine": 921
"registryLine": 931
}
]
}
+3 -2
View File
@@ -38,15 +38,16 @@ export interface RepoFacts {
}
export const FACTS: RepoFacts = {
"generatedAt": "2026-10-31T23:51:59.447Z",
"generatedAt": "2026-11-03T01:04:10.249Z",
"sourceRevision": null,
"sourceCommittedAt": null,
"version": "0.10.2",
"version": "0.10.3",
"crates": [
"account-cli",
"agent",
"agent-contract",
"app-server",
"app-server-protocol",
"build-support",
"cli",
"cloud-facts",