feat: Approvals Inbox, one cross-session queue for prompts waiting on a human

Permission dialogs, AskUserQuestion questions and idle prompts from every
session now land in a server-side inbox (web/approval-inbox.ts, one item per
session, claude-mode only) and are answerable in place: a header bell + drawer
on desktop, inline answer strips on the phone overview's NEEDS YOU rows, and
working push Approve/Deny buttons (previously dead ends, now answered straight
from sw.js with no tab open). Pending alerts survive reloads because the
frontend seeds from GET /api/approvals on init.

Answering sends the digit / Esc / prompt text through the existing tmux input
path; option digits are accepted only when they match options parsed from the
captured pane frame, and the answer path re-captures the pane first so a
dialog that already left the screen refuses with 409 instead of typing into
the composer. New elicitation_complete / elicitation_response hook matchers
resolve question items the moment they are answered in the terminal;
refreshStaleCodemanHooks heals existing cases.

Verified end-to-end against a live claude session: a real AskUserQuestion
dialog parsed into 5 option buttons and was answered from the drawer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer
2026-08-09 14:04:34 +02:00
parent b1614e89fc
commit ff10a50bc0
28 changed files with 2169 additions and 41 deletions
+40 -1
View File
@@ -479,7 +479,11 @@ html.mobile-init .file-browser-panel {
.btn-icon-header.btn-lifecycle-log,
.btn-icon-header.btn-away-digest,
.btn-icon-header.btn-session-manager,
.btn-icon-header.btn-file-viewer {
.btn-icon-header.btn-file-viewer,
/* Approvals bell: phones answer from the overview's NEEDS YOU rows instead
(inline approve/deny in mobile-overview.js); the bell would only crowd the
header it was designed to stay out of. */
.btn-icon-header.btn-approvals {
display: none !important;
}
@@ -2503,6 +2507,41 @@ html.mobile-init .file-browser-panel {
background: var(--bg-hover);
}
/* Approvals Inbox answer strip: sits under a NEEDS YOU row (sibling of the
row <button>, see _buildMobileOverviewApprovalStrip). Buttons inherit no
toolbar styling on purpose — they are one-tap dialog answers, not runs. */
.mobile-overview-row-wrap {
width: 100%;
}
.mobile-overview-approval-strip {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
padding: 0.4rem 0.2rem 0.1rem;
}
.mobile-overview-approval-btn {
border: 1px solid var(--border);
border-radius: 8px;
background: var(--bg-card);
color: var(--text);
font-family: inherit;
font-size: 0.72rem;
padding: 0.35rem 0.6rem;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.mobile-overview-approval-btn.primary {
background: var(--accent);
border-color: var(--accent);
color: white;
}
.mobile-overview-approval-btn.danger {
border-color: var(--error);
color: var(--error);
}
/* Attention states mirror the session tabs exactly: red blink when the agent
asked something (permission / question), yellow blink when it is waiting for
a prompt. Same hues and same cadence as tab-blink-red / tab-blink-yellow in