lowercase_bash_returns_one_ordered_stream uses printf/redirection
syntax that Windows cmd cannot execute; it ran on Windows CI only
after the pi_output compile gap was fixed and failed deterministically.
Gate it to unix like its neighbor. The approval-test failure on
Windows is being rerun to separate a flake from a real defect.
Source budget follows the added lines (687594).
The receipt embeds the build commit, which CI stamps with the full
40-hex GITHUB_SHA while local builds carry the literal 'unknown'.
A full-length sha pushed the serialized receipt to 394 bytes over the
384-byte admission cap, so every CI build refused child launches and
the receipt regression tests failed on GitHub runners (they passed
locally only because 'unknown' is short). Some legitimate routes also
reached 386 bytes even without the sha.
- Truncate the embedded build commit to 12 hex chars; version + short
sha still identify the build for attribution.
- Raise the cap to 448 bytes with headroom for route growth.
- Remove the temporary debug workflow used to capture the runner
failure output.
Verified with the exact CI condition reproduced locally: a build
with DEEPSEEK_BUILD_SHA set to a full 40-hex sha now passes the
receipt tests, and the full lib suite is green (10301 passed).
The exact-surface tests hardcoded tools whose registration is gated on
local backends: image_ocr (tesseract/native OCR) and pandoc_convert
(pandoc binary). CI runners and Linux containers lack those, so the
pinned catalogs drifted and CI has been red for these tests all day.
Expected sets now follow ocr_available()/resolve_pandoc(), and the
image_ocr allowed-tool assert is conditional the same way.
Verified in both directions: macOS host (tesseract + pandoc present)
and rust:1.97.1 Linux container (neither) — all four tests pass.
Also raises the source-structure budget by 4 lines for the rename.
Rename the lowercase bash tool struct and its bounded output
accumulator, plus the file read/write helper symbols and their
tests, to contract-based names. Tool names, schemas, and behavior
are unchanged; doc and test references updated to match; the
model-visible catalog text and budget files are untouched.
Verified: cargo test -p codewhale-tui --lib (shell/registry/file/
file_tool/tool_catalog filters) 308 passed; web
public-surface-contract 13/13 passed.
bf6def00d added the Pi-compatible bounded output accumulator
(pi_output) to BackgroundShell, but the #[cfg(windows)] test
initializer in shell/tests.rs was not updated. Windows CI test
builds fail with E0063 (missing field pi_output) while macOS/Linux
builds never compile that test. Add pi_output: None — the test
shell has no live process pipe, so collect_output falls back to
the stdout buffer path.
bf6def00d added todo_write to the model-facing toolbox
(read/write/edit/bash/agent/todo_write/tool_search) and updated
docs/TOOL_SURFACE.md to 'exactly seven model-facing names', but
web/lib/public-surface-contract.test.ts still asserted six. The
release-candidate and Web Frontend proof runs fail on that stale
assertion at the frozen SHA; align the contract test with the
documented surface. Verified: vitest public-surface-contract (13/13).
Regenerate crates/tui/CHANGELOG.md from the root CHANGELOG.md so the
embedded changelog matches the finalized 0.9.6 notes (sync-changelog.sh).
Verified with ./scripts/release/check-versions.sh --require-dated-release
(workspace/npm/lockfile in sync; OHOS gates green).
Use the native Firecrawl adapter as the zero-config search route, preserve visible DuckDuckGo and Bing fallback, and document explicit China-provider choices without inferring geography.
Derive archive mtimes from the pinned source commit in release artifacts, retain reproducible archive bytes and executable modes, and cover metadata plus input validation.
Apply a provably native-only exec allowlist before engine feature setup so MCP servers that cannot contribute a tool are not connected. Keep unknown names and wildcard rules conservative, and cover both paths with focused catalog tests.
Treat provider reasoning continuity as typed state instead of readable transcript text. Preserve OpenAI Responses encrypted items only for the exact originating provider, API, and model; strip them on route changes; keep DeepSeek, Kimi K3, Model Studio, Mistral, and Anthropic contract regressions intact; and stop generic model-name suffixes from authorizing replay.
The bounded renderer now owns both capped previews and the exact transcript path, so remove the dead compatibility wrapper exposed by all-target compilation.
Bind npm publication to the exact release SHA after the public asset freshness gate, without a long-lived registry token. Document the npm-side publisher binding and interactive 2FA recovery path.\n\nCloses #5299
Retain only each live view's wrapped-row budget while scanning the full diff for truthful omission counts. Keep transcript detail exact and remove the unreachable legacy DiffPreview cell.\n\nCloses #5087
Scope tool-call result integrity to each assistant turn so a provider can reuse an identifier on a later retry without the completed result being quarantined. This prevents deferred plugin tools from looping after successful MCP execution.\n\nVerified with the focused repair suite, the full cucumber acceptance target, all-feature Clippy, and source/runtime/dead-code budgets.
Document Work and To-do as the two product concepts, synchronize every shipped locale, publish the six-tool and typed-image contracts, and keep static web generation offline while live GitHub chrome refreshes after deployment.
Adopt the six-tool lowercase surface, keep To-do state out of repeated provider prompts, and store Codex-style compaction checkpoints in ordinary history. Preserve provider-specific reasoning continuity, typed image tool results, role capability boundaries, ACP tool turns, and exact regression coverage across the runtime.
Route ACP calls through shared hook and policy admission, keep sandbox and override authority aligned with native turns, preserve receipts across cancellation/provider errors, and freeze the per-prompt system prefix. Stateful terminal/background forms remain unavailable on the ACP surface.
Compose ACP prompts through the canonical headless builder so editor-driven turns receive the same project instructions, configured instructions, memory, locale, and route-budget context as the current runtime. Preserve client-specific JSON-RPC response IDs while tools are running and make the Bash cancellation regression deterministic.
Fail closed by requiring the client terminal capability, the explicit headless shell opt-in, and the stable ShellTool feature before registering Bash. Apply the current Agent/Suggest sandbox policy and gate ApplyPatch through its feature flag. ACP continues to reuse the shared ToolRegistry and executes tool calls sequentially.
The stale v0.8.68 PowerShell build script described in the original contribution is intentionally not carried into v0.9.6.
Co-authored-by: Rafael Cavalheri <144138270+rafaelcavalheri@users.noreply.github.com>
Review feedback on #5225 (Hunter):
1. build_acp_tool_registry set context.auto_approve = true, which
short-circuits the SafetyLevel::Dangerous check in
tools/shell.rs (only runs `if !context.auto_approve`), so every
command an ACP client's model emits ran unreviewed. ACP has no
session/request_permission round-trip yet to fall back on. Drop
the line and let ToolContext::new's default (auto_approve: false)
stand — matching mcp_server.rs's trust posture over a different
transport. A blocked command already surfaces as a normal
`success: false` "BLOCKED: ..." tool result fed back to the model
(execute_tool_calls_with_cancellation already round-trips tool
results), not a silent failure, so there's no UX regression from
restoring the gate.
2. agentic_turn_cancels_while_a_tool_is_running scripted a tool call
named "exec_shell", which with_shell_tools() never registers
(renamed to "Bash" in v0.9.3). The lookup miss made the tool
future resolve to an immediate error instead of actually running
SLOW_SHELL_COMMAND, so the test's `select!` raced two already-ready
futures and asserted PromptOutcome::Cancelled on a coin flip.
Renamed to "Bash" so the 5-second command genuinely runs and the
cancel path genuinely preempts it. Also swept the remaining
`exec_shell` references (doc comments, a test name/message) left
over from the pre-v0.9.3 tool spelling.
Verified: cargo test -p codewhale-tui acp_server (34/34) and
route_budget (11/11) pass; the renamed cancel test passes 15/15 runs
in isolation (was ~50/50 before the rename). cargo fmt and the
project's workspace clippy gate (fmt + clippy --workspace
--all-features -D warnings, CONTRIBUTING.md allow-list) are clean
except one pre-existing, unrelated lint in mcp.rs.
Blocker 3 (build_system_prompt, deleted in a98b184f5) is Hunter's to
carry per the review; not touched here.
Drafted with agent assistance (Claude Code); build-verified by the
human author before pushing.
The ACP session/prompt path only streamed text; it never executed the
tool calls a model requested, so editors driving CodeWhale over ACP
(Zed, and third-party bridges like acp-deepseek-adapter) got a
chat-only agent with no real code-editing capability. This wires the
existing ToolRegistry into the ACP turn loop instead of duplicating a
new one:
- run_agentic_prompt_turn drives multi-round tool_use/tool_result
turns (capped at MAX_ACP_TOOL_ROUNDS) over the same file/search/git/
patch/shell tools the TUI uses, and reuses response_id_policy so
every tool-round response still gets the client-specific id
translation (Zed/avante.nvim) the existing streaming path relies on.
- Shell access is gated on the client declaring `terminal` support at
`initialize` (default false/restrictive); MAX_ACP_SESSIONS caps
concurrent sessions with true insertion-order eviction (VecDeque,
not HashMap iteration order).
- Tool-call cancellation signals a CancellationToken and waits for the
running tool (including a child shell process) to actually stop
before returning, rather than abandoning it.
- max_tokens for the ACP path now resolves through the same
route-limits machinery the TUI/CLI use (effective_max_output_tokens_for_route)
instead of a fixed 4096 fallback.
- scripts/build.ps1: release build script for Windows PowerShell 5.1,
used to produce the ACP binary tested against Zed on Windows.
34 unit tests cover the turn loop, tool execution against a real
workspace, cancellation mid-tool, and concurrent sessions with
independent registries, all against in-memory streams (no live
provider needed).
Drafted with agent assistance (Claude Code); build-verified and
reviewed by the human author before submission.
A descendant subagent could widen the absolute recursion budget inherited
from its root session by passing an explicit max_depth on a nested spawn.
child_max_spawn_depth_for_spawn dropped the inherited budget for the
explicit-request arm, so child_max_spawn_depth_for_spawn(2, 2, Some(8), None)
returned 8 even though the root selected an absolute maximum of 2 — the
descendant could then keep spawning past the intended boundary.
Take the min with the inherited budget in the explicit-request arm, mirroring
the profile-hint arm that already did so. A request or hint may only narrow,
never widen, the root/session's chosen absolute depth. The global
MAX_SPAWN_DEPTH_CEILING added in #3931 stays the outer bound.
Adds a dedicated regression test for the issue scenario and updates the two
assertions in test_child_max_spawn_depth_profile_hint_only_narrows that had
encoded the old widen-up-to-ceiling behavior.
Fixes#5253
Implemented with AI-assisted tooling; authored and reviewed by the contributor.
(cherry picked from commit 4e5ac2ba39)
The mode dial (what the agent does: Work / Operate / Plan) and the
permission dial (how approvals happen: Ask / Auto-Review / Full
Access) both carried A-words — "Act" and "Ask" — and error copy
conflated them. Rename the mode's user-facing name to Work in every
locale; "work" parses everywhere "act" does, and "act" stays as a
back-compat alias for configs and muscle memory.
Internal identifiers (AppMode::Agent) are unchanged; aligning the
backend enum names with the frontend vocabulary is tracked as the
v0.9.7 vocabulary unification.
#3866 made start_mcp_server and rlm eval fail closed in Full Access
because that posture opens no approval modal — which stranded the
calls: an operator who had granted full access could not run the tool,
could not approve it, and had to leave the posture to proceed. Full
Access already grants everything these calls can do (the shell can
spawn the same processes), so the gate protected nothing while
blocking the documented flow.
Owner decision 2026-08-10: Full Access auto-approves. The resolver
now returns Allow for non-bypassable holds under auto-approve/Yolo;
every posture that can open the modal (default suggest, never) still
prompts or denies exactly as before, and repo law still overrides.
The #3310 split moved is_mcp_stale_session_body into mcp/wire.rs and
updated the callers the branch could see, but the v097 lane had grown
a stdio-reader call site in root mcp.rs after the branch point. The
merge kept both halves; the import now names both classifiers.
Three verbatim moves from the isolated agent worktree: types into
project_context/types.rs, the workspace pack pipeline, and the
constitution loader into project_context/constitution.rs. Root
re-exports keep every existing path resolving; assembled system
prompts unchanged.
Three verbatim moves from the isolated agent worktree: HTTP transport
into mcp/http.rs, shared wire-format helpers into mcp/wire.rs. Every
MCP transport now sits behind the same boundary; no behavior change.
The follow-up that was supposed to call this prompt builder never
landed: sibling wrap_synthesis did get wired, so the routing path
shipped without ever asking for a synthesis prompt. The doc's "public
so callers outside this crate can unit-test it" was false twice over —
no such test exists and the tools module is private. estimate_tokens,
EvidenceRouting, and wrap_synthesis all have callers and stay.
Deletion-work-order group 11; proof: RUSTFLAGS=-Dwarnings cargo test
-p codewhale-tui --lib large_output_router.
#5060: experimental search re-hardcoded a 16-worker ceiling instead of
reading the Fleet seam, so a deliberately small pool still admitted
16-wide batches and a larger pool could never use its width. Every
validation entry point now has a _with_limit twin that takes the
resolved Fleet ceiling ([workflow] max_concurrent and a profile's
delegation.max_concurrency, the lower present bound winning), and the
frozen receipt records which of Fleet limit or crate fallback actually
bounded the run — kept out of the preregistration hash on purpose,
because pool admission is an operational fact about the run, not a
scientific input.