Skip to content

v0.20.0

Choose a tag to compare

@github-actions github-actions released this 24 May 15:18
· 113 commits to main since this release
f49c540

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-CS15sac 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 synthesisprovision_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).