Skip to content

fix(agent): validate approval decisions and keep decision-less resumes paused - #131

Draft
LukasParke wants to merge 1 commit into
mainfrom
fix/approval-decision-dedup-and-paused-resume
Draft

LukasParke wants to merge 1 commit into
mainfrom
fix/approval-decision-dedup-and-paused-resume

Conversation

@LukasParke

Copy link
Copy Markdown
Contributor

Fixes OpenRouterTeam/typescript-sdk#518
Fixes OpenRouterTeam/typescript-sdk#517

What

Duplicate / conflicting decisions (#518). The ModelResult constructor now de-duplicates approveToolCalls and rejectToolCalls, so approveToolCalls: ['call_1', 'call_1'] executes the gated tool once and sends one function_call_output. An ID in both lists throws before any state is loaded or any tool runs:
Tool call IDs cannot be both approved and rejected: call_2. ...

Resume without decisions (#517). Before this change, loading an awaiting_approval or awaiting_hitl state with no decisions fell through to the normal path. That set the status to in_progress and sent a fresh model request past calls that still needed a decision. Now the run stays paused: no request is sent and the persisted state is untouched. requiresApproval() and getPendingToolCalls() work as before.

Clearer error. When a resume leaves the conversation paused (no decisions, or decisions for only some pending calls), getResponse() and getText() now reject with Conversation is awaiting approval (status 'awaiting_approval'); no response is available. Pending tool call IDs: .... Before, they rejected with the generic Response not available.

Behavior notes for reviewers

  • A caller that resumes a paused state with new input and no decisions now stays paused, and the new input is not sent. That matches the fix suggested in #517. Callers have to approve or reject first.
  • Streaming getters on a paused resume still report Stream not initialized. This PR doesn't change that.

Test plan

  • New approval decision validation and paused resumes (#517, #518) block in tests/unit/approval-gate-regressions.test.ts (6 tests): duplicate approve, duplicate reject, approve+reject conflict, awaiting_approval no-decision resume (no request, state unchanged, a later approval still completes), awaiting_hitl no-decision resume, and the partial-decision error message.
  • All 6 fail on main (e309ebf) and pass with the fix.
  • pnpm install --frozen-lockfile, pnpm lint, pnpm typecheck, pnpm test (agent: 1242 passed, mcp: 12 passed).

🤖 Generated with Claude Code

…s paused

De-duplicate approveToolCalls/rejectToolCalls in the ModelResult
constructor so a repeated call ID cannot execute an approval-gated tool
twice, and throw if one ID appears in both lists (typescript-sdk#518).

Resuming an awaiting_approval/awaiting_hitl conversation with no
decisions now stays paused instead of flipping to in_progress and
sending a new model request (typescript-sdk#517). getResponse()/getText()
reject with an explicit "awaiting approval" error listing the pending
call IDs instead of the generic "Response not available".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant