Skip to content

Commit 34040c9

Browse files
cj-antclaude
andauthored
Update claude-api skill: Managed Agents auto permission policy and ant beta:sessions connect (#1750)
Adds the third Managed Agents permission policy, `auto`, alongside `always_allow` / `always_ask`: the three outcomes (runs, denied as high-risk with an error tool result while the session keeps running, pauses for approval when indeterminate), a config example, what the evaluation trusts, and the "not a human checkpoint" warning. Documents the `evaluated_permission` and `evaluation` fields on `agent.tool_use` / `agent.mcp_tool_use`, and updates the client-pattern and multiagent guides to gate on `evaluated_permission === 'ask'` rather than the configured policy. Adds `ant beta:sessions connect` to the CLI guide: terminal viewer keybindings, the allow/deny prompt, and the `--web` local session viewer. Fixes the deny example to use `deny_message` (the real field) instead of `message`. Claude-Session: https://claude.ai/code/session_01UkZpc4FqLFPLBJF2Zcuq3a Co-authored-by: Claude <[email protected]>
1 parent 41bbe19 commit 34040c9

7 files changed

Lines changed: 116 additions & 16 deletions

File tree

‎skills/claude-api/SKILL.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,8 @@ Availability: `shared/platform-availability.md`. For agents on Bedrock / Vertex
412412

413413
**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, etc. For credentials, lead with vault `environment_variable` credentials - the first-class mechanism; secrets are substituted at egress and never enter the sandbox (`shared/managed-agents-tools.md` -> Vaults). Keeping credentials host-side via custom tools is the fallback where vault credentials don't fit (e.g. self-hosted sandboxes).
414414

415+
**When the user asks about tool approvals, permission policies, or "auto mode"** (which tool calls need a human, letting the server evaluate calls, `evaluated_permission` / `evaluation` on tool-use events): read `shared/managed-agents-tools.md` § Permission Policies - `always_allow` / `always_ask` / `auto` and the three `auto` outcomes (runs, denied as high-risk, pauses when indeterminate). For attaching a terminal to a live session (`ant beta:sessions connect`): `shared/anthropic-cli.md`.
416+
415417
**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 per-firing run records and lifecycle controls (pause/unpause/archive).
416418

417419
**When the agent's work fans out** (research across several sources, per-file or per-record work, "look into N things, then summarize") **or one loop would fill its context with reading:** read `shared/managed-agents-multiagent.md` and recommend a multiagent session - start with just `{"type": "self"}` in the roster so the agent can delegate to copies of itself, then move reading-heavy sub-tasks to a cheaper worker agent (e.g. Claude Haiku 4.5) referenced by ID.

‎skills/claude-api/shared/anthropic-cli.md‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,29 @@ ant beta:sessions:events list --session-id "$SID" --transform 'content.0.text' -
188188
ant beta:sessions:events stream --session-id "$SID" # live event stream
189189
```
190190

191+
### Attach a terminal to a session (`ant beta:sessions connect`)
192+
193+
`ant beta:sessions connect <session-id>` attaches your terminal to an existing session: it loads the transcript, follows it live, and lets you step in - send a message, interrupt, or allow/deny a tool call that is waiting for approval. Ctrl+C detaches; the session keeps running, and reconnecting reloads the full history. Read-only if the session is `terminated` or archived.
194+
195+
```sh
196+
ant beta:sessions connect sesn_011CZkZAtmR3yMPDzynEDxu7 # terminal view
197+
ant beta:sessions connect sesn_011CZkZAtmR3yMPDzynEDxu7 --web # Console session viewer, served locally
198+
```
199+
200+
| Key | Action |
201+
|---|---|
202+
| Enter | Send input as a `user.message` (Alt+Enter / Ctrl+J for a newline) |
203+
| Esc | Interrupt the running agent (`user.interrupt`) |
204+
| Ctrl+O | Toggle detail: tool inputs/results, token usage, status events (`--verbose` / `-v` starts expanded) |
205+
| PgUp / PgDn | Scroll; scrolling up pauses following, End resumes |
206+
| Ctrl+C (or Ctrl+D on empty input) | Detach |
207+
208+
When a call is waiting for approval (`always_ask`, or `auto` with no determination), the input line becomes **Allow tool call?** with **Yes** / **No** / **No, and tell the agent why** - the CLI sends `user.tool_confirmation`, with your typed reason as `deny_message`. In multiagent sessions the terminal view follows the primary thread only (which includes coordinator<->subagent messages).
209+
210+
`--web` serves the Console's session viewer from a local server on `127.0.0.1`, prints the URL, and opens the browser (`--no-browser` to skip). The URL works once, within two minutes (reloading that tab is fine; to open it elsewhere, run the command again). The page talks only to the local `ant` process, which makes the API calls, so credentials never leave the CLI; the server runs until Ctrl+C. Unlike the terminal view, the browser viewer follows every thread of a multiagent session.
211+
212+
Needs an interactive terminal (except `--web`) - for scripts use `ant beta:sessions:events stream` / `send`, below.
213+
191214
### Interactive session loop (stream-before-send)
192215

193216
`ant beta:sessions:events stream` only delivers events emitted *after* the stream opens - so open it **before** sending the kickoff to avoid missing early events. Use process substitution to hold the stream on a file descriptor, send, then read:

‎skills/claude-api/shared/live-sources.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ Use these when a managed-agents binding, behavior, or wire-level detail isn't co
105105
| Events and Streaming | `https://platform.claude.com/docs/en/managed-agents/events-and-streaming.md` | "Extract event stream types, stream-first ordering, reconnect/dedupe, and steering patterns" |
106106
| Tools | `https://platform.claude.com/docs/en/managed-agents/tools.md` | "Extract built-in toolset, custom tool definitions, and tool result wire format" |
107107
| Files | `https://platform.claude.com/docs/en/managed-agents/files.md` | "Extract file upload, mount paths, session resources, and listing/downloading session outputs" |
108-
| Permission Policies | `https://platform.claude.com/docs/en/managed-agents/permission-policies.md` | "Extract permission policy types (allow/deny/confirm) and per-tool config" |
108+
| Permission Policies | `https://platform.claude.com/docs/en/managed-agents/permission-policies.md` | "Extract permission policy types (`always_allow` / `always_ask` / `auto`), the three `auto` outcomes, the `evaluated_permission` + `evaluation` event fields, and per-tool config" |
109109
| Multi-Agent | `https://platform.claude.com/docs/en/managed-agents/multiagent-orchestration.md` | "Extract multi-agent composition patterns, sub-agent invocation, and result handoff" |
110110
| Observability | `https://platform.claude.com/docs/en/managed-agents/observability.md` | "Extract logging, tracing, and usage telemetry exposed by managed agents" |
111111
| Webhooks | `https://platform.claude.com/docs/en/managed-agents/webhooks.md` | "Extract webhook endpoint registration, HMAC signature verification, supported event types, and delivery semantics" |
@@ -125,6 +125,7 @@ The `ant` CLI provides terminal access to the Claude API. Every API resource is
125125
| Topic | URL | Extraction Prompt |
126126
| ------------- | ------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
127127
| Anthropic CLI | `https://platform.claude.com/docs/en/api/sdks/cli.md` | "Extract CLI install, authentication, command structure, and the beta:agents/environments/sessions commands" |
128+
| `ant beta:sessions connect` | `https://platform.claude.com/docs/en/cli-sdks-libraries/cli/sessions-connect.md` | "Extract the interactive session viewer: keybindings, tool-call allow/deny prompt, `--web` local viewer and its URL/lifetime rules" |
128129
| Authentication overview | `https://platform.claude.com/docs/en/manage-claude/authentication.md` | "Extract the credential options (API keys, interactive OAuth login, Workload Identity Federation) and when to use each" |
129130
| WIF reference | `https://platform.claude.com/docs/en/manage-claude/wif-reference.md` | "Extract credential precedence order, the profile configuration file schema, and the configuration directory layout" |
130131

‎skills/claude-api/shared/managed-agents-client-patterns.md‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,11 @@ Reference: `interrupt.ts` - sends the interrupt the moment it sees `span.model_r
8181

8282
## 4. `tool_confirmation` round-trip
8383

84-
When the agent has `permission_policy: { type: 'always_ask' }`, any call to that tool fires an `agent.tool_use` event with `evaluated_permission === 'ask'` and the session goes idle waiting for a decision. Respond with `user.tool_confirmation`.
84+
When a call evaluates to `ask` - the tool has `permission_policy: { type: 'always_ask' }`, or it has `{ type: 'auto' }` and the server reached no determination - the `agent.tool_use` / `agent.mcp_tool_use` event carries `evaluated_permission === 'ask'` and the session goes idle waiting for a decision. Respond with `user.tool_confirmation`.
8585

8686
```ts
8787
for await (const event of stream) {
88-
if (event.type === 'agent.tool_use' && event.evaluated_permission === 'ask') {
88+
if ((event.type === 'agent.tool_use' || event.type === 'agent.mcp_tool_use') && event.evaluated_permission === 'ask') {
8989
await client.beta.sessions.events.send(session.id, {
9090
events: [{
9191
type: 'user.tool_confirmation',
@@ -101,7 +101,9 @@ for await (const event of stream) {
101101
Key points:
102102
- `tool_use_id` is `event.id` (typically `sevt_...`), **not** a `toolu_...` ID.
103103
- `result` is `'allow' | 'deny'`. Use `deny_message` to tell the model *why* you denied - it gets surfaced back to the agent.
104-
- Multiple pending tools: respond once per `agent.tool_use` event with `evaluated_permission === 'ask'`.
104+
- Multiple pending tools: respond once per `agent.tool_use` / `agent.mcp_tool_use` event with `evaluated_permission === 'ask'`.
105+
- Gate on `evaluated_permission === 'ask'`, not on the policy you configured - it covers `always_ask` and `auto`-indeterminate alike. Calls the server **denies** under `auto` (`evaluated_permission === 'deny'`, `evaluation.evaluated_permission.reason_code === 'high_risk'`) never enter this flow: the agent gets an error tool result and the session keeps running; sending a confirmation for one is a 400.
106+
- Log `event.evaluation` for audit (`type` + `reason_code`), and tolerate a `type` or `reason_code` you don't recognize - branch on known values, pass unknown ones through.
105107

106108
Reference: `tool-permissions.ts`.
107109

@@ -123,7 +125,7 @@ for await (const event of stream) {
123125
```
124126

125127
`stop_reason.type` values on `session.status_idle`:
126-
- `requires_action` - agent is waiting on a client-side event (tool confirmation, custom tool result). Handle it, don't break. **Self-hosted exception:** if the session went `requires_action`-idle with no pending `agent.tool_use` (always_ask) or `agent.custom_tool_use` to answer, the worker failed the claimed work item (typically a memory-store mount error, logged only on the worker host). Don't `continue` forever on that - surface it, fix the host, and send `user.interrupt` to re-queue the work (`shared/managed-agents-self-hosted-sandboxes.md` § Memory stores -> Troubleshooting).
128+
- `requires_action` - agent is waiting on a client-side event (tool confirmation, custom tool result). Handle it, don't break. **Self-hosted exception:** if the session went `requires_action`-idle with no pending `agent.tool_use` / `agent.mcp_tool_use` (`ask`) or `agent.custom_tool_use` to answer, the worker failed the claimed work item (typically a memory-store mount error, logged only on the worker host). Don't `continue` forever on that - surface it, fix the host, and send `user.interrupt` to re-queue the work (`shared/managed-agents-self-hosted-sandboxes.md` § Memory stores -> Troubleshooting).
127129
- `retries_exhausted` - terminal failure. Break, then check `sessions.retrieve()` for the error state.
128130
- `end_turn` - normal completion.
129131
- `budget_reached` - the session hit its spend cap and paused. Not terminal and not resumable by any event: change (typically raise) or remove the session's `budget` to resume, or treat it as done. A `session.usage` event with the final cost immediately precedes this idle. See `shared/managed-agents-core.md` § Session budgets.

‎skills/claude-api/shared/managed-agents-events.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Send events to a session via `POST /v1/sessions/{id}/events`.
1010
| ------------------------- | --------------------------------------------------- |
1111
| `user.message` | Send a user message |
1212
| `user.interrupt` | Interrupt the agent while it's running |
13-
| `user.tool_confirmation` | Approve/deny a tool call (when `always_ask` policy) |
13+
| `user.tool_confirmation` | Approve/deny a tool call that paused for approval (`always_ask`, or `auto` when the server reached no determination) |
1414
| `user.custom_tool_result` | Provide result for a custom tool call |
1515
| `user.define_outcome` | Start a rubric-graded iterate loop - see `shared/managed-agents-outcomes.md` |
1616
| `system.message` | Append privileged system-level context for this turn and every turn after it; see § Adding system context mid-session |
@@ -63,9 +63,9 @@ Event types use dot notation, grouped by namespace:
6363
| --- | --- |
6464
| `agent.message` | Agent text output |
6565
| `agent.thinking` | Progress signal that the agent is thinking - it does **not** carry the thinking content |
66-
| `agent.tool_use` | Agent used a built-in tool (`agent_toolset_20260401`) |
66+
| `agent.tool_use` | Agent used a built-in tool (`agent_toolset_20260401`). Carries `evaluated_permission` (`allow`/`ask`/`deny`) and usually `evaluation` - see `shared/managed-agents-tools.md` § `evaluated_permission` and `evaluation` |
6767
| `agent.tool_result` | Result from a built-in tool |
68-
| `agent.mcp_tool_use` | Agent used an MCP tool |
68+
| `agent.mcp_tool_use` | Agent used an MCP tool. Carries `evaluated_permission` and usually `evaluation`, same as `agent.tool_use` |
6969
| `agent.mcp_tool_result` | Result from an MCP tool |
7070
| `agent.custom_tool_use` | Agent invoked a custom tool - session goes idle, you respond with `user.custom_tool_result` |
7171
| `agent.thread_context_compacted` | Conversation context was compacted |

‎skills/claude-api/shared/managed-agents-multiagent.md‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ agent = client.beta.agents.create(
2525
session = client.beta.sessions.create(agent=agent.id, environment_id=env.id) # unchanged
2626
```
2727

28-
**Step 2 - move the reading-heavy work to a cheaper model.** Delegated research work is mostly searching, reading, and extracting: many input tokens, little hard reasoning. Create a second agent on a smaller model with a narrow `system` prompt and only the tools it needs, and list it next to `self`. A roster entry is only a reference: the worker runs on its own `model`, `system`, and `tools`, and its tokens are billed at its own model's rates. The large model spends its tokens on planning, checking, and synthesis; the small model does the bulk reading.
28+
**Step 2 - move the reading-heavy work to a cheaper model.** Delegated research work is mostly searching, reading, and extracting: many input tokens, little hard reasoning. Create a second agent on a smaller current-generation model (Claude Haiku 4.5, or Claude Sonnet 5 when the worker needs more judgment) with a narrow `system` prompt and only the tools it needs, and list it next to `self`. A roster entry is only a reference: the worker runs on its own `model`, `system`, and `tools`, and its tokens are billed at its own model's rates. The large model spends its tokens on planning, checking, and synthesis; the small model does the bulk reading.
2929

3030
```python
3131
worker = client.beta.agents.create(
@@ -222,7 +222,7 @@ No `agent.tool_use` and no `agent.thread_message_sent` are emitted for a consult
222222

223223
## Tool permissions and custom tools from subagent threads
224224

225-
When a subagent needs your client (an `always_ask` confirmation, or a custom tool result), the request is **cross-posted to the primary thread** with `session_thread_id` identifying the originating thread - so you only need to watch the session stream. Reply with `user.tool_confirmation` (carrying `tool_use_id`) or `user.custom_tool_result` (carrying `custom_tool_use_id`), and **echo the `session_thread_id` from the originating event** (the SDK param type and docstring expect it). The server also routes by the tool-use ID, so the echo is belt-and-suspenders rather than load-bearing - but include it.
225+
When a subagent needs your client (a tool call that paused for approval - `always_ask`, or `auto` with no determination - or a custom tool result), the request is **cross-posted to the primary thread** with `session_thread_id` identifying the originating thread - so you only need to watch the session stream. Reply with `user.tool_confirmation` (carrying `tool_use_id`) or `user.custom_tool_result` (carrying `custom_tool_use_id`), and **echo the `session_thread_id` from the originating event** (the SDK param type and docstring expect it). The server also routes by the tool-use ID, so the echo is belt-and-suspenders rather than load-bearing - but include it.
226226

227227
```python
228228
for event_id in stop.event_ids:
@@ -239,6 +239,8 @@ for event_id in stop.event_ids:
239239

240240
The same pattern applies to `user.custom_tool_result`.
241241

242+
**`auto` in multiagent sessions.** Only your `user.message` events on the primary thread can lead the server to allow a call it would otherwise deny under `auto`; nothing in a subagent's thread carries that weight (your client posts no messages there, and the coordinator's messages to the subagent carry none). A call the server denies under `auto` is **not** cross-posted - its event and the error tool result appear only on the subagent's own thread stream, and the subagent keeps running.
243+
242244
---
243245

244246
## Interrupting and archiving threads

0 commit comments

Comments
 (0)