How sac works

scitex-agent-container (sac) materializes a spec.yaml into a long-lived, externally addressable Claude agent.

Launch flow

  spec.yaml   ─┐
  dot_claude/ ─┴─→ sac agents start ──→ apptainer instance
                                          │
                                          ▼
                              long-lived Claude SDK session
                              │
                              ├── <workdir>  (= spec.workdir, mounted rw)
                              │     CLAUDE.md / .mcp.json / .env / state.md     ← from dot_claude/
                              │     .claude/{commands,skills,hooks,...}         ← mirrored
                              │
                              ├── spec.mounts[]  ← explicit host-path allowlist (ro/rw)
                              │
                              ├── state-dir  (host: ~/.scitex/agent-container/runtime/<name>/)
                              │     pid, heartbeat.json,
                              │     session.jsonl, session_id, quota.json
                              │
                              ├─→ POST /v1/turn                 (per-agent A2A inbound)
                              │       ▲
                              │       │  live-runner route
                              │       │
  sac listen :7878 ───────────┼───────┘
  bearer-auth /v1/sac/{                 \
    health, agents,                      ─→ claude --resume <sid> -p
    agents/<n>/{status,send,card},                          (re-launch fallback when
    ...                                                      no live runner)
  }
                                                            ▲
  sac channel send TO MSG ─────────────────────────────────┤
  sac peer  post-turn  AGENT TEXT  ────────────────────────┘

What each piece does

spec.yaml (SSoT)

The single file that fully defines an agent. The agent name is the name of its parent directory — no name field in the YAML. See spec-reference.md.

dot_claude/ (optional)

A directory next to spec.yaml. At start, sac copies its contents into the agent’s <workdir>:

Source

Destination

Merge rule

CLAUDE.md

<workdir>/CLAUDE.md

marker-protected append

.mcp.json

<workdir>/.mcp.json

per-server merge

.env

<workdir>/.env

mode 0600, overwrite

state.md

<workdir>/state.md

full overwrite

commands/, skills/, hooks/

<workdir>/.claude/*/

recursive copy

Apptainer instance

sac agents start calls apptainer exec with:

  • the SIF at spec.apptainer.image

  • <workdir> bound rw at /work

  • any extra binds from spec.apptainer.binds[]

  • env vars from spec.apptainer.env

  • optional GPU passthrough (--nv / --rocm)

Claude SDK session

Inside the container, sac launches claude (Claude Code CLI) as a long-lived SDK session. Session state persists in the host-side state dir so it survives container restarts. spec.claude.session controls whether to start fresh (new-session), continue the last session (continue), or resume a specific one (resume <sid>).

A2A inbound (spec.a2a.port)

When spec.a2a.port is set, sac binds POST /v1/turn on that localhost port for this agent. Any process on the host can send a prompt without knowing the tmux pane — including other agents via sac peer post-turn.

sac listen (control plane)

A per-host REST API (bearer-auth, loopback-only) that exposes fleet-wide operations: health checks, agent status, send, agent-card, and more. sac channel send routes through it for local agent-to-agent messaging.

Restart / health

The runner supervisor checks spec.health probes and applies spec.restart policy (never / on-failure / always) with exponential backoff. Heartbeat state is written to runtime/<name>/heartbeat.json every tick.

See also