Skip to content

squadrant sessions / whoami — first-class agent session introspection (registry-backed, read-only) #669

Description

@tu11aa

Problem

There is no first-class way to ask "what agent sessions are live, and which one am I?" — pid, socket path, session id, status. Today that gets hand-rolled with ps/env incantations at the call site, and the obvious commands are wrong in non-obvious ways (see Traps).

All the data already exists on disk for Claude sessions. This is a read-only formatting command, not new machinery.

Why now

This is the lookup both phases of #667 need:

  • Phase 1 (liveness) — read status / waitingFor per session instead of scraping panes.
  • Phase 2 (delivery) — resolve a crew to its messagingSocketPath before posting an NDJSON frame.

Right now that resolution is ad-hoc and re-derived by hand every time.

Recommended scoping: generic command + thin seam

Per CLAUDE.md"does this work for non-Claude agents too?" — as scoped this is Claude-only, so it needs a migration path from the start.

Recommendation: ship a generic squadrant sessions backed by one introspection method on the existing AgentDriver seam, with Claude as the first and only implementation. codex and opencode return "unsupported" until someone implements them.

This is deliberately not a speculative abstraction (Karpathy principle 2). The returned shape — id, pid, cwd, status, address — is one squadrant already models today in its task and liveness records. The seam names a boundary where we know agents diverge, rather than inventing one for hypothetical future needs. The implementation behind it stays a thin file reader.

The alternative — a permanent squadrant claude sessions top-level verb — was considered and is not recommended. It adds Claude-only public surface with no migration path, which is exactly what the multi-agent direction statement warns against, and it is harder to walk back once shipped. We already know how that story ends: the v0.13.3 Claude-only delivery fix never got a cross-agent path and became #514/#657.

Proposed surface

squadrant sessions [--json] [--project <name>] [--live-only]
squadrant whoami   [--json]
  • sessions — the table, with a derived health column reconciling three independent signals (pid alive? socket present? status fresh?). They can disagree; the command should say so rather than silently pick one.
  • whoami — resolve the calling session via $CLAUDE_CODE_MESSAGING_SOCKET, falling back to a parent-tree walk. Must not use pgrep (Trap 1).
  • --json is the one that matters for the daemon; the table is for humans.

Available data

Verified against Claude Code 2.1.227, macOS. One file per live session at ~/.claude/sessions/<pid>.json:

field example note
pid 20523 also the filename, also the socket name
sessionId 09ba6f18-… the stable key — survives process restart
messagingSocketPath /tmp/cc-socks/20523.sock the peer inbox
name squadrant-59 mutable; auto-derived unless set
nameSource derived absent once renamed
status idle | busy | shell | waiting self-reported by the agent
waitingFor permission prompt only when status: waiting
cwd /Users/…/me/squadrant how you map a session → project
kind interactive | bg | daemon | daemon-worker
entrypoint cli | sdk-cli | …
version 2.1.227 gate capability checks on this
peerProtocol 1 envelope version
procStart Tue Aug 11 05:53:51 2026 pins pid → process instance
startedAt / updatedAt / statusUpdatedAt epoch ms

Two fields not in the registry that must be derived:

  • uidstat -f %u <socket> (macOS) or ps -o uid= -p <pid>. Always the current user in practice since the socket dir is 0700, but worth surfacing.
  • socket modestat -f %OLp <socket>. Expect 600. Surfacing it makes a misconfigured umask visible instead of silent.

Traps — the reason a shared command is worth having

These were each found the hard way. They are the test matrix.

  1. pgrep omits the session you're calling from. Measured: pgrep -x claude returned 8 pids while the registry held 9 — the missing one was the calling session itself. macOS pgrep skips the invoking process and its ancestors, and your session's claude is an ancestor of your shell. Anything using pgrep to identify self is silently wrong.
  2. pgrep -fl is unusable here — squadrant passes a multi-KB --settings JSON blob on the command line, so -f output is unreadable. -x stays clean.
  3. pid is not stable; sessionId is. One observed session moved pid 47716/squadrant-bb20523/squadrant-59 across a version upgrade, same sessionId. Any cache keyed on pid or name goes stale across a restart or upgrade.
  4. A crashed session leaves a fresh-looking file. It can't unlink on SIGKILL, and its last status is frozen forever. kill(pid, 0) is mandatory; statusUpdatedAt alone is not enough.
  5. sdk-cli sessions have no status field at all. Absence must render as unknown, never idle — otherwise we report an unknown session as ready for work.
  6. $CMUX_CLAUDE_PID is cmux-only. $CLAUDE_CODE_MESSAGING_SOCKET is set by Claude Code itself and is the portable self-identifier (basename … .sock). Fully generic fallback is a walk up the process tree from $$ until a claude comm matches.

Prior art — do not rewrite

  • packages/agents already owns the per-agent driver seam.
  • A reference reader is already written and tested: readRegistry() in squadrant-hub/spokes/squadrant/findings/2026-08-08-cc-peer-messaging-snippets.js (the peers and watch commands). It already does liveness reconciliation and stale handling, and it runs correctly against live sessions on 2.1.227. Lift it rather than rewriting.

Scope

Read-only. No delivery behaviour, no daemon changes, no state-machine changes. Landing this before #667 Phase 1 means the LifecycleSource work gets a tested reader instead of growing its own.

Related: #667 (peer-messaging transport), #618, #567.

Source: research side-session, 2026-08-11 — spokes/squadrant/side-handoffs/claude-session-introspection-cli.md.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions