Conversation
Add per-tool approval configuration to the Agent CRD so that specific MCP tools can require human approval before execution. When a tool marked with `requireApproval` is invoked, the agent pauses and sends an `input_required` event. The chat UI renders inline Approve/Reject buttons, and the user's decision is sent back as a structured A2A message to resume or redirect the agent. Changes across all layers: - CRD: `requireApproval` field on `McpServerTool` with CEL validation - Go ADK types + translator: pass `require_approval` through to config - Python `_approval.py`: `before_tool_callback` that blocks tools needing approval via a session-state-based one-time approval flow - Python executor: detects approval markers, sends `input_required` interrupt, handles approve/reject continuation on next message - UI: `pending_approval` status on ToolDisplay with approve/reject buttons, message handler detection of tool approval interrupts, `sendApprovalDecision()` in ChatInterface - Tests: golden translator test, 11 Python unit tests for callback - Docs: design doc and implementation reference Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Eitan Yarmush <eitan.yarmush@solo.io>
Bug 1: Approve/Reject buttons appeared to do nothing when clicked. The sendApprovalDecision function was moving messages from streaming to stored state, causing ToolCallDisplay components to unmount/remount. A race condition with the global toolCallCache prevented remounted components from re-registering their tool call IDs, making the approval card disappear without feedback. Fix: keep messages in place so the ToolDisplay's isSubmitting state is preserved naturally. Bug 2: Reloading the page lost the approval dialog. Messages were loaded from task history via extractMessagesFromTasks, but input_required status messages with tool_approval data were not reconstructed as ToolApprovalRequest messages. Fix: add extractApprovalMessagesFromTasks that detects tasks in input_required state with tool_approval data and reconstructs proper ToolApprovalRequest messages with approval buttons. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Eitan Yarmush <eitan.yarmush@solo.io>
Signed-off-by: Eitan Yarmush <eitan.yarmush@solo.io>
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
b99d3f7 to
a974155
Compare
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Screen.Recording.2026-02-27.at.10.48.58.PM.movAdded support for parallel tool calls (confirmation are gathered and batched sent). Some agents will not use parallel tool calls (from my experience Copilot and Antigravity don't do it, Cursor does it intermittently), meaning that they wait until one tool is approved and completed before generating another tool call, but apparently Kagent agents like calling parallel tools so they're supported in HITL now |
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
There was a problem hiding this comment.
Pull request overview
Implements ADK-native Human-in-the-Loop (HITL) tool approval end-to-end: adds requireApproval to the Agent CRD/tool configs, wires an ADK before_tool_callback to request confirmation, and updates the UI to render approval cards and send approval decisions back to resume execution.
Changes:
- Add
requireApprovalto MCP server tool configuration (CRD → Go translator/types → Python agent config). - Add ADK-native approval callback + executor resume logic (uniform + batch decisions).
- Update UI message handling and tool call rendering to display approval status and approval controls (including reload recovery), plus new documentation.
Reviewed changes
Copilot reviewed 28 out of 28 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| ui/src/types/index.ts | Adds requireApproval to MCP server tool type for UI consumption. |
| ui/src/lib/statusUtils.ts | Updates input-required UI text to “Awaiting approval…”. |
| ui/src/lib/messageHandlers.ts | Detects ADK confirmation parts, builds approval messages, and marks HITL rejections in tool results. |
| ui/src/components/sidebars/AgentDetailsSidebar.tsx | Shows “requires approval” indicator for tools based on requireApproval. |
| ui/src/components/chat/ToolCallDisplay.tsx | Adds ToolApprovalRequest handling, optimistic approve/reject UI, and HITL terminal states. |
| ui/src/components/chat/ChatMessage.tsx | Routes ToolApprovalRequest messages to ToolCallDisplay with approval callbacks. |
| ui/src/components/chat/ChatInterface.tsx | Adds pending decision collection + decision submission via A2A streaming. |
| ui/src/components/ToolDisplay.tsx | Adds approval/rejection UI and new statuses (pending/approved/rejected). |
| python/packages/kagent-core/tests/test_hitl_utils.py | Updates HITL parsing tests for structured DataPart decisions + batch. |
| python/packages/kagent-core/tests/test_hitl_handlers.py | Removes tests for old interrupt handler path. |
| python/packages/kagent-core/tests/test_hitl_consts.py | Removes tests for old HITL constants/keywords. |
| python/packages/kagent-core/src/kagent/core/a2a/_hitl.py | Simplifies HITL decision extraction + adds batch decision extraction. |
| python/packages/kagent-core/src/kagent/core/a2a/_consts.py | Adds batch decision constants; removes old interrupt/keyword constants. |
| python/packages/kagent-core/src/kagent/core/a2a/init.py | Updates public exports to the new HITL helpers/constants. |
| python/packages/kagent-adk/tests/unittests/test_approval.py | Adds tests for ADK-native approval callback behavior. |
| python/packages/kagent-adk/src/kagent/adk/types.py | Adds require_approval to tool configs; wires before_tool_callback. |
| python/packages/kagent-adk/src/kagent/adk/_approval.py | New ADK before_tool_callback that requests confirmation and blocks/exits accordingly. |
| python/packages/kagent-adk/src/kagent/adk/_agent_executor.py | Adds HITL resume path: converts A2A decision messages into ADK FunctionResponse confirmations; breaks on long-running confirmation events. |
| python/packages/kagent-adk/src/kagent/adk/_a2a.py | Enables ADK resumability and passes task_store into executor. |
| helm/kagent-crds/templates/kagent.dev_agents.yaml | CRD schema update to include requireApproval. |
| go/pkg/adk/types.go | Adds require_approval to ADK config JSON types. |
| go/internal/controller/translator/agent/testdata/outputs/agent_with_require_approval.json | Adds golden output for require_approval translation. |
| go/internal/controller/translator/agent/testdata/inputs/agent_with_require_approval.yaml | Adds translator input fixture covering requireApproval. |
| go/internal/controller/translator/agent/adk_api_translator.go | Propagates RequireApproval from CRD tool spec into ADK config structs. |
| go/config/crd/bases/kagent.dev_agents.yaml | Base CRD schema update to include requireApproval. |
| go/api/v1alpha2/zz_generated.deepcopy.go | Adds deepcopy support for RequireApproval. |
| go/api/v1alpha2/agent_types.go | Adds RequireApproval field on McpServerTool. |
| docs/architecture/human-in-the-loop.md | New architecture doc describing ADK-native HITL flow and UI/backend responsibilities. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
Supports confirmation flow for HITL (single and parallel tool use). Flow documented in
docs/architecture/human-in-the-loop.mdTodo: custom payload so the agent can invoke tools that require user input