Summary
When the SDK loads a persisted conversation whose status is awaiting_approval, the "resume from approval" path only runs if the caller passed at least one entry in approveToolCalls or rejectToolCalls. If both are empty, the SDK instead sets the conversation to in_progress and sends a fresh model request — advancing a conversation that was deliberately paused for a human decision, with no decision recorded.
This only affects applications that use the human-in-the-loop approval feature (a state accessor plus approveToolCalls/rejectToolCalls) and then resume a paused conversation. Apps that don't use approvals are unaffected.
Affected code
src/lib/model-result.ts (on main, d26f835), in initStream():
// Check if we're resuming from awaiting_approval with decisions
if (loadedState.status === 'awaiting_approval' &&
(this.approvedToolCalls.length > 0 || this.rejectedToolCalls.length > 0)) {
...
return; // Skip normal initialization, we're resuming
}
...
await this.saveStateSafely({ status: 'in_progress' });
With both arrays empty the guard is false, so execution falls through to the in_progress save and the normal request path runs.
Impact
The awaiting_approval state is meant to hold until a human approves or rejects. Advancing it without a decision defeats that invariant: the pending tool call can be re-emitted and executed on a later turn with no approval recorded, and the saved state no longer reflects that a decision was owed.
How to reproduce / confirm
- Persist a conversation in
awaiting_approval with a pending tool call via a state accessor.
- Resume it without passing
approveToolCalls or rejectToolCalls.
- Observe the status becomes
in_progress and a new model request is sent.
Suggested fix
Treat an awaiting_approval state with no decisions as still awaiting: keep the status paused and return without issuing a model request. Only advance to in_progress once an explicit approve/reject decision has been applied.
Summary
When the SDK loads a persisted conversation whose status is
awaiting_approval, the "resume from approval" path only runs if the caller passed at least one entry inapproveToolCallsorrejectToolCalls. If both are empty, the SDK instead sets the conversation toin_progressand sends a fresh model request — advancing a conversation that was deliberately paused for a human decision, with no decision recorded.This only affects applications that use the human-in-the-loop approval feature (a state accessor plus
approveToolCalls/rejectToolCalls) and then resume a paused conversation. Apps that don't use approvals are unaffected.Affected code
src/lib/model-result.ts(onmain,d26f835), ininitStream():With both arrays empty the guard is false, so execution falls through to the
in_progresssave and the normal request path runs.Impact
The
awaiting_approvalstate is meant to hold until a human approves or rejects. Advancing it without a decision defeats that invariant: the pending tool call can be re-emitted and executed on a later turn with no approval recorded, and the saved state no longer reflects that a decision was owed.How to reproduce / confirm
awaiting_approvalwith a pending tool call via a state accessor.approveToolCallsorrejectToolCalls.in_progressand a new model request is sent.Suggested fix
Treat an
awaiting_approvalstate with no decisions as still awaiting: keep the status paused and return without issuing a model request. Only advance toin_progressonce an explicit approve/reject decision has been applied.