feat(frontend): add sub-agent observability with orchestrator thinking and execution chain display #2365
feat(frontend): add sub-agent observability with orchestrator thinking and execution chain display #2365genni613 wants to merge 16 commits intobytedance:mainfrom
Conversation
…g, execution chain display and remark-breaks support
…g, execution chain display and remark-breaks support
…g, execution chain display and remark-breaks support
There was a problem hiding this comment.
Pull request overview
This PR improves agent-swarm UX by surfacing additional execution details: the orchestrator’s pre-delegation “thinking” and each subagent’s internal message/tool-call chain, plus better markdown rendering for reasoning text.
Changes:
- Extend
Subtaskto accumulatemessageHistoryfromtask_runningSSE events (deduped). - Add
OrchestratorThinkingand enhanceSubtaskCardto render reasoning + tool calls (via extracted sharedmessage-stepsutilities/components). - Improve markdown rendering of reasoning text by enabling single-newline line breaks (
remark-breaks).
Reviewed changes
Copilot reviewed 13 out of 14 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| frontend/src/core/threads/hooks.ts | Appends task_running messages into subtask state via _appendMessage. |
| frontend/src/core/tasks/types.ts | Adds messageHistory?: AIMessage[] to Subtask. |
| frontend/src/core/tasks/context.tsx | Implements _appendMessage protocol for deduped history accumulation. |
| frontend/src/core/streamdown/plugins.ts | Adds remark-breaks to Streamdown plugin sets. |
| frontend/src/core/i18n/locales/zh-CN.ts | Adds new subtask/orchestrator i18n strings (zh-CN). |
| frontend/src/core/i18n/locales/types.ts | Extends Translations.subtasks typing with new keys. |
| frontend/src/core/i18n/locales/en-US.ts | Adds new subtask/orchestrator i18n strings (en-US). |
| frontend/src/components/workspace/messages/subtask-card.tsx | Renders subagent execution chain from messageHistory. |
| frontend/src/components/workspace/messages/orchestrator-thinking.tsx | New component to display orchestrator analysis + decomposition count. |
| frontend/src/components/workspace/messages/message-steps.tsx | Extracted shared “steps” conversion + ToolCall renderer. |
| frontend/src/components/workspace/messages/message-list.tsx | Integrates OrchestratorThinking into subagent message groups. |
| frontend/src/components/workspace/messages/message-group.tsx | Refactors to consume shared message-steps module. |
| frontend/package.json | Adds remark-breaks dependency. |
| frontend/pnpm-lock.yaml | Locks remark-breaks and transitive deps. |
Files not reviewed (1)
- frontend/pnpm-lock.yaml: Language not supported
|
@genni613 Here are some additional comments for the code
Using an underscore-prefixed _appendMessage field that's destructured out and never makes it into the Subtask type is a side-channel pattern. It works but is easy to break accidentally — anyone reading Subtask type won't know this protocol exists. Suggestion: Consider making updateSubtask accept a second parameter or use a discriminated union for the update payload instead of smuggling data through underscore-prefixed fields.
In the old message-group.tsx, result was string | Record<string, unknown> (union). In the new message-steps.tsx, the interface declares it the same way, but convertToSteps assigns step.result = json where json is any (from JSON.parse). This means arrays also pass through — e.g., web_search results which are Array<...>. The type says Record<string, unknown> but arrays flow through fine at runtime. This isn't a bug, but the types are misleading. Suggestion: Either broaden the type to unknown or add explicit handling.
{Array.isArray(results) && ( The outer guard already confirmed that the results are an array. The inner Array.isArray(results) is redundant. |
…rtion safety - Extract _appendMessage side-channel into explicit useAppendMessage hook - Broaden CoTToolCallStep.result type to unknown for runtime accuracy - Remove redundant Array.isArray check in image_search branch - Guard toolCall.id with runtime check instead of non-null assertion - Replace useSubtask()! with safe early return and reorder hooks Co-Authored-By: Claude Opus 4.6 <[email protected]>
3c31aba to
61d1946
Compare
|
@genni613 Please run the below comments to fix the lint error. |
|
@genni613 Did you have a chance to try the latest frontend in your local box? My browser blocked when the backend started the subagent. |
|
@WillemJiang Already fixed. updateSubtask calls were moved from the render-phase groupMessages callback into a useEffect with a syncedTaskIdsRef dedup guard, breaking the render → setState → re-render loop. |
Summary
Improve sub-agent observability in agent swarm mode by surfacing the orchestrator's thinking process and each subagent's internal execution chain (reasoning + tool calls with parameters).
Changes
Subtasktype withmessageHistoryfield and implemented_appendMessageprotocol in task context for deduplicated message accumulation fromtask_runningSSE eventsOrchestratorThinkingcomponent showing the lead agent's reasoning before splitting subtasks, with task decomposition countSubtaskCardto render internal reasoning and tool calls with parameters in expanded state, using sharedconvertToSteps+ToolCallmodules extracted frommessage-group.tsxreasoning_content, fall back to textcontentas thinking indicatorremark-breaksplugin so single newlines in reasoning text render as line breaksScreenshots
Test plan
pnpm check— lint + typecheck passpnpm test— 19/19 unit tests passCloses #2315