Adds a per-child idle-state classifier (5 states × 3 strategies + regex promoters) wired through MCP ProcessInfo / get_process_status and rendered as a state glyph in the sidebar.
Adds the full Solo timer tool set: timer_set, timer_fire_when_idle_any / _all, timer_cancel, timer_pause, timer_resume, timer_list. timer_wait is now a thin wrapper over the shared manager. Top-level callers can attach timers via owner_process_id; orchestrator-level callers (empty owner) have universal management rights.
Exposes Emulator.Title() on the libghostty-vt wrapper so OSC 0/1/2 updates feed the title-stability and title-status strategies without a cgo callback.
Sidebar shows a nearest-pending-timer indicator (⏱ 12s / ⏸ paused) on rows that own a timer.
New idle_detection preset block with bundled defaults for the first-party claude / codex / opencode presets.
New harness wait_until_mcp step type that polls an MCP call until an assertion holds.
Out of scope (follow-ups)
LLM-driven auto-summarization (Solo's other half).
Manual smoke: spawn the bundled claude preset, watch the sidebar glyph cycle while the agent works / waits for input.
## Summary
- Adds a per-child idle-state classifier (5 states × 3 strategies + regex promoters) wired through MCP `ProcessInfo` / `get_process_status` and rendered as a state glyph in the sidebar.
- Adds the full Solo timer tool set: `timer_set`, `timer_fire_when_idle_any` / `_all`, `timer_cancel`, `timer_pause`, `timer_resume`, `timer_list`. `timer_wait` is now a thin wrapper over the shared manager. Top-level callers can attach timers via `owner_process_id`; orchestrator-level callers (empty owner) have universal management rights.
- Exposes `Emulator.Title()` on the libghostty-vt wrapper so OSC 0/1/2 updates feed the title-stability and title-status strategies without a cgo callback.
- Sidebar shows a nearest-pending-timer indicator (`⏱ 12s` / `⏸ paused`) on rows that own a timer.
- New `idle_detection` preset block with bundled defaults for the first-party claude / codex / opencode presets.
- New harness `wait_until_mcp` step type that polls an MCP call until an assertion holds.
## Out of scope (follow-ups)
- LLM-driven auto-summarization (Solo's other half).
- Interactive sidebar timer affordances (view / fire-now / pause inline).
## Test plan
- [x] `go build ./...`
- [x] `go test ./...` — all green
- [x] Harness scenarios (run individually): `idle_output_activity`, `idle_osc_title_stability`, `idle_osc_title_status`, `idle_regex_promote`, `timer_set_delivers`, `timer_cancel`, `timer_pause_resume`, `timer_idle_any_fires_on_transition`, `timer_idle_all_pending`, `timer_idle_all_already_satisfied`
- [ ] Manual smoke: spawn the bundled claude preset, watch the sidebar glyph cycle while the agent works / waits for input.
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.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
ProcessInfo/get_process_statusand rendered as a state glyph in the sidebar.timer_set,timer_fire_when_idle_any/_all,timer_cancel,timer_pause,timer_resume,timer_list.timer_waitis now a thin wrapper over the shared manager. Top-level callers can attach timers viaowner_process_id; orchestrator-level callers (empty owner) have universal management rights.Emulator.Title()on the libghostty-vt wrapper so OSC 0/1/2 updates feed the title-stability and title-status strategies without a cgo callback.⏱ 12s/⏸ paused) on rows that own a timer.idle_detectionpreset block with bundled defaults for the first-party claude / codex / opencode presets.wait_until_mcpstep type that polls an MCP call until an assertion holds.Out of scope (follow-ups)
Test plan
go build ./...go test ./...— all greenidle_output_activity,idle_osc_title_stability,idle_osc_title_status,idle_regex_promote,timer_set_delivers,timer_cancel,timer_pause_resume,timer_idle_any_fires_on_transition,timer_idle_all_pending,timer_idle_all_already_satisfied