fix: show every pending permission prompt for parallel tool calls #15

Merged
lucas merged 1 commit from feat/f-12 into main 2026-10-15 17:36:35 +02:00
Collaborator

When two tool calls hit an ask permission rule at the same time, the server kept a separate prompt for each, but the web client kept only one per session. The second prompt replaced the first, so one prompt was never shown and its call waited until the turn was aborted. The session snapshot also carried only one prompt, so a reload didn't help.

Prompts now form a queue per session, oldest first. The approval dialog shows the oldest prompt with a "+N more pending" count, and answering it reveals the next. A response or cancellation removes only its own toolCallId. The snapshot field is now pendingPermissionPrompts (an array), so a reload restores every pending prompt.

Decisions

  • Prompts are shown one at a time, oldest first, with a "+N more pending" count rather than all together. The issue allowed either; this keeps the single modal and its existing layout.
  • The snapshot field SessionSnapshot.pendingPermissionPrompt (one prompt) is replaced by pendingPermissionPrompts (an array), with no compatibility alias. Server and web ship together from this repo, and nothing else reads the field.
  • When an aborted call cancelled its prompt, the frame stayed in pendingFrames and could still reach a snapshot. The abort path now removes it too.
  • The add/remove logic for the queue lives in a new pure module, apps/web/src/lib/permission-prompts.ts, so it can be unit-tested without the zustand store.
  • Approving one prompt "for session" doesn't resolve other prompts for the same rule that are already queued; each is still answered on its own.

Verification

  • bun test apps/server/src/bridge/permission-filter.test.ts apps/server/src/bridge/in-process-queue.test.ts apps/web/src/lib/permission-prompts.test.ts: 41 pass, 0 fail. New tests cover:
    • Two concurrent ask calls each get their own prompt, and each response settles only its own call.
    • Aborting one call cancels only its prompt.
    • The client queue appends, ignores duplicates, and removing one prompt keeps the others.
  • bun run typecheck (protocol, server, web): all passed.
  • Full bun test: 962 pass, 0 fail across 70 files.
  • Not exercised: a manual browser run with two parallel prompts and a reload.

Closes #12

When two tool calls hit an `ask` permission rule at the same time, the server kept a separate prompt for each, but the web client kept only one per session. The second prompt replaced the first, so one prompt was never shown and its call waited until the turn was aborted. The session snapshot also carried only one prompt, so a reload didn't help. Prompts now form a queue per session, oldest first. The approval dialog shows the oldest prompt with a "+N more pending" count, and answering it reveals the next. A response or cancellation removes only its own `toolCallId`. The snapshot field is now `pendingPermissionPrompts` (an array), so a reload restores every pending prompt. ## Decisions - Prompts are shown one at a time, oldest first, with a "+N more pending" count rather than all together. The issue allowed either; this keeps the single modal and its existing layout. - The snapshot field `SessionSnapshot.pendingPermissionPrompt` (one prompt) is replaced by `pendingPermissionPrompts` (an array), with no compatibility alias. Server and web ship together from this repo, and nothing else reads the field. - When an aborted call cancelled its prompt, the frame stayed in `pendingFrames` and could still reach a snapshot. The abort path now removes it too. - The add/remove logic for the queue lives in a new pure module, `apps/web/src/lib/permission-prompts.ts`, so it can be unit-tested without the zustand store. - Approving one prompt "for session" doesn't resolve other prompts for the same rule that are already queued; each is still answered on its own. ## Verification - `bun test apps/server/src/bridge/permission-filter.test.ts apps/server/src/bridge/in-process-queue.test.ts apps/web/src/lib/permission-prompts.test.ts`: 41 pass, 0 fail. New tests cover: - Two concurrent ask calls each get their own prompt, and each response settles only its own call. - Aborting one call cancels only its prompt. - The client queue appends, ignores duplicates, and removing one prompt keeps the others. - `bun run typecheck` (protocol, server, web): all passed. - Full `bun test`: 962 pass, 0 fail across 70 files. - Not exercised: a manual browser run with two parallel prompts and a reload. Closes #12
Two tool calls hitting an `ask` rule at the same time each waited on
their own prompt, but the web client kept only one prompt per session,
so the other was never shown and its call blocked until the turn was
aborted. Session snapshots likewise carried a single prompt.

Prompts now queue per session, oldest first; answering or cancelling
one leaves the rest in place, and snapshots carry every pending prompt
so a reload restores them all. An aborted call's prompt is also
dropped from the snapshot instead of lingering after cancellation.

Co-Authored-By: Claude Opus 5.5 via omp-deck
lucas merged commit 96832816d8 into main 2026-10-15 17:36:35 +02:00
lucas deleted branch feat/f-12 2026-10-15 17:36:35 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
lucas/omp-deck!15
No description provided.