Files
wehub-resource-sync d68f003000
CI / Change detection (push) Has been cancelled
CI / Version drift (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Workflow RLM cache (push) Has been cancelled
CI / Test (macos-latest) (push) Has been cancelled
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / npm wrapper smoke (push) Has been cancelled
CI / Mobile runtime smoke (push) Has been cancelled
CI / Workflow lint (push) Has been cancelled
CI / Documentation (push) Has been cancelled
Web Frontend / Lint & Type Check (push) Failing after 1s
Auto-close harvested PRs / close (push) Failing after 1s
cargo-deny / cargo-deny (bans licenses sources) (push) Failing after 1s
Security audit / cargo-audit (push) Failing after 1s
Sync to CNB / sync (push) Failing after 1s
Web Frontend / Deploy to Cloudflare (push) Waiting to run
cargo-deny / cargo-deny (advisories) (push) Failing after 3s
chore: import upstream snapshot with attribution
2026-07-13 12:08:23 +08:00

10 KiB

The CodeWhale Agent Runtime — one durable substrate, familiar launchers

This document explains how sub-agents, the headless exec path, and Agent Fleet relate. It exists because these had drifted into two parallel "worker" systems, and the fix is to make the fleet-backed worker run the durable primitive. "Sub-agent" remains useful product vocabulary for a nested role, but it must not imply a separate execution substrate with weaker lifecycle semantics. It also answers the open direction question in #2972 ("how much Claude Code convergence is right?").

The core idea

There is exactly one thing that runs detached agent work: a headless agent runtime wrapped in a durable worker lifecycle. It is a model loop with the full (policy-gated) tool surface that can, in turn, delegate child work through the same lifecycle. Everything else is just a different way to launch that one runtime, or a different way to observe it.

                         ┌───────────────────────────────┐
                         │     headless agent runtime     │
                         │  (full tools + can sub-spawn)  │
                         └───────────────────────────────┘
                            ▲             ▲             ▲
            launches │              │              │ launches
                     │              │              │
        ┌────────────┴───┐  ┌───────┴────────┐  ┌──┴───────────────────┐
        │   TUI turn     │  │ `codewhale     │  │   Agent Fleet         │
        │  (interactive, │  │   exec`        │  │  (durable: ledger,    │
        │   in-process)  │  │  (headless CLI,│  │   scheduler, SSH,     │
        │                │  │   anyone/any-  │  │   alerts) — launches   │
        │                │  │   time)        │  │   `codewhale exec`     │
        └────────────────┘  └────────────────┘  │   per worker          │
                                                 └───────────────────────┘
  • A sub-agent is the user-facing name for a nested assignment with a role (explore, review, implementer, verifier, ...). It should be backed by the same worker run lifecycle as fleet. agent is the model-facing launcher, not a second runtime.
  • codewhale exec is the headless front door: usable by anyone at any time (CI, scripts, another agent), full tools, emits a stream-json event stream, and can spawn sub-agents. It is the runtime with a CLI on it.
  • A fleet worker is a codewhale exec run that the fleet launches and tracks durably — locally as a subprocess, or remotely as ssh host … codewhale exec …. The fleet does not re-implement execution; it adds orchestration (durable ledger, scheduling/leasing/retry, host transport, alert escalation) over the one runtime.

So "fleet vs sub-agent" is not two categories. It is the same headless run: Fleet is the durable control plane, while sub-agent is the role/UX vocabulary for a nested worker.

The cutover rule

If a detached agent child can fail on a one-off provider timeout with no retry while an equivalent fleet worker would retry and preserve ledger evidence, then the cutover is incomplete. Treat that as a CodeWhale runtime gap, not as normal "sub-agent behavior".

The compatibility agent runtime now retries transient provider header, stream, and timeout failures with backoff before marking a worker interrupted; when retries are exhausted it preserves a checkpoint and returns a continuation handle. The remaining convergence work is to keep that lifecycle durable across process restarts, remote execution, and full fleet-ledger scheduling.

The target rule is:

  • durable or long-running work goes through the fleet worker lifecycle;
  • agent should enqueue or observe a fleet-backed worker run instead of owning an independent lifecycle;
  • in-process children are allowed only as a small compatibility/latency optimization, and they must expose the same terminal states, retry semantics, receipts, and inspection handles as the fleet path.

In product language it is fine to say "open a sub-agent". In architecture language that means "start a nested fleet worker with this role".

Why this shape (and why it fixes the lag)

The motivating problem: spawning many in-process sub-agents made the TUI lag, because each child cloned a heavy runtime and rebuilt the whole tool registry, and the TUI rendered a full card/transcript per child.

Surveying Claude Code, Codex, and Kimi, the thing that keeps an orchestrator light at high fanout is not a process boundary — all three run sub-agents in-process. It is isolation + a compact event stream:

  • a child's transcript never flows back into the parent — the parent gets a result summary and a small lifecycle event stream;
  • the UI renders counts (2 running / 3 done), not a child session per worker;
  • each worker's tool surface is built directly from a role/capability profile, not "build everything then filter".

"Headless" therefore means the execution is not shaped like the UI — it does not mean fewer abilities. A headless worker keeps the full toolset and can spawn sub-agents.

When the work also needs to be durable (survive the TUI closing, a laptop sleeping) or remote (SSH), the fleet runs the worker out-of-process as codewhale exec. The heavy construction then lives in another process entirely, so the orchestrator stays smooth regardless of fanout, and the run survives restarts — the day-scale autonomy goal of #3154.

One recursion axis

A worker runs at spawn_depth = 0 and may spawn children while spawn_depth + 1 ≤ max_spawn_depth, so a budget of N affords N nested delegation levels. Sub-agents and fleet workers share one axis, sourced from codewhale_config:

  • DEFAULT_SPAWN_DEPTH = 3 — the default budget for both standalone sub-agents and fleet workers (so they cannot drift into "two moving targets");
  • MAX_SPAWN_DEPTH_CEILING = 8 — the opt-in cap that every configured value (fleet max_spawn_depth, agent's max_depth) clamps to.

The root worker always runs even at budget 0; the budget gates child delegation. The default affords at least three nested levels.

Event vocabulary

The fleet ledger persists the worker's own event stream rather than a separate, simulated taxonomy. codewhale exec --output-format stream-json emits {"type": "content" | "tool_use" | "tool_result" | "workflow_event" | "metadata" | "done" | "error"} lines, which map onto the fleet ledger's FleetWorkerEventPayload (RunningTool, WorkflowEvent, Running, Completed, Failed, …). workflow_event carries the typed run/phase/task/gate receipt while a Workflow is in flight and is retained as a typed WorkflowEvent in the Fleet ledger; the enclosing worker still owns the terminal done or error. One vocabulary, two surfaces.

Convergence with Claude Code (#2972)

CodeWhale should converge with Claude Code on shape, not on branding:

  • Adopt: a headless runtime with a real CLI/SDK front door; sub-agents as isolated runs that return summaries (not transcripts); a compact, event-driven fanout projection; capability/role tool profiles; the skills ecosystem (#2743); structured run receipts.
  • Keep distinct: CodeWhale branding and first-class DeepSeek/GLM/MiniMax/ multi-provider support; the local-first Agent Fleet (durable, SSH-capable orchestration) as CodeWhale's own layer above the shared runtime; Workflow as the orchestration overlay.
  • Do not fork execution semantics per surface. The TUI, agent, exec, the Runtime API, and the fleet must all drive the same runtime and observe the same event stream — divergence there is what produced the "two moving targets" this document exists to prevent.

The litmus test for any new agent surface: does it launch and observe the one runtime, or does it invent a second one? Only the former is allowed.

What a next-major ("0.9-level") release would mean today

Refreshed 2026-07-12 from a full audit of the 0.9-era documents. The release label is a maintainer decision; this section describes scope, not a version promise. Most of what the old cutover plan called "0.9.0" already shipped in v0.8.68 (Plan/Act/Operate + posture cycle, the wired Workflow engine with a durable run journal, the Lane CLI/runtime, the setup wizard with operate_ready, the constitution rebalance, ProviderLake/Models.dev). What genuinely remains:

  1. Rebrand completion — the only hard-dated obligations: remove the deepseek/deepseek-tui binary shims and shim release assets; finish the Homebrew codewhale formula rollout (docs/REBRAND.md).
  2. Operate as a value stream — a control-board surface over the underwater shell (WIP, queue age, bottleneck); model-visible Work state (#3983); phase ledger (#4039); Workrooms Phase 2 (#3209/#3210) as the inbox substrate; receipt reconciliation.
  3. Flow control — real WIP limits and visible queues (#4015, #4016), reconciled with the shipped 16-concurrent/1k-run access model (#4292).
  4. Fleet/Workflow convergence residuals — live tmux/verifier-gate dogfood closing #4175/#4177/#4178/#4179; Fleet consuming canonical AgentProfiles; Conductor/topology (#4010, #4012) as stretch.
  5. TTC_DESIGN implementation — approved and now unblocked post-0.8.68.
  6. HarnessProfile completion — the status/UX display lane (docs/rfcs/HARNESS_PROFILE_CUTLINE.md).
  7. File decomposition, re-scopedui.rs (~13.6k lines) and main.rs (~12.1k) are the current offenders (docs/rfcs/FILE_DECOMPOSITION_0_9_0.md).

Explicitly deferred by their own documents: external workflow memory (boundary only), automatic harness evolution, hosted workrooms, constitution_modules (needs sign-off), permission profiles (#3211, needs design), and plan-ceiling probing (needs a product decision).