Commit c48c1a8
feat(agents-server-ui): stream model reasoning into the UI (#4508)
## Summary
While the model is "thinking" (Anthropic extended thinking, DeepSeek-R1
reasoning, Moonshot K2, OpenAI Responses summaries) the agent response
now shows the reasoning text faded above the answer, with the existing
`Thinking` shimmer heading plus elapsed-time ticker. Once the reasoning
settles it collapses to `▸ Thought for 12s` — click to expand. Multiple
reasoning rows per run render independently in order (one per LLM step
in tool-using turns). UX intentionally mirrors Claude Code + OpenCode
patterns.
## Implementation (end-to-end)
- **Schema** — `reasoning` row gains `run_id`, `encrypted` (Anthropic
redacted-thinking opaque payload, must round-trip back to the model
verbatim), and `summary_title` (extracted at write time). New
`reasoningDeltas` collection mirrors `textDeltas`. Strictly additive.
- **Bridge** — `OutboundBridge` gains `onReasoningStart` /
`onReasoningDelta` / `onReasoningEnd`, parallel to the text path.
Reasoning counter added to `OutboundIdSeed`.
- **Adapter** — `pi-adapter.ts` routes pi-ai's `thinking_start` /
`thinking_delta` / `thinking_end` events to the bridge. Parses a
`**Title**\n\n<body>` heading once at write time (OpenAI Responses;
no-op for Anthropic / DeepSeek / Moonshot). Defensive: handles late
`thinking_delta` without a preceding `thinking_start`, and closes an
open reasoning row on `message_end` (e.g. provider abort).
- **Timeline** — Live `reasoning:
Collection<EntityTimelineReasoningItem>` on `EntityTimelineRunRow`,
content built via the same delta-join pattern as
`EntityTimelineTextItem.content`.
- **UI** — New `<ReasoningSection>` renders above items in
`AgentResponseLive`:
- **Live**: faded markdown via `Streamdown` with `ThinkingIndicator`
heading + summary title + elapsed-time ticker
- **Settled**: `▸ Thought for Ns` with click-to-expand. Closure duration
snapshotted from `Date.now() - timestamp` using the same
`sawStreamingRef` trick from the elapsed-time PR — accurate for
in-session settles, stays a bare `Thought` for rows already settled on
first mount (no real end timestamp available client-side).
- **Redacted**: Anthropic safety-filter payloads render `⊘ Reasoning
redacted by provider safety filters`. The encrypted payload is still
persisted server-side so the model gets it back on the next turn.
## Reference
Patterns informed by reading OpenCode's reasoning implementation:
- 3-event streaming protocol (`reasoning-start` / `reasoning-delta` /
`reasoning-end`)
- `ReasoningPart` storage shape including `encrypted` for Anthropic
round-trip
- `reasoningSummary()` headline parser (5-line regex, OpenAI Responses
only)
- Collapsed-by-default UX with click-to-expand
## Test plan
- [x] `pnpm typecheck` clean in `agents-runtime` + `agents-server-ui`
- [x] `pnpm test outbound-bridge pi-adapter entity-timeline` in
`agents-runtime` (95 passed: 18 bridge + 21 adapter + 56 timeline)
- [x] `pnpm test` in `agents-server-ui` (66 passed)
- [x] `pnpm -C packages/agents-runtime build` — dist artifacts emit
cleanly
- [ ] Manual: prompt Anthropic Claude with extended-thinking enabled;
verify streaming reasoning appears faded above the answer with elapsed
ticker, then collapses to `Thought for Ns` on settle
- [ ] Manual: multi-step tool-using turn; verify each step's reasoning
renders as a separate collapsible row
## Notes
- Cached `AgentResponse` (the non-Live path used for old scrollback
sections) doesn't yet surface reasoning — historical rows recorded
before this PR lack the data anyway. Follow-up if we discover sessions
where this matters.
- The pre-existing `runtime-dsl.test.ts` 401 failures (and
`dispatch-policy-routing.test.ts` 500 failures) reproduce identically on
clean `main` and were not introduced by this PR.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Kyle Mathews <mathews.kyle@gmail.com>1 parent 0b26edf commit c48c1a8
14 files changed
Lines changed: 1277 additions & 50 deletions
File tree
- .changeset
- packages
- agents-runtime
- src
- test
- agents-server-ui/src/components
- agents
- src
- test
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
187 | 187 | | |
188 | 188 | | |
189 | 189 | | |
| 190 | + | |
190 | 191 | | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
191 | 208 | | |
192 | 209 | | |
193 | 210 | | |
| |||
530 | 547 | | |
531 | 548 | | |
532 | 549 | | |
| 550 | + | |
533 | 551 | | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
| 555 | + | |
| 556 | + | |
| 557 | + | |
| 558 | + | |
| 559 | + | |
| 560 | + | |
| 561 | + | |
| 562 | + | |
| 563 | + | |
534 | 564 | | |
535 | 565 | | |
536 | 566 | | |
| |||
861 | 891 | | |
862 | 892 | | |
863 | 893 | | |
| 894 | + | |
864 | 895 | | |
865 | 896 | | |
866 | 897 | | |
| |||
951 | 982 | | |
952 | 983 | | |
953 | 984 | | |
| 985 | + | |
954 | 986 | | |
955 | 987 | | |
956 | 988 | | |
| |||
975 | 1007 | | |
976 | 1008 | | |
977 | 1009 | | |
| 1010 | + | |
| 1011 | + | |
978 | 1012 | | |
979 | 1013 | | |
980 | 1014 | | |
| |||
1010 | 1044 | | |
1011 | 1045 | | |
1012 | 1046 | | |
| 1047 | + | |
1013 | 1048 | | |
1014 | 1049 | | |
1015 | 1050 | | |
| |||
1062 | 1097 | | |
1063 | 1098 | | |
1064 | 1099 | | |
| 1100 | + | |
| 1101 | + | |
| 1102 | + | |
| 1103 | + | |
| 1104 | + | |
| 1105 | + | |
1065 | 1106 | | |
1066 | 1107 | | |
1067 | 1108 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
242 | 242 | | |
243 | 243 | | |
244 | 244 | | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
245 | 263 | | |
246 | 264 | | |
247 | 265 | | |
| |||
267 | 285 | | |
268 | 286 | | |
269 | 287 | | |
| 288 | + | |
270 | 289 | | |
271 | 290 | | |
272 | 291 | | |
| |||
1385 | 1404 | | |
1386 | 1405 | | |
1387 | 1406 | | |
| 1407 | + | |
| 1408 | + | |
| 1409 | + | |
| 1410 | + | |
| 1411 | + | |
| 1412 | + | |
| 1413 | + | |
1388 | 1414 | | |
1389 | 1415 | | |
1390 | 1416 | | |
| |||
1402 | 1428 | | |
1403 | 1429 | | |
1404 | 1430 | | |
1405 | | - | |
1406 | | - | |
1407 | | - | |
1408 | | - | |
1409 | | - | |
| 1431 | + | |
| 1432 | + | |
| 1433 | + | |
| 1434 | + | |
| 1435 | + | |
| 1436 | + | |
| 1437 | + | |
1410 | 1438 | | |
1411 | 1439 | | |
1412 | 1440 | | |
| |||
1422 | 1450 | | |
1423 | 1451 | | |
1424 | 1452 | | |
| 1453 | + | |
| 1454 | + | |
| 1455 | + | |
| 1456 | + | |
| 1457 | + | |
| 1458 | + | |
| 1459 | + | |
| 1460 | + | |
| 1461 | + | |
| 1462 | + | |
| 1463 | + | |
| 1464 | + | |
| 1465 | + | |
| 1466 | + | |
| 1467 | + | |
| 1468 | + | |
| 1469 | + | |
| 1470 | + | |
| 1471 | + | |
| 1472 | + | |
| 1473 | + | |
| 1474 | + | |
| 1475 | + | |
| 1476 | + | |
| 1477 | + | |
| 1478 | + | |
| 1479 | + | |
| 1480 | + | |
| 1481 | + | |
| 1482 | + | |
| 1483 | + | |
| 1484 | + | |
| 1485 | + | |
| 1486 | + | |
| 1487 | + | |
| 1488 | + | |
| 1489 | + | |
1425 | 1490 | | |
1426 | 1491 | | |
1427 | 1492 | | |
| |||
1484 | 1549 | | |
1485 | 1550 | | |
1486 | 1551 | | |
| 1552 | + | |
| 1553 | + | |
| 1554 | + | |
| 1555 | + | |
| 1556 | + | |
| 1557 | + | |
| 1558 | + | |
| 1559 | + | |
| 1560 | + | |
| 1561 | + | |
| 1562 | + | |
| 1563 | + | |
| 1564 | + | |
| 1565 | + | |
| 1566 | + | |
| 1567 | + | |
| 1568 | + | |
| 1569 | + | |
| 1570 | + | |
| 1571 | + | |
| 1572 | + | |
| 1573 | + | |
1487 | 1574 | | |
1488 | 1575 | | |
1489 | 1576 | | |
| |||
0 commit comments