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>
- modelsdev.rs: ModelCatalog parses models.dev api.json → ModelInfo keyed by
(provider, model); 24h file cache at ~/.cache/ai-harness/models.json with a
baked assets/models-snapshot.json fallback so cost/limits work offline.
load_cached_or_baked (no network) + refresh/refresh_default_cache (background).
- App: loads the catalog at init (cached-or-baked, never blocks), warms the cache
in a background task, and passes the session model's pricing into RunConfig.cost
so session cost accrues for real.
- TUI: AppState tracks session_cost/session_tokens from Session events; status bar
shows "<tokens> · $<cost>". Snapshots updated.
- 7 modelsdev tests (parse, defaults, unknown-model, baked snapshot, cache TTL,
cached-vs-baked load).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
App::init wires config loading, an in-memory Store, EventBus, PermissionService
with the M1 auto-approve stub frontend, the full built-in ToolRegistry, and a
ProviderRegistry populated with AnthropicProvider when an API key is available
(config or ANTHROPIC_API_KEY). App::run_prompt creates a root session, appends
the prompt as a user message, and drives engine::run_session to completion;
final_text reads back the concatenated text parts of the last message.
harness-tui's `harness` binary gains a `run -p "<prompt>" [-m provider/model]`
subcommand built on this. Manually verified end-to-end against the real
Anthropic API: an empty API key produced a genuine 401 that our SSE error
path correctly classified as ProviderError::Auth and surfaced as a clean
CLI error message (exit 1) -- confirming the full request/header/error-
handling pipeline works against the live service, not just fixtures.
This closes M1 (docs/10-milestones.md): headless core loop + Anthropic
provider, config loading, all six built-in tools, and the debug CLI.
131 tests passing across the workspace, clippy clean, fmt 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.