Stream Planner chat live: reasoning, tool calls, timers, and harness tools
lavaman131 opened ·
> **Scope (updated):** This issue covers only the server, harness and protocol pipeline. The chat presentation (working state, tool rows, timers, reasoning display, scrolling) belongs to @MaggieAppleton's design track, **#296** and #162. Items below that describe UI are kept as context for the data that UI needs, not as UI direction. Protocol additions stay optional and additive, so #296's client can use them when ready.
## Problem
A Planner request usually looks frozen until the turn ends. The transcript shows little or nothing while the Planner reasons or runs tools. Then text and tool rows show up in one burst. Running tools show no elapsed time. Under `HARNESS=atomic`, Atomic's own tools (shell, file read/edit, workflows, subagents, extension tools) don't appear at all.
The target is the claude.ai / ChatGPT experience: text streams token by token, a "thinking" state shows right away, tool calls appear the moment the model starts them and keep updating live, and timers count up while work runs.
## What the code does today
**Server: `translate()` in `apps/server/src/chat/service.ts`** - Handles only `text-*`, `tool-call`, `tool-result`/`tool-error`/`tool-output-denied`, `tool-approval-request`, `error`, `finish`. - Drops `reasoning-start/delta/end` (the Atomic adapter emits them from `thinking_delta`), so the turn shows nothing during long reasoning. - Drops `tool-input-start/delta/end`, so a tool appears only after its full input has streamed. - Records a start time in `chat.timings`, but `Wire.Activity` only gets `took` when the tool finishes. The client can't run a live timer. - Any `tool-call` whose name isn't in `ACTIVE_TOOLS` (= `PLANNER_TOOL_NAMES`) aborts the turn as a boundary failure.
**Atomic adapter: `apps/server/src/harness/atomic/adapter.ts`** - `hostTool()` emits `tool-call`/`tool-result` only for Chopin host tools. - In full mode (`policy.full`), the `tool_call` hook lets Atomic builtin and extension tools run, but `observe()` doesn't translate `tool_execution_*` events into stream parts. Those tools never reach the transcript. If they did, `ACTIVE_TOOLS` would abort the turn.
**Client (context only; UI owned by #296): `apps/web/src/chat/transcript.tsx` (`ToolRun`)** - The running state shows a loader, the current tool name, and "N done". There's no elapsed time and no per-tool progress. - `Chat.Activity` has no `startedAt`, input-streaming state, or partial output.
## Proposal
1. **Show progress right away.** Show a "Thinking…" or working indicator as soon as the turn starts. Optionally stream reasoning into a collapsed "Thinking" block, matching claude.ai/ChatGPT. Persist it or keep it ephemeral, whichever fits the transcript model. 2. **Stream tool calls live.** - Publish a `running` activity on `tool-input-start`, with the args filling in from `tool-input-delta`. - Update the same row on result or error. - Show each tool as its own row, interleaved with text in order, instead of a single "N done" summary. 3. **Add live timers.** Add a server-authored `startedAt` (or equivalent) to `Wire.Activity` and to turn state. The client then ticks elapsed time for running tools and for the whole turn, and freezes it at `took` when the tool finishes. Respect reduced motion. 4. **Stream text smoothly.** Coalesce `chat:delta` renders with rAF batching so long answers stay smooth and the scroll stays pinned to the bottom unless the user scrolls up. 5. **Ingest custom harness tools.** - Atomic adapter: translate `tool_execution_start/update/end` for non-host tools (builtins, extension tools, workflow/subagent tools) into `tool-call`/`tool-result` parts, including partial output where Atomic provides it. - Replace the static `ACTIVE_TOOLS` check with the session's active tool set. `opened.session.activeTools` is already used for job turns. Full Atomic sessions accept their builtin and extension tools without weakening the `copilot-sdk`/`pi` boundary. An unknown tool must still fail closed there. - Display: friendly labels for known tools, a generic row for unknown ones. Truncate and redact args and results. Keep `read_reference` content private, as it is today.
Protocol changes go in `packages/protocol/chat.d.ts` before or together with the server and client changes.
## Acceptance criteria
- [ ] A visible working/thinking state appears within one frame of a turn starting, before the first token or tool call. - [ ] Text streams incrementally. No turn looks frozen while the server receives deltas. - [ ] A tool row appears when the tool's input starts streaming, shows a live elapsed timer while running, and settles to done/failed/refused with its final duration. - [ ] A turn-level timer updates live and stops on finish, abort, or error. - [ ] Under `HARNESS=atomic` (full session), shell, file, workflow, subagent, and extension tool calls appear live in the transcript without aborting the turn. - [ ] Under `copilot-sdk` and `pi`, a tool outside the session's active set still aborts the turn as a boundary failure (regression test). - [ ] Reconnecting mid-turn restores the in-progress text, running tools, and timers. - [ ] Unit tests for `translate()` cover reasoning, tool-input streaming, and harness-specific tool sets. Adapter tests cover Atomic `tool_execution_*` translation. A Playwright test (fake harness, `AGENT=on`) asserts incremental rendering and a ticking timer.
## Related
- #162 covers the visual design of the tool-activity component. This issue covers the event and streaming pipeline that design depends on (its "event-model gaps" follow-up). - #213 added the full Atomic Planner session whose tools aren't surfaced yet.
Assistant-workflow: inline Assistant-duration: unmeasured Assistant-verification: code inspection passed: traced translate() in chat/service.ts, atomic adapter observe()/hostTool(), and ToolRun in transcript.tsx
lavaman131added label enhancementlast month on GitHub
lavaman131assigned lavaman131last month on GitHub
lavaman131referenced this in [Design] Rebuild the workflow run card with assistant-ui Elements #307last month on GitHub
lavaman131referenced this in [Design] Replace the composer @ mention picker with assistant-ui mentions #308last month on GitHub
lavaman131referenced this in [Design] Conversation controls with assistant-ui Elements: message queue, stopped run, error state #309last month on GitHub
lavaman131referenced this in [Design] Research and references with assistant-ui Elements #310last month on GitHub
lavaman131referenced this in [Design] Decisions display with assistant-ui Elements (option list, question flow) #311last month on GitHub
MaggieAppletonmentionedlast month on GitHub
MaggieAppletonsubscribedlast month on GitHub
lavaman131referenced this in Stream Planner tool calls from their own start and end, including Atomic's own tools #314last month on GitHub
lavaman131referenced f5d6164c9bcalast month on GitHub
lavaman131referenced 8e4c52f278d9last month on GitHub
lavaman131referenced dc226131a4ddlast month on GitHub
lavaman131referenced 9efc834927cclast month on GitHub
lavaman131referenced 594ec938da80last month on GitHub
lavaman131referenced 962fcf0a9506last month on GitHub
lavaman131referenced 17d4f44314balast month on GitHub
lavaman131referenced d618e483469dlast month on GitHub
lavaman131referenced 2cfc207f9ad1last month on GitHub
github-actions[bot]added label triage/human-responselast month on GitHub
lavaman131referenced cecdfd07cad3last month on GitHub
lavaman131referenced b312afd5890blast month on GitHub
lavaman131referenced dfe16a95f1dflast month on GitHub
lavaman131referenced cbc71f22f08alast month on GitHub
lavaman131referenced 89b4c32b0772last month on GitHub
lavaman131referenced a46b07252e62last month on GitHub