feat(web): adapt Web UI for Wire 1.6 subagent and approval changes - #1555
Conversation
There was a problem hiding this comment.
Pull request overview
Updates the Web UI to align with Wire protocol 1.6 changes (subagent tracing + approvals), and improves the approval UX by adding “decline with feedback”, source labeling, and approval preview display blocks.
Changes:
- Extend wire and UI message types to support Wire 1.6 fields (subagent metadata, approval sources, approval feedback, display blocks).
- Update session stream event handling for
SubagentEvent,ApprovalRequest, andApprovalRequestResolved, including legacy replay fallbacks. - Enhance approval and tool rendering: feedback textarea + shortcuts, source badge/indent for sub-agent-origin approvals, and subagent type labels in activity headers.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
web/src/hooks/wireTypes.ts |
Adds Wire 1.6 fields for approvals and subagent events; extends tool approval state. |
web/src/hooks/types.ts |
Extends LiveMessage/tool call shape with feedback, approval source metadata, and subagent identity/origin flags. |
web/src/hooks/useSessionStream.ts |
Implements Wire 1.6 event processing and fallback behavior; threads approval display blocks and rejection feedback through the pipeline. |
web/src/features/chat/components/approval-dialog.tsx |
Adds “Decline with feedback” flow (textarea, IME-safe Enter, shortcuts) and source badge rendering. |
web/src/features/chat/chat.tsx |
Threads optional rejection reason/feedback through approval action handlers to the stream hook. |
web/src/components/ai-elements/subagent-steps.tsx |
Displays subagent type label (“Coder agent”, etc.) in subagent activity header text. |
web/src/features/chat/components/assistant-message.tsx |
Visually demotes sub-agent-origin approval/tool cards and passes subagentType down to activity rendering. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| result: { | ||
| request_id: pending.requestId ?? requestId, | ||
| response, | ||
| ...(response === "reject" && trimmedReason | ||
| ? { feedback: trimmedReason } | ||
| : {}), | ||
| }, |
There was a problem hiding this comment.
respondToApproval conditionally sends feedback in the JSON-RPC response, but the local optimistic updatedApproval state update below still only updates reason/response. To keep the UI state consistent with what’s sent over the wire (and with the new feedback field), also populate updatedApproval.feedback when rejecting with feedback so the value is available immediately without waiting for ApprovalRequestResolved.
| const parentToolCallId = | ||
| subPayload.parent_tool_call_id ?? | ||
| (subPayload as Record<string, unknown>).task_tool_call_id as string | undefined; |
There was a problem hiding this comment.
The legacy fallback for task_tool_call_id relies on a chained as cast in the nullish coalescing expression. For readability and to avoid any TS precedence confusion, wrap the cast in parentheses and consider validating that the legacy value is actually a string before using it (e.g., typeof legacy === 'string').
| const parentToolCallId = | |
| subPayload.parent_tool_call_id ?? | |
| (subPayload as Record<string, unknown>).task_tool_call_id as string | undefined; | |
| const legacyTaskToolCallId = (subPayload as Record<string, unknown>).task_tool_call_id; | |
| const parentToolCallId = | |
| subPayload.parent_tool_call_id ?? | |
| (typeof legacyTaskToolCallId === "string" ? legacyTaskToolCallId : undefined); |
| if (prevApprovalIdRef.current !== currentApprovalId) { | ||
| prevApprovalIdRef.current = currentApprovalId; | ||
| // Always clear stale feedback text, not just when feedbackMode is active. | ||
| // Otherwise old text leaks into the next approval's feedback input. | ||
| if (feedbackMode || feedbackText) { | ||
| setFeedbackMode(false); | ||
| setFeedbackText(""); | ||
| } | ||
| } |
There was a problem hiding this comment.
State is being updated during render (if (prevApprovalIdRef.current !== currentApprovalId) { ... setFeedbackMode/setFeedbackText ... }). Updating state while rendering can trigger React warnings and render loops (especially under StrictMode). Move this reset logic into a useEffect that runs when currentApprovalId changes (and clear feedback state there).
| if (prevApprovalIdRef.current !== currentApprovalId) { | |
| prevApprovalIdRef.current = currentApprovalId; | |
| // Always clear stale feedback text, not just when feedbackMode is active. | |
| // Otherwise old text leaks into the next approval's feedback input. | |
| if (feedbackMode || feedbackText) { | |
| setFeedbackMode(false); | |
| setFeedbackText(""); | |
| } | |
| } | |
| useEffect(() => { | |
| if (prevApprovalIdRef.current !== currentApprovalId) { | |
| prevApprovalIdRef.current = currentApprovalId; | |
| // Always clear stale feedback text, not just when feedbackMode is active. | |
| // Otherwise old text leaks into the next approval's feedback input. | |
| setFeedbackMode(false); | |
| setFeedbackText(""); | |
| } | |
| }, [currentApprovalId]); |
| submitted: true, | ||
| approved, | ||
| reason: reason ?? approval.reason, | ||
| reason: effectiveReason, |
There was a problem hiding this comment.
ApprovalRequestResolved now includes a feedback field, but the updated approval state only folds it into reason and never stores it in updatedApproval.feedback. Since ToolApprovalState/LiveMessage now include feedback, consider persisting it separately (e.g., feedback: feedback ?? approval.feedback) so replay and future UI can distinguish structured feedback from generic reasons.
| reason: effectiveReason, | |
| reason: effectiveReason, | |
| feedback: feedback ?? approval.feedback, |
| - Web: Improve Switch toggle proportions and alignment — the toggle track is now larger (36×20) with a consistent 16px thumb and smoother 16px travel animation | ||
| - Web: Show subagent type labels in activity panels — subagent activities now display their type (e.g. "Coder agent working") instead of the generic "Agent" label | ||
| - Web: Add feedback mode to approval dialog — press `4` to reject with written feedback text that guides the model's next attempt; approval requests from subagents show a source label and preview content (diffs, commands) | ||
| - Web: Visually distinguish sub-agent origin tool calls — tool messages originating from a subagent are rendered with a left border and a source type label for clearer attribution |
There was a problem hiding this comment.
🟡 CHANGELOG uses "sub-agent" instead of "subagent" violating docs/AGENTS.md term mapping
The docs/AGENTS.md term mapping table specifies that the English term for 子 Agent is "subagent" (no hyphen). This line uses "sub-agent" (hyphenated) in the title while correctly using "subagent" in the body, creating an inconsistency. The root CHANGELOG.md is auto-synced to docs/en/release-notes/changelog.md:24, so both files are affected.
| - Web: Visually distinguish sub-agent origin tool calls — tool messages originating from a subagent are rendered with a left border and a source type label for clearer attribution | |
| - Web: Visually distinguish subagent origin tool calls — tool messages originating from a subagent are rendered with a left border and a source type label for clearer attribution |
Was this helpful? React with 👍 or 👎 to provide feedback.
| ### 数字键快速选择 | ||
|
|
||
| 在审批面板中,按 `1`–`3` 可以直接选中并提交对应的审批选项,无需先用方向键选择再按 `Enter`。 | ||
| 在审批面板中,按 `1`–`3` 可以直接选中并提交对应的审批选项,无需先用方向键选择再按 `Enter`。按 `4` 进入反馈模式,输入拒绝原因后按 Enter 提交,反馈文本会传递给 Agent 以指导下一次尝试。 |
There was a problem hiding this comment.
🟡 Missing backticks around "Enter" keyboard shortcut in Chinese keyboard reference
The docs/AGENTS.md naming conventions require backticks for keyboard shortcuts. The second occurrence of "Enter" on this line is not backtick-formatted (按 Enter 提交), while the first occurrence (按 \Enter`) and all existing references in the file (e.g. docs/zh/reference/keyboard.md:64`) correctly use backticks.
| 在审批面板中,按 `1`–`3` 可以直接选中并提交对应的审批选项,无需先用方向键选择再按 `Enter`。按 `4` 进入反馈模式,输入拒绝原因后按 Enter 提交,反馈文本会传递给 Agent 以指导下一次尝试。 | |
| 在审批面板中,按 `1`–`3` 可以直接选中并提交对应的审批选项,无需先用方向键选择再按 `Enter`。按 `4` 进入反馈模式,输入拒绝原因后按 `Enter` 提交,反馈文本会传递给 Agent 以指导下一次尝试。 |
Was this helpful? React with 👍 or 👎 to provide feedback.
| ### Number key quick selection | ||
|
|
||
| In the approval panel, press `1`–`3` to directly select and submit the corresponding approval option without navigating with arrow keys first. | ||
| In the approval panel, press `1`–`3` to directly select and submit the corresponding approval option without navigating with arrow keys first. Press `4` to enter feedback mode, where you can type a reason for declining and press Enter to submit; the feedback text is passed to the agent to guide its next attempt. |
There was a problem hiding this comment.
🟡 Missing backticks around "Enter" keyboard shortcut in English keyboard reference
The docs/AGENTS.md naming conventions require backticks for keyboard shortcuts. "Enter" in press Enter to submit is not backtick-formatted, while all existing references in the file (e.g. docs/en/reference/keyboard.md:64) correctly use backticks.
| In the approval panel, press `1`–`3` to directly select and submit the corresponding approval option without navigating with arrow keys first. Press `4` to enter feedback mode, where you can type a reason for declining and press Enter to submit; the feedback text is passed to the agent to guide its next attempt. | |
| In the approval panel, press `1`–`3` to directly select and submit the corresponding approval option without navigating with arrow keys first. Press `4` to enter feedback mode, where you can type a reason for declining and press `Enter` to submit; the feedback text is passed to the agent to guide its next attempt. |
Was this helpful? React with 👍 or 👎 to provide feedback.
| | `3` | Decline | | ||
| | `4` | Decline with feedback | | ||
|
|
||
| Press `4` to enter feedback mode, where you can type a reason for declining or instructions on how the agent should adjust, then press Enter to submit. The feedback text is passed to the agent to guide its next attempt. |
There was a problem hiding this comment.
🟡 Missing backticks around "Enter" keyboard shortcut in English Web UI reference
The docs/AGENTS.md naming conventions require backticks for keyboard shortcuts. "Enter" in then press Enter to submit is not backtick-formatted. Existing text elsewhere in the docs (e.g. docs/en/guides/interaction.md:110: press \Enter``) consistently uses backticks for this shortcut.
| Press `4` to enter feedback mode, where you can type a reason for declining or instructions on how the agent should adjust, then press Enter to submit. The feedback text is passed to the agent to guide its next attempt. | |
| Press `4` to enter feedback mode, where you can type a reason for declining or instructions on how the agent should adjust, then press `Enter` to submit. The feedback text is passed to the agent to guide its next attempt. |
Was this helpful? React with 👍 or 👎 to provide feedback.
| | `3` | 拒绝 | | ||
| | `4` | 附带反馈拒绝 | | ||
|
|
||
| 按 `4` 进入反馈模式后,可以输入文字说明拒绝的原因或期望 Agent 如何调整,然后按 Enter 提交。反馈文本会传递给 Agent 以指导下一次尝试。 |
There was a problem hiding this comment.
🟡 Missing backticks around "Enter" keyboard shortcut in Chinese Web UI reference
The docs/AGENTS.md naming conventions require backticks for keyboard shortcuts. "Enter" in 按 Enter 提交 is not backtick-formatted, inconsistent with the same file's existing convention (e.g. docs/zh/reference/keyboard.md:64: 按 \Enter``).
| 按 `4` 进入反馈模式后,可以输入文字说明拒绝的原因或期望 Agent 如何调整,然后按 Enter 提交。反馈文本会传递给 Agent 以指导下一次尝试。 | |
| 按 `4` 进入反馈模式后,可以输入文字说明拒绝的原因或期望 Agent 如何调整,然后按 `Enter` 提交。反馈文本会传递给 Agent 以指导下一次尝试。 |
Was this helpful? React with 👍 or 👎 to provide feedback.
Summary
Adapts the Web UI to support the Wire protocol 1.6 changes introduced by #1552 (unified subagent execution, approvals, and tracing).
Wire protocol alignment
task_tool_call_id→parent_tool_call_id, addagent_idandsubagent_typefields with backward-compatible fallback for legacy wire.jsonl replaysource_kind,source_id,agent_id,subagent_type,source_description, anddisplayfieldsfeedbackfield when rejecting; read it back on replay viaApprovalRequestResolvedNew capabilities
4). Feedback flows through the full chain to the backendfeedbackwire field.ApprovalRequest.display(diffs, shell commands) ontotoolCall.displayso users can preview what they are approving instead of acting blind.Coder agent,Explore agent,Plan agent) inSubagentActivityheaders and in sub-agent origin tool cards.Sub-agent approval rendering fix
ApprovalRequestdirectly to the root wire (not wrapped inSubagentEvent). Previously, the Web UI created phantom tool-call cards in the main timeline indistinguishable from the root agent's own tool calls.isSubagentOriginand rendered with a left-border indent + source label (e.g.coder agent) to visually separate them from the root agent's tool calls.Source badge in approval dialog
subagent_type+agent_idwith fallback chain:source_description→ type + ID (e.g.Background · coder (a1f3e8b2)) → generic label.Changed files
web/src/hooks/wireTypes.ts— Wire 1.6 type definitionsweb/src/hooks/types.ts— LiveMessage type extensionsweb/src/hooks/useSessionStream.ts— Event handling for SubagentEvent, ApprovalRequest, ApprovalRequestResolved, respondToApprovalweb/src/features/chat/components/approval-dialog.tsx— Feedback input, source badge, IME guardweb/src/features/chat/chat.tsx— Threadreasonparam through approval action handlersweb/src/components/ai-elements/subagent-steps.tsx— Subagent type label in activity headerweb/src/features/chat/components/assistant-message.tsx— Sub-agent origin visual treatment, passsubagentTypeto SubagentActivityTest plan