diff --git a/CHANGELOG.md b/CHANGELOG.md index b4b2648b40..53f6425708 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,9 @@ Only write entries that are worth mentioning to users. - Web: Dismiss stale approval and question dialogs on session replay — when replaying a session or when the backend reports idle/stopped/error status, any pending approval/question dialogs are now properly dismissed to prevent orphaned interactive elements - Web: Enable inline math formula rendering — single-dollar inline math (`$...$`) is now supported in addition to block math (`$$...$$`) - 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 ## 1.24.0 (2026-03-18) diff --git a/docs/en/guides/interaction.md b/docs/en/guides/interaction.md index b776adcce8..b2af3c9a7c 100644 --- a/docs/en/guides/interaction.md +++ b/docs/en/guides/interaction.md @@ -162,6 +162,7 @@ The confirmation prompt will show operation details, including shell command and - **Allow**: Execute this operation - **Allow for this session**: Automatically approve similar operations in the current session (this decision is persisted with the session and automatically restored when resuming) - **Reject**: Do not execute this operation +- **Reject with feedback**: Decline the operation and provide written feedback telling the agent how to adjust If you trust the AI's operations, or you're running Kimi Code CLI in a safe isolated environment, you can enable "YOLO mode" to automatically approve all requests: diff --git a/docs/en/reference/keyboard.md b/docs/en/reference/keyboard.md index 5cd9d3550a..fd5f394b50 100644 --- a/docs/en/reference/keyboard.md +++ b/docs/en/reference/keyboard.md @@ -13,7 +13,7 @@ Kimi Code CLI shell mode supports the following keyboard shortcuts. | `Alt-Enter` | Insert newline (same as `Ctrl-J`) | | `Ctrl-V` | Paste (supports images and video files) | | `Ctrl-E` | Expand full approval request content | -| `1`–`3` | Quick select approval option | +| `1`–`4` | Quick select approval option (`4` for decline with feedback) | | `1`–`5` | Select question option by number | | `Ctrl-D` | Exit Kimi Code CLI | | `Ctrl-C` | Interrupt current operation | @@ -92,7 +92,7 @@ Useful for viewing longer shell commands or file diff content. ### 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. ## Structured question operations diff --git a/docs/en/reference/kimi-web.md b/docs/en/reference/kimi-web.md index 030718520c..7049b4e1cb 100644 --- a/docs/en/reference/kimi-web.md +++ b/docs/en/reference/kimi-web.md @@ -255,9 +255,14 @@ When the agent sends an approval request, you can use keyboard shortcuts to resp | `1` | Approve | | `2` | Approve for session | | `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. + +When an approval request originates from a subagent, the dialog shows a source label (e.g. "coder agent") so you know which agent initiated the request. ::: info Added -Approval keyboard shortcuts added in version 1.10. +Approval keyboard shortcuts added in version 1.10. Feedback mode added in version 1.25. ::: ### Tool output @@ -271,8 +276,10 @@ Web UI provides rich display for tool call output: - **Context compaction**: A compaction indicator is shown when context compaction is in progress - **Quick URL open**: The URL parameter of the `FetchURL` tool supports Cmd/Ctrl+Click to open the link in a new tab +- **Subagent origin indicators**: Tool calls originating from a subagent are rendered with a left border and a source type label (e.g. "coder agent") for clearer attribution; subagent activity panels display the specific agent type (e.g. "Coder agent working") instead of a generic label + ::: info Added -Media preview, shell command, and todo list display components added in version 1.9. Quick URL open added in version 1.14. +Media preview, shell command, and todo list display components added in version 1.9. Quick URL open added in version 1.14. Subagent origin indicators added in version 1.25. ::: ### Rich media support diff --git a/docs/en/release-notes/changelog.md b/docs/en/release-notes/changelog.md index 334e428e4d..b7d6fe5561 100644 --- a/docs/en/release-notes/changelog.md +++ b/docs/en/release-notes/changelog.md @@ -19,6 +19,9 @@ This page documents the changes in each Kimi Code CLI release. - Web: Dismiss stale approval and question dialogs on session replay — when replaying a session or when the backend reports idle/stopped/error status, any pending approval/question dialogs are now properly dismissed to prevent orphaned interactive elements - Web: Enable inline math formula rendering — single-dollar inline math (`$...$`) is now supported in addition to block math (`$$...$$`) - 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 ## 1.24.0 (2026-03-18) diff --git a/docs/zh/guides/interaction.md b/docs/zh/guides/interaction.md index 45a1287167..f02a3bd2a1 100644 --- a/docs/zh/guides/interaction.md +++ b/docs/zh/guides/interaction.md @@ -160,6 +160,7 @@ AI 只会在你的选择真正影响后续操作时才使用此工具。对于 - **允许**:执行这次操作 - **本会话允许**:在当前会话中自动批准同类操作(此决策会随会话持久化,恢复会话时自动还原) - **拒绝**:不执行此操作 +- **附带反馈拒绝**:拒绝操作并输入文字反馈,告诉 Agent 应该如何调整 如果你信任 AI 的操作,或者你正在安全的隔离环境中运行 Kimi Code CLI,可以启用「YOLO 模式」来自动批准所有请求: diff --git a/docs/zh/reference/keyboard.md b/docs/zh/reference/keyboard.md index 5b44c2a2e6..fc225ecbec 100644 --- a/docs/zh/reference/keyboard.md +++ b/docs/zh/reference/keyboard.md @@ -13,7 +13,7 @@ Kimi Code CLI Shell 模式支持以下键盘快捷键。 | `Alt-Enter` | 插入换行(同 `Ctrl-J`) | | `Ctrl-V` | 粘贴(支持图片和视频文件) | | `Ctrl-E` | 展开审批请求完整内容 | -| `1`–`3` | 审批面板快速选择 | +| `1`–`4` | 审批面板快速选择(`4` 为附带反馈拒绝) | | `1`–`5` | 问题面板按编号选择选项 | | `Ctrl-D` | 退出 Kimi Code CLI | | `Ctrl-C` | 中断当前操作 | @@ -92,7 +92,7 @@ Kimi Code CLI Shell 模式支持以下键盘快捷键。 ### 数字键快速选择 -在审批面板中,按 `1`–`3` 可以直接选中并提交对应的审批选项,无需先用方向键选择再按 `Enter`。 +在审批面板中,按 `1`–`3` 可以直接选中并提交对应的审批选项,无需先用方向键选择再按 `Enter`。按 `4` 进入反馈模式,输入拒绝原因后按 Enter 提交,反馈文本会传递给 Agent 以指导下一次尝试。 ## 结构化问答操作 diff --git a/docs/zh/reference/kimi-web.md b/docs/zh/reference/kimi-web.md index 29909673dc..0ce22037f4 100644 --- a/docs/zh/reference/kimi-web.md +++ b/docs/zh/reference/kimi-web.md @@ -255,9 +255,14 @@ Assistant 消息提供以下操作按钮: | `1` | 批准 | | `2` | 本次会话批准 | | `3` | 拒绝 | +| `4` | 附带反馈拒绝 | + +按 `4` 进入反馈模式后,可以输入文字说明拒绝的原因或期望 Agent 如何调整,然后按 Enter 提交。反馈文本会传递给 Agent 以指导下一次尝试。 + +当审批请求来自子 Agent 时,对话框会显示来源标签(如 "coder agent"),帮助你了解请求的发起者。 ::: info 新增 -审批键盘快捷键新增于 1.10 版本。 +审批键盘快捷键新增于 1.10 版本。反馈模式新增于 1.25 版本。 ::: ### 工具输出 @@ -271,8 +276,10 @@ Web UI 对工具调用的输出提供了丰富的展示方式: - **上下文压缩**:上下文压缩进行时会显示压缩指示器 - **URL 快速打开**:`FetchURL` 工具的 URL 参数支持 Cmd/Ctrl+点击在新标签页中打开链接 +- **子 Agent 来源标识**:来自子 Agent 的工具调用以左边框和来源类型标签(如 "coder agent")渲染,便于区分归属;子 Agent 活动面板显示具体的 Agent 类型(如 "Coder agent working")而非通用标签 + ::: info 新增 -媒体预览、Shell 命令和 Todo 列表显示组件新增于 1.9 版本。URL 快速打开功能新增于 1.14 版本。 +媒体预览、Shell 命令和 Todo 列表显示组件新增于 1.9 版本。URL 快速打开功能新增于 1.14 版本。子 Agent 来源标识新增于 1.25 版本。 ::: ### 富媒体支持 diff --git a/docs/zh/release-notes/changelog.md b/docs/zh/release-notes/changelog.md index cb684e7213..f6611b5d01 100644 --- a/docs/zh/release-notes/changelog.md +++ b/docs/zh/release-notes/changelog.md @@ -19,6 +19,9 @@ - Web:会话重放时消除过期的审批和问答对话框——重放会话或后端报告 idle/stopped/error 状态时,所有待处理的审批/问答对话框现在会被正确消除,防止产生孤立的交互元素 - Web:支持行内数学公式渲染——除块级数学公式(`$$...$$`)外,新增支持单美元符号行内数学公式(`$...$`) - Web:优化 Switch 切换开关的比例和对齐——切换轨道现在更大(36×20),拇指按钮保持 16px 并具备更平滑的 16px 位移动画 +- Web:在活动面板中显示子 Agent 类型标签——子 Agent 活动现在显示其类型(如 "Coder agent working")而非通用的 "Agent" 标签 +- Web:审批对话框新增反馈模式——按 `4` 可附带反馈文字拒绝,指导模型的下一次尝试;来自子 Agent 的审批请求会显示来源标签和预览内容(Diff、命令等) +- Web:视觉区分子 Agent 来源的工具调用——来自子 Agent 的 Tool 消息以左边框和来源类型标签渲染,便于区分归属 ## 1.24.0 (2026-03-18) diff --git a/web/src/components/ai-elements/subagent-steps.tsx b/web/src/components/ai-elements/subagent-steps.tsx index 498ce57f49..18b6699958 100644 --- a/web/src/components/ai-elements/subagent-steps.tsx +++ b/web/src/components/ai-elements/subagent-steps.tsx @@ -25,6 +25,8 @@ export type SubagentActivityProps = ComponentProps<"div"> & { steps: SubagentStep[]; isRunning?: boolean; defaultOpen?: boolean; + /** Built-in subagent type (coder / explore / plan) */ + subagentType?: string; }; export const SubagentActivity = memo( @@ -33,8 +35,12 @@ export const SubagentActivity = memo( steps, isRunning = false, defaultOpen = false, + subagentType, ...props }: SubagentActivityProps) => { + const agentLabel = subagentType + ? `${subagentType.charAt(0).toUpperCase() + subagentType.slice(1)} agent` + : "Agent"; const [isOpen, setIsOpen] = useState(defaultOpen); const toolCallCount = steps.filter((s) => s.kind === "tool-call").length; @@ -63,7 +69,7 @@ export const SubagentActivity = memo( {isRunning ? ( <> - Agent working + {agentLabel} working ) : toolCallCount > 0 ? ( - `Agent completed · ${toolCallCount} tool call${toolCallCount !== 1 ? "s" : ""}` + `${agentLabel} completed · ${toolCallCount} tool call${toolCallCount !== 1 ? "s" : ""}` ) : ( - "Agent completed" + `${agentLabel} completed` )} { + async (approval: ToolApproval, decision: ApprovalResponseDecision, reason?: string) => { if (!(approval?.id && onApprovalResponse)) { return; } @@ -185,7 +185,7 @@ export const ChatWorkspace = memo(function ChatWorkspaceComponent({ })); try { - await onApprovalResponse(approval.id, decision); + await onApprovalResponse(approval.id, decision, reason); } catch (error) { console.error("[ChatWorkspace] Failed to respond to approval", error); toast.error("Approval action failed", { @@ -205,13 +205,13 @@ export const ChatWorkspace = memo(function ChatWorkspaceComponent({ // Wrapper for ApprovalDialog that routes through handleApprovalAction // so pendingApprovalMap is properly managed (prevents duplicate requests) const handleDialogApprovalResponse = useCallback( - async (requestId: string, decision: ApprovalResponseDecision) => { + async (requestId: string, decision: ApprovalResponseDecision, reason?: string) => { for (const message of messages) { if ( message.variant === "tool" && message.toolCall?.approval?.id === requestId ) { - await handleApprovalAction(message.toolCall.approval, decision); + await handleApprovalAction(message.toolCall.approval, decision, reason); return; } } diff --git a/web/src/features/chat/components/approval-dialog.tsx b/web/src/features/chat/components/approval-dialog.tsx index 75d7bde74c..f9284ef12e 100644 --- a/web/src/features/chat/components/approval-dialog.tsx +++ b/web/src/features/chat/components/approval-dialog.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useMemo } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Button } from "@/components/ui/button"; import { Kbd } from "@/components/ui/kbd"; import { cn } from "@/lib/utils"; @@ -22,6 +22,10 @@ export function ApprovalDialog({ pendingApprovalMap, canRespondToApproval, }: ApprovalDialogProps) { + const [feedbackMode, setFeedbackMode] = useState(false); + const [feedbackText, setFeedbackText] = useState(""); + const feedbackInputRef = useRef(null); + // from messages, extract the pending approval request const pendingApproval = useMemo(() => { for (const message of messages) { @@ -41,15 +45,28 @@ export function ApprovalDialog({ return null; }, [messages]); + // Reset feedback state when the pending approval changes + const currentApprovalId = pendingApproval?.approval?.id; + const prevApprovalIdRef = useRef(currentApprovalId); + 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(""); + } + } + const handleResponse = useCallback( - async (decision: ApprovalResponseDecision) => { + async (decision: ApprovalResponseDecision, reason?: string) => { if (!(pendingApproval && onApprovalResponse)) return; const { approval } = pendingApproval; if (!approval.id) return; try { - await onApprovalResponse(approval.id, decision); + await onApprovalResponse(approval.id, decision, reason); } catch (error) { console.error("[ApprovalDialog] Failed to respond", error); } @@ -57,6 +74,14 @@ export function ApprovalDialog({ [pendingApproval, onApprovalResponse], ); + const handleFeedbackSubmit = useCallback(() => { + const trimmed = feedbackText.trim(); + if (!trimmed) return; + setFeedbackMode(false); + setFeedbackText(""); + handleResponse("reject", trimmed); + }, [feedbackText, handleResponse]); + // Compute disable state before early return (hooks must run unconditionally) const approvalId = pendingApproval?.approval?.id; const approvalPending = approvalId @@ -65,9 +90,21 @@ export function ApprovalDialog({ const disableActions = !(canRespondToApproval && onApprovalResponse) || approvalPending; - // Keyboard shortcuts: 1=Approve, 2=Approve for session, 3=Decline + // Focus the feedback input when feedback mode is activated + useEffect(() => { + if (feedbackMode) { + // Use rAF to wait for the DOM to be ready after state update + requestAnimationFrame(() => { + feedbackInputRef.current?.focus(); + }); + } + }, [feedbackMode]); + + // Keyboard shortcuts: 1=Approve, 2=Approve for session, 3=Decline, 4=Decline with feedback useEffect(() => { if (!pendingApproval || disableActions) return; + // When in feedback mode, don't handle number shortcuts + if (feedbackMode) return; const handleKeyDown = (event: KeyboardEvent) => { if (event.defaultPrevented) return; @@ -83,6 +120,12 @@ export function ApprovalDialog({ if ((el as HTMLElement).isContentEditable) return; } + if (event.key === "4") { + event.preventDefault(); + setFeedbackMode(true); + return; + } + const keyMap: Record = { "1": "approve", "2": "approve_for_session", @@ -97,23 +140,31 @@ export function ApprovalDialog({ window.addEventListener("keydown", handleKeyDown); return () => window.removeEventListener("keydown", handleKeyDown); - }, [pendingApproval, disableActions, handleResponse]); + }, [pendingApproval, disableActions, handleResponse, feedbackMode]); // if no pending approval request, do not render anything if (!pendingApproval) return null; const { approval, toolCall } = pendingApproval; - const options = [ - { key: "approve", label: "Approve", pendingLabel: "Approving...", index: 1 }, - { - key: "approve_for_session", - label: "Approve for session", - pendingLabel: "Approving...", - index: 2, - }, - { key: "reject", label: "Decline", pendingLabel: "Declining...", index: 3 }, - ] as const; + const sourceLabel = (() => { + if (approval.sourceDescription) return approval.sourceDescription; + const agentType = toolCall.subagentType; + const agentId = toolCall.subagentAgentId; + const idSuffix = agentId ? ` (${agentId})` : ""; + if (approval.sourceKind === "background_agent") { + return agentType + ? `Background · ${agentType}${idSuffix}` + : `Background agent${idSuffix}`; + } + // Foreground sub-agent approvals (isSubagentOrigin) + if (toolCall.isSubagentOrigin) { + return agentType + ? `${agentType}${idSuffix}` + : `Sub-agent${idSuffix}`; + } + return null; + })(); return (
@@ -140,6 +191,11 @@ export function ApprovalDialog({ · {approval.sender} )} + {sourceLabel && ( + + {sourceLabel} + + )}
{/* Description */} @@ -182,28 +238,104 @@ export function ApprovalDialog({ {/* Action buttons */}
- {options.map((option) => ( - + + + +
+ + {/* Feedback input */} + {feedbackMode && ( +
+