Repository navigation
Agent status records, sucoder attention, and a coordinator example - #89
Merged
Merged
Conversation
Detection, not steering, is what costs the human when several agent sessions run at once: knowing without looking which session has stopped at a permission prompt or is waiting for input. `sucoder sessions` cannot say; its pane probe tells a live agent from a bare shell, and a waiting Claude looks the same as a working one from ps. With `attention.hooks: true`, a SuCoder-launched Claude session gets hooks through `claude --settings` (added to the user's own; a command that already passes --settings is left alone). Each hook is plain POSIX shell that atomically writes ~/.sucoder/attention/<mirror>--<target>.rec wherever the agent runs: a header line and the hook's JSON payload. Notification marks the session waiting (permission prompt, or about a minute idle at the prompt); PostToolUse records only its event name, never the tool output. The hook always exits 0. `sucoder attention` reads this host's agent-account records and each cluster's, one round trip per cluster, and lists waiting sessions first. --watch redraws, keeps ~/.sucoder/attention.json and runs attention.notify_command for each session that starts waiting, using warm connections only. --tmux prints one status-bar line from that cache and skips configuration loading and startup checks. Verified against Claude Code 2.1.316 on vantage: a --settings hook fired and wrote a group-readable record. Design, the coordinator agent and open items: docs/attention.org. Tests: tests/test_attention.py (43). The full suite's 49 failures on vantage reproduce unchanged on main (Codex runner, SSH include, /tmp permission tests). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only blocking notifications mark a session waiting. Claude tags each Notification with notification_type (read from Claude Code 2.1.316), and several are informational: auth_success, agent_completed, computer_use_*. The Notification hook now carries a matcher for the blocking types, each mapped to a kind -- permission, question or idle -- and a record from an older Claude without the field is classified by its message, else other. attention.notify_kinds (default all) and attention.notify_after_minutes (default 0) bound the push volume: every finished turn becomes an idle request a minute later. A request is one record, notified once; the watcher's first pass is its baseline. The notifier also gets SUCODER_ATTENTION_KIND. `attention --cached` lists the watcher's last pass without connecting. On the agent account with session_relay, `attention` and `--cached` run as the relay's user through `relay attention`, like `sessions`; --watch and --cache are refused there, so the caller can start no loop and pick no file under the human's home. The record keeps transcript_path, so a coordinator running as the agent account can read a local session's transcript, which peek cannot reach. examples/coordinator/: setup, a v0 brief (triage on request, one block per waiting session, send only what the human approves, never answer a permission prompt) and attention-notify, which posts to ntfy and types one sanitized line into the coordinator's tmux pane, skipping the coordinator's own mirror. The tmux route exists because `message` reaches cluster sessions only. Found on vantage and recorded, not changed: every coder-side sucoder command stops at the startup check, because ~coder/.sucoder/config.yaml is owned by coder. The relay needs that fixed by the human first. Tests: tests/test_attention.py now 70, including the notifier script through a fake tmux. Full suite: 2505 passed; the same 49 vantage failures as on main, none new. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…artup A session launched before attention.hooks was on (or by an older SuCoder, or a harness without hooks) writes no record, and `sucoder attention` silently left it out: on vantage the coordinator could not see a Savio job or the SuCoder session, both started before the install. The listing now also asks what is running and shows a live session with no record, or only an `ended` one, as `untracked`: - this host: the process table. A local launch is `sudo -u <agent> bash -c '... cd <mirror> || exit 1; umask ..; <agent>'` and cmdline is world-readable, so the human-side watcher sees it; the directory maps to a configured mirror, and a Claude --session-id gives the transcript path the coordinator reads; - a cluster: `squeue --me` fused into the existing records read (one round trip), mapped to mirror/target as `sessions` does; RUNNING jobs only; - a host without a scheduler: its sucoder- tmux sessions. Untracked sorts after idle, counts on the tmux line, never notifies. A live query that does not answer is an error, not an absence. Also: the startup check ran `sudo -u <agent> test -r config` even when already running as the agent, which needs a sudoers rule the agent lacks, so every coder-side command (relay, peek, message, attention) failed with "cannot read configuration" for a readable file. It now checks directly when the current user is the agent, as _get_use_sudo_for_agent already does for later commands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
attention.hooks: true now also hooks Codex (codex-cli 0.164.0), writing the same ~/.sucoder/attention/<key>.rec records, so `sucoder attention`, the watcher and notify_kinds treat both harnesses alike. - Per launch, as `-c hooks.<Event>=[...]` overrides; nothing is written to ~/.codex/config.toml by SuCoder. Events: SessionStart (matcher excludes post-compaction), UserPromptSubmit, PostToolUse -> working; PermissionRequest -> waiting/permission; PreToolUse on request_user_input -> waiting/question; Stop, Interrupt -> idle; SessionEnd -> ended. - Codex trusts a -c hook by a hash of its definition, so the hook command is identical for every launch and the key rides in SUCODER_ATTENTION_KEY (an `env` argv prefix, outermost but for the environment file: no extra cluster round trip). Verified in a throwaway CODEX_HOME: one "trust all", then a launch with another key ran every hook without review. --dangerously-bypass-hook-trust is not used. - Codex has no idle notification: a Codex Stop/Interrupt a minute old is promoted to waiting/idle (synthesize_idle_waits), dated when the minute ran out, so it notifies once like Claude's idle_prompt. - Records carry the harness in a fourth header field (older records read as Claude's); a truncated payload (a large PermissionRequest input) is salvaged for its leading fields instead of reading as unknown. - The untracked-session scan looks past the `env` prefix. Not yet seen live: PermissionRequest, Stop, Interrupt and the request_user_input question from a real model turn; to verify on the first real launch (docs/attention.org, Codex). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ubuntu-latest moved to 26.04 on 2026-10-19, and actions/setup-python has no 3.9 build for it, so the 3.9 leg failed at setup and cancelled the 3.11 leg. Pin the test job to ubuntu-24.04. Dropping 3.9 support is a separate decision (pyproject still says ^3.9). The Codex tests imported tomllib at module level, which would have failed collection of all of test_attention.py on 3.9. Use pytest.importorskip inside the helper, as test_remote_control does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Lets one screen show which agent sessions need a reply, instead of tabbing through a tmux per project.
attention.hooks: true, SuCoder-launched Claude and Codex sessions write one small record per session (~/.sucoder/attention/<mirror>--<target>.rec, wherever the agent runs) on each turn, tool use, permission request, finished turn and session end. Off by default.--settingshooks; only blockingNotificationtypes count as waiting.-c hooks.<Event>=...overrides, identical for every launch so that Codex's per-definition trust needs one review per account; the record key rides inSUCODER_ATTENTION_KEY. A Codex turn idle for a minute is reported as waiting (kind idle), as Claude reports itself. Nothing is written to~/.codex/config.tomlby SuCoder, and--dangerously-bypass-hook-trustis not used.sucoder attention. Lists every session, waiting ones first, across vantage and each cluster (one read per cluster, over warm connections). Modes:--json,--watch N(dashboard that also writes a cache and runsnotify_command),--cached, and--tmux(one line forstatus-right, no config load).untrackedinstead of vanishing: local launches from the process table, cluster jobs fromsqueuein the same round trip as the records.notify_kinds(permission, question, idle, other) andnotify_after_minutes.attentionruns as the human throughrelay attention;--watchand--cacheare refused there.sudo -u <agent>even when already running as the agent, so every agent-side command (relay, peek, message, attention) failed with "cannot read configuration". It now checks directly in that case.examples/coordinator/): a v0 brief for a local Claude session that triages waiting sessions and suggests replies, a notifier for ntfy and the coordinator's pane, and setup notes.Design note:
docs/attention.org.Testing
tests/test_attention.py: 120 tests, including the real hook shell for both harnesses, the listing and live-session scripts, the process scan, idle synthesis, the relay argv, and the notifier against a fake tmux.ubuntu-24.04, becauseubuntu-latest(now 26.04) has no Python 3.9 build and the 3.9 leg failed at setup. The Codex TOML tests skip on 3.9 (notomllib).-chooks, one "trust all" covering every later launch, and a real record fromUserPromptSubmitandSessionEnd.PermissionRequest,Stop,Interruptand therequest_user_inputquestion from a real model turn.🤖 Generated with Claude Code