Commit Graph
2 Commits
Author SHA1 Message Date
Shakti Prasad Mohapatra 9bad0fdd8e fix: don't inject placeholder for trailing pending tool calls
The repair was synthesising placeholder tool results for any dangling
call, including calls in the last assistant message that are simply
pending (interrupted for approval/question). This broke durable resume
because the provider saw the placeholder and thought the tool already
ran, so resolve_inbox could not re-execute the tool.

Now trailing calls (assistant tool_calls as the last message with no
result) are left untouched — the engine will resume them. Placeholders
are only injected when the thread has moved past the call, proving it
is corrupt rather than pending.

Fixes test_durable_resume_question and
test_durable_resume_approval_executes_tool.
2026-08-26 21:55:48 +05:30
Shakti Prasad Mohapatra 1789a0fd86 fix: repair tool-call/result pairing on load to prevent unrecoverable 400 errors
When a turn is interrupted at the wrong moment, the append-only JSONL can
end up with a user message between an assistant tool_calls block and its
tool result. Providers reject this ordering (Anthropic 400/2013, OpenAI
'tool_call_ids did not have response messages'), making the session
permanently unrecoverable from the UI.

_repair_tool_pairing() runs in ConversationStore.load() and:
- moves a real tool result found later in the thread to sit right after its call
- synthesises a placeholder result for a call with no matching tool message
- is idempotent — well-formed threads pass through unchanged

Fixes #331
2026-08-26 21:55:48 +05:30