A tool batch counted as committed only once every call had its result, so a
batch cut short was dropped whole and the retry ran tools that had already
had effects. Keep the calls that did report a result: their assistant row
stays as the pairing carrier (a request must answer every tool_calls id),
without the text of the unfinished turn, and the calls without a result are
absent so the retry re-issues them instead of assuming they ran. The kept
rows go to the store right away, and a failed rollback write is not allowed
to mask the failure being rolled back.
Add prek.toml so git commit runs ruff-pre-commit (fix then format) and
pdm-based basedpyright. Document install/install-hook; clear type warnings
that made basedpyright exit non-zero under recommended mode.
After each completed assistant+tool batch, persist and keep that prefix on
failure. Rollback only unfinished assistant/stream suffix. pending_retry
and /retry also apply when history ends with tool results (continue model
without re-executing tools).
Decode and stream reasoning_content as ReasoningDeltaEvent; keep it on
assistant messages. CLI shows dim reasoning, toggles stream/verbose, and
prints full tool results when verbose.
Keep incomplete user messages in memory/DB; roll back only
assistant/tool on failure. /retry and resume of orphan-user sessions
re-run without duplicating the user message.
Tool-loop rounds re-send history, so summed prompt tokens are billing
not context size. Track last_request_usage and label turn/session as
billed; clarify /status and end-of-turn lines.
Max-rounds and destructive-tool prompts use asyncio.to_thread with
cancel paused on the main thread so multi-tool turns no longer look
like user cancel. Hooks may be sync or async.