v2.3.1-permission-questions: enrich ACP permission wire for interactive questions and elicitations

The permission_requested WS frame now carries kind ('tool'|'question'|'plan'|
'elicitation'), input (the tool's rawInput payload), and description fields.
PermissionCard detects question-type permissions (Claude Code's AskUserQuestion)
and renders an interactive radio/checkbox form instead of approve/deny buttons.
Submitting answers auto-selects the first allow option.

Also wires up ACP createElicitation (unstable/experimental) — JSON Schema-driven
forms for structured user input. The same PermissionCard renders elicitation
fields with type-appropriate inputs. Both flows use the existing permission-waiter
blocking pattern with 120s timeout.

The response path (POST /api/coder/tasks/:id/permission) now accepts optional
updated_input alongside option_id, forwarded to the ACP agent as the user's
answer payload. Elicitation responses map to accept/decline/cancel actions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-26 21:28:14 +00:00
parent 792bbb9da3
commit 154ef78f7c
10 changed files with 507 additions and 18 deletions

View File

@@ -290,7 +290,9 @@ function useCoderMessages(sessionId: string, chatId: string | undefined, handler
} else if (frame.type === 'permission_requested') {
handlersRef.current.onPermissionRequested?.({
taskId: frame.task_id,
kind: frame.kind,
toolTitle: frame.tool_title,
...(frame.input ? { input: frame.input as Record<string, unknown> } : {}),
options: (frame.options ?? []).map((o: { option_id: string; label: string }) => ({
optionId: o.option_id,
label: o.label,
@@ -565,11 +567,11 @@ export function CoderPane({
setProviderCommands(commands);
}, []);
const handlePermissionRespond = useCallback(async (optionId: string | null) => {
const handlePermissionRespond = useCallback(async (optionId: string | null, updatedInput?: Record<string, unknown>) => {
if (!permissionPrompt) return;
setPermissionBusy(true);
try {
await api.coder.respondTaskPermission(permissionPrompt.taskId, optionId);
await api.coder.respondTaskPermission(permissionPrompt.taskId, optionId, updatedInput);
setPermissionPrompt(null);
} finally {
setPermissionBusy(false);
@@ -716,7 +718,7 @@ export function CoderPane({
{permissionPrompt && (
<PermissionCard
prompt={permissionPrompt}
onRespond={(id) => void handlePermissionRespond(id)}
onRespond={(id, input) => void handlePermissionRespond(id, input)}
busy={permissionBusy}
/>
)}