Remove system strategy (legacy maps to developer). synthetic_tool still
forges the call but uses real stack text as the tool result, and now
yields ToolCall/Result events so the CLI flushes and does not glue nag
text to the next assistant stream.
Split tags: [TODO OPEN WORK] for pending/in_progress (undone work) vs
[TODO HYGIENE] for all-terminal leftover groups. End-of-turn review lists
open titles; tool docs stress open items as unfinished work.
Inject turn-start [TODO REMINDER] for non-empty stacks; rewrite end-of-turn
[TODO OPEN] copy so open items read as unfinished work. needs_review now
always true while open items remain (even if todo_* ran this turn); terminal-only
stacks still need review if untouched. Update tool docs and tests.
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.
TOP = last element only for pop; multi-push puts first title on top for DFS
breakdown. Dropped nested-frame model; migrate old frames JSON to flat stack.
push of one or more titles opens a new frame (siblings at one depth);
pop leaves the frame. Supports T1,T2 → T1.1,T1.2 → pop → T2.1 pattern.
Legacy flat session JSON migrates to a single root frame.
Sub-task stack stored on session.todo_stack; model tools todo_*; slash
/todos for human ops; agent loop injects a review user message when open
todos exist and no todo_* tool ran this turn. Stack carries across /compact.
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).
Add agent config compact_system_prompt, compact_user_prefix, and
compact_seed_text; wire through summarize and seed messages. Fix default
user prefix to include {transcript}; use safe seed substitution so braces
in summaries do not break formatting.
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.
Drop stored pending field; incomplete turns are just history ending in
UserChatMessage. Compact seeds use an assistant summary so they are not
mistaken for retryable orphans.
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.
After the first model call, scale char-based token estimates by
API/resolved prompt size so budget checks track near-real tokens.
/compact uses the same calibration when available.
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.
Accept async-def returning async iterator; stream via
chat_completions(..., stream=True); typed StreamToolCallDelta merge;
update scripted test clients to emit real chunks.