agentos-protocol 0.2.15

Agent OS extension protocol types
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
# Agent OS ACP extension protocol schema, version 1.
# This schema is intentionally standalone. It imports no agentos schema.

type JsonUtf8 str

type AcpRuntimeKind enum {
  JAVA_SCRIPT
  PYTHON
  WEB_ASSEMBLY
}

# Legacy connection-owned ACP messages below remain encoded only for the
# dormant browser reference runtime. They are not part of the public AgentOS
# session API, and the native sidecar rejects them. Native durable orchestration
# uses the same structs internally as a private adapter-process driver until the
# browser protocol can be split into its own schema.
type AcpCreateSessionRequest struct {
  agentType: str
  runtime: AcpRuntimeKind
  cwd: str
  additionalDirectories: list<str>
  args: list<str>
  env: map<str><str>
  protocolVersion: i32
  clientCapabilities: JsonUtf8
  mcpServers: JsonUtf8
  skipOsInstructions: bool
  additionalInstructions: optional<str>
}

type AcpSessionRequest struct {
  sessionId: str
  method: str
  params: optional<JsonUtf8>
}

# Enumerate the agents available in this VM. The sidecar answers from the already
# projected `/opt/agentos` packages (client parses no manifests).
type AcpListAgentsRequest struct {
  reserved: bool
}

type AcpAgentEntry struct {
  id: str
  installed: bool
  adapterEntrypoint: str
}

type AcpListAgentsResponse struct {
  agents: list<AcpAgentEntry>
}

# Durable AgentOS session API. Omitted session IDs target `main`, including
# deletion; callers that operate on another public identity provide it.
#
# `openSession` is idempotent. Opening an existing durable ID restores its
# adapter when unloaded; getSession/listSessions remain the storage-only ways to
# inspect it without starting an adapter. The sidecar owns ACP session/new versus
# native resume/load selection and returns only after negotiation has populated
# the cached configuration/capability/agent-info fields. Omitted cwd resolves to
# `/home/agentos` in the sidecar. Restoration reapplies the original cwd,
# additionalDirectories, env, exact native ACP McpServer values, instruction
# settings, and current configuration values. The response is intentionally
# empty: the caller owns the requested public ID (or the documented `main`
# default), and getSession is the only API that returns durable metadata.
# permissionPolicy is an immutable AgentOS-side strategy for resolving native
# ACP session/request_permission calls: allow_all (the default) selects an
# adapter-provided allow option, reject_all selects a reject option or returns a
# typed permission_policy_unsatisfied error when none is offered,
# and ask durably exposes the adapter's exact options for a caller response. It
# does not change VM permissions and is not sent to the ACP adapter as config.
type AcpOpenSessionRequest struct {
  sessionId: optional<str>
  agent: str
  cwd: optional<str>
  additionalDirectories: optional<JsonUtf8>
  env: optional<JsonUtf8>
  mcpServers: optional<JsonUtf8>
  permissionPolicy: optional<str>
  skipOsInstructions: optional<bool>
  additionalInstructions: optional<str>
}

# This lookup never starts, restores, or queries an ACP adapter.
type AcpGetDurableSessionRequest struct {
  sessionId: optional<str>
}

# Listing is an ordinary updatedAt/sessionId keyset traversal. It never starts,
# restores, or queries an ACP adapter. It deliberately does not freeze a database
# snapshot across pages: a session updated between page requests can move in the
# ordering, which keeps cursors simple and avoids a snapshot registry.
type AcpListDurableSessionsRequest struct {
  cursor: optional<str>
  limit: optional<u32>
}

# Deletion is naturally idempotent by its target ID. It permanently
# removes durable metadata and history after orderly runtime teardown.
type AcpDeleteSessionRequest struct {
  sessionId: optional<str>
}

# Unload cancels active work and releases the adapter but preserves the durable
# session and all history. A later prompt transparently restores it.
type AcpUnloadSessionRequest struct {
  sessionId: optional<str>
}

# Prompt never creates a missing session. It transparently restores an unloaded
# adapter, durably accepts input before dispatch, blocks through the turn, and
# never automatically replays a prompt whose delivery became uncertain. The
# sidecar imposes no absolute session/prompt deadline: long turns and ask-policy
# permission waits remain active while the actor keep-awake scope is held.
type AcpPromptRequest struct {
  sessionId: optional<str>
  idempotencyKey: optional<str>
  content: JsonUtf8
}

# Cancellation is first-writer-wins against prompt completion and returns its
# typed race outcome.
type AcpCancelPromptRequest struct {
  sessionId: optional<str>
}

# Respond to a pending native ACP session/request_permission. sessionId is
# always the explicit caller-owned AgentOS identity; it never defaults to main.
# requestId is globally unique AgentOS-owned correlation and never contains the
# adapter JSON-RPC ID. optionId is one of the exact adapter-supplied ACP option
# identifiers. Resolution is first-writer-wins. accepted means the decision won
# that race and was delivered to the active ACP waiter, not that the tool ran.
# Invalid options are typed invalid_permission_option errors and do not consume
# the request. A terminal late response is non-throwing and names its reason.
type AcpRespondPermissionRequest struct {
  sessionId: str
  requestId: str
  optionId: str
}

# History is SQLite-only and never starts, restores, or queries an adapter. It
# stores the generic durable event union below; nested wire payloads retain
# unknown extension metadata and clients mechanically expose them as one flat
# top-level type union. Agent message/thought deltas are emitted live as ephemeral entries;
# they enter durable history only when the message completes. Non-delta ACP
# updates interleaved during a message remain in native arrival order.
# before/after are exclusive and mutually exclusive. Cursor expiry is a typed
# error, never a silent skip. History retention and response pages are bounded
# by the VM's generous `limits.acp.*` settings; retention prunes oldest events.
# Durable sequence values can repeat on live delivery because observers
# deduplicate by (sessionId, sequence).
type AcpReadHistoryRequest struct {
  sessionId: optional<str>
  before: optional<u64>
  after: optional<u64>
  limit: optional<u32>
}

# Cached getters never start, restore, or query an adapter.
type AcpGetSessionConfigRequest struct { sessionId: optional<str> }
type AcpGetSessionCapabilitiesRequest struct { sessionId: optional<str> }
type AcpGetSessionAgentInfoRequest struct { sessionId: optional<str> }

# Setting configuration may transparently restore the adapter; ACP owns
# validation and the response replaces the complete cached option collection.
# `value` encodes exactly one native ACP string or boolean config value.
type AcpSetSessionConfigOptionRequest struct {
  sessionId: optional<str>
  configId: str
  value: JsonUtf8
}

type AcpGetSessionStateRequest struct {
  sessionId: str
}

type AcpCloseSessionRequest struct {
  sessionId: str
}

# Resume a session that exists in durable storage but is not live in the current
# VM (e.g. after a Rivet actor slept and woke with a fresh VM). The sidecar runs
# the stateless resume state machine (native session/load when the agent supports
# it, else a fresh session/new + transcript continuation preamble). `runtime`,
# `cwd`, and `env` describe the fresh adapter launch used by the fallback tier.
# `transcriptPath`, when present, is a guest-readable path the fallback preamble
# points the agent at.
type AcpResumeSessionRequest struct {
  sessionId: str
  agentType: str
  runtime: AcpRuntimeKind
  transcriptPath: optional<str>
  cwd: str
  additionalDirectories: list<str>
  mcpServers: JsonUtf8
  env: map<str><str>
}

# Browser RESUMABLE path only (AGENTOS-WEB-ASYNC-AGENTS.md §3.2.1): the kernel
# worker feeds a chunk of the agent's stdout into the in-flight create_session /
# session/prompt handshake. The synchronous sidecar would block inside one
# pushFrame; the resumable browser path returns between steps so the worker can
# service the agent's own syscalls (incl. pi's net call for inference) on fresh,
# non-nested pushFrames. `processId` is the handshake handle returned in the
# AcpPendingResponse for the originating create/prompt request.
type AcpDeliverAgentOutputRequest struct {
  processId: str
  chunk: data
}

type AcpRequest union {
  AcpOpenSessionRequest |
  AcpGetDurableSessionRequest |
  AcpListDurableSessionsRequest |
  AcpDeleteSessionRequest |
  AcpUnloadSessionRequest |
  AcpPromptRequest |
  AcpCancelPromptRequest |
  AcpRespondPermissionRequest |
  AcpReadHistoryRequest |
  AcpGetSessionConfigRequest |
  AcpSetSessionConfigOptionRequest |
  AcpGetSessionCapabilitiesRequest |
  AcpGetSessionAgentInfoRequest |
  AcpCreateSessionRequest |
  AcpSessionRequest |
  AcpGetSessionStateRequest |
  AcpCloseSessionRequest |
  AcpResumeSessionRequest |
  AcpDeliverAgentOutputRequest |
  AcpListAgentsRequest
}

type AcpDurableSessionInfo struct {
  sessionId: str
  agent: str
  cwd: str
  additionalDirectories: JsonUtf8
  state: JsonUtf8
  latestSequence: u64
  title: optional<str>
  metadata: optional<JsonUtf8>
  createdAt: str
  updatedAt: str
}

type AcpOpenSessionResponse struct { reserved: bool }
type AcpGetDurableSessionResponse struct { session: AcpDurableSessionInfo }
type AcpListDurableSessionsResponse struct {
  sessions: list<AcpDurableSessionInfo>
  nextCursor: optional<str>
}
type AcpDeleteSessionResponse struct { reserved: bool }
type AcpUnloadSessionResponse struct { reserved: bool }
type AcpPromptResponse struct {
  sessionId: str
  message: optional<JsonUtf8>
  stopReason: str
}
type AcpCancelPromptResponse struct { status: str }
type AcpRespondPermissionResponse struct {
  status: str
  reason: optional<str>
}

# The generic internal wire/storage union. Native payloads retain their
# negotiated ACP field names, optional values, and opaque _meta. Public clients
# flatten SessionUpdate.sessionUpdate into top-level type and flatten permission
# request/response fields beside the durability envelope; they do not expose
# these internal update/request/response wrappers. Native adapter request IDs
# and private ACP session IDs never enter the public union. Automatically
# resolved permission requests are never persisted or emitted. An ask request
# has no permission timeout: the active prompt remains awake until a response
# or explicit adapter/session/VM lifecycle transition wins.
type AcpDurableSessionUpdate struct { update: JsonUtf8 }
type AcpDurablePermissionRequest struct {
  requestId: str
  request: JsonUtf8
}
type AcpDurablePermissionResponse struct {
  requestId: str
  response: JsonUtf8
  status: str
  reason: optional<str>
}
type AcpDurableEvent union {
  AcpDurableSessionUpdate |
  AcpDurablePermissionRequest |
  AcpDurablePermissionResponse
}

# AgentOS adds only this public identity, sequence, and timestamp envelope.
type AcpDurableHistoryEntry struct {
  sessionId: str
  sequence: u64
  timestamp: str
  event: AcpDurableEvent
}
type AcpHistoryPageResponse struct {
  events: list<AcpDurableHistoryEntry>
  hasMoreBefore: bool
  hasMoreAfter: bool
}
type AcpSessionConfigResponse struct {
  revision: u64
  options: JsonUtf8
}
type AcpSessionCapabilitiesResponse struct { capabilities: optional<JsonUtf8> }
type AcpSessionAgentInfoResponse struct { agentInfo: optional<JsonUtf8> }

type AcpSessionCreatedResponse struct {
  sessionId: str
  pid: optional<u32>
  modes: optional<JsonUtf8>
  configOptions: list<JsonUtf8>
  agentCapabilities: optional<JsonUtf8>
  agentInfo: optional<JsonUtf8>
}

type AcpSessionRpcResponse struct {
  sessionId: str
  response: JsonUtf8
  # Number of request-scoped AcpSessionEvent frames emitted before this
  # terminal response. Clients use this as an event-delivery barrier because
  # response and event frames travel on separate priority lanes.
  eventCount: u32
}

type AcpSessionStateResponse struct {
  sessionId: str
  agentType: str
  processId: str
  pid: optional<u32>
  closed: bool
  exitCode: optional<i32>
  modes: optional<JsonUtf8>
  configOptions: list<JsonUtf8>
  agentCapabilities: optional<JsonUtf8>
  agentInfo: optional<JsonUtf8>
}

type AcpSessionClosedResponse struct {
  sessionId: str
}

# Result of AcpResumeSessionRequest. `sessionId` is the live ACP session id after
# resume: equal to the requested id for native loads, or the freshly assigned id
# for the fallback tier (the caller remaps external -> live). `mode` is "native"
# (session/load|resume succeeded) or "fallback" (a new session was created and the
# transcript-continuation preamble was armed for the next prompt).
type AcpSessionResumedResponse struct {
  sessionId: str
  mode: str
}

type AcpErrorResponse struct {
  code: str
  message: str
}

# Browser RESUMABLE path: the create_session / session/prompt request (and each
# AcpDeliverAgentOutputRequest that has not yet completed the handshake) returns
# this, carrying the `processId` handle the kernel worker drives the interaction
# with. The real result (AcpSessionCreatedResponse / AcpSessionRpcResponse) is
# delivered as the response to the AcpDeliverAgentOutputRequest that completes it.
type AcpPendingResponse struct {
  processId: str
}

type AcpResponse union {
  AcpOpenSessionResponse |
  AcpGetDurableSessionResponse |
  AcpListDurableSessionsResponse |
  AcpDeleteSessionResponse |
  AcpUnloadSessionResponse |
  AcpPromptResponse |
  AcpCancelPromptResponse |
  AcpRespondPermissionResponse |
  AcpHistoryPageResponse |
  AcpSessionConfigResponse |
  AcpSessionCapabilitiesResponse |
  AcpSessionAgentInfoResponse |
  AcpSessionCreatedResponse |
  AcpSessionRpcResponse |
  AcpSessionStateResponse |
  AcpSessionClosedResponse |
  AcpSessionResumedResponse |
  AcpErrorResponse |
  AcpPendingResponse |
  AcpListAgentsResponse
}

# Live delivery uses the identical durable union/history envelope. Consumers
# reconnect by subscribing, fetching after their last sequence, then
# deduplicating by (sessionId, sequence); there is no second replay protocol.
type AcpDurableSessionEvent struct {
  sessionId: str
  sequence: u64
  timestamp: str
  event: AcpDurableEvent
}

# Ephemeral entries are live-only ACP agent_message_chunk or
# agent_thought_chunk updates. They are never persisted or sequenced; the
# durable completed message is authoritative after reconnect.
type AcpEphemeralSessionUpdateEvent struct {
  sessionId: str
  afterSequence: u64
  update: JsonUtf8
}

# Legacy browser-reference live event. Native durable clients ignore it and
# consume AcpDurableSessionEvent/AcpEphemeralSessionUpdateEvent instead.
type AcpSessionEvent struct {
  sessionId: str
  notification: JsonUtf8
}

type AcpAgentStderrEvent struct {
  sessionId: str
  agentType: str
  processId: str
  chunk: data
}

# Emitted when the ACP adapter process exits unexpectedly — a crash from the
# host's perspective (any spontaneous exit, including code 0).
# `restart` is "not_attempted": the sidecar never respawns an adapter or
# replays an interrupted request implicitly. `restartCount` and `maxRestarts`
# are therefore both zero. Explicit session restoration is a separate caller
# operation.
# `exitCode` is absent when the exit was observed indirectly (e.g. a write to
# the adapter's stdin failed because the process was already gone).
# `pid` is the host pid reported when the adapter process was launched.
type AcpAgentExitedEvent struct {
  sessionId: str
  agentType: str
  processId: str
  pid: optional<u32>
  exitCode: optional<i32>
  restart: str
  restartCount: u32
  maxRestarts: u32
}

type AcpEvent union {
  AcpDurableSessionEvent |
  AcpEphemeralSessionUpdateEvent |
  AcpSessionEvent |
  AcpAgentStderrEvent |
  AcpAgentExitedEvent
}

type AcpHostRequestCallback struct {
  sessionId: str
  request: JsonUtf8
}

type AcpCallback union {
  AcpHostRequestCallback
}

type AcpHostRequestCallbackResponse struct {
  response: optional<JsonUtf8>
}

type AcpCallbackResponse union {
  AcpHostRequestCallbackResponse
}