The `task` tool delegates to a SubagentSpawner (owned by the composition root):
resolves the agent, enforces the depth limit, applies permission intersection,
filters tools per-agent, and runs the child session foreground or background.
Foreground returns the child's final text; background registers on the job board
and detaches under the parent run token. The run loop injects the board into
primary-agent requests and reconciles terminal jobs each step.
Integration tests cover foreground run + alias reuse continuing the same child
session, depth-limit and unknown-agent rejection, and board prompt injection.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
evaluate_intersected() returns the more restrictive of a parent-effective
and child verdict (deny > ask > allow). JobBoard tracks subagent tasks with
per-agent aliases, reuse/LRU trimming, context-file reporting, reconcile, and
a formatForPrompt injection block; persisted to the `job` table so resume works.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- types::ModelCost {input, output, cache_read, cache_write} (USD per 1M tokens)
with cost_of(usage); reasoning tokens are billed within output by our providers
so they are not charged separately. ModelInfo gains cost + reasoning/tool_call/
attachment capability flags (all #[serde(default)] for forward-compat).
- Engine: RunConfig.cost threads pricing into process_step; on_finish now stamps
the real dollar cost onto the StepFinish part; StepOutcome carries per-step cost.
- run_session accumulates each step's usage and cost onto the session (previously
never updated) and republishes SessionUpdated — best-effort, store errors logged
not fatal.
- Store gains a single-session getter (Session cmd + get_session).
- App passes cost: None for now (real rates land with models.dev wiring).
- Tests: ModelCost::cost_of math (+ reasoning exclusion), and the multi-step
engine test now asserts session usage/cost accumulation.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
/v1/messages request builder (cache_control breakpoints on the first 2
system blocks + last 2 messages, tool schema -> input_schema, extended
thinking budget) and an SSE decoder built on eventsource-stream, mapping
content_block_start/delta/stop and message_delta into our normalized
LlmEvent stream (text, thinking+signature, streamed tool-call JSON
accumulated and parsed at content_block_stop, usage merged from
message_start + message_delta). AnthropicProvider wires this to reqwest
with x-api-key/anthropic-version/anthropic-beta headers and classifies
HTTP errors into RateLimited/Auth/Overloaded/Http. ProviderRegistry
resolves "provider/model" strings.
Also tightened processor::process_step's cancellation: the event loop now
selects the stream poll against ctx.cancel instead of only checking at the
top of the loop, so a blocked provider stream is actually interrupted by
abort (matches docs/02-engine.md's cancellation semantics).
127 tests passing, clippy clean.
Five of the six M1 built-ins (edit's replacer chain is a separate port).
bash uses process_group(0) + SIGKILL-the-group on timeout/cancel; glob/grep
are gitignore-aware via ignore::WalkBuilder and don't ask permission
(read-only); read/write/bash ask through ctx.ask with opencode's coarser
"always" pattern (first word + wildcard for bash, path for edit/read).
Wired tool output through the central 30k-char truncate() in the processor
so every tool gets spill-to-disk behavior for free. 70 tests passing,
clippy clean.
harness-core now has everything the headless agent loop needs:
- Tool trait/ToolCtx/ToolRegistry + 30k-char head+tail output truncation
- PermissionService: async ask over a oneshot + AppEvent::PermissionAsked,
Once/Always/Reject replies, an auto-approve stub for tests/headless runs
- Config: JSONC loading, bundled/global/project-chain/env precedence,
{env:VAR} and {file:path} interpolation
- llm.rs: LlmEvent/LlmRequest/Provider trait, wire message/content types
- engine/: outer loop (run_session), inner stream processor (persists
parts/messages as events arrive, executes tool calls inline), retry
policy (retries only the pre-first-event window), doom-loop guard,
system prompt assembly
Verified end-to-end against a scripted MockProvider: text -> tool call
(read) -> final text, with messages/parts persisted in the right shape,
plus a provider-error-before-any-event case surfacing as Errored (no
partial message left behind). 49 tests passing, clippy clean.
Seven-crate workspace per docs/01-architecture.md. harness-core gets the
domain types (Session/Message/Part/ToolState), a broadcast EventBus, the
last-match-wins wildcard permission evaluator, and a SQLite storage actor
(dedicated thread + mpsc, JSON-blob rows) with roundtrip tests. All other
crates are compiling stubs. CI runs fmt/clippy -D warnings/test.