Skip to content

v0.1.5 — subagent model override · observability · debug-mcp

Choose a tag to compare

@justrach justrach released this 07 May 09:12
· 170 commits to main since this release

What's new

feat(task) — per-spawn model override for subagents

The Task tool now accepts an optional model field, so the parent can
spawn a subagent on a different model from itself in the same run:

{
  "tasks": ["summarize crates/forge_app"],
  "agent_id": "muse",
  "model": "gpt-5.5-medium"
}

The override is validated against the agent's already-authenticated
provider — pass a model that isn't on the parent agent's authenticated
provider list and you get a clean error listing what is, instead of a
silent cross-provider switch or a confused 401 deep inside the request
path. The subagent banner surfaces the override
(MUSE [Agent · gpt-5.5-medium]) so the parent/child story stays
legible in the trace.

Plumbed end-to-end: TaskInput.modelChatRequest.model_override
validated in ForgeApp::chatagent.model(override) → recorded in
the trajectory as a requested-vs-resolved diagnostic round-trip.

feat(observability)agent_run + agent_run_end trajectory events

Two new TrajectoryPayload variants make per-spawn behaviour
inspectable:

  • agent_run captures the spawn diagnostic: agent_id,
    parent_agent_id, requested_model, resolved_model, plus a
    agent_version SHA-256 prefix of the agent's system prompt template.
    Two edits to forge.md are still both forge but produce different
    hashes — so a rollup query can group runs by behaviourally distinct
    variants rather than by agent_id alone.
  • agent_run_end carries the per-spawn fitness vector: turns,
    prompt/completion tokens, total tool calls, tool errors, wall-clock
    ms, and an interrupt_reason if the run terminated abnormally. Sums
    what's already computed turn-by-turn so the bottom of run() writes
    the full picture without re-walking events.

/trace renders both, including the requested-vs-resolved diff when
they differ. This is the substrate for an empirical archive of agent
variants — no mutation logic yet, just observation.

feat(debug)graff debug last-mcp-call

A new top-level subcommand that prints recent MCP request/response
pairs as JSONL, no log-spelunking required:

graff debug last-mcp-call -n 5 --server codedb --tool codedb_bundle --pretty

forge_infra::mcp_debug writes a ring buffer at
<base>/debug/mcp-recent.jsonl capturing the literal arguments that
hit rmcp::call_tool, the round-trip duration, and the outcome
(returned vs failed + error). When an MCP server complains "arguments
arrived empty," this pins the loss to the wire vs upstream of the
client.

fix(tools) — rewrite oneOfanyOf for OpenAI Responses

OpenAI's tool-schema validator (including
chatgpt.com/codex/responses) rejects oneOf outright with
'oneOf' is not permitted', regardless of strict mode. anyOf is
accepted by both OpenAI and Anthropic, and for discriminated unions
whose branches pin a property to different const values (like
codedb_bundle's ops schema) the two are functionally equivalent —
no input matches more than one branch anyway.

A new forge_app::utils::rewrite_one_of_to_any_of recursively
rewrites every oneOf to anyOf before strict-mode normalization
runs, in both the legacy chat-completions and Responses-API paths.
Bundled codedb is now started with CODEDB_DISCRIMINATED_SCHEMA=1
so its discriminated branches actually flow through the rewrite (vs
arriving as a bare {type: "object"} and triggering missing-path /
missing-pattern runtime errors). Regression test in
forge_repo covers the codedb_bundle shape end-to-end.

feat(ui) — banner logs path, softer status icons, reasoning hidden by default

  • The interactive banner now shows the log directory under Logs: so
    agents debugging graff can find it without spelunking through
    forge_tracker.
  • Status icons softened: for completion, for error, dimmer
    info dot, brighter timestamps. Reads less like a syslog and more
    like a chat trace.
  • Reasoning summaries (Evaluating ..., Exploring ...) are hidden
    unless --verbose. They're available in the trajectory if you want
    them; the live REPL stays signal-rich.
  • Tracing: FORGE_LOG falls back to RUST_LOG so anyone with Rust
    muscle memory just works. Default filter is module-segment-agnostic
    (debug not forge=debug) so events from forge_infra,
    forge_domain, forge_main, etc. actually land in the log file.

Parallel tool calls — visible header

When the model emits ≥2 tool calls in a single assistant turn, an
⇉ N parallel tool calls (breakdown) header lands above them — the
batch is now visible as a group rather than dissolving into a stream
of unrelated icons.

Release pipeline

The CI release workflow is removed in this version. The previous
generator pushed to antinomyhq/npm-code-forge,
antinomyhq/npm-forgecode, and antinomyhq/homebrew-code-forge
upstream-fork repos this codegraff fork doesn't own. Releases are
manual from 0.1.5 onwards: build locally, gh release upload. We'll
re-add CI builds once we have target distribution channels under our
own org.

Binaries

This release ships macOS arm64 only (codesigned with Developer ID,
notarized by Apple). Other platforms can be built from source:

cargo build --release --bin graff --bin codegraff

Notes

  • Workspace version bumped to 0.1.5.
  • Tag: v0.1.5.

🤖 Generated with Claude Code