Agent monitor: live canvas of running agents, lanes, and messages - #24
Merged
Merged
Conversation
Add the core of the agent monitor: a domain model, one adapter per transcript format, and a reducer that derives each agent's status from evidence (process records, lifecycle events, parent tool results) rather than file modification times. A byte-level tailer consumes only complete JSONL lines, and an index discovers, tails, and pages transcript files. `pstack-monitor doctor` indexes the recent window and prints per-source parse health, record counts, and CLI versions, never content. Unknown record types and malformed records are counted and surfaced instead of dropped. Refs #23
`pstack-monitor start` launches a detached server on 127.0.0.1 and prints a link carrying a per-start token; a second start reuses the running server, and a server from another build is replaced. `status` and `stop` complete the lifecycle. Nothing times the server out. The server keeps the index current by polling, with recursive file events only as a hint, probes recorded processes by pid and start time, and pushes snapshots, deltas, and the watched agent's new timeline items over server-sent events. Every route but the health check requires the token; foreign Host and Origin headers are rejected to stop DNS rebinding, and the API is read-only. Refs #23
The server now bundles a page in memory at start: a node canvas of the selected session's agents, a session rail, live counters, and a detail panel that streams one agent's timeline. Spawn links run left to right from each parent's output port; every agent with a known model carries a model node on a dashed tether. Motion reports work rather than decorating it: running wires flow, running cards sweep, each activity event sends a spark down its wire, and a new agent emerges from its parent. Reduced motion keeps the state colors and drops the movement. The theme follows the session's harness, warm terracotta for Claude Code and blue for Codex, and the first paint takes it from the link so it never flashes the wrong one. The page has no framework or new dependency, renders transcript text only through textContent, and runs under a strict same-origin content security policy. Refs #23
`/pstack:monitor` starts the monitor and hands the user its link. The parent passes `--parent` for the harness it is, the same rule the external runner follows; the flag sets the page theme and first selection and routes nothing. Document the sources, the evidence each status rests on, the lifecycle, and the security model in the reference, and record the new fork-owned files in UPSTREAM-FLEX.md and NOTICE.md. Refs #23
`pstack-runner` keeps a child CLI's output in memory until it exits, so an external lane such as a Codex cross-judge launched from Claude Code was invisible while it ran. The runner now writes an opt-in journal per lane under ~/.pstack-flex/lanes/: the start record, the child's stdout as it arrives, and a copy of the receipt. It writes only when that directory exists; `pstack-monitor start` creates it, and `pstack-monitor journal off` deletes it with everything it recorded. The server prunes lanes older than seven days. The tap is a synchronous callback inside the existing read loop, passed only to the model run, so it adds nothing to the cancellation and deadline races. Every journal write is guarded: a failure stops the journal and never changes the lane's receipt, exit status, or output. The existing runner tests point the journal at a missing directory so they never write into a real home. `--label` names a lane by the role it fills and routes nothing. The monitor's lanes adapter turns the journal into canvas nodes under the session that launched each lane, streaming Codex and Grok lanes live and showing Claude-style lanes' single result when they exit. Refs #23
The skill now maps the request to one command: open (start), stop, kill, or shut down (stop), and check or relink (status). It states that stop ends the server only and never touches the agents the page shows, and says where to stop a running agent instead. Refs #23
Agents talk as well as spawn: Codex parents and children exchange agent messages, and Claude Code agents use SendMessage. The canvas now draws each sender-to-recipient pair as a dashed, arrowed arc with a message count, bowing to the left of travel so a reply takes the other side, and each new message sends a spark along its arc. The bar counts the session's messages and the panel shows what an agent sent and received. Codex records each message once, in the recipient's rollout, addressed by agent path; the store resolves the path within that tree. Claude Code targets resolve by agent id, name, or `main` within the session. Messages are keyed by their own id, so one seen twice counts once, and a message whose ends are not both indexed, or that goes to another session's socket, is left out rather than guessed. Refs #23
The monitor never starts by itself, which the docs did not say. Add a "Watching your agents" section to USAGE.md: starting it from Claude Code, Codex, or a terminal (with the installed launcher's path), the options, stopping it and the other commands, the lane journal, what it reads and writes, and a troubleshooting table. The README gains an optional third get-started step that points there. Refs #23
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.
Closes #23
What changed
pstack fans work out to many agents, across two harnesses and several providers, and nothing showed that fan-out while it ran. This adds
/pstack:monitor: a read-only local server with a live node canvas of the agents on this machine.SendMessagecalls.pstack-runnerkept a child CLI's output in memory until exit, so a Codex cross-judge launched from Claude Code was invisible while it ran. The runner now writes an opt-in journal under~/.pstack-flex/lanes/(start record, stdout as it arrives, receipt copy) while that directory exists. A new--labelflag names a lane by its role and routes nothing. A journal failure never changes a lane's receipt, exit status, or output.pstack-monitor doctorprints parse health without content.HttpOnly,SameSite=Strictcookie, foreignHostandOriginrejected, no writes, strict same-origin CSP, transcript text rendered only as text. No hooks, no harness settings changed, no idle timeout.pstack-monitor start|status|stop|doctor|journal on|off|status.startreuses a running server of the same build and replaces one from another build. The skill maps "stop / kill / shut down the monitor" tostop./pstack:monitor, by asking Codex forpstack:monitor, or withpstack-monitor start; it runs in the background until stopped or the machine restarts.docs/USAGE.mdgains a "Watching your agents" section (start, stop, terminal paths, options, lane journal, troubleshooting), and the README gets an optional third get-started step.Reviewing
The commits read in order: core indexing, the server, the canvas, the skill and docs, the runner journal, stop handling, and message arcs. Everything under
skills/poteto-mode/scripts/monitor/andskills/monitor/is new and fork-owned. Upstream-authored files touched, all additive and recorded inUPSTREAM-FLEX.md:runner/run.ts: an optional stdout callback on the model run, the journal opened after output reservation and finished on both return paths.runner/cli.ts,runner/types.ts: the optional--label.runner/run.test.ts: points the journal at a missing directory so existing tests never write into a real home, plus four journal tests.references/provider-dispatch.md: one pstack-flex paragraph after the invocation block.scripts/package.json:monitorin the test filter and two tsconfigs intypecheck.Verification
Local gate on Bun 1.3.11:
bun run test301 pass,bun run typecheckclean,PSTACK_STATIC_ONLY=1 bash tests/skill-collision-repro.shexit 0, manifest parse ok,claude plugin validate plugins/pstackpassed,git diff --checkclean.Checked before install:
pstack-monitor doctoragainst this machine's transcripts (Claude Code 2.1.281 to 2.1.287, Codex 0.155 to 0.160, last 7 days): every line parsed, no malformed records.pstack-runneragainst a fakecodexCLI with the journal on: the "arena cross-judge" lane appeared under its parent session while running, streamed its tool calls, and finished done with the receipt's token counts; the journal's receipt equals the runner's.Live evidence: pending. To record it:
/pstack:monitor: expect a link, the orange theme, and the current session live. Run a small arena with one Codex lane: expect a labeled lane that streams and finishes. Run/pstack:monitor stop: expectpstack-monitor stopped.pstack:monitor: expect the blue theme and the root thread. Spawn a child: expect it linked under the root with its messages drawn.pstack-monitor journal off, then one external lane per harness: expect an unchanged receipt and nothing under~/.pstack-flex/lanes.Known limits: a Codex thread killed mid-turn stays "in turn" because Codex writes nothing on a crash; Claude Desktop and SDK sessions show "unknown" because they keep no process record; Claude, DeepSeek, and MiniMax lanes show their output only at exit because those CLIs print one result.
A pull request without live evidence remains a draft. Do not merge, tag, release, or roll it out.