session-processes
Skill9 starsChanged 17 days agoApache-2.0View on GitHub
ShipIt session worker processes: Claude CLI spawning and NDJSON parsing, agent abstraction (AgentProcess, ProxyAgentProcess), Docker Compose services (ServiceManager), file watcher, terminal PTY, session worker endpoints. Load when working on Claude process management, compose services, file watcher, terminal, or agent adapters.
In this repository15 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- add-endpointSkill
- android-buildSkill
- client-architectureSkill
- deployment-architectureSkill
- design-languageSkill
- docs-navigatorSkill
- dogfooding-shipitSkill
- frontend-design:frontend-designSkill
- git-architectureSkill
- prompt-architectureSkill
- server-architectureSkill
- session-containersSkill
- session-lifecycleSkill
- testing-and-qualitySkill
The file
We split this skill into the rules it gives an agent
1 rule. About 0 are common, 0 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 48
If terminal is alive -> sends empty terminal_data signal
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Session Worker Processes
Each session runs in a Docker container with a Fastify server (port 9100) that manages the agent CLI, file watcher, and terminal. Preview/dev servers are not among them — they moved to Docker Compose under the orchestrator's ServiceManager. The orchestrator communicates with session workers via HTTP (commands) and SSE (events).
Session Worker
src/server/session/session-worker.ts — Fastify server running inside each container.
Endpoints
| Method | Path | Purpose |
POST | /agent/start | Start Claude with prompt, images, system prompt |
POST | /agent/interrupt | Graceful interrupt (Ctrl+C) |
POST | /agent/kill | Force terminate |
POST | /agent/stdin | Write to agent stdin (answer questions) |
POST | /terminal/start | Start interactive shell |
POST | /terminal/input | Write to terminal |
POST | /terminal/resize | Resize terminal |
POST | /files/watch | Start file watcher (idempotent) |
POST | /files/unwatch | Stop file watcher |
GET | /files/tree | Scan file tree |
GET | /events | SSE event stream |
GET | /health | Health check |
SSE Event Stream
The /events endpoint is a long-lived SSE connection. The worker broadcasts events from all managed processes:
| Event | Source | Data |
agent_event | Claude CLI | Parsed NDJSON agent events |
agent_done | Claude CLI | Exit code |
agent_error | Claude CLI | Error message |
agent_auth_required | Claude CLI | Auth URL |
agent_log | Claude CLI | Non-JSON output lines |
terminal_data | Terminal | Output data |
terminal_exit | Terminal | Exit code |
file_changes | FileWatcher | Changed file paths |
On SSE connect (or reconnect), the worker replays current state:
- If terminal is alive -> sends empty
terminal_datasignal
Note: Preview/dev servers are now managed by Docker Compose via ServiceManager in the orchestrator, not inside the session worker. See service-manager.ts and compose-generator.ts.
Claude Process
src/server/session/agents/claude/process.ts — spawns the Claude CLI and manages streaming NDJSON interaction. (docs/155 split the agent code into per-agent directories: agents/claude/, agents/codex/.)
Spawning
Spawned with child_process.spawn("claude", args) using --output-format stream-json, --permission-mode
(plan or auto) and --allowedTools. The tool list comes from AUTO_TOOL_SPEC / PLAN_TOOL_SPEC plus
mcp__<server>__* globs for user MCP servers (plan mode excludes third-party MCP tools); read process.ts for the current args.
NDJSON Parsing
Claude CLI outputs one JSON object per line. The process:
- Accumulates PTY data into a line buffer
- On each newline, attempts
JSON.parse() - Valid JSON -> emit as
"event"(typed asClaudeEvent) - Invalid JSON -> emit as
"log"(non-event output)
Watchdog
A 30-second inactivity timer detects hung processes. If no output is received within the timeout, it logs a warning. This handles edge cases where the CLI process stalls.
Prompt Delivery
Prompts are written to stdin as JSON content blocks:
[{"type":"text","text":"user prompt"},{"type":"image","source":{"type":"base64","media_type":"image/png","data":"..."}}]File context references are prepended as text blocks with XML-like markers.
Teardown kills the whole tree
Every ShipIt-initiated termination of an agent CLI — turn end, interrupt escalation, runner dispose — goes through killProcessTree (shared/kill-child.ts), never a bare killChild on the CLI's pid. An agent CLI is the root of a tree: MCP servers below it, and below those whatever they spawn. A pid-only kill leaves those running, reparented to the container's pid 1, until the container dies — one production Codex turn left a Playwright Chromium burning half a core for 19 minutes after it ended.
A process-group kill is not the equivalent: playwright-core spawns every browser detached, in a session of its own, so a group signal never reaches it. The helper snapshots descendants from /proc before the first signal (once the CLI dies, its grandchildren are orphaned and unfindable), signals the root through killChild, signals the snapshot, then SIGKILLs survivors after a 5s grace. It walks a tree only when /proc says the root is our own child, and identity-checks starttime before every delayed signal.
SIGINT-first paths (ClaudeProcess.interrupt, the OpenCode and Grok interrupts) still signal the CLI alone so it can flush; they reach the tree teardown through their existing escalation to kill(). Detail: docs/289-agent-process-tree-teardown.
Agent Abstraction
src/server/session/agents/claude/adapter.ts wraps ClaudeProcess as an AgentProcess (defined in src/server/shared/types/agent-types.ts). The adapter:
- Translates
ClaudeEvent->AgentEvent(unified event format) - Reports capabilities (supported tools, models, permission modes)
- Forwards all events from the inner process
The AgentProcess interface is transport-agnostic:
interface AgentProcess extends EventEmitter {
agentId: AgentId;
capabilities: AgentCapabilities;
run(params: AgentRunParams): void;
writeStdin(data: string): void;
interrupt(): void;
kill(): void;
}In production, the orchestrator never creates agents directly. Instead, ContainerSessionRunner.createAgent() returns a ProxyAgentProcess that delegates to the worker via HTTP:
run()->POST /agent/startinterrupt()->POST /agent/interruptkill()->POST /agent/killwriteStdin()->POST /agent/stdin
Agent events flow back via SSE -> proxied to the ProxyAgentProcess event emitter.
Docker Compose Services
Dev servers and other services are managed by Docker Compose, not the session worker. The orchestrator runs:
ServiceManager(src/server/orchestrator/service-manager.ts) — per-session compose lifecycle: start/stop, status polling, log streaming, IP resolutioncompose-generator.ts— generates override files with ShipIt labels, session network, volume rewrites, port strippingsession-network-release.ts— removesshipit-session-<id>when a stack or its agent container stops, noServiceManageris registered for the session, and no container but the orchestrator names it;compose downleaves such a network (docs/091)
Services are defined in docker-compose.yml at the workspace root. shipit.yaml references the compose file and configures the agent container (install commands, resource limits).
The agent container can query service status/logs via the orchestrator API using SHIPIT_HOST, SHIPIT_PORT, and SHIPIT_SESSION_ID environment variables.
File Watcher
src/server/session/file-watcher.ts — uses fs.watch with recursive: true.
- 300ms debounce to collapse bulk operations
- Set-based deduplication of file paths
- Ignores:
node_modules,.git,.vite,.next,.cache,dist, etc. - Emits
"changes"event with deduplicated paths
Terminal
src/server/session/terminal.ts — wraps node-pty for interactive shell.
- Spawns shell in the workspace directory
- Emits
"data"for output,"exit"on close - Supports resize during session
- Output buffer capped at 10K characters
Resource Limits
| Resource | Limit | Location |
| Message queue | 50 | SessionRunnerInterface |
| Turn event buffer | 1,000 | SessionRunnerInterface |
| Terminal output buffer | 10K chars | SessionRunnerInterface |
| Service log buffer | 80K chars | ServiceManager |
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

