- Anchor file becomes temp/.active_project.<host pid>: dies with the GA process,
so closing GA auto-deactivates; re-activation requires user confirmation per SOP
- Multiple GA instances each keep their own active project, no cross-talk
- Startup sweep removes only legacy unsuffixed anchors and own-pid leftovers
(pid reuse); anchors of other pids are never touched — deliberately no
liveness probe, because os.kill(pid, 0) on Windows terminates the process
Activate a project with a file anchor (temp/.active_project); every turn
the agent_before hook appends an L1 context (rules + memory file pointer
+ wrap-up discipline) to the latest user message, while the full project
memory (L2) stays on disk for the model to read on demand.
Zero core changes: plugin + SOP + gitignore whitelist only.
Slash commands returned from _on_enter before _expand ran, so a command
argument carried the literal [Pasted text #N] / [File #N] / [Image #N]
placeholder instead of the real content — e.g. `/morphling <pasted
multi-line target>` reached the agent as the marker string, not the paste.
Expand placeholders at the top of the command branch (mirroring the normal
submit path) and clear the placeholder stores afterward. No-op for commands
without placeholders.
Ctrl+C / selection copy fell back to Textual's OSC 52 escape on Windows/Linux.
Some terminals (notably VSCode's integrated terminal) decode the OSC 52 base64
payload as latin-1 instead of UTF-8, so non-ASCII copied text lands in the
clipboard as mojibake (e.g. 需要 -> éè¦). Classic Windows conhost ignores
OSC 52 clipboard writes entirely, so copy was a silent no-op there (incl.
Windows Server).
Prefer the OS-native clipboard (_copy_to_clipboard: win32 CF_UNICODETEXT /
pbcopy / wl-copy / xclip), which writes real Unicode and bypasses the terminal.
Keep OSC 52 only as a fallback, and force it under SSH (SSH_CONNECTION /
SSH_TTY) where the native call would write the remote host's clipboard instead
of the user's local one.
- _term_write(): route OSC/control sequences through Textual's driver writer
queue instead of direct sys.__stdout__. A title/OSC write landing mid-frame
during streaming used to split Textual's own escape sequences, rendering as
flashing ANSI garbage; serializing on the same writer queue lands it cleanly.
- sidebar: wrap the session list in a VerticalScroll (#sidebar-scroll) so it
scrolls when overflowing (scrollbar-gutter reserved to avoid row reflow);
inner #sidebar Static keeps padding so the click hit-test math is unchanged.
Keyboard session-switch scrolls the active session into view.
Rename tracked email.py/lark.py to _email_example.py/_lark_example.py so
the poller (which skips _-prefixed files) no longer polls them on a fresh
clone. Activate by copying to email.py/lark.py (now gitignored). Also nudge
manager to reuse existing subagents on IM signals.
/continue list previews showed JSON debris (e.g. `{`, `" } ]} === Response ===`)
for some sessions. Two causes in _preview_from_file:
1. Models sometimes emit an unclosed <summary>, so the non-greedy DOTALL regex
pairs it with a far-away </summary> and captures === block headers / JSON
across rounds. Now only the latest <summary> is used, and it is rejected if
it looks like such a cross-segment capture (contains `=== `/`"role"` or
>200 chars) — falling through instead of digging for older summaries.
2. The fallback scanned for the first non-=== line, returning JSON structure
lines. It now uses the last real user prompt via a new _last_user(), which
scans Prompt blocks directly (so response-less/aborted sessions still
preview) and reuses _user_text() to skip tool_result continuations and all
_INJECT_MARKERS (WORKING MEMORY / SYSTEM TIPS / DANGER ...).
Scanned 261 local logs: garbage previews 26 -> 0.
* fix(tui): stop CRLF stdout rendering as blank lines (v2)
Windows child stdout is `\r\n`; the streaming path fed it straight to
`Text.from_ansi`, which treats `\r` as a carriage return and erases each
line's text, so a `[Stdout]` block rendered as a run of blank lines until
the turn finished (the done-state Markdown render strips `\r`). Normalise
`\r\n` -> `\n` before `from_ansi`; lone `\r` is kept so progress-bar
overwrites still work.
* fix(tui): stop leaking empty per-session task_dir signal folders
Both v2 and v3 eagerly `os.makedirs` the per-session `_intervene` /
`_keyinfo` signal dir, but the only writer (`_intervene`) already creates
it lazily — so every session that never used intervene left a stale empty
`temp/_tui_v2_<pid>_<id>` / `temp/_tui_v3_<pid>` behind (85+ accumulated).
`consume_file` tolerates a missing dir, so the eager create is redundant.
- Set only the task_dir *path*; let the writer create it on first inject.
- Remove empty dirs on session close (v2 `_cmd_close`), on graceful exit
(v2 `on_unmount`, v3 `_cleanup`), and sweep stale ones left by crashes
at startup (v2 `on_mount`, v3 `main`). `os.rmdir` only removes empty
dirs, so a still-pending `_intervene` is never clobbered.
* fix(tui): repair /continue search box cursor & up-nav loop (v2)
The SearchablePicker swallowed Right while the search Input had focus
(always treating it as "select"), so the caret could never move right in
the query. And Up could leave the list but never return: from the search
box Down entered the list, yet Up at the top row was stuck.
- Right commits the highlight only when the caret is already at end of the
query; otherwise it moves the caret within the search text.
- Up on the first list row returns focus to the search box (scoped to
/continue — no-op for ChoiceLists outside a SearchablePicker), clearing
the highlight so the next Down re-enters at the first row, not the second.
- Up from the search box wraps around to the bottom of the list.
CommonStack (https://commonstack.ai) is an OAI-compatible gateway
aggregating Claude, GPT, Gemini, DeepSeek, MiniMax, Zhipu, xAI,
Moonshot, Qwen and Xiaomi models behind a single key.
Adds it to the configure_mykey wizard as a native_oai entry,
placed alongside other multi-provider relays (openrouter, crs, gmi).
The wizard's existing probe_models() flow fetches the live
/v1/models list at config time; model_choices keeps the same
2-item fallback shape as openrouter.