5.7 KiB
Human-in-the-Loop Patterns
Comparison of how each coding agent handles interactive permission and question flows.
Summary
| Agent | Permission System | Question System | Event Types |
|---|---|---|---|
| OpenCode | Full (permission.asked) |
Full (question.asked) |
SSE events |
| Claude | permissionMode option |
Via AskUserQuestion tool |
Stream JSON |
| Codex | sandboxMode levels |
None documented | JSONL events |
| Amp | PermissionRule[] with actions |
None documented | Stream JSON |
OpenCode (Most Complete)
OpenCode is the only agent with a fully interactive bidirectional HITL protocol. It emits events that require explicit responses via dedicated API endpoints.
Permission Requests
interface PermissionRequest {
id: string;
sessionID: string;
permission: string; // e.g., "file:write"
patterns: string[]; // Affected paths
metadata: Record<string, unknown>;
always: string[]; // "Always allow" options
tool?: { messageID: string; callID: string };
}
Responding:
await clientV2.permission.reply({
requestID: requestId,
reply: "once" | "always" | "reject"
});
Question Requests
interface QuestionRequest {
id: string;
sessionID: string;
questions: Array<{
header?: string;
question: string;
options: Array<{ label: string; description?: string }>;
multiSelect?: boolean;
}>;
tool?: { messageID: string; callID: string };
}
Responding:
// Reply with selected options
await clientV2.question.reply({
requestID: requestId,
answers: [["selected option"]] // Array of selected labels per question
});
// Or reject
await clientV2.question.reject({ requestID: requestId });
Event Types
| Event | Description |
|---|---|
permission.asked |
Agent requesting permission for an action |
question.asked |
Agent asking user a question with options |
Claude Code
Claude uses static permission modes rather than interactive permission requests. Questions are handled via a built-in tool.
Permission Modes (Static)
interface Options {
permissionMode?: 'default' | 'acceptEdits' | 'bypassPermissions' | 'plan';
}
| Mode | Behavior |
|---|---|
default |
Normal permission prompts |
acceptEdits |
Auto-accept file edits |
bypassPermissions |
Skip all permission prompts |
plan |
Read-only exploration mode |
Interactive Questions via Tool
Claude exposes an AskUserQuestion tool that the agent can invoke:
interface AskUserQuestionInput {
questions: Array<{
question: string;
header: string; // Max 12 characters
options: Array<{
label: string;
description: string;
}>;
multiSelect: boolean;
}>;
}
The tool result contains user selections, but the SDK handles UI internally.
Codex
Codex uses sandbox levels rather than interactive permission requests. No question system is documented in SDK mode.
Sandbox Modes (Static)
interface ThreadOptions {
sandboxMode?: "read-only" | "workspace-write" | "danger-full-access";
}
| Mode | Behavior |
|---|---|
read-only |
No file modifications allowed |
workspace-write |
Can modify files in workspace |
danger-full-access |
Full system access |
CLI Flags
| Flag | Behavior |
|---|---|
--full-auto |
Auto-approve with workspace-write sandbox |
--dangerously-bypass-approvals-and-sandbox |
Skip all prompts |
No Interactive HITL
Codex does not expose events for permission or question requests in SDK mode. All approval decisions must be made upfront via sandbox mode selection.
Amp
Amp uses declarative permission rules configured before execution. No SDK-level API for interactive responses is documented.
Permission Rules (Declarative)
interface PermissionRule {
tool: string; // Glob pattern: "Bash", "mcp__playwright__*"
matches?: { [argumentName: string]: string | string[] | boolean };
action: "allow" | "reject" | "ask" | "delegate";
context?: "thread" | "subagent";
}
| Action | Behavior |
|---|---|
allow |
Automatically permit |
reject |
Automatically deny |
ask |
Prompt user (CLI handles internally) |
delegate |
Delegate to subagent context |
Example Rules
const permissions: PermissionRule[] = [
{ tool: "Read", action: "allow" },
{ tool: "Bash", matches: { command: "git *" }, action: "allow" },
{ tool: "Write", action: "ask" },
{ tool: "mcp__*", action: "reject" }
];
No SDK-Level Response API
While "ask" suggests runtime prompting, Amp does not expose an API for programmatically responding to permission requests. The CLI handles user interaction internally.
Architectural Comparison
Fully Interactive (Bidirectional)
- OpenCode: Emits events, expects responses via API
Tool-Based Questions
- Claude: Agent invokes
AskUserQuestiontool, SDK handles UI
Static/Declarative Only
- Codex: Sandbox mode set upfront
- Amp: Permission rules declared upfront
Implications for Integration
| Approach | Pros | Cons |
|---|---|---|
| Interactive (OpenCode) | Full control, runtime decisions | Complex to implement, requires event loop |
| Tool-Based (Claude) | Agent-driven, flexible | Limited to predefined tool schema |
| Static (Codex/Amp) | Simple, predictable | No runtime flexibility |
For building a unified agent interface:
- OpenCode requires implementing question/permission response handlers
- Claude can be used with
bypassPermissionsor handled via tool interception - Codex/Amp only need upfront configuration