You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This commit was created on GitHub.com and signed with GitHub’s verified signature.
Added
feat(account): per-agent OAuth account assignment
(spec.claude.account) — pin an agent to a specific saved
Anthropic account (from sac account list). Multi-account
load-balance enabler: agents on distinct accounts no longer collide
on one server-side rate limit. Frozen boot-copy mechanism — the
apptainer runtime copies the named account's .credentials.json
snapshot into the agent's own state dir at start and binds the copy
RW, so two agents on two accounts never fight one mount and a host /login never moves a pinned agent. Takes effect on next
start/restart; account="" (default) keeps the host live-file
behaviour. New runtimes/_apptainer_creds.resolve_cred_file;
load-time soft-WARN when the named snapshot is absent (never fails).
feat(account): sac account list plan/tier + usage columns —
OFFLINE plan label (Pro / Max 5x / Max 20x) and rate-limit tier for
every stored account, read from the snapshot's whitelisted fields;
CACHE-ONLY usage (— until a per-account usage cache exists). New _state/account_store.read_account_plan / read_account_usage_cache.
feat(status): pinned-account display — resolve_agent_account_label(assigned_account=) so a pinned agent
shows <name> (<email>) in agent list / agent status regardless
of the host's current /login.
Fixed
fix(spec): migrate bundled sdk-test spec off removed v3
top-level fields (runtime: docker→apptainer; image/model
under their engine blocks; dropped dockerfile).
Changed (BREAKING)
refactor(api): drop reply field; text is the single canonical
field — the A2A sidecar's POST /v1/turn response now returns {"text", "session_id", "exit_after", "metadata"} only; the
back-compat reply alias (introduced alongside the bounded-timeout
fix in 63deaee) is removed. Affects all consumers:
sac peer post-turn --json now emits {"text", "exit_after"}
(was {"reply", "exit_after"}). Pipe consumers using jq -r .reply must update to jq -r .text.
sac listen's live-runner forward response carries text
instead of reply.
a2a_proxy runner's non-JSON upstream fallback wraps the
upstream body under text instead of reply.
The Python _network.peer.post_turn_to_url parses payload["text"]
explicitly; a missing key now raises PeerError (no silent .get fallback). Callers must adapt.
Added
feat(mcp): expose agent_send tool — the MCP server now ships
37 tools (up from 36); the new agent_send lets a lead drive
another agent via a prompt instead of shelling out to sac agents send. Returns a structured {status, response_text, response_metadata} dict (status one of "ok", "error", "timeout"); cross-host agents route through the same ssh+curl
control plane the CLI uses. Library-facing helper lives at cli_pkg/_send.py::send_to_agent so the HTTP / ssh logic isn't
duplicated.
feat(apptainer): declarative overlay auto-create via spec.apptainer.overlay_size — operators no longer have to run apptainer overlay create --size N proj-<peer>.overlay.img by hand
for every new peer. Set spec.apptainer.overlay_size: "5G" (units
M/MB/G/GB) alongside spec.apptainer.overlay: <path> and sac
creates the overlay image on first launch if it's missing. Gated by
the new overlay_create_if_missing flag (default true); set to false to keep "operator must pre-create" semantics. When overlay_size is empty AND the overlay is missing, sac now fails
earlier with a clear FileNotFoundError ("set spec.apptainer.overlay_size for auto-create, or pre-create with apptainer overlay create") instead of letting apptainer error out
cryptically at exec time. Behaviour for specs with existing overlay
files is unchanged. See docs/isolation.md §7.
Changed
docs/spec-reference.md — verified field-by-field against config/_parsers/ + config/_validation.py; corrected spec.runtime (REQUIRED → optional with default), spec.dot_claude
(no auto-discover), spec.startup_commands[] (list of dicts not
strings), spec.listen (LIST of port-declarations, not host-listen
override), spec.telegram.* (field names: bot_token_env / allowed_users / auto_connect / greeting, not enabled / chat_id), and apptainer image (REQUIRED → optional). Flagged spec.apptainer.relaxed / fakeroot as (DESIGN — not yet implemented in parser) and noted that spec.multiplexer / spec.env-file are parsed but currently missing from _KNOWN_SPEC_KEYS (parser/validator drift to fix in a follow-up).
Companion audit at docs/spec-reference.audit.md.
Removed
docs/agent-spec-schema.yaml moved to docs/legacy/agent-spec-schema-cld-v1.yaml. It documented the
pre-v3 cld-agent/v1 schema (metadata.name, runtime: claude-code | slurm | slurm-tenant, container: block, orochi: block) which
is now rejected at load time. Kept under docs/legacy/ for
historical reference only.
Added
telegram fold (Phase 2 + Phase 3) — the Telegram fold is now a
first-class sac feature. TelegramBridge is fully ported from orochi
(_telegram/_bridge.py): aiohttp long-poll against the Telegram Bot
API, allowed_users filter enforced on inbound updates (empty list
fails closed), and graceful shutdown that closes the API session and
releases the singleton lock. The per-bot-token flock
(_telegram/_lock.py) lives at ~/.scitex/agent-container/runtime/telegram/<token-hash>.lock and
reclaims the lock when the recorded PID is dead — the failure mode
that the standalone telegrammer hits and that previously required a
manual rm. The bridge boots from _mcp/server.py via _maybe_boot_telegram_bridge, which only fires when LEAD_TELEGRAM_AUTH_TOKEN is set in the env (i.e. only on the lead
session — subagents inherit a sanitised env and get a structured {"error": ...} response from the tools). The six transport tools
(telegram_send, telegram_reply, telegram_react, telegram_edit_message, telegram_download_attachment, telegram_send_document) are wired to the in-process bridge and
registered by default — opt out via SCITEX_AGENT_CONTAINER_TELEGRAM_FOLD=0 (previously opt-in via =1). New sac.telegram Python API submodule re-exports the verbs
for §6 parity. Inbound messages emit notifications/claude/channel
payloads shaped {"content": ..., "meta": {"source": "telegram", "chat_id": ..., "message_id": ..., "user_id": ..., "username": ..., ...}}; these only render when the Claude Code launcher is invoked
with --dangerously-load-development-channels server:scitex-agent-container — see docs/design/telegram-fold.md
for the launcher dependency note + WARN-log signal.
telegram fold (Phase 1) — design + scaffolding for folding
claude-code-telegrammer's transport tools into sac MCP (Option A from GITIGNORED/dev/05_sac-mcp-telegram.md). New _telegram/ package with a TelegramBridge skeleton (Phase 2 port target documented in the module
docstring) and _mcp/_tools/_telegram.py with 6 transport tool stubs
(telegram_send, telegram_reply, telegram_react, telegram_edit_message, telegram_download_attachment, telegram_send_document). Registration is feature-flagged off behind SCITEX_AGENT_CONTAINER_TELEGRAM_FOLD=1 — no user-visible behaviour
change yet. Design doc at docs/design/telegram-fold.md.
F-CS15 — sac mcp server. New _mcp/ package + cli_pkg/mcp_cmds.py
Click group exposing start, doctor, list-tools, install against a
FastMCP server with 36 sac_* tools mirroring the CLI surface (agent /
db / host / image / template / account / skills / introspection). Install
with pip install scitex-agent-container[mcp].
F-CS3 phase 2 — runner-side autonomous loop: SDK runner consumes spec.autonomous and drives turns until drive_until matches or max_turns is reached. New _runners/claude_session._autonomous_loop
coroutine + --autonomous-* CLI args; container.py forwards the spec.
OAuth credentials synthesis — provision_anthropic_auth writes a
minimal ~/.claude/.credentials.json when given a sk-ant-oat-*
access token via SAC_ANTHROPIC_API_KEY, so Pro/Max plans work in
containers without the host file.
Changed
F-CS17 stage 3a–3d — CLI/TUI runtime, SLURM runtime, action/context
pane abstractions, and the legacy Dockerfile all removed. sac is now
container-only: docker / podman / apptainer engines, the SDK-persistent
base image, and the standalone Python runner under _runners/.
Auth handoff renamed to SAC_ANTHROPIC_API_KEY. The previous SCITEX_AGENT_CONTAINER_CI_ANTHROPIC_API_KEY[_OAUTH] envs are gone. provision_anthropic_auth only honours ANTHROPIC_API_KEY when set
explicitly by the operator; otherwise it bridges (api-key form) or
synthesises a credentials file (oat- form) from SAC_ANTHROPIC_API_KEY.
Container daemon argv — drops --rm; lifecycle.stop now does an
explicit docker rm -f after stop. Adds --env HOME=/tmp so the
bundled claude binary has a writable home even when the host UID
has no /etc/passwd entry inside the image. Forwards SAC_ANTHROPIC_API_KEY (and ANTHROPIC_API_KEY when explicit).
F-CS16 — schema flatten (spec.image, spec.dockerfile top-level),
auto-build at start, validator hard-errors on legacy runtimes.
Removed
F-CS17: runtimes/slurm{,_tenant,sbatch_spartan}.py, the entire CLI/TUI
stack (context_manager, actions/{compact,nonce_probe}, auto/response, cli_pkg/action_cmds), the legacy containers/Dockerfile, and all
associated tests (~7,500 LOC).