Merge release into ci.yml: tag pushes run check then publish (needs:
check). Remove standalone python-publish so PyPI never ships on red CI.
Retag (delete+push v*) still retriggers the pipeline.
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.
After setsid(), ioctl(TIOCSCTTY) (or reopen slave) so the session leader
has a real controlling terminal. Live-tested: open_pty([sudo,echo,test])
shows a password prompt; ask_into_pty + read_pty yields test with exit 0.
Denylist hits ask the user (default deny, timeout) instead of only hard
rejecting when a policy hook is installed. Independent of YOLO soft-confirm;
session grants skip re-prompt for the same basename. CLI installs the hook
via install_cli_limit_hooks.
Prompt the human (optional no-echo secret) and write only to the PTY.
Tool results never include the answer so secrets stay off external APIs.
Empty/Ctrl-C cancel without writing; submit defaults to trailing newline.
Map CancelledError and TimeoutError from models() to RuntimeError so
merged_model_choices falls back to config/cache. Stub remote fetch in
/provider unit tests so they never hit the network.
Prefer $VISUAL then $EDITOR (blocking). Without either, fall back to
xdg-open / open / startfile (non-blocking) for config paths only.
/edit stays blocking-only so the temp buffer can be re-read.
Default still replaces first match only. Report how many matches were
found, how many replaced, and remaining hits so agents can raise
max_replaces or narrow old_string.
Unset/empty url still maps to durable user-data chat.db.
Explicit :memory: is not rewritten; CLI warns when not quiet.
DatabaseConfig.url default is None; engine treats None as memory.
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.
Only fetch remote catalog when interactive model pick needs it; otherwise
use config/--model/session and warm Tab cache in the background. Cap list
fetch at 5s so a dead API cannot hang startup.
asyncio signal handlers freeze ContextVars at install time; reinstalling
while allow_task_cancel was still False left cancel permanently disabled.
Use a process-level prompt depth counter and reinstall only after depth=0.
Complete /todos actions and todo ids, session ids for /resume|/delete
(from cache after /sessions), /rounds hints, and --flags. Pass prior
tokens so multi-arg completion works.
Create scratch dirs under system temp (tempfile), allow absolute paths
under them without rebinding the project workspace or session. Cleanup
owned temps on chat exit. Cap 16 concurrent temps; sanitize prefix.
classify_danger prompts on interactive shell/runtime launches and any -c
code (with argv + snippet preview). Confirm deny can attach a user comment
for the model. Restores ToolRegistry.execute after WIP breakage.
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 /compact, reload seed from DB so _persist_from matches stored rows.
rebuild_client preserves the checkpoint via replace_messages. Clarify that
the active session is summary-only and full history stays on the old id.