Codex uses the osc_title_stability idle strategy, but it draws its
progress in the pane body ('Working … esc to interrupt'), not the OSC
title. The title goes stable mid-turn, so ~2s later the classifier
declared codex idle while it was still working. Add a thinking-promoter
pattern ((?i)esc to interrupt) to the codex built-in preset; classify()
checks promoter regexes against the rendered screen before the
title-stability verdict, so codex stays in thinking until the turn's
in-progress footer actually disappears.
Resolves the [CODEX IDLE] TODO item.
- Palette's per-child "Kill <name>" action is now labelled "Close <name>"
(action kind unchanged; still SIGTERM). Matches the existing "Close
agent: …" context entry and reads less violent for a graceful term.
- New "New Terminal" palette entry spawns a bare interactive $SHELL pane
via LaunchTerminal (kind=terminal). Replaces the default "shell"
process preset that was seeded on first run.
- Exited KindTerminal entries are now dropped from the session in
reapChild — terminals have no restart path, so leaving them behind as
greyed rows in the Processes sidebar was just clutter. processList
also filters defensively.
Classifies every running child as idle/working/thinking/permission/error
using one of three pluggable strategies (output_activity,
osc_title_stability, osc_title_status) plus optional regex promoters
applied to the tail of recent output. State and last-match reason are
exposed via MCP on ProcessInfo and get_process_status. Per-preset
configuration lives on a new preset.IdleDetection block with bundled
defaults for the first-party claude/codex/opencode presets.
OSC title plumbing is exposed as Emulator.Title(), polled from the
session pump after each emulator write so title-change activity feeds
into the classifier without an extra cgo callback.
The MCP timer surface expands to match Solo: timer_set,
timer_fire_when_idle_any/all, timer_cancel, timer_pause, timer_resume,
timer_list. timer_wait is now a thin wrapper that shares the same
manager so it shows up in timer_list while pending. Timer bodies are
delivered to the owner process through the existing
InjectAsOrchestrator path. Top-level (non-agent) callers can attach
timers to a specific process via owner_process_id; omitting it grants
universal cancel/pause/resume/list privileges.
The sidebar gains a state glyph per process row and appends a
nearest-timer indicator when one is pending or paused.
Tests: idle_test.go covers the classify() pure function across the
three strategies and regex promotion; timers_test.go covers the
manager. Harness scenarios cover output_activity, osc_title_stability,
osc_title_status, and regex promotion, plus timer_set delivery,
cancel, pause/resume, idle_any-on-transition, idle_all-pending, and
idle_all-already-satisfied. A new wait_until_mcp harness step type
polls an MCP method until an assertion holds.
This batches the in-flight [Unreleased] block from CHANGELOG.md into a
single commit. Highlights:
- Real MCP protocol layer (initialize / tools/list / tools/call) so
vendor MCP clients can complete the handshake against the per-PID
socket. Legacy direct-dispatch preserved for the harness.
- New mcp_injection kinds — cli_override for codex, config_env for
opencode — joining the existing env-var and config_file paths so
patterm can slot into more agents without touching their real
config or auth.
- Ctrl+A/D and Ctrl+W/S focus navigation across tabs and intra-tab
process lists, recognised in legacy / kitty CSI u / xterm
modifyOtherKeys encodings.
- Palette macros (sw / k / sp ) and reordering so open sessions
surface above spawn-new entries.
- Two-row tab bar, sidebar/tabbar/status chrome cache, viewport-wipe
on agent spawn, CR-terminated orchestrator injections, and split-
Enter PTY writes so paste-detecting TUIs see Enter as a key event.
Also fixes the bug logged in TODO: claude's Ctrl+O tool-call expansion
emits CSI 0 J, which the viewport renderer was forwarding verbatim —
wiping the sidebar to the right of the cursor and leaving the chrome
cache convinced nothing had changed. CSI 0 J and CSI 1 J are now
translated into per-row ECH sequences clamped to the viewport, same
as CSI 2 J and CSI K already were.
Agent guides (CLAUDE.md / AGENTS.md) now spell out the
TODO->CHANGELOG workflow so completed items land in the changelog
rather than as ticked entries left behind in TODO.
Rename list_children/read_output/kill/send_message_to to their SPEC §7
process_id-shaped names; drop report_to_parent (direction inferred by
send_message) and policy_check (replaced by per-project trust gating).
Add the SPEC's missing tools: start_process, restart_process,
close_process, rename_process, select_process, get_process_status,
get_project_status, get_process_raw_output, search_output,
get_process_ports, whoami, help.
Process model now distinguishes agent/terminal/command kinds with
opaque p_<6hex> IDs. Command entries are session-persistent so they
survive PTY exit and can be Restart'd. Status enum gains starting and
stopped. screen_version, port detection, and bracketed-paste send_input
land alongside.
Trust gating (internal/trust) replaces the regex policy: command-preset
spawns return needs_trust on first use; the user confirms in a
status-line modal and the grant persists to
\$XDG_DATA_HOME/patterm/projects/<key>/trust.json.
Tests cover send_message direction inference (parent↔child, sibling
rejection, nil caller paths) and trust grant persistence across reopen.