Skip to content

Commit 80d79f3

Browse files
committed
feat(managed-agent): add session lifecycle operations
Adds an operation selector to the Claude Managed Agents block, backed by nine new tools alongside the existing run-session behavior: - create session (non-blocking, seeds initial_events) - send message to an existing session - get session (surfaces tool calls awaiting approval) - list events - update session (title/metadata) - interrupt session - respond to tool confirmation (allow/deny) - archive session - delete session Run Session stays the default, so blocks saved before the selector existed keep their exact behavior and field layout.
1 parent c0e7ea9 commit 80d79f3

19 files changed

Lines changed: 2235 additions & 28 deletions

apps/docs/content/docs/en/integrations/managed_agent.mdx

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,179 @@ Open a Claude Platform Managed Agent session and return the assistant response a
6464
| `inputTokens` | number | Cumulative input tokens for the session. |
6565
| `outputTokens` | number | Cumulative output tokens for the session. |
6666

67+
### `managed_agent_create_session`
68+
69+
Create a Claude Platform Managed Agent session and return its id without waiting for a reply.
70+
71+
#### Input
72+
73+
| Parameter | Type | Required | Description |
74+
| --------- | ---- | -------- | ----------- |
75+
| `agent` | string | Yes | Managed-agent id inside the linked Claude workspace. |
76+
| `environment` | string | Yes | Environment id inside the linked Claude workspace. |
77+
| `environmentType` | string | No | Environment execution model hint \('cloud' \| 'self_hosted'\). |
78+
| `userMessage` | string | No | Optional first message; seeds initial_events and starts the agent immediately. |
79+
| `vaults` | array | No | Zero or more vault ids for MCP tool auth. |
80+
| `vaultsAck` | boolean | No | Acknowledgement that the author may use the attached vaults. |
81+
| `memoryStoreId` | string | No | Optional Agent Memory Store id. |
82+
| `memoryAccess` | string | No | Memory store access mode: 'read_write' \(default\) or 'read_only'. |
83+
| `memoryInstructions` | string | No | Per-attachment guidance for how the agent should use the memory store. |
84+
| `files` | array | No | File attachments \(cloud envs only\), as \[\{fileId, mountPath?\}\]. |
85+
| `sessionParameters` | object | No | Key/value session metadata forwarded to the session. |
86+
87+
#### Output
88+
89+
| Parameter | Type | Description |
90+
| --------- | ---- | ----------- |
91+
| `sessionId` | string | Anthropic session id \(sesn_...\). |
92+
| `started` | boolean | True when a first message was seeded, so the agent is already running. |
93+
94+
### `managed_agent_send_message`
95+
96+
Send a user message to an existing Claude Platform Managed Agent session.
97+
98+
#### Input
99+
100+
| Parameter | Type | Required | Description |
101+
| --------- | ---- | -------- | ----------- |
102+
| `userMessage` | string | Yes | The user message to send to the session. |
103+
104+
#### Output
105+
106+
| Parameter | Type | Description |
107+
| --------- | ---- | ----------- |
108+
| `sessionId` | string | The session the message was sent to. |
109+
| `sent` | boolean | True when the event was accepted by the API. |
110+
111+
### `managed_agent_get_session`
112+
113+
Read a Managed Agent session: status, stop reason, token usage, metadata, and any tool calls awaiting approval.
114+
115+
#### Input
116+
117+
| Parameter | Type | Required | Description |
118+
| --------- | ---- | -------- | ----------- |
119+
120+
#### Output
121+
122+
| Parameter | Type | Description |
123+
| --------- | ---- | ----------- |
124+
| `sessionId` | string | The session that was read. |
125+
| `status` | string | Session status — 'idle', 'running', 'rescheduling', or 'terminated'. |
126+
| `stopReason` | string | Why the session last stopped, e.g. 'end_turn' or 'requires_action'. |
127+
| `requiresAction` | boolean | True when the session is waiting on a tool confirmation or custom tool result. |
128+
| `pendingTools` | json | Tool calls awaiting approval — \[\{id, eventType, name, input\}\]. Pass each id to Respond To Tool Confirmation. |
129+
| `metadata` | json | Session metadata. |
130+
| `title` | string | Session title. |
131+
| `inputTokens` | number | Cumulative input tokens. |
132+
| `outputTokens` | number | Cumulative output tokens. |
133+
134+
### `managed_agent_list_events`
135+
136+
Read a Managed Agent session's event history and the agent's reply text.
137+
138+
#### Input
139+
140+
| Parameter | Type | Required | Description |
141+
| --------- | ---- | -------- | ----------- |
142+
| `eventTypes` | array | No | Optional event-type filter, e.g. \['agent.message'\]. Omit to return every event. |
143+
| `limit` | number | No | Maximum events to return \(default $\{DEFAULT_EVENT_LIMIT\}\). |
144+
145+
#### Output
146+
147+
| Parameter | Type | Description |
148+
| --------- | ---- | ----------- |
149+
| `sessionId` | string | The session that was read. |
150+
| `events` | json | Session events, oldest first. |
151+
| `count` | number | Number of events returned. |
152+
| `assistantText` | string | Concatenated text of every persisted agent.message, in order. |
153+
154+
### `managed_agent_update_session`
155+
156+
Update a Managed Agent session's title or metadata.
157+
158+
#### Input
159+
160+
| Parameter | Type | Required | Description |
161+
| --------- | ---- | -------- | ----------- |
162+
| `title` | string | No | New session title. |
163+
| `sessionParameters` | object | No | Replacement metadata map \(replaces all stored metadata, not merged\). |
164+
165+
#### Output
166+
167+
| Parameter | Type | Description |
168+
| --------- | ---- | ----------- |
169+
| `sessionId` | string | The session that was updated. |
170+
| `updated` | boolean | True when the update was accepted. |
171+
| `metadata` | json | Metadata after the update. |
172+
| `title` | string | Title after the update. |
173+
174+
### `managed_agent_interrupt_session`
175+
176+
Stop a running Managed Agent session; it stays usable afterwards.
177+
178+
#### Input
179+
180+
| Parameter | Type | Required | Description |
181+
| --------- | ---- | -------- | ----------- |
182+
183+
#### Output
184+
185+
| Parameter | Type | Description |
186+
| --------- | ---- | ----------- |
187+
| `sessionId` | string | The session that was interrupted. |
188+
| `interrupted` | boolean | True when the interrupt was accepted. |
189+
190+
### `managed_agent_respond_tool_confirmation`
191+
192+
Allow or deny the tool calls a Managed Agent session is waiting on before it can continue.
193+
194+
#### Input
195+
196+
| Parameter | Type | Required | Description |
197+
| --------- | ---- | -------- | ----------- |
198+
| `toolUseIds` | array | Yes | Blocking tool-use EVENT ids, from Get Session pendingTools\[\].id \(not toolu_ ids\). |
199+
| `decision` | string | Yes | 'allow' to let the tools run, or 'deny' to reject them. |
200+
| `denyMessage` | string | No | Reason surfaced to the agent. Only sent when the decision is deny. |
201+
202+
#### Output
203+
204+
| Parameter | Type | Description |
205+
| --------- | ---- | ----------- |
206+
| `sessionId` | string | The session that was answered. |
207+
| `decision` | string | The decision applied — 'allow' or 'deny'. |
208+
| `confirmedToolUseIds` | json | The tool-use event ids that were answered. |
209+
210+
### `managed_agent_archive_session`
211+
212+
Archive a Managed Agent session, preserving its history. Not reversible.
213+
214+
#### Input
215+
216+
| Parameter | Type | Required | Description |
217+
| --------- | ---- | -------- | ----------- |
218+
219+
#### Output
220+
221+
| Parameter | Type | Description |
222+
| --------- | ---- | ----------- |
223+
| `sessionId` | string | The session that was archived. |
224+
| `archived` | boolean | True when the archive was accepted. |
225+
226+
### `managed_agent_delete_session`
227+
228+
Permanently delete a Managed Agent session, its events, and its sandbox. Not reversible.
229+
230+
#### Input
231+
232+
| Parameter | Type | Required | Description |
233+
| --------- | ---- | -------- | ----------- |
234+
235+
#### Output
236+
237+
| Parameter | Type | Description |
238+
| --------- | ---- | ----------- |
239+
| `sessionId` | string | The session that was deleted. |
240+
| `deleted` | boolean | True when the delete was accepted. |
241+
67242

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
/**
2+
* @vitest-environment node
3+
*/
4+
import { describe, expect, it } from 'vitest'
5+
import { evaluateSubBlockCondition } from '@/lib/workflows/subblocks/visibility'
6+
import { ManagedAgentBlock } from '@/blocks/blocks/managed_agent'
7+
8+
const subBlockById = (id: string) => {
9+
const config = ManagedAgentBlock.subBlocks.find((sub) => sub.id === id)
10+
if (!config) throw new Error(`No sub-block '${id}' on the Managed Agent block`)
11+
return config
12+
}
13+
14+
/** Mirrors how the editor and serializer evaluate visibility: raw stored values. */
15+
const isVisible = (id: string, values: Record<string, unknown>) =>
16+
evaluateSubBlockCondition(subBlockById(id).condition, values)
17+
18+
const selectTool = (params: Record<string, unknown>) =>
19+
ManagedAgentBlock.tools.config?.tool?.(params as never)
20+
21+
/**
22+
* The block gained an operation selector after it shipped. Blocks saved before
23+
* that have NO stored `operation` value, so these tests pin the promise that
24+
* they keep behaving — and looking — exactly as they did.
25+
*/
26+
describe('Managed Agent block — legacy blocks without a stored operation', () => {
27+
const legacyValues = {
28+
credential: 'cred-1',
29+
agent: 'agent_1',
30+
environment: 'env_1',
31+
environmentType: 'cloud',
32+
userMessage: 'do the thing',
33+
}
34+
35+
it('resolves to the run-session tool', () => {
36+
expect(selectTool(legacyValues)).toBe('managed_agent_run_session')
37+
})
38+
39+
it('resolves to the run-session tool when operation is explicitly absent', () => {
40+
expect(selectTool({})).toBe('managed_agent_run_session')
41+
expect(selectTool({ operation: undefined })).toBe('managed_agent_run_session')
42+
expect(selectTool({ operation: null })).toBe('managed_agent_run_session')
43+
})
44+
45+
it.each(['agent', 'environment', 'environmentType', 'userMessage'])(
46+
'keeps the %s field visible',
47+
(id) => {
48+
expect(isVisible(id, legacyValues)).toBe(true)
49+
}
50+
)
51+
52+
it('keeps cloud-only fields visible', () => {
53+
expect(isVisible('memoryStoreId', legacyValues)).toBe(true)
54+
expect(isVisible('files', legacyValues)).toBe(true)
55+
expect(isVisible('sessionParameters', legacyValues)).toBe(true)
56+
})
57+
58+
it('still hides cloud-only fields on a self-hosted legacy block', () => {
59+
const selfHosted = { ...legacyValues, environmentType: 'self_hosted' }
60+
expect(isVisible('memoryStoreId', selfHosted)).toBe(false)
61+
expect(isVisible('files', selfHosted)).toBe(false)
62+
// Metadata applies to both environment models.
63+
expect(isVisible('sessionParameters', selfHosted)).toBe(true)
64+
})
65+
66+
it('does not show session-targeting fields', () => {
67+
expect(isVisible('sessionId', legacyValues)).toBe(false)
68+
expect(isVisible('toolUseIds', legacyValues)).toBe(false)
69+
})
70+
71+
it('keeps the legacy run-session output shape as the fallback', () => {
72+
expect(Object.keys(ManagedAgentBlock.outputs)).toEqual(
73+
expect.arrayContaining(['content', 'sessionId', 'inputTokens', 'outputTokens'])
74+
)
75+
})
76+
})
77+
78+
describe('Managed Agent block — operation routing', () => {
79+
it.each([
80+
['run_session', 'managed_agent_run_session'],
81+
['create_session', 'managed_agent_create_session'],
82+
['send_message', 'managed_agent_send_message'],
83+
['get_session', 'managed_agent_get_session'],
84+
['list_events', 'managed_agent_list_events'],
85+
['update_session', 'managed_agent_update_session'],
86+
['interrupt_session', 'managed_agent_interrupt_session'],
87+
['respond_tool_confirmation', 'managed_agent_respond_tool_confirmation'],
88+
['archive_session', 'managed_agent_archive_session'],
89+
['delete_session', 'managed_agent_delete_session'],
90+
])('maps %s to %s', (operation, toolId) => {
91+
expect(selectTool({ operation })).toBe(toolId)
92+
})
93+
94+
it('falls back to run session for an unknown operation', () => {
95+
expect(selectTool({ operation: 'not_a_real_operation' })).toBe('managed_agent_run_session')
96+
})
97+
98+
it('declares every mapped tool in tools.access', () => {
99+
const operations = subBlockById('operation').options as Array<{ id: string }>
100+
for (const { id } of operations) {
101+
expect(ManagedAgentBlock.tools.access).toContain(selectTool({ operation: id }))
102+
}
103+
})
104+
})
105+
106+
describe('Managed Agent block — per-operation field visibility', () => {
107+
it('shows the session id for operations that target an existing session', () => {
108+
for (const operation of [
109+
'send_message',
110+
'get_session',
111+
'list_events',
112+
'update_session',
113+
'interrupt_session',
114+
'respond_tool_confirmation',
115+
'archive_session',
116+
'delete_session',
117+
]) {
118+
expect(isVisible('sessionId', { operation })).toBe(true)
119+
}
120+
})
121+
122+
it('hides the agent and environment pickers once a session exists', () => {
123+
expect(isVisible('agent', { operation: 'send_message' })).toBe(false)
124+
expect(isVisible('environment', { operation: 'get_session' })).toBe(false)
125+
expect(isVisible('environmentType', { operation: 'archive_session' })).toBe(false)
126+
})
127+
128+
it('shows the user message for the three operations that send one', () => {
129+
expect(isVisible('userMessage', { operation: 'run_session' })).toBe(true)
130+
expect(isVisible('userMessage', { operation: 'create_session' })).toBe(true)
131+
expect(isVisible('userMessage', { operation: 'send_message' })).toBe(true)
132+
expect(isVisible('userMessage', { operation: 'get_session' })).toBe(false)
133+
})
134+
135+
it('makes the user message optional only for create session', () => {
136+
const required = subBlockById('userMessage').required
137+
expect(evaluateSubBlockCondition(required as never, { operation: 'create_session' })).toBe(
138+
false
139+
)
140+
expect(evaluateSubBlockCondition(required as never, { operation: 'send_message' })).toBe(true)
141+
expect(evaluateSubBlockCondition(required as never, { operation: 'run_session' })).toBe(true)
142+
// A legacy block has no stored operation and must stay required.
143+
expect(evaluateSubBlockCondition(required as never, {})).toBe(true)
144+
})
145+
146+
it('reveals the deny reason only when denying a tool confirmation', () => {
147+
expect(
148+
isVisible('denyMessage', { operation: 'respond_tool_confirmation', decision: 'deny' })
149+
).toBe(true)
150+
expect(
151+
isVisible('denyMessage', { operation: 'respond_tool_confirmation', decision: 'allow' })
152+
).toBe(false)
153+
expect(isVisible('denyMessage', { operation: 'send_message', decision: 'deny' })).toBe(false)
154+
})
155+
156+
it('shows metadata for update session but not the agent config fields', () => {
157+
expect(isVisible('sessionParameters', { operation: 'update_session' })).toBe(true)
158+
expect(isVisible('title', { operation: 'update_session' })).toBe(true)
159+
expect(isVisible('vaults', { operation: 'update_session' })).toBe(false)
160+
})
161+
})
10.1 KB
Binary file not shown.

0 commit comments

Comments
 (0)