From 57546260929473d4e0d1c1bb75297be2fdfa1949 Mon Sep 17 00:00:00 2001 From: Lance Martin <122662504+rlancemartin@users.noreply.github.com> Date: Tue, 9 Jun 2026 13:35:16 -0700 Subject: [PATCH] Update claude-api skill: scheduled deployments, vault env-var credentials, system.message events (#1297) - Add Managed Agents scheduled deployments: new shared/managed-agents-scheduled-deployments.md (cron schedules, deployment runs, pause/auto-pause), Deployments and Deployment Runs API reference, beta-header coverage, and SKILL.md routing - Vault environment_variable credentials: secrets substituted at egress with networking allowlists; secrets guidance in tools, client-patterns, and onboarding docs rewritten around them; self-hosted sandbox caveats noted - Add system.message event for mid-session system prompt updates (Opus 4.8 only) to the events guide and API reference --- skills/claude-api/SKILL.md | 6 +- .../shared/managed-agents-api-reference.md | 50 +++++- .../shared/managed-agents-client-patterns.md | 4 +- .../claude-api/shared/managed-agents-core.md | 2 +- .../shared/managed-agents-events.md | 25 +++ .../shared/managed-agents-onboarding.md | 4 +- .../shared/managed-agents-overview.md | 11 +- .../managed-agents-scheduled-deployments.md | 144 ++++++++++++++++++ .../managed-agents-self-hosted-sandboxes.md | 1 + .../claude-api/shared/managed-agents-tools.md | 55 +++++-- 10 files changed, 279 insertions(+), 23 deletions(-) create mode 100644 skills/claude-api/shared/managed-agents-scheduled-deployments.md diff --git a/skills/claude-api/SKILL.md b/skills/claude-api/SKILL.md index 4053b35d..c4c35ce4 100644 --- a/skills/claude-api/SKILL.md +++ b/skills/claude-api/SKILL.md @@ -249,7 +249,7 @@ For placement patterns, architectural guidance, and the silent-invalidator audit **Mandatory flow:** Agent (once) → Session (every run). `model`/`system`/`tools` live on the agent, never the session. See `shared/managed-agents-overview.md` for the full reading guide, beta headers, and pitfalls. -**Beta headers:** `managed-agents-2026-04-01` — the SDK sets this automatically for all `client.beta.{agents,environments,sessions,vaults,memory_stores}.*` calls. Skills API uses `skills-2025-10-02` and Files API uses `files-api-2025-04-14`, but you don't need to explicitly pass those in for endpoints other than `/v1/skills` and `/v1/files`. +**Beta headers:** `managed-agents-2026-04-01` — the SDK sets this automatically for all `client.beta.{agents,environments,sessions,vaults,memory_stores,deployments,deployment_runs}.*` calls. Skills API uses `skills-2025-10-02` and Files API uses `files-api-2025-04-14`, but you don't need to explicitly pass those in for endpoints other than `/v1/skills` and `/v1/files`. **Subcommands** — invoke directly with `/claude-api `: @@ -257,12 +257,14 @@ For placement patterns, architectural guidance, and the silent-invalidator audit |---|---| | `managed-agents-onboard` | Walk the user through setting up a Managed Agent from scratch. **Read `shared/managed-agents-onboarding.md` immediately** and follow its interview script: mental model → know-or-explore branch → template config → session setup → **pre-flight viability check** → emit code. The viability check (reconcile the stated job against configured tools/credentials/data) catches under-resourced setups — missing a tool, credential, or data access — before the agent burns budget. Do not summarize — run the interview. | -**Reading guide:** Start with `shared/managed-agents-overview.md`, then the topical `shared/managed-agents-*.md` files (core, environments, tools, events, outcomes, multiagent, webhooks, memory, client-patterns, onboarding, api-reference). For Python, TypeScript, Go, Ruby, PHP, and Java, read `{lang}/managed-agents/README.md` for code examples. For cURL, read `curl/managed-agents.md`. **Agents are persistent — create once, reference by ID.** Store the agent ID returned by `agents.create` and pass it to every subsequent `sessions.create`; do not call `agents.create` in the request path. The Anthropic CLI (`ant`) is one convenient way to create agents and environments from version-controlled YAML — see `shared/anthropic-cli.md`. If a binding you need isn't shown in the language README, WebFetch the relevant entry from `shared/live-sources.md` rather than guess. C# has beta Managed Agents support via `client.Beta.Agents` and related namespaces. +**Reading guide:** Start with `shared/managed-agents-overview.md`, then the topical `shared/managed-agents-*.md` files (core, environments, tools, events, outcomes, multiagent, webhooks, memory, scheduled-deployments, client-patterns, onboarding, api-reference). For Python, TypeScript, Go, Ruby, PHP, and Java, read `{lang}/managed-agents/README.md` for code examples. For cURL, read `curl/managed-agents.md`. **Agents are persistent — create once, reference by ID.** Store the agent ID returned by `agents.create` and pass it to every subsequent `sessions.create`; do not call `agents.create` in the request path. The Anthropic CLI (`ant`) is one convenient way to create agents and environments from version-controlled YAML — see `shared/anthropic-cli.md`. If a binding you need isn't shown in the language README, WebFetch the relevant entry from `shared/live-sources.md` rather than guess. C# has beta Managed Agents support via `client.Beta.Agents` and related namespaces. **When the user wants to set up a Managed Agent from scratch** (e.g. "how do I get started", "walk me through creating one", "set up a new agent"): read `shared/managed-agents-onboarding.md` and run its interview — same flow as the `managed-agents-onboard` subcommand. **When the user asks "how do I write the client code for X":** reach for `shared/managed-agents-client-patterns.md` — covers lossless stream reconnect, `processed_at` queued/processed gate, interrupt, `tool_confirmation` round-trip, the correct idle/terminated break gate, post-idle status race, stream-first ordering, file-mount gotchas, keeping credentials host-side via custom tools, etc. +**When the user wants the agent to run on a schedule** (cron, "every night", "weekly report"): read `shared/managed-agents-scheduled-deployments.md` — deployments fire sessions autonomously on a cron cadence, with run records, retries, and auto-pause. + --- diff --git a/skills/claude-api/shared/managed-agents-api-reference.md b/skills/claude-api/shared/managed-agents-api-reference.md index d627dcd0..92827075 100644 --- a/skills/claude-api/shared/managed-agents-api-reference.md +++ b/skills/claude-api/shared/managed-agents-api-reference.md @@ -8,7 +8,7 @@ All endpoints require `x-api-key` and `anthropic-version: 2023-06-01` headers. M anthropic-beta: managed-agents-2026-04-01 ``` -The SDK adds this header automatically for all `client.beta.{agents,environments,sessions,vaults,memory_stores}.*` calls. Skills endpoints use `skills-2025-10-02`; Files endpoints use `files-api-2025-04-14`. +The SDK adds this header automatically for all `client.beta.{agents,environments,sessions,vaults,memory_stores,deployments,deployment_runs}.*` calls. Skills endpoints use `skills-2025-10-02`; Files endpoints use `files-api-2025-04-14`. --- @@ -26,6 +26,8 @@ All resources are under the `beta` namespace. Python and TypeScript share identi | Session Events | `sessions.events.list` / `send` / `stream` | `Sessions.Events.List` / `Send` / `StreamEvents` | | Session Threads | `sessions.threads.list` / `retrieve` / `archive`; `sessions.threads.events.list` / `stream` | `Sessions.Threads.List` / `Get` / `Archive`; `Sessions.Threads.Events.List` / `StreamEvents` | | Session Resources | `sessions.resources.add` / `retrieve` / `update` / `list` / `delete` | `Sessions.Resources.Add` / `Get` / `Update` / `List` / `Delete` | +| Deployments | `deployments.create` / `pause` / `unpause` / `archive` / `run` | Not yet documented — WebFetch the SDK repo (`shared/live-sources.md`) | +| Deployment Runs | `deployment_runs.list` (TS: `deploymentRuns.list`) | Not yet documented — WebFetch the SDK repo (`shared/live-sources.md`) | | Vaults | `vaults.create` / `retrieve` / `update` / `list` / `delete` / `archive` | `Vaults.New` / `Get` / `Update` / `List` / `Delete` / `Archive` | | Credentials | `vaults.credentials.create` / `retrieve` / `update` / `list` / `delete` / `archive` / `mcp_oauth_validate` | `Vaults.Credentials.New` / `Get` / `Update` / `List` / `Delete` / `Archive` / `McpOauthValidate` | | Memory Stores | `memory_stores.create` / `retrieve` / `update` / `list` / `delete` / `archive` | `MemoryStores.New` / `Get` / `Update` / `List` / `Delete` / `Archive` | @@ -113,9 +115,29 @@ Per-subagent event streams in multiagent sessions. See `shared/managed-agents-mu For `type: "self_hosted"`, `config` is the bare `{"type": "self_hosted"}` — `networking` and `packages` do not apply. +## Deployments + +Scheduled deployments (`depl_` IDs) run an agent on a recurring cron schedule — each firing creates a session. See `shared/managed-agents-scheduled-deployments.md` for the conceptual guide (cron/DST semantics, failure behavior, lifecycle). + +| Method | Path | Operation | Description | +| -------- | ------------------------------------------------ | ---------------- | ---------------------------------------- | +| `POST` | `/v1/deployments` | CreateDeployment | Create a scheduled deployment | +| `POST` | `/v1/deployments/{deployment_id}/pause` | PauseDeployment | Suppress scheduled triggers (reversible; manual runs still allowed) | +| `POST` | `/v1/deployments/{deployment_id}/unpause` | UnpauseDeployment | Resume from the next occurrence (no backfill) | +| `POST` | `/v1/deployments/{deployment_id}/archive` | ArchiveDeployment | **Terminal** — schedule stops, deployment becomes immutable | +| `POST` | `/v1/deployments/{deployment_id}/run` | RunDeployment | Trigger a manual run immediately (`trigger_context.type: "manual"`); works while paused | + +## Deployment Runs + +Each trigger attempt (scheduled or manual) writes a `deployment_run` record (`drun_` IDs) carrying either the created `session_id` or an `error.type` (`environment_archived`, `agent_archived`, `vault_not_found`, `session_rate_limited`, `service_unavailable`). + +| Method | Path | Operation | Description | +| -------- | ------------------------------------------------ | ---------------- | ---------------------------------------- | +| `GET` | `/v1/deployment_runs?deployment_id=...` | ListDeploymentRuns | List runs for a deployment (paginated; filter failures with `has_error=true`) | + ## Vaults -Vaults store MCP credentials that Anthropic manages on your behalf — OAuth credentials with auto-refresh, or static bearer tokens. Attach to sessions via `vault_ids`. See `managed-agents-tools.md` §Vaults for the conceptual guide and credential shapes. +Vaults store credentials that Anthropic manages on your behalf — MCP credentials (OAuth with auto-refresh, or static bearer tokens) and `environment_variable` credentials substituted into outbound requests at egress. Attach to sessions via `vault_ids`. See `managed-agents-tools.md` §Vaults for the conceptual guide and credential shapes. | Method | Path | Operation | Description | | -------- | ------------------------------------------------ | ---------------- | ---------------------------------------- | @@ -258,7 +280,7 @@ Immutable per-mutation snapshots (`memver_...`) — the audit and rollback surfa "checkout": { "type": "branch", "name": "main" } } ], - "vault_ids": ["vlt_abc123 (optional — MCP credentials with auto-refresh)"], + "vault_ids": ["vlt_abc123 (optional — vault credentials: MCP auth + environment variables)"], "metadata": { "key": "value" } @@ -286,6 +308,26 @@ Immutable per-mutation snapshots (`memver_...`) — the audit and rollback surfa } ``` +### CreateDeployment Request Body + +```json +{ + "name": "Weekly compliance scan", + "agent": "agent_abc123 (required — same shapes as CreateSession)", + "environment_id": "env_abc123 (required)", + "initial_events": [ + { "type": "user.message", "content": [{ "type": "text", "text": "Run the weekly compliance scan." }] } + ], + "schedule": { + "type": "cron", + "expression": "0 20 * * 5", + "timezone": "America/New_York" + } +} +``` + +> Optional session config (`resources`, `vault_ids`, etc.) is supported the same way as on CreateSession. Response includes `status`, `paused_reason`, and `schedule.upcoming_runs_at` (next fire times). See `shared/managed-agents-scheduled-deployments.md`. + ### SendEvents Request Body ```json @@ -304,6 +346,8 @@ Immutable per-mutation snapshots (`memver_...`) — the audit and rollback surfa } ``` +> `system.message` events (update the system prompt between turns) use the same envelope with `type: "system.message"` — Claude Opus 4.8 only; see `shared/managed-agents-events.md` § Updating the system prompt mid-session. + ### Define Outcome Event ```json diff --git a/skills/claude-api/shared/managed-agents-client-patterns.md b/skills/claude-api/shared/managed-agents-client-patterns.md index ac0e60b0..73f2f2ea 100644 --- a/skills/claude-api/shared/managed-agents-client-patterns.md +++ b/skills/claude-api/shared/managed-agents-client-patterns.md @@ -183,7 +183,9 @@ Delete the original via `files.delete(uploaded.id)`; the session-scoped copy is ## 9. Secrets for non-MCP APIs and CLIs — keep them host-side via custom tools -**Problem:** you want the agent to call a third-party API or run a CLI that needs a secret (API key, token, service-account credential), but there is currently no way to set environment variables inside the session container, and vaults currently hold MCP credentials only — they are not exposed to the container's shell. So `curl`, installed CLIs, or SDK clients running via the `bash` tool have no first-class place to read a secret from. +**Problem:** you want the agent to call a third-party API or run a CLI that needs a secret (API key, token, service-account credential), but you can't or don't want to hand the secret to a vault. + +**First check:** for cloud environments, the first-class answer is now a vault `environment_variable` credential — the agent's shell sees an opaque placeholder and the real secret is substituted at egress. See `shared/managed-agents-tools.md` → Vaults. Use this pattern instead when that doesn't fit: **self-hosted sandboxes** (env-var credentials not yet supported there), clients that reject the placeholder via local format validation, secrets that must never leave your infrastructure, or calls that need host-side binaries. **Solution:** move the authenticated call to your side. Declare a custom tool on the agent; when the agent emits `agent.custom_tool_use`, your orchestrator (the process reading the SSE stream) executes the call with its own credentials and responds with `user.custom_tool_result`. The container never sees the key. diff --git a/skills/claude-api/shared/managed-agents-core.md b/skills/claude-api/shared/managed-agents-core.md index 4fa930ba..5ada9b9b 100644 --- a/skills/claude-api/shared/managed-agents-core.md +++ b/skills/claude-api/shared/managed-agents-core.md @@ -132,7 +132,7 @@ const session = await client.beta.sessions.create( | `environment_id`| string | **Yes** | Environment ID | | `title` | string | No | Human-readable name (appears in logs/dashboards) | | `resources` | array | No | Files, GitHub repos, or memory stores, attached to the container at startup. Memory stores are session-create-only (not addable via `resources.add()`). | -| `vault_ids` | array | No | Vault IDs (`vlt_*`) — MCP credentials with auto-refresh. See `shared/managed-agents-tools.md` → Vaults. | +| `vault_ids` | array | No | Vault IDs (`vlt_*`) — MCP credentials with auto-refresh + `environment_variable` secrets substituted at egress. See `shared/managed-agents-tools.md` → Vaults. | | `metadata` | object | No | User-provided key-value pairs | **Agent configuration fields** (passed to `agents.create()`, not `sessions.create()`): diff --git a/skills/claude-api/shared/managed-agents-events.md b/skills/claude-api/shared/managed-agents-events.md index 28e3fbcb..2c267551 100644 --- a/skills/claude-api/shared/managed-agents-events.md +++ b/skills/claude-api/shared/managed-agents-events.md @@ -13,6 +13,31 @@ Send events to a session via `POST /v1/sessions/{id}/events`. | `user.tool_confirmation` | Approve/deny a tool call (when `always_ask` policy) | | `user.custom_tool_result` | Provide result for a custom tool call | | `user.define_outcome` | Start a rubric-graded iterate loop — see `shared/managed-agents-outcomes.md` | +| `system.message` | Update the agent's system prompt between turns — **Claude Opus 4.8 only**; see § Updating the system prompt mid-session | + +#### Updating the system prompt mid-session (`system.message`) + +Unlike the `system` field on the agent definition (fixed at session creation), a `system.message` event changes the system prompt **as the session progresses** — a different persona, revised constraints, or runtime-fetched context that should shape behavior going forward: + +```python +client.beta.sessions.events.send( + session.id, + events=[ + { + "type": "system.message", + "content": [ + {"type": "text", "text": "The user's current timezone is America/New_York."}, + ], + }, + ], +) +``` + +Constraints: + +- **Claude Opus 4.8 only.** If any model configured on the agent does not support mid-conversation system injection, the event is rejected with a `model_does_not_support_mid_conversation_system` validation error. +- **Cannot be sent while the session is idle with `stop_reason: requires_action`** (blocked on `user.custom_tool_result` / `user.tool_confirmation`). +- `content` accepts 1–1000 text items. ### Receiving Events diff --git a/skills/claude-api/shared/managed-agents-onboarding.md b/skills/claude-api/shared/managed-agents-onboarding.md index 23bc4281..2d8d3258 100644 --- a/skills/claude-api/shared/managed-agents-onboarding.md +++ b/skills/claude-api/shared/managed-agents-onboarding.md @@ -83,10 +83,10 @@ Emit as `resources: [{type: "file", file_id, mount_path}]`. Max 999 file resourc Per-run. Points at the agent + environment, attaches credentials, kicks off. -**Vault credentials** (if the agent declared MCP servers): +**Vault credentials** (if the agent declared MCP servers, or the job needs an API key for a CLI/SDK/direct API call): - [ ] Existing vault, or create one? (`client.beta.vaults.create()` + `vaults.credentials.create()`) -Credentials are write-only, matched to MCP servers by URL, auto-refreshed. See `shared/managed-agents-tools.md` → Vaults. +Credentials are write-only. MCP credentials are matched to MCP servers by URL and auto-refreshed; `environment_variable` credentials are substituted into outbound requests at egress (the sandbox sees only a placeholder). See `shared/managed-agents-tools.md` → Vaults. **Kickoff — pick one:** - [ ] **Conversational:** a first `user.message` to the agent. diff --git a/skills/claude-api/shared/managed-agents-overview.md b/skills/claude-api/shared/managed-agents-overview.md index 858ef18c..f918396b 100644 --- a/skills/claude-api/shared/managed-agents-overview.md +++ b/skills/claude-api/shared/managed-agents-overview.md @@ -25,11 +25,11 @@ Managed Agents is in beta. The SDK sets required beta headers automatically: | Beta Header | What it enables | | ------------------------------ | ---------------------------------------------------- | -| `managed-agents-2026-04-01` | Agents, Environments, Sessions, Events, Session Resources, Session Threads, Outcomes, Multiagent, Vaults, Credentials, Memory Stores | +| `managed-agents-2026-04-01` | Agents, Environments, Sessions, Events, Session Resources, Session Threads, Outcomes, Multiagent, Vaults, Credentials, Memory Stores, Deployments | | `skills-2025-10-02` | Skills API (for managing custom skill definitions) | | `files-api-2025-04-14` | Files API for file uploads | -**Which beta header goes where:** The SDK sets `managed-agents-2026-04-01` automatically on `client.beta.{agents,environments,sessions,vaults,memory_stores}.*` calls, and `files-api-2025-04-14` / `skills-2025-10-02` automatically on `client.beta.files.*` / `client.beta.skills.*` calls. You do NOT need to add the Skills or Files beta header when calling Managed Agents endpoints. **Exception — session-scoped file listing:** `client.beta.files.list({scope_id: session.id})` is a Files endpoint that takes a Managed Agents parameter, so it needs **both** headers. Pass `betas: ["managed-agents-2026-04-01"]` explicitly on that call (the SDK adds the Files header; you add the Managed Agents one). See `shared/managed-agents-environments.md` → Session outputs. +**Which beta header goes where:** The SDK sets `managed-agents-2026-04-01` automatically on `client.beta.{agents,environments,sessions,vaults,memory_stores,deployments,deployment_runs}.*` calls, and `files-api-2025-04-14` / `skills-2025-10-02` automatically on `client.beta.files.*` / `client.beta.skills.*` calls. You do NOT need to add the Skills or Files beta header when calling Managed Agents endpoints. **Exception — session-scoped file listing:** `client.beta.files.list({scope_id: session.id})` is a Files endpoint that takes a Managed Agents parameter, so it needs **both** headers. Pass `betas: ["managed-agents-2026-04-01"]` explicitly on that call (the SDK adds the Files header; you add the Managed Agents one). See `shared/managed-agents-environments.md` → Session outputs. ## Reading Guide @@ -53,14 +53,15 @@ Managed Agents is in beta. The SDK sets required beta headers automatically: | Upload files / attach repos | `shared/managed-agents-environments.md` (Resources) | | Give agents persistent memory across sessions | `shared/managed-agents-memory.md` — memory stores, `memory_store` session resource, preconditions, versions/redact | | Define agents/environments as version-controlled YAML; drive the API from the shell | `shared/anthropic-cli.md` — `ant beta:agents create < agent.yaml`, `--transform`, `@file` inlining | -| Store MCP credentials | `shared/managed-agents-tools.md` (Vaults section) | -| Call a non-MCP API / CLI that needs a secret | `shared/managed-agents-client-patterns.md` Pattern 9 — no container env vars; vaults are MCP-only; keep the secret host-side via a custom tool | +| Store credentials (MCP auth, API keys for CLIs/SDKs) | `shared/managed-agents-tools.md` (Vaults section) — `mcp_oauth` / `static_bearer` / `environment_variable` | +| Call a non-MCP API / CLI that needs a secret | `shared/managed-agents-tools.md` (Vaults section) — `environment_variable` credential, substituted at egress. If that doesn't fit (e.g. self-hosted sandboxes), `shared/managed-agents-client-patterns.md` Pattern 9 keeps the secret host-side via a custom tool | +| Run an agent on a recurring cron schedule | `shared/managed-agents-scheduled-deployments.md` — deployments, deployment runs, pause/auto-pause | ## Common Pitfalls - **Agent FIRST, then session — NO EXCEPTIONS** — the session's `agent` field accepts **only** a string ID or `{type: "agent", id, version}`. `model`, `system`, `tools`, `mcp_servers`, `skills` are **top-level fields on `POST /v1/agents`**, never on `sessions.create()`. If the user hasn't created an agent, that is step zero of every example. - **Agent ONCE, not every run** — `agents.create()` is a setup step. Store the returned `agent_id` and reuse it; don't call `agents.create()` at the top of your hot path. If the agent's config needs to change, `POST /v1/agents/{id}` — each update creates a new version, and sessions can pin to a specific version for reproducibility. -- **MCP auth goes through vaults** — the agent's `mcp_servers` array declares `{type, name, url}` only (no auth). Credentials live in vaults (`client.beta.vaults.credentials.create`) and attach to sessions via `vault_ids`. Anthropic auto-refreshes OAuth tokens using the stored refresh token. +- **MCP auth goes through vaults** — the agent's `mcp_servers` array declares `{type, name, url}` only (no auth). Credentials live in vaults (`client.beta.vaults.credentials.create`) and attach to sessions via `vault_ids`. Anthropic auto-refreshes OAuth tokens using the stored refresh token. Vaults also hold `environment_variable` credentials for non-MCP services (CLIs, SDKs, direct API calls) — substituted at egress, never visible in the sandbox. - **Reconcile resources before the first run** — a session with a clear ask but a missing tool, credential, data mount, or context will discover the gap mid-run, then flail and give up. Before creating the session, check that every action in the task maps to a configured tool/MCP server, every MCP server has a vault credential, and every referenced file/host is mounted/reachable. When helping a user set one up, run the reconciliation in `shared/managed-agents-onboarding.md` → §3 Pre-flight viability check. - **Stream to get events** — `GET /v1/sessions/{id}/events/stream` is the primary way to receive agent output in real-time. - **SSE stream has no replay — reconnect with consolidation** — if the stream drops while a `agent.tool_use`, `agent.mcp_tool_use`, or `agent.custom_tool_use` is pending resolution (`user.tool_confirmation` for the first two, `user.custom_tool_result` for the last one), the session deadlocks (client disconnects → session idles → reconnect happens → no client resolution happens). On every (re)connect: open stream with `GET /v1/sessions/{id}/events/stream` , fetch `GET /v1/sessions/{id}/events`, dedupe by event ID, then proceed. See `shared/managed-agents-events.md` → Reconnecting after a dropped stream. diff --git a/skills/claude-api/shared/managed-agents-scheduled-deployments.md b/skills/claude-api/shared/managed-agents-scheduled-deployments.md new file mode 100644 index 00000000..f391044f --- /dev/null +++ b/skills/claude-api/shared/managed-agents-scheduled-deployments.md @@ -0,0 +1,144 @@ +# Managed Agents — Scheduled Deployments + +A **scheduled deployment** runs an agent on a recurring cron schedule — each firing creates a session autonomously. Use it for predictable-cadence work: nightly triage, weekly compliance scans, hourly monitors. + +Requires the `managed-agents-2026-04-01` beta header (the SDK sets it automatically for `client.beta.deployments.*` / `client.beta.deployment_runs.*` calls). + +## Create a deployment + +A deployment bundles everything a session needs (agent, environment, optional files / GitHub / memory stores / vaults) plus a `schedule` and the `initial_events` that kick off each run: + +- `agent` and `environment_id` are required — same shapes as `sessions.create` (see `shared/managed-agents-core.md`). +- `initial_events` must contain the starting `user.message`. +- `schedule` takes a cron `expression` and an IANA `timezone`. Minute-level granularity is the maximum. + +```bash +curl -fsSL https://api.anthropic.com/v1/deployments \ + -H "x-api-key: $ANTHROPIC_API_KEY" \ + -H "anthropic-version: 2023-06-01" \ + -H "anthropic-beta: managed-agents-2026-04-01" \ + -H "content-type: application/json" \ + -d @- < ⚠️ **DST edge:** wall-clock times that don't exist on a spring-forward day (e.g. 2AM) are **skipped**; times that occur twice on a fall-back day **fire twice**. Schedule outside the 1–3AM local window, or use UTC, when missed or duplicate executions are unacceptable. + +## Deployment runs + +Every trigger attempt — successful or not — writes a **deployment run** record (`drun_` prefix), so you can audit failures independent of the session lifecycle. A successful run carries the created `session_id`; follow that session via the event stream (`shared/managed-agents-events.md`) or webhooks (`shared/managed-agents-webhooks.md`) as usual. A failed run carries an `error` whose `type` explains why session creation was rejected. + +```python +# All runs for a deployment +for run in client.beta.deployment_runs.list(deployment_id=deployment.id): + print(run.created_at, run.session_id or run.error.type) + +# Failures only +for run in client.beta.deployment_runs.list(deployment_id=deployment.id, has_error=True): + print(run.created_at, run.error.type, run.error.message) +``` + +```typescript +for await (const run of client.beta.deploymentRuns.list({ + deployment_id: deployment.id, + has_error: true, +})) { + console.log(run.created_at, run.error?.type, run.error?.message); +} +``` + +Raw HTTP: `GET /v1/deployment_runs?deployment_id=...&has_error=true`. + +A failed run looks like: + +```json +{ + "type": "deployment_run", + "id": "drun_01abc124", + "deployment_id": "depl_01xyz", + "trigger_context": { "type": "schedule", "scheduled_at": "2026-05-09T00:00:00Z" }, + "session_id": null, + "error": { "type": "environment_archived", "message": "environment `env_01abc` is archived" }, + "agent": { "type": "agent", "id": "agent_01ghi789", "version": 3 }, + "created_at": "2026-05-09T00:00:01Z" +} +``` + +Error types include `environment_archived`, `agent_archived`, `vault_not_found`, `session_rate_limited`, and `service_unavailable`. + +## Lifecycle: pause / unpause / archive + +| Operation | SDK | Effect | +|---|---|---| +| Pause | `client.beta.deployments.pause(id)` | Suppresses scheduled triggers go-forward. Sessions already running continue. **Manual runs are still permitted while paused.** Sets `paused_reason: {"type": "manual"}`. | +| Unpause | `client.beta.deployments.unpause(id)` | Resumes from the next scheduled occurrence. **Missed triggers are not backfilled.** Clears `paused_reason`. | +| Archive | `client.beta.deployments.archive(id)` | **Terminal** — the schedule stops and the deployment can no longer be modified. Use pause for anything reversible. | + +Raw HTTP: `POST /v1/deployments/{deployment_id}/pause` (likewise `/unpause`, `/archive`). + +### Failure behavior + +- **Rate-limited:** recorded immediately as a `session_rate_limited` run, **no retry** — the schedule simply tries again at the next occurrence. (Rate limits on API calls *inside* a session are handled by the session itself.) +- **Other failed runs** (e.g. `environment_archived`, `vault_not_found`, `service_unavailable`): the run records the `error.type` — monitor runs and fix the referenced resource, or pause the deployment. +- **Agent archived or deleted:** the deployment is automatically **archived** (terminal) and no further sessions are created. + +## Manual runs + +`POST /v1/deployments/{deployment_id}/run` (SDK: `client.beta.deployments.run(id)`) creates a session immediately and writes a run with `trigger_context.type: "manual"`. Use it to **test a deployment before committing to the schedule** — and remember it works even while the deployment is paused. diff --git a/skills/claude-api/shared/managed-agents-self-hosted-sandboxes.md b/skills/claude-api/shared/managed-agents-self-hosted-sandboxes.md index 091becc6..497b103b 100644 --- a/skills/claude-api/shared/managed-agents-self-hosted-sandboxes.md +++ b/skills/claude-api/shared/managed-agents-self-hosted-sandboxes.md @@ -156,6 +156,7 @@ These are **control-plane** calls — authenticate with `x-api-key` (not the env | Container lifecycle, hardening, networking | Anthropic | **You** — run non-root, read-only rootfs, drop caps; egress is whatever your VPC/firewall allows | | `file` / `github_repository` resource mounting | Anthropic mounts into the container | **You** — pass pointers via `sessions.create(metadata={...})` and have your orchestrator fetch/clone before dispatch | | `memory_store` resources | Supported | **Not yet supported** | +| Vault `environment_variable` credentials | Supported (substituted at Anthropic-managed egress) | **Not yet supported** — egress is yours, so there's nowhere to substitute the secret. Use MCP credentials or a host-side custom tool (`shared/managed-agents-client-patterns.md` Pattern 9) | | Built-in tools | Via `agent_toolset_20260401` | Supplied by your worker (`EnvironmentWorker` default / `beta_agent_toolset(env)` / `ant` CLI fixed set) | | Skills download | Automatic | `EnvironmentWorker` / `AgentToolContext` fetch into `{workdir}/skills/` (needs `client` + `session_id`) | | Claude Platform on AWS | Supported | **Not available** | diff --git a/skills/claude-api/shared/managed-agents-tools.md b/skills/claude-api/shared/managed-agents-tools.md index dec92eb1..9cf91369 100644 --- a/skills/claude-api/shared/managed-agents-tools.md +++ b/skills/claude-api/shared/managed-agents-tools.md @@ -190,9 +190,14 @@ This keeps secrets out of reusable agent definitions. Each vault credential is t > ⚠️ **MCP auth tokens ≠ REST API tokens.** Hosted MCP servers (`mcp.notion.com`, `mcp.linear.app`, etc.) typically require **OAuth bearer tokens**, not the service's native API keys. A Notion `ntn_` integration token authenticates against Notion's REST API but will **not** work as a vault credential for the Notion MCP server. These are different auth systems. -### Vaults — the MCP credential store +### Vaults — the credential store -**Vaults** store OAuth credentials (access token + refresh token) that Anthropic auto-refreshes on your behalf via standard OAuth 2.0 `refresh_token` grant. This is the only way to authenticate MCP servers in the launch SDK. +**Vaults** store credentials that Anthropic manages on your behalf. Two credential categories: + +- **MCP credentials** (`mcp_oauth`, `static_bearer`) — keyed by `mcp_server_url`. When the agent connects to a server at that URL, the token is injected automatically. `mcp_oauth` tokens are auto-refreshed via the standard OAuth 2.0 `refresh_token` grant. This is the only way to authenticate MCP servers. +- **Environment variables** (`environment_variable`) — keyed by `secret_name` (the env var name). The sandbox sees only an **opaque placeholder**; the real secret is substituted into the outbound request **at egress**. Use this for any service that authenticates through an environment variable: CLIs (`aws`, `gcloud`, `stripe`), SDKs, or direct `curl` calls from the `bash` tool. + +Secret fields you supply (`token`, `access_token`, `refresh_token`, `client_secret`, `secret_value`) are write-only — never returned in API responses. #### Credentials and the sandbox @@ -200,11 +205,9 @@ Vaults store credentials; those credentials **never enter the sandbox**. This is - **MCP tool calls** are routed through an Anthropic-side proxy that fetches the credential from the vault and adds it to the outbound request. - **Git operations on attached GitHub repositories** (`git pull`, `git push`, GitHub REST calls) are routed through a git proxy that injects the `github_repository` resource's `authorization_token` the same way. +- **Environment-variable credentials** appear in the sandbox as an opaque placeholder; the real value replaces the placeholder at egress, on requests to the credential's allowed hosts only. -**Not yet supported:** running other authenticated CLIs (e.g. `aws`, `gcloud`, `stripe`) directly inside the sandbox. There is currently no way to set container environment variables or expose vault credentials to arbitrary processes. If you need one of these today: - -- **Prefer an MCP server** for that service if one exists — it gets the same vault-backed injection. -- **Otherwise, register a custom tool:** the agent emits `agent.custom_tool_use`, your orchestrator (which already holds the credential) executes the call and returns `user.custom_tool_result` over the same authenticated event stream. No public endpoint is exposed; the sandbox never sees the secret. See `shared/managed-agents-client-patterns.md` → Pattern 9. +**When vault credentials don't fit** (e.g. self-hosted sandboxes — `environment_variable` is not yet supported there), **register a custom tool:** the agent emits `agent.custom_tool_use`, your orchestrator (which already holds the credential) executes the call and returns `user.custom_tool_result` over the same authenticated event stream. No public endpoint is exposed; the sandbox never sees the secret. See `shared/managed-agents-client-patterns.md` → Pattern 9. **Do not put API keys in the system prompt or user messages as a workaround** — they persist in the session's event history. @@ -213,11 +216,11 @@ Vaults store credentials; those credentials **never enter the sandbox**. This is **Flow:** 1. Create a vault (`client.beta.vaults.create(...)`) — one per tenant/user, or one shared, depending on your model -2. Add MCP credentials to it (`client.beta.vaults.credentials.create(...)`) — each credential is tied to one MCP server URL +2. Add credentials to it (`client.beta.vaults.credentials.create(...)`) — MCP credentials are keyed by MCP server URL; environment-variable credentials by `secret_name` 3. Reference the vault on session create via `vault_ids: ["vlt_..."]` -4. Anthropic auto-refreshes tokens before they expire; the agent uses the current access token when calling MCP tools +4. Anthropic auto-refreshes OAuth tokens before they expire and substitutes secrets at runtime -**Credential shape**: +**MCP OAuth credential shape**: ```json { @@ -249,6 +252,40 @@ Omit `refresh` entirely if you only have an access token with no refresh capabil > 💡 **Getting an OAuth token.** How you obtain the initial access and refresh tokens depends on the MCP server — consult its documentation. Once you have them, store them in a vault credential using the shape above; Anthropic auto-refreshes via the `refresh.token_endpoint` from there. +**Environment-variable credential shape**: + +```json +{ + "display_name": "Twilio API key for sandbox", + "auth": { + "type": "environment_variable", + "secret_name": "TWILIO_API_KEY", + "secret_value": "sk-your-secret-here", + "networking": { + "type": "limited", + "allowed_hosts": ["api.twilio.com", "*.twilio.com"] + } + } +} +``` + +`networking.allowed_hosts` controls which outbound hosts the secret can be substituted for — `{"type": "limited", "allowed_hosts": [...]}` or `{"type": "unrestricted"}` if you can't enumerate the domains in advance. Limiting is strongly recommended: it prevents the key from ever being sent to unauthorized hosts. + +> ⚠️ **Two networking layers, both required.** `networking.allowed_hosts` on the credential controls which requests *use the secret*, not which requests are *allowed*. The agent must also be able to reach the domain at the **environment level** (`unrestricted`, or the host listed in the environment's `allowed_hosts` — see `shared/managed-agents-environments.md`). A domain missing from either layer means the secret-substituted request fails. + +> ⚠️ **Client-side validation caveat.** Substitution happens at egress, not inside the sandbox — clients that validate the credential *format* locally before making a network request (e.g. a CLI that checks the key starts with `sk-`) will see the opaque placeholder and may fail at startup. If a client rejects the credential before any network call, that's why. + +> 💡 **Scope the key minimally.** The agent can do anything the key allows; a key with broader permissions than the task needs increases the blast radius if the agent behaves unexpectedly. + +**Not supported with self-hosted sandboxes** — `environment_variable` credentials require Anthropic-managed egress. See `shared/managed-agents-self-hosted-sandboxes.md`. + +**Constraints (all credential types):** + +- **Unique key per vault.** `mcp_server_url` (MCP credentials) and `secret_name` (environment-variable credentials) must be unique among active credentials in a vault; duplicates return a 409. +- **Keys are immutable.** Secret values and `display_name` can be updated (rotation); to change `mcp_server_url`, `secret_name`, `token_endpoint`, or `client_id`, archive the credential and create a new one. Archiving purges the secret and frees the key for a replacement. +- **Maximum 20 credentials per vault.** +- Credentials are stored as provided and **not validated until session runtime** — an invalid credential surfaces as an authentication or downstream error during the session, which is emitted but does not block the session from continuing. + **Scoping:** Vaults are workspace-scoped. Anyone with developer+ role in the API workspace can create, read (metadata only — secrets are write-only), and attach vaults. `vault_ids` can be set at session **create** time but not via session update (the SDK docstring says "Not yet supported; requests setting this field are rejected"). ---