A thread-backed turn shows no reasoning and can call no tool #164

Closed AtlantisPleb opened this 3d ago 3 comments

Outcome

A thread-backed coder session shows the model's reasoning and its tool calls, as the conversation-backed one did.

Current behavior

5f36e4c839 moved openagents coder onto its own thread, which is what stops a terminal prompt from landing in the account's one conversation. Two things did not survive the move, and both were asked for explicitly.

Reasoning is gone. OpenAgents.Providers.ProviderEvent has no reasoning member. Its union is:

response_started | text_delta | tool_call | usage | response_completed | failed | cancelled

lib/openagents_web/controllers/inference_proxy_controller.ex drops what it cannot name, so a grant-backed caller receives text and nothing else. The reasoning_delta the chat path emitted came from OpenAgents.Chat.OpenRouter and the Gemini decoder — a different stack that the proxy does not use. The dim-italic reasoning entry the CLI renders now appears only under --offline.

Tool calls do not occur. The chat lane executed tools server-side. The proxy is a bare completions surface that forwards whatever tools a caller declares; the CLI declares none and holds no runtime to execute them, so the mapping never fires. ProviderEvent does carry tool_call, so the wire supports it — nothing produces one on this path.

Neither is a defect in the CLI. Both live in OpenAgents.Providers and the proxy.

Why this matters more than it looks

The reasoning display was built deliberately. In a captured live turn, 163 reasoning deltas arrived against 234 text deltas — a large share of what a turn produces. Without it, a turn that thinks for twenty seconds shows an elapsed clock and nothing else, which is the exact complaint that produced the reasoning work in the first place.

Tool calls are the difference between a chat window and a coding agent. Their absence also removes the evidence a reader needs to understand a reply: two sentences either side of an invisible tool call read as one confused sentence, which is what the tool-call rendering was built to fix.

Work

  • Add a reasoning member to ProviderEvent and carry it through the proxy. The providers already parse reasoning — OpenAgents.Chat.OpenRouter.ResponsesStreamDecoder splits it, and the Gemini adapter reads parts flagged thought: true (5375462). The gap is the neutral union in the middle, not the providers at either end.
  • Decide how a thread-backed caller gets tools at all. Two shapes, and they are different products: the caller declares tools and executes them locally, or the server executes them as the chat lane does. The zero-base document (docs/2026-08-23-agent-tools-zero-base.md) and the reach work in TOOL-005 both bear on this, and the answer should be consistent with them rather than a third mechanism.
  • Whatever is chosen, keep ReplyChunk in the CLI unchanged. It already carries text | reasoning | tool_call | tool_result, and the interface renders all four today under --offline.

Acceptance criteria

  • A thread-backed turn streams reasoning, rendered as the conversation-backed one was.
  • A thread-backed turn can call a tool, and the transcript shows the call and its outcome.
  • --offline behavior is unchanged.
  • The proxy still never lets a provider credential or raw provider JSON cross its boundary.

Verification

A proxy test asserting a reasoning event survives translation; an end-to-end thread turn showing reasoning in the terminal; a tool call executed and rendered; mix precommit and the CLI's pnpm run verify.

Dependencies

Follows #163 (5f36e4c839). Audit stage 4 — chunked delivery, since the proxy currently builds the whole SSE body and sends it once — is independent but related: reasoning that arrives in one buffered frame is not the same feature as reasoning that streams.

  1. AtlantisPleb opened this issue 3d ago
  2. A AtlantisPleb Author 2d ago

    Server half landed on main as c26c188 (pushed, WAL receipt to follow this push; full mix precommit green: 4172 tests, 0 failures). Root causes and fixes:

    • ProviderEvent gains a reasoning_delta member; the OpenRouter decoder emits it from delta.reasoning (the live ox-alpha coder path was silently discarding it) and the OpenAI decoder from response.reasoning_*_text.delta.
    • The proxy now emits reasoning as choices[0].delta.reasoning (whole, since the proxy still buffers the SSE body), keeps assistant tool_calls on replayed history (previously dropped when content was empty), and both adapters replay faithful transcripts: OpenRouter as assistant tool_calls + role:"tool" messages, OpenAI as interleaved function_call/function_call_output items.
    • Conversation lane verified unchanged (reasoning is a no-op in turn_server/job_server; persisted messages carry no reasoning). PROVIDER-001 prose updated in INVARIANTS.md.

    Remaining half is CLI-side and is filed as OpenAgentsInc/openagents#31: parse delta.reasoning into the coder's reasoning entries and switch the client tool loop to faithful tool_calls replay instead of plain-turn feedback. This issue stays open until the coder session visibly shows both.

  3. A AtlantisPleb Author 2d ago

    Both halves are now on main: the server half as c26c188 (reasoning member, faithful tool replay) and the CLI half as OpenAgentsInc/openagents cf1861c9cb (delta.reasoning parsed and rendered, faithful tool_calls history). The outcome is delivered in code end to end with tests on both sides. This issue can close once the deployed forge serves c26c188 or later (see #187 for the main-versus-deployed gap) and a coder session against production visibly shows reasoning and tool calls.

  4. A AtlantisPleb Author 2d ago

    Fixed on main and proven live. OpenAgents.Providers.ProviderEvent carries a :reasoning_delta member, and a thread-lane turn streams it — probed against the dev server just now with a reasoning-inducing prompt on ox-alpha: 58 reasoning deltas over the thread grant.

    The tool half is proven at scale rather than by inspection: tonight's whole delegation fleet ran on the thread lane, and those sessions read files, ran shell commands, and called their delegate tool throughout. A thread-backed session shows reasoning and calls tools, as the conversation-backed one did.

    Production still has to deploy main for this to be true there — #187.

  5. closed this as completed 2d ago
Sign in with GitHub to comment on this issue.