feat(chat): omp threads show thinking, live subagents, diffs, and real tool output - #5
Merged
Merged
Conversation
…l tool output The omp provider rendered a bare timeline: thinking was discarded, tool rows flattened to generic labels with one-line 84-char output summaries, inline diffs were dropped on the wire, and subagents never reached the Agents panel because omp's subagent frames are RPC-mode-only. Server: - OmpAdapter buffers agent_thought_chunk segments and settles them as reasoning items; ingestion appends them as reasoning.completed activities. - The wire projection keeps a capped multi-line rawOutput.fullText and the ACP diff content blocks on tool.completed rows (tool.updated stays slim), plus a bounded toolInfo (device/tool name, action, scalar args, eval code) derived from rawInput — retroactive for already-persisted threads. - omp's task tool calls synthesize the full subagent lifecycle from the TaskToolDetails streamed in rawOutput: task.started per subtask, deduped task.progress with intent/tool/usage/model, and task.completed driven by per-subtask progress status — the async-job mode acks the call as completed while agents still run, and the runtime evicts completed calls from its merge map, so the roster is cached per toolCallId and agents outliving the turn are marked backgrounded instead of spinning forever. - AcpSessionRuntime gains an opt-in emission predicate so omp task progress in rawOutput survives the title/detail update-suppression gate. - Informative provider titles (omp per-call intents) win the row heading over flattened action labels, and update frames no longer overwrite them with the bare "Tool" fallback. Web: - Thinking rows render italic/dimmed and expand to markdown. - Expanded tool rows show the full output, per-file FileDiff blocks built from the preserved diff contents, structured tool args, syntax-highlighted eval code, and markdown LSP results; icons key on the tool identity. Verified live against a real omp session: thinking, intent titles, inline diffs, and the Agents panel roster with per-agent status, intent, model, token and tool counts. Built with Claude Fable 5 on Oh My Pi.
…chat # Conflicts: # apps/web/src/components/chat/MessagesTimeline.tsx
leonardoxr
added a commit
that referenced
this pull request
Aug 19, 2026
…every thread Rich omp chat rows (#5) put a 4000-char output excerpt and inline diffs on every `tool.completed` wire payload. Those rows are permanent, so a resumed thread carried the output of every tool it had ever run: the transfer budget measured a 28.3 KiB thread snapshot against a 7.5 KiB budget and 40.8 KiB of total thread wire against 15.5 KiB, for every provider, not just omp. It also made the wire projection *add* fields, which broke the invariant that a projected payload derives exactly what the raw one does. Output and diffs now travel on request. `orchestration.getActivityOutput` takes the threadId plus the activity id clients already hold and reads the persisted projection row — full payloads were always kept there, only the wire was slimmed — through a primary-key point read on the read-model facade. The body is capped at 200k chars with a `truncated` flag, since it is fetched for the one row a user expanded rather than broadcast. A missing row (a revert can prune the turn still on screen) reads as an empty body, which renders as the summary the row already shows. Web rows fetch on expand through a per-activity cached query, so scrolling a virtualized row out and back does not refetch, and the one-line detail stays as the body until the output arrives. ACP diff paths now feed the wire's `files` list, so a collapsed file-change row still names what it touched without a fetch. Mobile never rendered inline output and stays summary-only. Wire cost, codex column of the budget report, before → after: - thread snapshot HTTP wire: 28.3 KiB → 6.4 KiB (budget 7.3) - measured turn WS wire: 12.2 KiB → 6.5 KiB (budget 7.8) - measured turn WS decoded: 76.0 KiB → 51.9 KiB (budget 66.4) - total thread wire: 39.8 KiB → 12.9 KiB (budget 15.1) Verified: apps/server 2722 passed (3 were failing), apps/web 2560, apps/mobile 705, typecheck clean. Built with Claude (opus-5) on Oh My Pi.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The omp provider rendered a bare chat timeline compared to the omp CLI: thinking was discarded at ingestion, tool rows flattened to generic "Ran command"/"Tool" labels with one-line 84-char output summaries, omp's inline per-file diffs were dropped by the wire projection, lsp/debug/browser/eval calls all rendered as anonymous hammer rows, and subagents never reached the spawn CTA or Agents panel (omp's subagent frames are RPC-transport-only; ACP carries none).
Fix
All the missing data already crosses the ACP wire — it was being dropped at the parser, the adapter, and the wire-slimming projection. The UI is payload-driven, so most of the work is server-side and applies retroactively to persisted threads.
Server
OmpAdapterbuffersagent_thought_chunksegments and settles them asreasoningitems →reasoning.completedactivities.ActivityPayloadProjectionkeeps a capped multi-linerawOutput.fullText, the ACP{type:"diff"}content blocks, and a boundedtoolInfo(xd:// device name, action, scalar args, eval code) ontool.completedrows;tool.updatedstays slim.tasktool calls synthesize the full subagent lifecycle from theTaskToolDetailsstreamed inrawOutput:task.startedper subtask, fingerprint-dedupedtask.progress(intent, current tool, tokens, duration, model), andtask.completeddriven by per-subtask progress status. The async-job mode acks the call as completed while agents still run and the ACP runtime evicts completed calls from its merge map, so the roster is cached pertoolCallId; agents outliving the turn are marked backgrounded.AcpSessionRuntimegains an opt-in emission predicate so task progress riding inrawOutputsurvives the title/detail update-suppression gate (other ACP providers unchanged).Web
FileDiffblocks (viaparseDiffFromFile, no new deps), structured args, syntax-highlighted eval code, and markdown LSP results; icons key on tool identity.Verification
omp acpsession end-to-end: thinking rows, intent titles, inline diff cards, spawn CTA transitioning "2 working → ✓ completed · Σ 33.4k", and the Agents panel roster with per-agent status/intent/model/token/tool telemetry.reasoning.completedrows fall through to the generic info row (mobile parity is follow-up work).Built with Claude Fable 5 on Oh My Pi.
Screenshots
All captured live against a real
omp acpsession (worktree dev server, scratch project). No "before" shots — before this change these turns rendered as a text bubble plus generic collapsed "Tool" rows.Intent titles mid-run — the tool row heading is omp's per-call intent ("Adding farewell function"), not "Changed files":
Subagent spawn CTA, live — synthesized
task.*lifecycle drives the existing CTA: "Kicked off 2 subagents · 2 working", plus the background-agents banner:Agents panel, live roster — scout badges, elapsed timers, working state, fed entirely from the ACP-streamed TaskToolDetails:
Settled CTA with usage rollup — "Ran 2 subagents · ✓ completed · Σ 33.4k":
Agents panel, settled — per-agent intent ("Reading greet.js"), model (
claude-fable-5:low), token and tool counts, durations; italic thinking row and intent-titled hub row in the timeline:Thinking + tool rows in the expanded fold — italic thought rows interleaved with read/command/edit rows and the changed-files card:
Assets live on the orphan branch
assets/omp-rich-chatto keep them out of the feature history.