@sema-agent/sdk 0.0.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +103 -0
- package/README.md +53 -0
- package/dist/client.d.ts +55 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +113 -0
- package/dist/client.js.map +1 -0
- package/dist/control-client.d.ts +36 -0
- package/dist/control-client.d.ts.map +1 -0
- package/dist/control-client.js +72 -0
- package/dist/control-client.js.map +1 -0
- package/dist/control-types.d.ts +282 -0
- package/dist/control-types.d.ts.map +1 -0
- package/dist/control-types.js +2 -0
- package/dist/control-types.js.map +1 -0
- package/dist/errors.d.ts +99 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +190 -0
- package/dist/errors.js.map +1 -0
- package/dist/events.d.ts +87 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +4 -0
- package/dist/events.js.map +1 -0
- package/dist/idempotency.d.ts +3 -0
- package/dist/idempotency.d.ts.map +1 -0
- package/dist/idempotency.js +9 -0
- package/dist/idempotency.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/resources/approvals.d.ts +40 -0
- package/dist/resources/approvals.d.ts.map +1 -0
- package/dist/resources/approvals.js +83 -0
- package/dist/resources/approvals.js.map +1 -0
- package/dist/resources/assistant.d.ts +23 -0
- package/dist/resources/assistant.d.ts.map +1 -0
- package/dist/resources/assistant.js +59 -0
- package/dist/resources/assistant.js.map +1 -0
- package/dist/resources/control/auth-providers.d.ts +18 -0
- package/dist/resources/control/auth-providers.d.ts.map +1 -0
- package/dist/resources/control/auth-providers.js +16 -0
- package/dist/resources/control/auth-providers.js.map +1 -0
- package/dist/resources/control/config.d.ts +31 -0
- package/dist/resources/control/config.d.ts.map +1 -0
- package/dist/resources/control/config.js +28 -0
- package/dist/resources/control/config.js.map +1 -0
- package/dist/resources/control/fleet.d.ts +31 -0
- package/dist/resources/control/fleet.d.ts.map +1 -0
- package/dist/resources/control/fleet.js +31 -0
- package/dist/resources/control/fleet.js.map +1 -0
- package/dist/resources/control/images.d.ts +19 -0
- package/dist/resources/control/images.d.ts.map +1 -0
- package/dist/resources/control/images.js +28 -0
- package/dist/resources/control/images.js.map +1 -0
- package/dist/resources/control/lifecycle.d.ts +15 -0
- package/dist/resources/control/lifecycle.d.ts.map +1 -0
- package/dist/resources/control/lifecycle.js +15 -0
- package/dist/resources/control/lifecycle.js.map +1 -0
- package/dist/resources/control/publish.d.ts +20 -0
- package/dist/resources/control/publish.d.ts.map +1 -0
- package/dist/resources/control/publish.js +20 -0
- package/dist/resources/control/publish.js.map +1 -0
- package/dist/resources/control/secrets.d.ts +27 -0
- package/dist/resources/control/secrets.d.ts.map +1 -0
- package/dist/resources/control/secrets.js +26 -0
- package/dist/resources/control/secrets.js.map +1 -0
- package/dist/resources/control/users.d.ts +27 -0
- package/dist/resources/control/users.d.ts.map +1 -0
- package/dist/resources/control/users.js +23 -0
- package/dist/resources/control/users.js.map +1 -0
- package/dist/resources/control/versioning.d.ts +21 -0
- package/dist/resources/control/versioning.d.ts.map +1 -0
- package/dist/resources/control/versioning.js +23 -0
- package/dist/resources/control/versioning.js.map +1 -0
- package/dist/resources/control/workers.d.ts +18 -0
- package/dist/resources/control/workers.d.ts.map +1 -0
- package/dist/resources/control/workers.js +17 -0
- package/dist/resources/control/workers.js.map +1 -0
- package/dist/resources/elicitations.d.ts +28 -0
- package/dist/resources/elicitations.d.ts.map +1 -0
- package/dist/resources/elicitations.js +15 -0
- package/dist/resources/elicitations.js.map +1 -0
- package/dist/resources/fleet.d.ts +65 -0
- package/dist/resources/fleet.d.ts.map +1 -0
- package/dist/resources/fleet.js +55 -0
- package/dist/resources/fleet.js.map +1 -0
- package/dist/resources/images.d.ts +24 -0
- package/dist/resources/images.d.ts.map +1 -0
- package/dist/resources/images.js +28 -0
- package/dist/resources/images.js.map +1 -0
- package/dist/resources/leader.d.ts +19 -0
- package/dist/resources/leader.d.ts.map +1 -0
- package/dist/resources/leader.js +18 -0
- package/dist/resources/leader.js.map +1 -0
- package/dist/resources/memory.d.ts +29 -0
- package/dist/resources/memory.d.ts.map +1 -0
- package/dist/resources/memory.js +36 -0
- package/dist/resources/memory.js.map +1 -0
- package/dist/resources/models.d.ts +13 -0
- package/dist/resources/models.d.ts.map +1 -0
- package/dist/resources/models.js +10 -0
- package/dist/resources/models.js.map +1 -0
- package/dist/resources/policy.d.ts +10 -0
- package/dist/resources/policy.d.ts.map +1 -0
- package/dist/resources/policy.js +10 -0
- package/dist/resources/policy.js.map +1 -0
- package/dist/resources/questions.d.ts +38 -0
- package/dist/resources/questions.d.ts.map +1 -0
- package/dist/resources/questions.js +15 -0
- package/dist/resources/questions.js.map +1 -0
- package/dist/resources/runs.d.ts +43 -0
- package/dist/resources/runs.d.ts.map +1 -0
- package/dist/resources/runs.js +72 -0
- package/dist/resources/runs.js.map +1 -0
- package/dist/resources/session-sync.d.ts +38 -0
- package/dist/resources/session-sync.d.ts.map +1 -0
- package/dist/resources/session-sync.js +97 -0
- package/dist/resources/session-sync.js.map +1 -0
- package/dist/resources/sessions.d.ts +46 -0
- package/dist/resources/sessions.d.ts.map +1 -0
- package/dist/resources/sessions.js +70 -0
- package/dist/resources/sessions.js.map +1 -0
- package/dist/resources/tasks.d.ts +19 -0
- package/dist/resources/tasks.d.ts.map +1 -0
- package/dist/resources/tasks.js +53 -0
- package/dist/resources/tasks.js.map +1 -0
- package/dist/resources/trace.d.ts +43 -0
- package/dist/resources/trace.d.ts.map +1 -0
- package/dist/resources/trace.js +65 -0
- package/dist/resources/trace.js.map +1 -0
- package/dist/resources/usage.d.ts +10 -0
- package/dist/resources/usage.d.ts.map +1 -0
- package/dist/resources/usage.js +10 -0
- package/dist/resources/usage.js.map +1 -0
- package/dist/resources/workflows.d.ts +20 -0
- package/dist/resources/workflows.d.ts.map +1 -0
- package/dist/resources/workflows.js +43 -0
- package/dist/resources/workflows.js.map +1 -0
- package/dist/settings.d.ts +44 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +2 -0
- package/dist/settings.js.map +1 -0
- package/dist/sse.d.ts +26 -0
- package/dist/sse.d.ts.map +1 -0
- package/dist/sse.js +244 -0
- package/dist/sse.js.map +1 -0
- package/dist/sync.d.ts +34 -0
- package/dist/sync.d.ts.map +1 -0
- package/dist/sync.js +143 -0
- package/dist/sync.js.map +1 -0
- package/dist/transport.d.ts +37 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +134 -0
- package/dist/transport.js.map +1 -0
- package/dist/types.d.ts +574 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/openapi.yaml +3434 -0
- package/package.json +42 -0
package/openapi.yaml
ADDED
|
@@ -0,0 +1,3434 @@
|
|
|
1
|
+
openapi: 3.1.0
|
|
2
|
+
|
|
3
|
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
4
|
+
# M0 wire contract β the SINGLE SOURCE OF TRUTH for the usage plane.
|
|
5
|
+
# Consumed by @sema-agent/sdk (β both doors: the CC/Codex MCP faΓ§ade and the
|
|
6
|
+
# portal BFF). Producer = the service. BOTH sides anchor contract tests to THIS
|
|
7
|
+
# file; drift fails in CI, not in prod (CORE-PRINCIPLES Β§5).
|
|
8
|
+
#
|
|
9
|
+
# Authoritative producer input: ../docs/SERVICE-CORE-CONTEXT.md Β§2/Β§4.
|
|
10
|
+
# packages/sdk/src/types.ts + events.ts + errors.ts MIRROR these schemas (kept in sync by hand;
|
|
11
|
+
# the contract test guards the drift). Where types.ts intentionally leaves an M2 surface as
|
|
12
|
+
# `unknown`, this spec may be slightly ahead β those are marked `x-status: draft`.
|
|
13
|
+
#
|
|
14
|
+
# x-status legend:
|
|
15
|
+
# live β implemented & verified server-side; in the contract-test assertion set.
|
|
16
|
+
# draft β verb exists but the response shape is not fully pinned; co-design pending (do not over-assert).
|
|
17
|
+
# planned β NOT yet implemented server-side; documented for SDK/producer alignment; EXCLUDED from
|
|
18
|
+
# contract-test assertions until it ships.
|
|
19
|
+
# gated β implemented but off by a server deploy flag (e.g. leader); not on the default product path.
|
|
20
|
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
21
|
+
|
|
22
|
+
info:
|
|
23
|
+
title: AI Agent Fleet β Usage-Plane Wire Contract
|
|
24
|
+
version: 0.1.0
|
|
25
|
+
description: >
|
|
26
|
+
M0 contract for the agent-service worker as consumed by the usage plane.
|
|
27
|
+
Conventions (enforced by the SDK + this contract):
|
|
28
|
+
β’ Every request carries `Authorization: Bearer <SERVICE_AUTH_TOKEN>` (SERVER-SIDE ONLY β the SDK's
|
|
29
|
+
browser guard refuses to hold this in a browser; the portal's browser talks to its own BFF) and
|
|
30
|
+
`x-agent-principal: <principal>` (REQUIRED; opaque to the service; principal-first, CORE-PRINCIPLES Β§3).
|
|
31
|
+
β’ Submit-class POSTs (`/v1/tasks`, `/v1/runs`) carry an `Idempotency-Key` (the SDK auto-generates one;
|
|
32
|
+
in-process dedupe window = 15 min; cross-replica hard-dedupe = the session-active 409, see Β§below).
|
|
33
|
+
β’ New contract fields are OPTIONAL with no default (CORE-PRINCIPLES Β§6).
|
|
34
|
+
β’ Async results are delivered by POLL (`GET /v1/runs/:id`) or SSE (`/v1/runs/:id/events`).
|
|
35
|
+
There is NO generic webhook/callback and NO signing (SERVICE-CORE-CONTEXT Β§2.4).
|
|
36
|
+
x-sources:
|
|
37
|
+
- ../docs/SERVICE-CORE-CONTEXT.md # Β§2 wire answers, Β§4 verb surface (authoritative producer input)
|
|
38
|
+
- ../docs/CORE-PRINCIPLES.md # Β§2 secret discipline, Β§3 principal-first, Β§5 contract-as-anchor, Β§6 optional-no-default
|
|
39
|
+
x-contract-test:
|
|
40
|
+
producer: the service
|
|
41
|
+
consumer: '@sema-agent/sdk (packages/sdk)'
|
|
42
|
+
note: 'Both sides assert against this file. `x-status` planned|draft operations are excluded until live/pinned.'
|
|
43
|
+
|
|
44
|
+
servers:
|
|
45
|
+
- url: '{baseUrl}'
|
|
46
|
+
description: >
|
|
47
|
+
Worker base URL β injected from deploy env, NEVER hardcoded (secret/config discipline, CORE-PRINCIPLES Β§2:
|
|
48
|
+
config stores env-NAME, not values). e.g. a Traefik route prefix in front of a worker pod.
|
|
49
|
+
variables:
|
|
50
|
+
baseUrl:
|
|
51
|
+
default: http://worker.invalid
|
|
52
|
+
description: Placeholder. Real value comes from the integrator's env (e.g. SERVICE_BASE_URL).
|
|
53
|
+
|
|
54
|
+
security:
|
|
55
|
+
- bearerAuth: []
|
|
56
|
+
|
|
57
|
+
tags:
|
|
58
|
+
- name: tasks
|
|
59
|
+
description: Synchronous tasks (run-to-completion + live token stream).
|
|
60
|
+
- name: runs
|
|
61
|
+
description: Asynchronous, durable, resumable runs (the door-B / HITL path).
|
|
62
|
+
- name: sessions
|
|
63
|
+
description: Conversation continuity (the "ε―Ήθ―ζ‘") audit reads.
|
|
64
|
+
- name: approvals
|
|
65
|
+
description: HITL β approve/deny a policy gate or answer an AskUserQuestion (SERVICE-CORE-CONTEXT Β§3.4).
|
|
66
|
+
- name: assistant
|
|
67
|
+
description: >
|
|
68
|
+
Assistant-scheduler + HITL decision surface (ASSISTANT-WIRE-CONTRACT.md, service main 6cd0164 /
|
|
69
|
+
core 1.110.0, LIVE on worker): severity-sorted inbox, N-task overview, and the 3 DISTINCT decision
|
|
70
|
+
wires (resume / plan_review / preempt). Owner-gated via the principal header (NOT D-G crypto-bound β
|
|
71
|
+
only /decide is, Β§6); a non-owner non-operator gets 404 (no existence oracle), never 403.
|
|
72
|
+
- name: trace
|
|
73
|
+
description: Workspace data source β list/turns/stream + jobId grouping (the anti-fragmentation substrate).
|
|
74
|
+
- name: usage
|
|
75
|
+
description: Read-only per-principal cost/quota snapshot (the CLI `/cost` panel's real data).
|
|
76
|
+
- name: policy
|
|
77
|
+
description: Read-only effective-governance snapshot (the CLI `/permissions` panel's real data; inspect-only).
|
|
78
|
+
- name: workflows
|
|
79
|
+
description: >
|
|
80
|
+
S8 self-orchestration run observability (design/97/98) β owner-scoped list / detail / live SSE.
|
|
81
|
+
Gated server-side (SELF_ORCHESTRATION_ENABLED, default OFF β 501); replica-local (list/stream see this
|
|
82
|
+
replica's runs). NOT resumable on the stream (in-process subscription; a drop is a full restart).
|
|
83
|
+
- name: fleet
|
|
84
|
+
description: >
|
|
85
|
+
MF-Fleet (shell-host data contract) β the LIVE multi-row fleet view SSE (active task + workflow rows,
|
|
86
|
+
transitioning over time). Owner-scoped by the credential principal (scope wire-stripped) unless fleet-wide;
|
|
87
|
+
in-process bus, NOT resumable (a drop is a full restart). No fleet wiring β 501.
|
|
88
|
+
- name: leader
|
|
89
|
+
description: v2 leader pipeline (gated; E2B + git push). NOT the default door-B brain.
|
|
90
|
+
- name: metrics
|
|
91
|
+
description: Worker health/cost (ops; uses metricsToken, not a principal).
|
|
92
|
+
|
|
93
|
+
paths:
|
|
94
|
+
|
|
95
|
+
/v1/capabilities:
|
|
96
|
+
get:
|
|
97
|
+
tags: [metrics]
|
|
98
|
+
operationId: capabilities
|
|
99
|
+
x-status: live # service 7aa0f3d. Booleans share deps with the route gates.
|
|
100
|
+
summary: Worker capability discovery (what surfaces are enabled).
|
|
101
|
+
description: >
|
|
102
|
+
One call replaces probe-by-501. Booleans are computed from the SAME deps as the route gates β
|
|
103
|
+
"capabilities says yes but the route 501s" is structurally impossible (producer-tested). Auth = same
|
|
104
|
+
as config surfaces (Bearer authToken; NO principal header). Open set: ignore unknown keys.
|
|
105
|
+
`pricingConfigured` disambiguates stats.costMicroUsd=0 (true zero vs MODEL_COST_* unset) β it is
|
|
106
|
+
worker-level, deliberately NOT on per-result stats (the pinned wire contract β‘).
|
|
107
|
+
responses:
|
|
108
|
+
'200':
|
|
109
|
+
description: Capability map.
|
|
110
|
+
content:
|
|
111
|
+
application/json:
|
|
112
|
+
schema: { $ref: '#/components/schemas/Capabilities' }
|
|
113
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
114
|
+
|
|
115
|
+
/v1/memory:
|
|
116
|
+
parameters:
|
|
117
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
118
|
+
get:
|
|
119
|
+
tags: [sessions]
|
|
120
|
+
operationId: memoryGet
|
|
121
|
+
x-status: live # service 170c384. Owner-scoped by construction.
|
|
122
|
+
summary: Read your own <user_memory> (transparency surface).
|
|
123
|
+
description: >
|
|
124
|
+
Scope derives from the request principal (same rule as task execution) β a caller CANNOT address
|
|
125
|
+
anyone else's memory. No principal header β 401. Discover via capabilities().memory.
|
|
126
|
+
responses:
|
|
127
|
+
'200':
|
|
128
|
+
description: The memory record (content null = empty).
|
|
129
|
+
content:
|
|
130
|
+
application/json:
|
|
131
|
+
schema: { $ref: '#/components/schemas/MemoryRecord' }
|
|
132
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
133
|
+
delete:
|
|
134
|
+
tags: [sessions]
|
|
135
|
+
operationId: memoryClear
|
|
136
|
+
x-status: live
|
|
137
|
+
summary: Clear your WHOLE memory scope (per-entry deletion is a future additive).
|
|
138
|
+
responses:
|
|
139
|
+
'200':
|
|
140
|
+
description: Cleared.
|
|
141
|
+
content:
|
|
142
|
+
application/json:
|
|
143
|
+
schema:
|
|
144
|
+
type: object
|
|
145
|
+
required: [ok, scope]
|
|
146
|
+
properties:
|
|
147
|
+
ok: { type: boolean }
|
|
148
|
+
scope: { type: string }
|
|
149
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
150
|
+
|
|
151
|
+
/v1/tasks:
|
|
152
|
+
parameters:
|
|
153
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
154
|
+
post:
|
|
155
|
+
tags: [tasks]
|
|
156
|
+
operationId: tasksSubmit
|
|
157
|
+
x-status: live
|
|
158
|
+
summary: Run a short task to completion (synchronous).
|
|
159
|
+
description: >
|
|
160
|
+
Synchronous run-to-completion. Auto `Idempotency-Key` (β retry-safe). Supports `verify`/`cascade`.
|
|
161
|
+
For anything that may take minutes or hit a HITL gate, use `/v1/runs` instead (this call blocks).
|
|
162
|
+
parameters:
|
|
163
|
+
- $ref: '#/components/parameters/IdempotencyKey'
|
|
164
|
+
requestBody:
|
|
165
|
+
required: true
|
|
166
|
+
content:
|
|
167
|
+
application/json:
|
|
168
|
+
schema: { $ref: '#/components/schemas/TaskRequest' }
|
|
169
|
+
responses:
|
|
170
|
+
'200':
|
|
171
|
+
description: Task result.
|
|
172
|
+
content:
|
|
173
|
+
application/json:
|
|
174
|
+
schema: { $ref: '#/components/schemas/TaskResult' }
|
|
175
|
+
'400': { $ref: '#/components/responses/BadRequest' }
|
|
176
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
177
|
+
'409': { $ref: '#/components/responses/Conflict' }
|
|
178
|
+
'429': { $ref: '#/components/responses/RateLimited' }
|
|
179
|
+
'503': { $ref: '#/components/responses/Unauthorized' }
|
|
180
|
+
get:
|
|
181
|
+
tags: [trace]
|
|
182
|
+
operationId: traceList
|
|
183
|
+
x-status: live
|
|
184
|
+
summary: List runs, optionally filtered by jobId (the work-view grouping).
|
|
185
|
+
description: >
|
|
186
|
+
Lists runs (keyset pagination). `?jobId=<id>` returns all runs of one logical job β the substrate the
|
|
187
|
+
workspace uses to group a job's scattered runs across BOTH doors (jobId LIVE, service 63b8696).
|
|
188
|
+
Summary items carry `jobId` when present. Full per-task shape beyond the summary is draft (see traceTurns).
|
|
189
|
+
parameters:
|
|
190
|
+
- in: query
|
|
191
|
+
name: jobId
|
|
192
|
+
required: false
|
|
193
|
+
schema: { type: string, minLength: 1, maxLength: 64 }
|
|
194
|
+
description: Filter to one logical job's runs (idx_job, keyset).
|
|
195
|
+
- in: query
|
|
196
|
+
name: status
|
|
197
|
+
required: false
|
|
198
|
+
schema: { $ref: '#/components/schemas/RunStatus' }
|
|
199
|
+
description: Filter by run status.
|
|
200
|
+
- in: query
|
|
201
|
+
name: source
|
|
202
|
+
required: false
|
|
203
|
+
schema: { type: string }
|
|
204
|
+
description: Filter by credential-derived system (oa | cc-mcp | portal β¦). the pinned wire contract.
|
|
205
|
+
- in: query
|
|
206
|
+
name: owner
|
|
207
|
+
required: false
|
|
208
|
+
schema: { type: string }
|
|
209
|
+
description: >
|
|
210
|
+
Filter to one principal's runs. AUTHZ IS THE BFF'S RESPONSIBILITY (trace surface trusts the
|
|
211
|
+
system token): ordinary users MUST be forced to owner=<their principal>; operator role may omit.
|
|
212
|
+
the pinned wire contract.
|
|
213
|
+
- in: query
|
|
214
|
+
name: limit
|
|
215
|
+
required: false
|
|
216
|
+
schema: { type: integer, minimum: 1, maximum: 100, default: 50 }
|
|
217
|
+
- in: query
|
|
218
|
+
name: cursor
|
|
219
|
+
required: false
|
|
220
|
+
schema: { type: string }
|
|
221
|
+
description: Keyset pagination cursor (opaque).
|
|
222
|
+
responses:
|
|
223
|
+
'200':
|
|
224
|
+
description: Run summaries.
|
|
225
|
+
content:
|
|
226
|
+
application/json:
|
|
227
|
+
schema: { $ref: '#/components/schemas/TaskList' }
|
|
228
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
229
|
+
'429': { $ref: '#/components/responses/RateLimited' }
|
|
230
|
+
|
|
231
|
+
/v1/tasks/stream:
|
|
232
|
+
parameters:
|
|
233
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
234
|
+
post:
|
|
235
|
+
tags: [tasks]
|
|
236
|
+
operationId: tasksStream
|
|
237
|
+
x-status: live
|
|
238
|
+
summary: Live token stream (synchronous, per-token).
|
|
239
|
+
description: >
|
|
240
|
+
Same instance, transient, per-token SSE. `verify`/`cascade` are REJECTED here with 400 (multi-attempt,
|
|
241
|
+
not a stream). NOT a resumable surface β a client disconnect ABORTS the run (kills token burn). HITL can
|
|
242
|
+
still checkpoint mid-stream, but RESUME is only via a durable `/v1/runs` + `/v1/approvals` (the pinned wire contract/[5]
|
|
243
|
+
Q3). Use this only for fast, single-connection, no-HITL interactions; the portal standardizes on `/v1/runs`.
|
|
244
|
+
requestBody:
|
|
245
|
+
required: true
|
|
246
|
+
content:
|
|
247
|
+
application/json:
|
|
248
|
+
schema: { $ref: '#/components/schemas/TaskRequest' }
|
|
249
|
+
responses:
|
|
250
|
+
'200':
|
|
251
|
+
description: SSE stream of AgentEvents (per-token text/reasoning carry the `delta` flag).
|
|
252
|
+
content:
|
|
253
|
+
text/event-stream:
|
|
254
|
+
schema: { $ref: '#/components/schemas/AgentEvent' }
|
|
255
|
+
'400':
|
|
256
|
+
description: Bad request β e.g. `verify`/`cascade` not supported on the stream, or invalid `jobId`.
|
|
257
|
+
content:
|
|
258
|
+
application/json:
|
|
259
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
260
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
261
|
+
'429': { $ref: '#/components/responses/RateLimited' }
|
|
262
|
+
|
|
263
|
+
/v1/runs:
|
|
264
|
+
parameters:
|
|
265
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
266
|
+
post:
|
|
267
|
+
tags: [runs]
|
|
268
|
+
operationId: runsCreate
|
|
269
|
+
x-status: live
|
|
270
|
+
summary: Start an async run (202).
|
|
271
|
+
description: >
|
|
272
|
+
Async; runs in the background. Requires a TiDB run store (absent β 501). Auto `Idempotency-Key`.
|
|
273
|
+
Poll `GET /v1/runs/:id` or stream `GET /v1/runs/:id/events`. A client disconnect does NOT stop the run.
|
|
274
|
+
parameters:
|
|
275
|
+
- $ref: '#/components/parameters/IdempotencyKey'
|
|
276
|
+
requestBody:
|
|
277
|
+
required: true
|
|
278
|
+
content:
|
|
279
|
+
application/json:
|
|
280
|
+
schema: { $ref: '#/components/schemas/TaskRequest' }
|
|
281
|
+
responses:
|
|
282
|
+
'202':
|
|
283
|
+
description: Run accepted (the capability token is NEVER on the wire β server-internal).
|
|
284
|
+
content:
|
|
285
|
+
application/json:
|
|
286
|
+
schema: { $ref: '#/components/schemas/RunReceipt' }
|
|
287
|
+
'400': { $ref: '#/components/responses/BadRequest' }
|
|
288
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
289
|
+
'409': { $ref: '#/components/responses/Conflict' }
|
|
290
|
+
'429': { $ref: '#/components/responses/RateLimited' }
|
|
291
|
+
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
292
|
+
'503': { $ref: '#/components/responses/Unauthorized' }
|
|
293
|
+
|
|
294
|
+
/v1/runs/{taskId}:
|
|
295
|
+
parameters:
|
|
296
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
297
|
+
- $ref: '#/components/parameters/TaskIdPath'
|
|
298
|
+
get:
|
|
299
|
+
tags: [runs]
|
|
300
|
+
operationId: runsGet
|
|
301
|
+
x-status: live
|
|
302
|
+
summary: Read run state (poll).
|
|
303
|
+
description: Owner-scoped read. Response includes `jobId` when the run has one.
|
|
304
|
+
responses:
|
|
305
|
+
'200':
|
|
306
|
+
description: Run state.
|
|
307
|
+
content:
|
|
308
|
+
application/json:
|
|
309
|
+
schema: { $ref: '#/components/schemas/RunRecord' }
|
|
310
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
311
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
312
|
+
|
|
313
|
+
/v1/runs/{taskId}/events:
|
|
314
|
+
parameters:
|
|
315
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
316
|
+
- $ref: '#/components/parameters/TaskIdPath'
|
|
317
|
+
get:
|
|
318
|
+
tags: [runs]
|
|
319
|
+
operationId: runsEvents
|
|
320
|
+
x-status: live
|
|
321
|
+
summary: Resumable SSE event stream (durable log).
|
|
322
|
+
description: >
|
|
323
|
+
Replays durable events from after `Last-Event-ID` (each event's `id:` = the TiDB `task_event.seq`,
|
|
324
|
+
monotonic; events are NOT rolling-deleted, so the normal path never evicts), then tails to a terminal
|
|
325
|
+
state. Durable `text`/`reasoning` are turn-AGGREGATED (no per-token deltas β those are only on
|
|
326
|
+
`/v1/tasks/stream`). Heartbeat = 30s (`:` comment lines); the SDK declares a drop at β₯~45s and re-enters
|
|
327
|
+
the resume loop without consuming a retry. On a rare window eviction the server returns `416` with
|
|
328
|
+
`{ error, retainedFrom }` (NO machine code field β match the STATUS, not a body code); the SDK then
|
|
329
|
+
full-syncs via `GET /v1/tasks/:id/turns` and resumes events from `retainedFrom` (no event lost;
|
|
330
|
+
consumer is event-id idempotent).
|
|
331
|
+
parameters:
|
|
332
|
+
- $ref: '#/components/parameters/LastEventId'
|
|
333
|
+
responses:
|
|
334
|
+
'200':
|
|
335
|
+
description: SSE stream of AgentEvents (turn-aggregated; resumable).
|
|
336
|
+
content:
|
|
337
|
+
text/event-stream:
|
|
338
|
+
schema: { $ref: '#/components/schemas/AgentEvent' }
|
|
339
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
340
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
341
|
+
'416':
|
|
342
|
+
description: >
|
|
343
|
+
Resume point evicted past retention. Match the STATUS 416 (there is NO `code` field); read
|
|
344
|
+
`retainedFrom`, full-sync via `GET /v1/tasks/:id/turns`, then resume from `retainedFrom`.
|
|
345
|
+
content:
|
|
346
|
+
application/json:
|
|
347
|
+
schema: { $ref: '#/components/schemas/ResumeEvicted' }
|
|
348
|
+
|
|
349
|
+
/v1/runs/{taskId}/cancel:
|
|
350
|
+
parameters:
|
|
351
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
352
|
+
- $ref: '#/components/parameters/TaskIdPath'
|
|
353
|
+
post:
|
|
354
|
+
tags: [runs]
|
|
355
|
+
operationId: runsCancel
|
|
356
|
+
x-status: live # LIVE (service ced3d88). SDK verb runs.cancel(id) = M1.5.
|
|
357
|
+
summary: Cancel a durable async run (LIVE).
|
|
358
|
+
description: >
|
|
359
|
+
Cancels a durable async run (one created by `POST /v1/runs`). Auth = same as `runs.get`: owner-checked
|
|
360
|
+
(non-owner β 404, no existence leak; missing principal β 401). All responses are synchronous ACKs; actual
|
|
361
|
+
termination is async (same-replica immediate; cross-replica β30s via the heartbeat tick). A cancelled run
|
|
362
|
+
settles to `status` failed + `errorCode` "cancelled" (NO new status β distinguish via errorCode; the UI
|
|
363
|
+
shows "cancelled", not an error). A SUSPENDED run has no running stream to abort β 409; cancel it by
|
|
364
|
+
DENYING its approval instead. LIVE (service ced3d88).
|
|
365
|
+
responses:
|
|
366
|
+
'202':
|
|
367
|
+
description: >
|
|
368
|
+
Ack. A running run returns status "cancelling"; an already-terminal run is an idempotent no-op
|
|
369
|
+
(returns the terminal status + a note).
|
|
370
|
+
content:
|
|
371
|
+
application/json:
|
|
372
|
+
schema: { $ref: '#/components/schemas/CancelAck' }
|
|
373
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
374
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
375
|
+
'409':
|
|
376
|
+
description: Run is suspended (HITL) β cancel by denying its approval (`/v1/approvals/:sessionId/decide`), not here.
|
|
377
|
+
content:
|
|
378
|
+
application/json:
|
|
379
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
380
|
+
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
381
|
+
|
|
382
|
+
/v1/runs/{taskId}/steer:
|
|
383
|
+
parameters:
|
|
384
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
385
|
+
- $ref: '#/components/parameters/TaskIdPath'
|
|
386
|
+
post:
|
|
387
|
+
tags: [runs]
|
|
388
|
+
operationId: runsSteer
|
|
389
|
+
x-status: draft # design/80 D-A; core landing the slice (pendingSteer freeze/close-tag). Skeleton per the pinned wire contract.
|
|
390
|
+
summary: Steer a RUNNING durable run β inject mid-flight direction (supervision).
|
|
391
|
+
description: >
|
|
392
|
+
design/80 D-A. The core of *supervision* (vs mere approve/deny): inject direction into a running run.
|
|
393
|
+
AT-MOST-ONCE β steer is NOT idempotent (no retry). `trusted` is NOT a body field β the server derives it
|
|
394
|
+
from the authenticated operator (the BFF's operator role); a network steer defaults to untrusted. A
|
|
395
|
+
durable-`suspended` run β 409 `errorCode` "steering.not_running" (steer is not queued; resolve its
|
|
396
|
+
approval instead). The pendingSteer freeze / close-tag guard is server-side; the client only sends text.
|
|
397
|
+
requestBody:
|
|
398
|
+
required: true
|
|
399
|
+
content:
|
|
400
|
+
application/json:
|
|
401
|
+
schema:
|
|
402
|
+
type: object
|
|
403
|
+
required: [text]
|
|
404
|
+
properties:
|
|
405
|
+
text: { type: string, description: 'The steering direction (untrusted DATA; the server fences it).' }
|
|
406
|
+
mode:
|
|
407
|
+
type: string
|
|
408
|
+
enum: [all, one-at-a-time]
|
|
409
|
+
description: >
|
|
410
|
+
Engine QueueMode (default one-at-a-time) β how many queued steers drain per TURN BOUNDARY:
|
|
411
|
+
"all" = inject ALL queued steers together at the next turn; "one-at-a-time" = inject ONE per
|
|
412
|
+
turn (FIFO), spread across turns. FORWARD-DRAFT: core's steer is harness-level today, not
|
|
413
|
+
per-call; the per-call mapping is pending D-A core.
|
|
414
|
+
responses:
|
|
415
|
+
'200': { description: Steer accepted and applied. }
|
|
416
|
+
'202': { description: Steer accepted (queued to the next turn-boundary drain). }
|
|
417
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
418
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
419
|
+
'409':
|
|
420
|
+
description: 'errorCode "steering.not_running" β the run is suspended/terminal, not running (steer is not queued).'
|
|
421
|
+
content:
|
|
422
|
+
application/json:
|
|
423
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
424
|
+
'422':
|
|
425
|
+
description: 'errorCode "steering.invalid_content" β the steer text carried a control-plane escape (e.g. </system-reminder>); core rejected it.'
|
|
426
|
+
content:
|
|
427
|
+
application/json:
|
|
428
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
429
|
+
|
|
430
|
+
/v1/runs/{taskId}/subagents/{target}/steer:
|
|
431
|
+
parameters:
|
|
432
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
433
|
+
- $ref: '#/components/parameters/TaskIdPath'
|
|
434
|
+
- name: target
|
|
435
|
+
in: path
|
|
436
|
+
required: true
|
|
437
|
+
schema: { type: string }
|
|
438
|
+
description: >
|
|
439
|
+
The delegating tool call's id (`parentToolCallId`, stamped on every forwarded subagent content
|
|
440
|
+
event when `forwardSubagentEvents` is on) or the subagent's agentName (an ambiguous name β 409).
|
|
441
|
+
post:
|
|
442
|
+
tags: [runs]
|
|
443
|
+
operationId: runsSteerSubagent
|
|
444
|
+
x-status: live # core 1.219 C2 (onSubagentSpawn steer handles) / server 1.89 SubagentSteerRegistry; wire-verified 2026-07-04 (subagent-wire C1C2).
|
|
445
|
+
summary: Steer a RUNNING SUBAGENT of this run (drains at the subagent's next tool round).
|
|
446
|
+
description: >
|
|
447
|
+
core 1.219 C2 / server 1.89. The CC "enter the agent's view and type to it" semantic (AGe
|
|
448
|
+
pendingMessages): the message is queued and drains at the subagent's NEXT TOOL ROUND. BODY SHAPE
|
|
449
|
+
differs from the main-run steer β `{ content }`, NOT `{ text }`. AT-MOST-ONCE (no retry). A
|
|
450
|
+
settled/unknown subagent β 409 `errorCode` "steering.not_running".
|
|
451
|
+
requestBody:
|
|
452
|
+
required: true
|
|
453
|
+
content:
|
|
454
|
+
application/json:
|
|
455
|
+
schema:
|
|
456
|
+
type: object
|
|
457
|
+
required: [content]
|
|
458
|
+
properties:
|
|
459
|
+
content: { type: string, description: 'The message for the subagent (untrusted DATA; the server fences it β same framing as the workflow-agent steer).' }
|
|
460
|
+
responses:
|
|
461
|
+
'200':
|
|
462
|
+
description: >
|
|
463
|
+
Queued/applied. Body `{ taskId, target, status, delivery, note }` β `note` carries the
|
|
464
|
+
CC-verbatim "Message queued for delivery to <name> at its next tool round." receipt copy.
|
|
465
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
466
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
467
|
+
'409':
|
|
468
|
+
description: 'errorCode "steering.not_running" β the run/subagent is settled or unknown (also: ambiguous agentName).'
|
|
469
|
+
content:
|
|
470
|
+
application/json:
|
|
471
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
472
|
+
|
|
473
|
+
/v1/sessions:
|
|
474
|
+
parameters:
|
|
475
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
476
|
+
get:
|
|
477
|
+
tags: [sessions]
|
|
478
|
+
operationId: sessionsList
|
|
479
|
+
x-status: live # E16, service 6cbf61e (durable session store).
|
|
480
|
+
summary: List the caller's sessions (keyset page).
|
|
481
|
+
description: >
|
|
482
|
+
E16 (shell-host sessionPicker) β owner-scoped keyset page of the caller's sessions; each row aggregates a
|
|
483
|
+
session's run ledger (last activity + most-recent objective preview + status). `owner` is honored only for
|
|
484
|
+
a fleet-wide credential (a principal caller is force-scoped to itself). 501 without a durable session store
|
|
485
|
+
(SESSION_BACKEND=tidb) β gate the picker off `capabilities()` instead of trial-by-501.
|
|
486
|
+
parameters:
|
|
487
|
+
- in: query
|
|
488
|
+
name: limit
|
|
489
|
+
schema: { type: integer, minimum: 1, maximum: 100, default: 50 }
|
|
490
|
+
- in: query
|
|
491
|
+
name: cursor
|
|
492
|
+
schema: { type: string }
|
|
493
|
+
description: Opaque keyset cursor from a previous page's `nextCursor` (echo verbatim). Absent = first page.
|
|
494
|
+
- in: query
|
|
495
|
+
name: owner
|
|
496
|
+
schema: { type: string }
|
|
497
|
+
description: Fleet-wide credential only β scope the page to a tenant. Ignored for a principal caller.
|
|
498
|
+
responses:
|
|
499
|
+
'200':
|
|
500
|
+
description: Keyset page of session summaries.
|
|
501
|
+
content:
|
|
502
|
+
application/json:
|
|
503
|
+
schema: { $ref: '#/components/schemas/SessionListPage' }
|
|
504
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
505
|
+
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
506
|
+
|
|
507
|
+
/v1/sessions/{sessionId}:
|
|
508
|
+
parameters:
|
|
509
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
510
|
+
- in: path
|
|
511
|
+
name: sessionId
|
|
512
|
+
required: true
|
|
513
|
+
schema: { type: string }
|
|
514
|
+
get:
|
|
515
|
+
tags: [sessions]
|
|
516
|
+
operationId: sessionsGet
|
|
517
|
+
x-status: live # true shape from source.
|
|
518
|
+
summary: Session audit (current context + owner + compaction state).
|
|
519
|
+
description: >
|
|
520
|
+
Owner-checked read of a durable session (same semantics as runs: non-owner β 404). `messages` is the
|
|
521
|
+
CURRENT model context (older history already folded into a summary message) β can be LARGE, the portal
|
|
522
|
+
should lazy-expand. Absent entirely on workers without the TiDB pool (capabilities.asyncRuns is the
|
|
523
|
+
practical same-prereq signal).
|
|
524
|
+
responses:
|
|
525
|
+
'200':
|
|
526
|
+
description: Session record.
|
|
527
|
+
content:
|
|
528
|
+
application/json:
|
|
529
|
+
schema: { $ref: '#/components/schemas/SessionRecord' }
|
|
530
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
531
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
532
|
+
delete:
|
|
533
|
+
tags: [sessions]
|
|
534
|
+
operationId: sessionsDelete
|
|
535
|
+
x-status: live # E21, service durable session store.
|
|
536
|
+
summary: Purge a session's history + run ledger (idempotent).
|
|
537
|
+
description: >
|
|
538
|
+
E21 (shell-host) β purge the session's conversation history + service-owned run-ledger/checkpoint/tool-result
|
|
539
|
+
rows. Owner-scoped (non-owner β 404). Idempotent (`{deleted:false}` when nothing existed). π΄ 409 while a run
|
|
540
|
+
is LIVE on the session (`activeTaskId` in the body) β cancel it first so the session lock isn't orphaned.
|
|
541
|
+
501 without a durable session store.
|
|
542
|
+
responses:
|
|
543
|
+
'200':
|
|
544
|
+
description: Delete result (idempotent).
|
|
545
|
+
content:
|
|
546
|
+
application/json:
|
|
547
|
+
schema:
|
|
548
|
+
type: object
|
|
549
|
+
required: [deleted]
|
|
550
|
+
properties:
|
|
551
|
+
deleted: { type: boolean }
|
|
552
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
553
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
554
|
+
'409': { $ref: '#/components/responses/Conflict' }
|
|
555
|
+
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
556
|
+
|
|
557
|
+
/v1/sessions/{sessionId}/fork:
|
|
558
|
+
parameters:
|
|
559
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
560
|
+
- in: path
|
|
561
|
+
name: sessionId
|
|
562
|
+
required: true
|
|
563
|
+
schema: { type: string }
|
|
564
|
+
post:
|
|
565
|
+
tags: [sessions]
|
|
566
|
+
operationId: sessionsFork
|
|
567
|
+
x-status: live # E17, service durable session store.
|
|
568
|
+
summary: Branch a NEW session off this one's whole history.
|
|
569
|
+
description: >
|
|
570
|
+
E17 (shell-host) β branch a fresh session off the source's WHOLE history (a "branch this conversation" /
|
|
571
|
+
regenerate-from-here). Owner-scoped (non-owner β 404; the fork is owned by the caller). 400 on a non-uuidv7
|
|
572
|
+
id. 501 without a durable session store.
|
|
573
|
+
responses:
|
|
574
|
+
'200':
|
|
575
|
+
description: The new branched session id.
|
|
576
|
+
content:
|
|
577
|
+
application/json:
|
|
578
|
+
schema:
|
|
579
|
+
type: object
|
|
580
|
+
required: [sessionId]
|
|
581
|
+
properties:
|
|
582
|
+
sessionId: { type: string }
|
|
583
|
+
'400': { $ref: '#/components/responses/BadRequest' }
|
|
584
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
585
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
586
|
+
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
587
|
+
|
|
588
|
+
/v1/sessions/{sessionId}/policy:
|
|
589
|
+
parameters:
|
|
590
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
591
|
+
- in: path
|
|
592
|
+
name: sessionId
|
|
593
|
+
required: true
|
|
594
|
+
schema: { type: string }
|
|
595
|
+
get:
|
|
596
|
+
tags: [sessions]
|
|
597
|
+
operationId: sessionsGetPolicy
|
|
598
|
+
x-status: live # E6, service + core session-policy store.
|
|
599
|
+
summary: Read the per-session tool-permission rules (permissionMode).
|
|
600
|
+
description: >
|
|
601
|
+
E6 (shell-host permissionMode) β the operator-tightened per-session tool-permission rules core reads at
|
|
602
|
+
prepare-time (SUBTRACT-only). Owner-scoped (non-owner β 404); ownerless session β 409. Read-only snapshot
|
|
603
|
+
(enforcement is 100% core-side). 501 without a session-policy store.
|
|
604
|
+
responses:
|
|
605
|
+
'200':
|
|
606
|
+
description: The stored rules (incl. the OCC `rev`).
|
|
607
|
+
content:
|
|
608
|
+
application/json:
|
|
609
|
+
schema:
|
|
610
|
+
type: object
|
|
611
|
+
required: [rules]
|
|
612
|
+
properties:
|
|
613
|
+
rules: { $ref: '#/components/schemas/StoredSessionRules' }
|
|
614
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
615
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
616
|
+
'409': { $ref: '#/components/responses/Conflict' }
|
|
617
|
+
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
618
|
+
put:
|
|
619
|
+
tags: [sessions]
|
|
620
|
+
operationId: sessionsPutPolicy
|
|
621
|
+
x-status: live
|
|
622
|
+
summary: Write the per-session tool-permission rules (tighten-only).
|
|
623
|
+
description: >
|
|
624
|
+
E6 β write the per-session rules. π΄ TIGHTEN-ONLY for a normal principal: a loosen (remove a deny / widen
|
|
625
|
+
an allowlist or allowDirs) β 403 `loosen_forbidden` (operator-only). `expectedRev` = optimistic-concurrency
|
|
626
|
+
CAS; a stale rev β 409 `conflict`. Each rule field must be `string[]` β malformed body 400. ownerless
|
|
627
|
+
session β 409; non-owner β 404. 501 without a session-policy store.
|
|
628
|
+
requestBody:
|
|
629
|
+
required: true
|
|
630
|
+
content:
|
|
631
|
+
application/json:
|
|
632
|
+
schema:
|
|
633
|
+
type: object
|
|
634
|
+
required: [rules]
|
|
635
|
+
properties:
|
|
636
|
+
rules: { $ref: '#/components/schemas/SessionPermissionRules' }
|
|
637
|
+
expectedRev: { type: integer, minimum: 0, description: CAS β reject if the stored rev differs. }
|
|
638
|
+
responses:
|
|
639
|
+
'200':
|
|
640
|
+
description: The stored rules with the new `rev`.
|
|
641
|
+
content:
|
|
642
|
+
application/json:
|
|
643
|
+
schema:
|
|
644
|
+
type: object
|
|
645
|
+
required: [rules]
|
|
646
|
+
properties:
|
|
647
|
+
rules: { $ref: '#/components/schemas/StoredSessionRules' }
|
|
648
|
+
'400': { $ref: '#/components/responses/BadRequest' }
|
|
649
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
650
|
+
'403':
|
|
651
|
+
description: 403 `loosen_forbidden` β a non-operator write tried to relax the rules (tighten-only).
|
|
652
|
+
content:
|
|
653
|
+
application/json:
|
|
654
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
655
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
656
|
+
'409': { $ref: '#/components/responses/Conflict' }
|
|
657
|
+
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
658
|
+
|
|
659
|
+
/v1/sessions/{sessionId}/mcp:
|
|
660
|
+
parameters:
|
|
661
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
662
|
+
- in: path
|
|
663
|
+
name: sessionId
|
|
664
|
+
required: true
|
|
665
|
+
schema: { type: string }
|
|
666
|
+
get:
|
|
667
|
+
tags: [sessions]
|
|
668
|
+
operationId: sessionsMcp
|
|
669
|
+
x-status: live # E9, service + core 1.124.
|
|
670
|
+
summary: Per-server MCP status (materialization-time projection).
|
|
671
|
+
description: >
|
|
672
|
+
E9 (shell-host mcpStatus) β per-server MCP status for the `/mcp` panel. π΄ MATERIALIZATION-TIME, not live
|
|
673
|
+
session health: core never persists MCP connections, so the service fresh-connectsβlistsβdisposes the
|
|
674
|
+
worker's configured MCP servers on demand and projects their status (`asOf` = that moment; render "as of
|
|
675
|
+
<asOf>"). `degraded:true` (servers empty) β materialize timed out/failed (NOT "no MCP"). No MCP configured
|
|
676
|
+
β `servers:[]` without `degraded`.
|
|
677
|
+
responses:
|
|
678
|
+
'200':
|
|
679
|
+
description: The MCP status panel.
|
|
680
|
+
content:
|
|
681
|
+
application/json:
|
|
682
|
+
schema: { $ref: '#/components/schemas/McpStatusPanel' }
|
|
683
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
684
|
+
|
|
685
|
+
/v1/sessions/{sessionId}/memory:
|
|
686
|
+
parameters:
|
|
687
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
688
|
+
- in: path
|
|
689
|
+
name: sessionId
|
|
690
|
+
required: true
|
|
691
|
+
schema: { type: string }
|
|
692
|
+
post:
|
|
693
|
+
tags: [sessions]
|
|
694
|
+
operationId: memoryAppend
|
|
695
|
+
x-status: live # MF-30, service @sema-agent/server 1.3.0 (server.ts:2880).
|
|
696
|
+
summary: Append a memory note (MF-30 β the /memory write verb).
|
|
697
|
+
description: >
|
|
698
|
+
π΄ Memory is PER-PRINCIPAL, not per-session β {sessionId} is only the shell's addressing context (the
|
|
699
|
+
/memory command runs inside some session); a note appended for a principal is visible from ALL that
|
|
700
|
+
principal's sessions. Note stored VERBATIM (β€8 KiB, user-authored = intentional, NOT redacted).
|
|
701
|
+
Empty/oversized β 400. Always available when capabilities().memoryWrite.
|
|
702
|
+
requestBody:
|
|
703
|
+
required: true
|
|
704
|
+
content:
|
|
705
|
+
application/json:
|
|
706
|
+
schema:
|
|
707
|
+
type: object
|
|
708
|
+
required: [content]
|
|
709
|
+
properties: { content: { type: string } }
|
|
710
|
+
responses:
|
|
711
|
+
'200':
|
|
712
|
+
description: Append ack β unified { ok, id, scope } (service; handler always returns ok).
|
|
713
|
+
content:
|
|
714
|
+
application/json:
|
|
715
|
+
schema: { $ref: '#/components/schemas/MemoryWriteAck' }
|
|
716
|
+
'400': { description: "Empty / oversized / missing content." }
|
|
717
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
718
|
+
|
|
719
|
+
/v1/sessions/{sessionId}/memory/{noteId}:
|
|
720
|
+
parameters:
|
|
721
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
722
|
+
- in: path
|
|
723
|
+
name: sessionId
|
|
724
|
+
required: true
|
|
725
|
+
schema: { type: string }
|
|
726
|
+
- in: path
|
|
727
|
+
name: noteId
|
|
728
|
+
required: true
|
|
729
|
+
schema: { type: string }
|
|
730
|
+
patch:
|
|
731
|
+
tags: [sessions]
|
|
732
|
+
operationId: memoryEdit
|
|
733
|
+
x-status: live # MF-30 (server.ts:2881).
|
|
734
|
+
summary: Edit one memory note by id (id unchanged).
|
|
735
|
+
description: >
|
|
736
|
+
π΄ 501 when the backend lacks id-addressable update (honest degrade, never a fake 200). Per-PRINCIPAL.
|
|
737
|
+
requestBody:
|
|
738
|
+
required: true
|
|
739
|
+
content:
|
|
740
|
+
application/json:
|
|
741
|
+
schema:
|
|
742
|
+
type: object
|
|
743
|
+
required: [content]
|
|
744
|
+
properties: { content: { type: string } }
|
|
745
|
+
responses:
|
|
746
|
+
'200':
|
|
747
|
+
description: Edited note ack.
|
|
748
|
+
content:
|
|
749
|
+
application/json:
|
|
750
|
+
schema: { $ref: '#/components/schemas/MemoryWriteAck' }
|
|
751
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
752
|
+
'501': { description: "Backend lacks id-addressable update." }
|
|
753
|
+
delete:
|
|
754
|
+
tags: [sessions]
|
|
755
|
+
operationId: memoryForget
|
|
756
|
+
x-status: live # MF-30 (server.ts:2882).
|
|
757
|
+
summary: Forget one memory note by id.
|
|
758
|
+
description: >
|
|
759
|
+
π΄ 501 when the backend lacks id-addressable delete (honest degrade). Per-PRINCIPAL.
|
|
760
|
+
responses:
|
|
761
|
+
'200':
|
|
762
|
+
description: Forgotten note ack.
|
|
763
|
+
content:
|
|
764
|
+
application/json:
|
|
765
|
+
schema: { $ref: '#/components/schemas/MemoryWriteAck' }
|
|
766
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
767
|
+
'501': { description: "Backend lacks id-addressable delete." }
|
|
768
|
+
|
|
769
|
+
# ββ 2c session-sync (P1d) β the cloud as a SYNC PEER ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
770
|
+
# The local shell's local backend β this cloud service. PULL a session's whole state out, or PUSH the local
|
|
771
|
+
# peer's in. Gate the whole surface off `capabilities.sessionSync` (= durable backend + entry export + a
|
|
772
|
+
# file-snapshot store). Owner-gated like fork/delete (Β§9): a PULL 404s a non-owner; a PUSH is own-or-FRESH.
|
|
773
|
+
/v1/sessions/{sessionId}/sync/manifest:
|
|
774
|
+
parameters:
|
|
775
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
776
|
+
- in: path
|
|
777
|
+
name: sessionId
|
|
778
|
+
required: true
|
|
779
|
+
schema: { type: string }
|
|
780
|
+
get:
|
|
781
|
+
tags: [sessions]
|
|
782
|
+
operationId: sessionSyncManifest
|
|
783
|
+
x-status: live # 2c P1d, service @sema-agent/server@1.2.0 (LIVE pending worker 1.2.0 deploy).
|
|
784
|
+
summary: PULL the session manifest (thin cross-backend snapshot).
|
|
785
|
+
description: >
|
|
786
|
+
2c session-sync PULL β the THIN snapshot: entry IDS (oldest-first, NOT payloads β those stream via
|
|
787
|
+
`/sync/entries`) + per-snapshot relPathβblobHash + policy + anchors + leaf. The local peer feeds `entryIds`
|
|
788
|
+
to the Β§7 classifier to decide fast-forward/fork BEFORE pulling the entry stream + only the blob hashes it
|
|
789
|
+
lacks. Owner-scoped (non-owner β 404, no oracle); 400 on a non-uuidv7 id; 501 without a durable session store.
|
|
790
|
+
responses:
|
|
791
|
+
'200':
|
|
792
|
+
description: The manifest, wrapped as `{ manifest }`.
|
|
793
|
+
content:
|
|
794
|
+
application/json:
|
|
795
|
+
schema:
|
|
796
|
+
type: object
|
|
797
|
+
required: [manifest]
|
|
798
|
+
properties:
|
|
799
|
+
manifest: { $ref: '#/components/schemas/SessionManifest' }
|
|
800
|
+
'400': { $ref: '#/components/responses/BadRequest' }
|
|
801
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
802
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
803
|
+
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
804
|
+
|
|
805
|
+
/v1/sessions/{sessionId}/sync/entries:
|
|
806
|
+
parameters:
|
|
807
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
808
|
+
- in: path
|
|
809
|
+
name: sessionId
|
|
810
|
+
required: true
|
|
811
|
+
schema: { type: string }
|
|
812
|
+
- in: query
|
|
813
|
+
name: afterSeq
|
|
814
|
+
required: false
|
|
815
|
+
schema: { type: integer, minimum: 0 }
|
|
816
|
+
description: >
|
|
817
|
+
Resume cursor = the backend-native durable `seq` (yield entries whose seq > afterSeq). SAME-BACKEND only.
|
|
818
|
+
A non-integer / negative value β 400.
|
|
819
|
+
get:
|
|
820
|
+
tags: [sessions]
|
|
821
|
+
operationId: sessionSyncEntries
|
|
822
|
+
x-status: live
|
|
823
|
+
summary: PULL the entry payloads as an NDJSON stream.
|
|
824
|
+
description: >
|
|
825
|
+
2c session-sync PULL β the entry PAYLOADS as `application/x-ndjson`: a `{"__sync":"begin",sessionId}` header
|
|
826
|
+
line, then ONE entry JSON per line, then a `{"__sync":"end",count}` TRAILER line. π΄ The status is committed
|
|
827
|
+
at 200 BEFORE the first line, so a mid-stream failure ends the body WITHOUT the trailer β the trailer's
|
|
828
|
+
ABSENCE (or a count mismatch) is the SOLE truncation signal (NO 5xx mid-stream). The SDK reader throws
|
|
829
|
+
`SyncTruncatedError` on a missing trailer / count mismatch. Owner-scoped (404); 400 on a bad afterSeq /
|
|
830
|
+
non-uuidv7 id; 501 without a durable entry-streaming store.
|
|
831
|
+
responses:
|
|
832
|
+
'200':
|
|
833
|
+
description: The NDJSON entry stream (begin / entryΓ N / end-trailer).
|
|
834
|
+
content:
|
|
835
|
+
application/x-ndjson:
|
|
836
|
+
schema: { type: string }
|
|
837
|
+
'400': { $ref: '#/components/responses/BadRequest' }
|
|
838
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
839
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
840
|
+
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
841
|
+
|
|
842
|
+
/v1/sessions/{sessionId}/sync/snapshots/{key}/blobs/{hash}:
|
|
843
|
+
parameters:
|
|
844
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
845
|
+
- in: path
|
|
846
|
+
name: sessionId
|
|
847
|
+
required: true
|
|
848
|
+
schema: { type: string }
|
|
849
|
+
- in: path
|
|
850
|
+
name: key
|
|
851
|
+
required: true
|
|
852
|
+
schema: { type: string }
|
|
853
|
+
description: The snapshot key (= a `SessionTreeEntry.id`).
|
|
854
|
+
- in: path
|
|
855
|
+
name: hash
|
|
856
|
+
required: true
|
|
857
|
+
schema: { type: string, pattern: '^[0-9a-f]{64}$' }
|
|
858
|
+
description: The content-address (SHA-256, 64 lowercase-hex).
|
|
859
|
+
get:
|
|
860
|
+
tags: [sessions]
|
|
861
|
+
operationId: sessionSyncGetBlob
|
|
862
|
+
x-status: live
|
|
863
|
+
summary: PULL one content-addressed blob's raw bytes (scope-checked).
|
|
864
|
+
description: >
|
|
865
|
+
2c session-sync PULL β one content-addressed blob's RAW bytes (`application/octet-stream`). π΄ SCOPE BOUNDARY:
|
|
866
|
+
a content-addressed blob is shared across sessions, so the cloud serves a hash ONLY if THIS session's `:key`
|
|
867
|
+
manifest references it (a foreign / unreferenced hash β 404 `blob not found`, never a blind read). 400 on a
|
|
868
|
+
non-64-hex hash or an undecodable key/hash. Owner-scoped (404).
|
|
869
|
+
responses:
|
|
870
|
+
'200':
|
|
871
|
+
description: The blob bytes.
|
|
872
|
+
content:
|
|
873
|
+
application/octet-stream:
|
|
874
|
+
schema: { type: string, format: binary }
|
|
875
|
+
'400': { $ref: '#/components/responses/BadRequest' }
|
|
876
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
877
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
878
|
+
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
879
|
+
|
|
880
|
+
/v1/sessions/{sessionId}/sync/blobs/{hash}:
|
|
881
|
+
parameters:
|
|
882
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
883
|
+
- in: path
|
|
884
|
+
name: sessionId
|
|
885
|
+
required: true
|
|
886
|
+
schema: { type: string }
|
|
887
|
+
- in: path
|
|
888
|
+
name: hash
|
|
889
|
+
required: true
|
|
890
|
+
schema: { type: string, pattern: '^[0-9a-f]{64}$' }
|
|
891
|
+
put:
|
|
892
|
+
tags: [sessions]
|
|
893
|
+
operationId: sessionSyncPutBlob
|
|
894
|
+
x-status: live
|
|
895
|
+
summary: PUSH one content-addressed blob (own-or-fresh, integrity-checked).
|
|
896
|
+
description: >
|
|
897
|
+
2c session-sync PUSH β store one content-addressed blob's RAW bytes ahead of import (`application/octet-stream`
|
|
898
|
+
body). The cloud verifies `sha256(body) === :hash` (the content-address integrity guard) BEFORE storing β a
|
|
899
|
+
mismatch β 400 `blob hash mismatch`. own-or-FRESH gate. Per-blob cap 64 MiB β 413 mid-stream beyond it. A
|
|
900
|
+
backend write failure (e.g. a MinIO non-2xx) β 502 `blob store write failed`. 204 on success (no body). The
|
|
901
|
+
PUSH path is the BARE `blobs/:hash` (the PULL is scoped under `snapshots/:key`).
|
|
902
|
+
requestBody:
|
|
903
|
+
required: true
|
|
904
|
+
content:
|
|
905
|
+
application/octet-stream:
|
|
906
|
+
schema: { type: string, format: binary }
|
|
907
|
+
responses:
|
|
908
|
+
'204': { description: Stored (no body). }
|
|
909
|
+
'400': { $ref: '#/components/responses/BadRequest' }
|
|
910
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
911
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
912
|
+
'413': { $ref: '#/components/responses/PayloadTooLarge' }
|
|
913
|
+
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
914
|
+
'502':
|
|
915
|
+
description: 502 `blob store write failed` β the backend (MinIO/β¦) rejected the write. Re-PUT (idempotent).
|
|
916
|
+
content:
|
|
917
|
+
application/json:
|
|
918
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
919
|
+
|
|
920
|
+
/v1/sessions/{sessionId}/sync/plan:
|
|
921
|
+
parameters:
|
|
922
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
923
|
+
- in: path
|
|
924
|
+
name: sessionId
|
|
925
|
+
required: true
|
|
926
|
+
schema: { type: string }
|
|
927
|
+
post:
|
|
928
|
+
tags: [sessions]
|
|
929
|
+
operationId: sessionSyncPlan
|
|
930
|
+
x-status: live
|
|
931
|
+
summary: DRY-RUN β classify what a PUSH would do (no write).
|
|
932
|
+
description: >
|
|
933
|
+
2c session-sync PLAN β classify the Β§7 relation between the LOCAL peer's `entryIds` (oldest-first, the SOURCE)
|
|
934
|
+
and the cloud's current log (the DST), WITHOUT writing. Returns the `SyncRelation` (`fresh` for a fresh cloud
|
|
935
|
+
session). own-or-FRESH gate; 400 on a non-string-array body / non-uuidv7 id. Unlike import, plan NEVER 409s β
|
|
936
|
+
a fork/stale is REPORTED in the relation, not raised.
|
|
937
|
+
requestBody:
|
|
938
|
+
required: true
|
|
939
|
+
content:
|
|
940
|
+
application/json:
|
|
941
|
+
schema:
|
|
942
|
+
type: object
|
|
943
|
+
required: [entryIds]
|
|
944
|
+
properties:
|
|
945
|
+
entryIds: { type: array, items: { type: string } }
|
|
946
|
+
responses:
|
|
947
|
+
'200':
|
|
948
|
+
description: The classified relation.
|
|
949
|
+
content:
|
|
950
|
+
application/json:
|
|
951
|
+
schema: { $ref: '#/components/schemas/SyncRelation' }
|
|
952
|
+
'400': { $ref: '#/components/responses/BadRequest' }
|
|
953
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
954
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
955
|
+
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
956
|
+
|
|
957
|
+
/v1/sessions/{sessionId}/sync/import:
|
|
958
|
+
parameters:
|
|
959
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
960
|
+
- in: path
|
|
961
|
+
name: sessionId
|
|
962
|
+
required: true
|
|
963
|
+
schema: { type: string }
|
|
964
|
+
post:
|
|
965
|
+
tags: [sessions]
|
|
966
|
+
operationId: sessionSyncImport
|
|
967
|
+
x-status: live
|
|
968
|
+
summary: PUSH Phase A β classify + stage (mint a stagingId).
|
|
969
|
+
description: >
|
|
970
|
+
2c session-sync PUSH Phase A β a SMALL metadata body (`entryIds, snapshots, policy, anchors, resolution?`); the
|
|
971
|
+
entries stream in Phase B. The cloud classifies (Β§7), pre-checks blob presence (a missing referenced hash β
|
|
972
|
+
422 `missing_blob` β PUT it first), guards the import lease + any active run (409), then mints a stagingId.
|
|
973
|
+
`identical` β `{ relation:"identical" }` with NO stagingId (skip Phase B); `fresh`/`fast_forward` β
|
|
974
|
+
`{ stagingId, relation }`. π΄ a `fork`/`stale` without `resolution:"overwrite-dst"` β 409
|
|
975
|
+
`conflict` carrying the `relation` (the SDK surfaces SyncConflictError).
|
|
976
|
+
requestBody:
|
|
977
|
+
required: true
|
|
978
|
+
content:
|
|
979
|
+
application/json:
|
|
980
|
+
schema:
|
|
981
|
+
type: object
|
|
982
|
+
required: [entryIds, snapshots, policy, anchors]
|
|
983
|
+
properties:
|
|
984
|
+
entryIds: { type: array, items: { type: string } }
|
|
985
|
+
snapshots: { type: array, items: { $ref: '#/components/schemas/SyncSnapshot' } }
|
|
986
|
+
policy: { type: array, items: { $ref: '#/components/schemas/SessionRulesRecord' } }
|
|
987
|
+
anchors: { type: array, items: { $ref: '#/components/schemas/SyncAnchor' } }
|
|
988
|
+
resolution: { type: string, enum: [overwrite-dst], description: Override a fork/stale loss-of-history conflict. }
|
|
989
|
+
responses:
|
|
990
|
+
'200':
|
|
991
|
+
description: Staged (or `identical` no-op).
|
|
992
|
+
content:
|
|
993
|
+
application/json:
|
|
994
|
+
schema: { $ref: '#/components/schemas/ImportStaged' }
|
|
995
|
+
'400': { $ref: '#/components/responses/BadRequest' }
|
|
996
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
997
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
998
|
+
'409':
|
|
999
|
+
description: >
|
|
1000
|
+
409 `conflict` β a `fork`/`stale` PUSH without `resolution:"overwrite-dst"` (carries `relation`; the SDK
|
|
1001
|
+
surfaces SyncConflictError), OR an active run (`session_active`, `activeTaskId`) / an in-flight import
|
|
1002
|
+
(`import_in_flight`) on the session (ConflictError).
|
|
1003
|
+
content:
|
|
1004
|
+
application/json:
|
|
1005
|
+
schema: { $ref: '#/components/schemas/SyncConflict' }
|
|
1006
|
+
'422': { $ref: '#/components/responses/UnprocessableEntity' }
|
|
1007
|
+
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
1008
|
+
|
|
1009
|
+
/v1/sessions/{sessionId}/sync/import/{stagingId}/entries:
|
|
1010
|
+
parameters:
|
|
1011
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
1012
|
+
- in: path
|
|
1013
|
+
name: sessionId
|
|
1014
|
+
required: true
|
|
1015
|
+
schema: { type: string }
|
|
1016
|
+
- in: path
|
|
1017
|
+
name: stagingId
|
|
1018
|
+
required: true
|
|
1019
|
+
schema: { type: string }
|
|
1020
|
+
description: The token minted by Phase A.
|
|
1021
|
+
post:
|
|
1022
|
+
tags: [sessions]
|
|
1023
|
+
operationId: sessionSyncImportEntries
|
|
1024
|
+
x-status: live
|
|
1025
|
+
summary: PUSH Phase B β stream the entry log (NDJSON) β commit.
|
|
1026
|
+
description: >
|
|
1027
|
+
2c session-sync PUSH Phase B β stream the entry log as an NDJSON REQUEST body into the staged shadow id; the
|
|
1028
|
+
cloud validates per-line (the streaming invariant gate) then `finish()` (exactly-one-root + leaf-resolvable) β
|
|
1029
|
+
commits (the atomic swap) β replays snapshots/policy/anchors. Returns `{ relation }` (the AUTHORITATIVE in-txn
|
|
1030
|
+
re-classify). Re-POSTing the same stagingId is idempotent. Typed errors: a per-entry / structural violation β
|
|
1031
|
+
422 `invalid_entries`; a line too large β 413; a commit-time TOCTOU flip to fork/stale β 409 `conflict`
|
|
1032
|
+
(SyncConflictError); a refused policy loosen β 403 `loosen_forbidden`; a wrong/foreign stagingId β 404.
|
|
1033
|
+
requestBody:
|
|
1034
|
+
required: true
|
|
1035
|
+
content:
|
|
1036
|
+
application/x-ndjson:
|
|
1037
|
+
schema: { type: string, description: One entry JSON per line. }
|
|
1038
|
+
responses:
|
|
1039
|
+
'200':
|
|
1040
|
+
description: Committed.
|
|
1041
|
+
content:
|
|
1042
|
+
application/json:
|
|
1043
|
+
schema: { $ref: '#/components/schemas/ImportCommitted' }
|
|
1044
|
+
'400': { $ref: '#/components/responses/BadRequest' }
|
|
1045
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1046
|
+
'403':
|
|
1047
|
+
description: 403 `loosen_forbidden` β a post-commit policy replay tried to relax the rules (tighten-only).
|
|
1048
|
+
content:
|
|
1049
|
+
application/json:
|
|
1050
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
1051
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
1052
|
+
'409':
|
|
1053
|
+
description: 409 `conflict` β a commit-time TOCTOU re-classify flipped to fork/stale (carries `relation`).
|
|
1054
|
+
content:
|
|
1055
|
+
application/json:
|
|
1056
|
+
schema: { $ref: '#/components/schemas/SyncConflict' }
|
|
1057
|
+
'413': { $ref: '#/components/responses/PayloadTooLarge' }
|
|
1058
|
+
'422': { $ref: '#/components/responses/UnprocessableEntity' }
|
|
1059
|
+
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
1060
|
+
|
|
1061
|
+
/v1/approvals:
|
|
1062
|
+
parameters:
|
|
1063
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
1064
|
+
get:
|
|
1065
|
+
tags: [approvals]
|
|
1066
|
+
operationId: approvalsList
|
|
1067
|
+
x-status: live # true shape from source. NEVER returns a capability token.
|
|
1068
|
+
summary: List pending HITL checkpoints (operator-scoped).
|
|
1069
|
+
description: >
|
|
1070
|
+
Pending approvals/questions for runs that went `suspended` (an F4 policy gate or an AskUserQuestion;
|
|
1071
|
+
AskUserQuestion appears as the question gate's toolName). Envelope key = `pending` (NOT a bare array),
|
|
1072
|
+
ordered by created_at asc. Operator view sees all tenants; `scope` is enforced in the decide CAS.
|
|
1073
|
+
NEVER returns the capability token (secret discipline).
|
|
1074
|
+
responses:
|
|
1075
|
+
'200':
|
|
1076
|
+
description: Pending checkpoints.
|
|
1077
|
+
content:
|
|
1078
|
+
application/json:
|
|
1079
|
+
schema: { $ref: '#/components/schemas/PendingList' }
|
|
1080
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1081
|
+
|
|
1082
|
+
/v1/approvals/{sessionId}/decide:
|
|
1083
|
+
parameters:
|
|
1084
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
1085
|
+
- in: path
|
|
1086
|
+
name: sessionId
|
|
1087
|
+
required: true
|
|
1088
|
+
schema: { type: string }
|
|
1089
|
+
post:
|
|
1090
|
+
tags: [approvals]
|
|
1091
|
+
operationId: approvalsDecide
|
|
1092
|
+
x-status: live
|
|
1093
|
+
summary: Resolve a suspend β approve/deny or answer.
|
|
1094
|
+
description: >
|
|
1095
|
+
Resolves a `suspended` run: an F4 approve/deny, or an answer to an AskUserQuestion. The resumed run
|
|
1096
|
+
continues its durable stream (`/v1/runs/:id/events`). design/80 D-1: the decision SHOULD carry the
|
|
1097
|
+
action binding (checkpointToken/boundCallId/boundInputHash read off the PendingCheckpoint) β the server
|
|
1098
|
+
resolves THAT exact checkpoint, fail-closed on mismatch. (Path param stays `sessionId` until service
|
|
1099
|
+
finalizes a checkpointToken-keyed route; the binding travels in the body regardless.)
|
|
1100
|
+
requestBody:
|
|
1101
|
+
required: true
|
|
1102
|
+
content:
|
|
1103
|
+
application/json:
|
|
1104
|
+
schema: { $ref: '#/components/schemas/ApprovalDecision' }
|
|
1105
|
+
responses:
|
|
1106
|
+
'200':
|
|
1107
|
+
description: Decision accepted; the run resumes.
|
|
1108
|
+
content:
|
|
1109
|
+
application/json:
|
|
1110
|
+
schema: { type: object, additionalProperties: true }
|
|
1111
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1112
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
1113
|
+
'409':
|
|
1114
|
+
description: >
|
|
1115
|
+
Either a session-CAS conflict (`ErrorResponse.activeTaskId`), OR a design/80 D-1
|
|
1116
|
+
`approval_binding_mismatch` (`errorCode`, `field`: boundCallId|boundInputHash) β your binding does
|
|
1117
|
+
NOT match the current pending action (TOCTOU/swap). The SDK splits these by `errorCode` into
|
|
1118
|
+
ConflictError vs ApprovalBindingMismatchError. binding_mismatch = SAFETY signal: refetch + re-present
|
|
1119
|
+
to the human, NEVER auto-retry.
|
|
1120
|
+
content:
|
|
1121
|
+
application/json:
|
|
1122
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
1123
|
+
'410':
|
|
1124
|
+
description: >
|
|
1125
|
+
design/80 D-1 `approval_stale` (`errorCode`, `terminal`: resolved|expired_abort|expired_sla) β the
|
|
1126
|
+
checkpoint is already terminal (someone resolved it / abandonment-TTL GC / SLA resolve-deny). Client
|
|
1127
|
+
action: refetch the inbox; this pending is gone. SDK β ApprovalStaleError.
|
|
1128
|
+
content:
|
|
1129
|
+
application/json:
|
|
1130
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
1131
|
+
|
|
1132
|
+
/v1/approvals/{sessionId}/exemptions:
|
|
1133
|
+
parameters:
|
|
1134
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
1135
|
+
- in: path
|
|
1136
|
+
name: sessionId
|
|
1137
|
+
required: true
|
|
1138
|
+
schema: { type: string }
|
|
1139
|
+
get:
|
|
1140
|
+
tags: [approvals]
|
|
1141
|
+
operationId: approvalsListExemptions
|
|
1142
|
+
x-status: live # service exemptions ruling β server.ts exemptions surface; 501 without a store backend.
|
|
1143
|
+
summary: List the session's approval exemptions ("don't ask again").
|
|
1144
|
+
description: >
|
|
1145
|
+
The session's granted per-tool approval exemptions () β an APPROVAL MEMORY, not a policy loosen
|
|
1146
|
+
(deny/neverAuto always outrank a grant). Envelope key = `exemptions`. OWNER-GATED with a 404 (no
|
|
1147
|
+
existence oracle): a non-owner and an unknown session are indistinguishable. Explicit operators may
|
|
1148
|
+
inspect any session. `toolName` is in CANONICAL tool-name space. GET is list-only (a per-tool GET
|
|
1149
|
+
`/exemptions/:toolName` is 404).
|
|
1150
|
+
responses:
|
|
1151
|
+
'200':
|
|
1152
|
+
description: The session's exemptions.
|
|
1153
|
+
content:
|
|
1154
|
+
application/json:
|
|
1155
|
+
schema: { $ref: '#/components/schemas/ExemptionList' }
|
|
1156
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1157
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
1158
|
+
'501':
|
|
1159
|
+
description: Worker has no exemption store backend (DB_BACKEND / LOCAL lane required).
|
|
1160
|
+
content:
|
|
1161
|
+
application/json:
|
|
1162
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
1163
|
+
|
|
1164
|
+
/v1/approvals/{sessionId}/exemptions/{toolName}:
|
|
1165
|
+
parameters:
|
|
1166
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
1167
|
+
- in: path
|
|
1168
|
+
name: sessionId
|
|
1169
|
+
required: true
|
|
1170
|
+
schema: { type: string }
|
|
1171
|
+
- in: path
|
|
1172
|
+
name: toolName
|
|
1173
|
+
required: true
|
|
1174
|
+
schema: { type: string }
|
|
1175
|
+
description: Canonical tool name (echo `toolName` off the list row; the service canonicalizes).
|
|
1176
|
+
delete:
|
|
1177
|
+
tags: [approvals]
|
|
1178
|
+
operationId: approvalsRevokeExemption
|
|
1179
|
+
x-status: live # service exemptions ruling; 501 without a store backend.
|
|
1180
|
+
summary: Revoke one approval exemption (the tool asks again).
|
|
1181
|
+
description: >
|
|
1182
|
+
Revokes a single (sessionId, toolName) exemption. 200 `{revoked: true}` on success; 404 when
|
|
1183
|
+
there is nothing to revoke OR the caller is not the owner / the session is unknown (deliberately
|
|
1184
|
+
indistinguishable β no existence oracle). The SDK surfaces this as a boolean
|
|
1185
|
+
(`approvals.revokeExemption` β false on 404), matching the store's idempotent revoke.
|
|
1186
|
+
responses:
|
|
1187
|
+
'200':
|
|
1188
|
+
description: Revoked.
|
|
1189
|
+
content:
|
|
1190
|
+
application/json:
|
|
1191
|
+
schema:
|
|
1192
|
+
type: object
|
|
1193
|
+
required: [revoked]
|
|
1194
|
+
properties:
|
|
1195
|
+
revoked: { const: true }
|
|
1196
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1197
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
1198
|
+
'501':
|
|
1199
|
+
description: Worker has no exemption store backend.
|
|
1200
|
+
content:
|
|
1201
|
+
application/json:
|
|
1202
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
1203
|
+
|
|
1204
|
+
/v1/assistant/inbox:
|
|
1205
|
+
parameters:
|
|
1206
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
1207
|
+
- in: query
|
|
1208
|
+
name: owner
|
|
1209
|
+
required: false
|
|
1210
|
+
schema: { type: string }
|
|
1211
|
+
description: >
|
|
1212
|
+
Operator-only β inspect ONE owner's inbox. An ordinary user OMITS it β own scope. Non-owner
|
|
1213
|
+
non-operator β 404 (owner gate, no existence oracle).
|
|
1214
|
+
get:
|
|
1215
|
+
tags: [assistant]
|
|
1216
|
+
operationId: inboxList
|
|
1217
|
+
x-status: live # ASSISTANT-WIRE-CONTRACT Β§2 (server.ts ~1388), LIVE on worker.
|
|
1218
|
+
summary: The single severity-sorted HITL inbox (per-user).
|
|
1219
|
+
description: >
|
|
1220
|
+
ASSISTANT-WIRE-CONTRACT Β§2 β a PER-USER view of pending checkpoints. Each row = a CheckpointSummary
|
|
1221
|
+
(Β§1) + an `objective` (enriched from task ctx; `null` if unavailable). ALREADY severity-sorted by
|
|
1222
|
+
core's `listPending` β the consumer MUST NOT re-sort, and must tolerate `undefined` severity
|
|
1223
|
+
(present only on human/irreversible_ask gates).
|
|
1224
|
+
responses:
|
|
1225
|
+
'200':
|
|
1226
|
+
description: Severity-sorted inbox rows.
|
|
1227
|
+
content:
|
|
1228
|
+
application/json:
|
|
1229
|
+
schema: { $ref: '#/components/schemas/InboxList' }
|
|
1230
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1231
|
+
|
|
1232
|
+
/v1/assistant/tasks:
|
|
1233
|
+
parameters:
|
|
1234
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
1235
|
+
get:
|
|
1236
|
+
tags: [assistant]
|
|
1237
|
+
operationId: assistantTasks
|
|
1238
|
+
x-status: live # ASSISTANT-WIRE-CONTRACT Β§3 (server.ts ~1410), LIVE on worker.
|
|
1239
|
+
summary: The N-task scheduler overview (per-user cross-task).
|
|
1240
|
+
description: >
|
|
1241
|
+
ASSISTANT-WIRE-CONTRACT Β§3 β a PER-USER cross-task view = run-store `listRuns(owner)` joined with the
|
|
1242
|
+
core scheduler seam `listByScope`. Triage-ordered server-side (needs-attention first, then severity
|
|
1243
|
+
DESC, then spend DESC) β DO NOT re-sort. Requires the TiDB run store, else `{ "tasks": [] }` (no 501).
|
|
1244
|
+
β οΈ `createdAt`/`updatedAt` are ISO date-time strings here (unlike inbox/Β§1 epoch-ms).
|
|
1245
|
+
responses:
|
|
1246
|
+
'200':
|
|
1247
|
+
description: Triage-ordered task rows (empty `{tasks:[]}` when no TiDB run store).
|
|
1248
|
+
content:
|
|
1249
|
+
application/json:
|
|
1250
|
+
schema: { $ref: '#/components/schemas/AssistantTaskList' }
|
|
1251
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1252
|
+
|
|
1253
|
+
/v1/assistant/tasks/{taskId}/resume:
|
|
1254
|
+
parameters:
|
|
1255
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
1256
|
+
- $ref: '#/components/parameters/TaskIdPath'
|
|
1257
|
+
post:
|
|
1258
|
+
tags: [assistant]
|
|
1259
|
+
operationId: assistantResume
|
|
1260
|
+
x-status: live # ASSISTANT-WIRE-CONTRACT Β§4b (server.ts ~1490), LIVE on worker.
|
|
1261
|
+
summary: Resume a resource_limit-suspended task (CONTINUE).
|
|
1262
|
+
description: >
|
|
1263
|
+
ASSISTANT-WIRE-CONTRACT Β§4b β resumes a task durably SUSPENDED on a `resource_limit` gate (scheduler
|
|
1264
|
+
preempt OR a budget/turns/walltime slice). NO request body. π΄ GATE-KIND GUARD: a `human`/`policy_ask`
|
|
1265
|
+
gate β 409 `gate_not_resumable` (a "continue" must NEVER bypass a human approval β use `/decide`).
|
|
1266
|
+
Owner-gated (NOT D-G); non-owner β 404. SDK β GateNotResumableError on the 409.
|
|
1267
|
+
responses:
|
|
1268
|
+
'200':
|
|
1269
|
+
description: Resumed; `{status}` (e.g. "completed").
|
|
1270
|
+
content:
|
|
1271
|
+
application/json:
|
|
1272
|
+
schema:
|
|
1273
|
+
type: object
|
|
1274
|
+
required: [status]
|
|
1275
|
+
additionalProperties: true
|
|
1276
|
+
properties:
|
|
1277
|
+
status: { type: string }
|
|
1278
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1279
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
1280
|
+
'409':
|
|
1281
|
+
description: >
|
|
1282
|
+
`errorCode:"gate_not_resumable"` β the gate is `human`/`policy_ask`, NOT `resource_limit`. A
|
|
1283
|
+
"continue" must NEVER bypass a human approval β resolve via `/v1/approvals/:sessionId/decide`.
|
|
1284
|
+
SEMANTIC (wrong-gate), NOT a session-CAS conflict. SDK β GateNotResumableError.
|
|
1285
|
+
content:
|
|
1286
|
+
application/json:
|
|
1287
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
1288
|
+
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
1289
|
+
|
|
1290
|
+
/v1/assistant/tasks/{taskId}/plan_review:
|
|
1291
|
+
parameters:
|
|
1292
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
1293
|
+
- $ref: '#/components/parameters/TaskIdPath'
|
|
1294
|
+
post:
|
|
1295
|
+
tags: [assistant]
|
|
1296
|
+
operationId: assistantPlanReview
|
|
1297
|
+
x-status: live # ASSISTANT-WIRE-CONTRACT Β§4c (server.ts ~1505), LIVE on worker.
|
|
1298
|
+
summary: Resolve a plan_review gate (3-state approve/edit/reject).
|
|
1299
|
+
description: >
|
|
1300
|
+
ASSISTANT-WIRE-CONTRACT Β§4c (design/80 D-B) β resolves a PRE-ACTION `plan_review` gate: the human
|
|
1301
|
+
reviews the PLAN (no bound tool action). A SIBLING of `/resume` (owner-gated via the run store), a
|
|
1302
|
+
DISTINCT wire from `/decide` and from the `needs_review` wire. `approve` β resume with the original
|
|
1303
|
+
plan; `edit` β resume with `editedPlan`; `reject` β resume to handle the rejection. approve/edit/reject
|
|
1304
|
+
all run the model β BILLABLE.
|
|
1305
|
+
requestBody:
|
|
1306
|
+
required: true
|
|
1307
|
+
content:
|
|
1308
|
+
application/json:
|
|
1309
|
+
schema: { $ref: '#/components/schemas/PlanReviewRequest' }
|
|
1310
|
+
responses:
|
|
1311
|
+
'200':
|
|
1312
|
+
description: Resolved; `{status}` (e.g. "completed").
|
|
1313
|
+
content:
|
|
1314
|
+
application/json:
|
|
1315
|
+
schema:
|
|
1316
|
+
type: object
|
|
1317
|
+
required: [status]
|
|
1318
|
+
additionalProperties: true
|
|
1319
|
+
properties:
|
|
1320
|
+
status: { type: string }
|
|
1321
|
+
'400':
|
|
1322
|
+
description: >
|
|
1323
|
+
Bad/missing `decision`; `edit` without a non-empty `editedPlan`; `editedPlan` present on
|
|
1324
|
+
approve/reject (FORBIDDEN); non-string `reason`.
|
|
1325
|
+
content:
|
|
1326
|
+
application/json:
|
|
1327
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
1328
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1329
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
1330
|
+
'409':
|
|
1331
|
+
description: >
|
|
1332
|
+
`errorCode:"gate_not_plan_review"` β a non-`plan_review` gate. A plan decision must NEVER resolve a
|
|
1333
|
+
tool approval / resource_limit suspend (the checkpoint is never consumed). SEMANTIC (wrong-gate),
|
|
1334
|
+
NOT a session-CAS conflict. SDK β GateNotPlanReviewError.
|
|
1335
|
+
content:
|
|
1336
|
+
application/json:
|
|
1337
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
1338
|
+
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
1339
|
+
|
|
1340
|
+
/v1/assistant/tasks/{taskId}/preempt:
|
|
1341
|
+
parameters:
|
|
1342
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
1343
|
+
- $ref: '#/components/parameters/TaskIdPath'
|
|
1344
|
+
post:
|
|
1345
|
+
tags: [assistant]
|
|
1346
|
+
operationId: assistantPreempt
|
|
1347
|
+
x-status: live # ASSISTANT-WIRE-CONTRACT Β§4d (server.ts ~1434), LIVE on worker.
|
|
1348
|
+
summary: Gracefully preempt (YIELD) a task.
|
|
1349
|
+
description: >
|
|
1350
|
+
ASSISTANT-WIRE-CONTRACT Β§4d β durable cross-replica "yield this task" (the OPPOSITE of
|
|
1351
|
+
`/v1/runs/:id/cancel`'s kill). NO body. NOT billable. `202 {status:"preempting"|β¦}`; a terminal task β
|
|
1352
|
+
`202` no-op; an already-`suspended` task β 409; `RESOURCE_SUSPEND` off β 501. Owner-gated; non-owner β 404.
|
|
1353
|
+
responses:
|
|
1354
|
+
'202':
|
|
1355
|
+
description: Preempting (or 202 no-op on a terminal task); `{status}`.
|
|
1356
|
+
content:
|
|
1357
|
+
application/json:
|
|
1358
|
+
schema:
|
|
1359
|
+
type: object
|
|
1360
|
+
required: [status]
|
|
1361
|
+
additionalProperties: true
|
|
1362
|
+
properties:
|
|
1363
|
+
status: { type: string }
|
|
1364
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1365
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
1366
|
+
'409':
|
|
1367
|
+
description: The task is already `suspended` β there is nothing running to yield.
|
|
1368
|
+
content:
|
|
1369
|
+
application/json:
|
|
1370
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
1371
|
+
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
1372
|
+
|
|
1373
|
+
/v1/tasks/{taskId}/turns:
|
|
1374
|
+
parameters:
|
|
1375
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
1376
|
+
- $ref: '#/components/parameters/TaskIdPath'
|
|
1377
|
+
get:
|
|
1378
|
+
tags: [trace]
|
|
1379
|
+
operationId: traceTurns
|
|
1380
|
+
x-status: live # shape confirmed the pinned wire contract.
|
|
1381
|
+
summary: Full per-task turn trace (the workspace action timeline).
|
|
1382
|
+
description: >
|
|
1383
|
+
The full turn-by-turn trace that the workspace renders as the action timeline (tool cards, etc.).
|
|
1384
|
+
Requires a `SESSION_BACKEND=tidb` worker. Turn shape is draft (co-design with the artifacts shape, Q7).
|
|
1385
|
+
responses:
|
|
1386
|
+
'200':
|
|
1387
|
+
description: Turn trace β envelope `{ turns, nextCursor?, retainedFrom }` (key `turns`; item shape draft).
|
|
1388
|
+
content:
|
|
1389
|
+
application/json:
|
|
1390
|
+
schema: { $ref: '#/components/schemas/TurnList' }
|
|
1391
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1392
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
1393
|
+
|
|
1394
|
+
/v1/tasks/source-summary:
|
|
1395
|
+
parameters:
|
|
1396
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
1397
|
+
get:
|
|
1398
|
+
tags: [trace]
|
|
1399
|
+
operationId: traceSourceSummary
|
|
1400
|
+
x-status: live # the pinned wire contract β per-door usage rollup (prove-before-scale data spine).
|
|
1401
|
+
summary: Per-source run counts (door-level usage).
|
|
1402
|
+
parameters:
|
|
1403
|
+
- in: query
|
|
1404
|
+
name: sinceSec
|
|
1405
|
+
required: false
|
|
1406
|
+
schema: { type: integer }
|
|
1407
|
+
responses:
|
|
1408
|
+
'200':
|
|
1409
|
+
description: Rollup rows.
|
|
1410
|
+
content:
|
|
1411
|
+
application/json:
|
|
1412
|
+
schema:
|
|
1413
|
+
type: object
|
|
1414
|
+
required: [rows]
|
|
1415
|
+
properties:
|
|
1416
|
+
rows:
|
|
1417
|
+
type: array
|
|
1418
|
+
items:
|
|
1419
|
+
type: object
|
|
1420
|
+
required: [source, status, count]
|
|
1421
|
+
properties:
|
|
1422
|
+
source: { type: string }
|
|
1423
|
+
status: { type: string }
|
|
1424
|
+
count: { type: integer }
|
|
1425
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1426
|
+
|
|
1427
|
+
/v1/tasks/{taskId}/artifacts:
|
|
1428
|
+
parameters:
|
|
1429
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
1430
|
+
- $ref: '#/components/parameters/TaskIdPath'
|
|
1431
|
+
get:
|
|
1432
|
+
tags: [trace]
|
|
1433
|
+
operationId: traceArtifacts
|
|
1434
|
+
x-status: live # producer shipped (service 2779155 co-signed).
|
|
1435
|
+
summary: Workspace artifacts for a run (projection of the persisted trace).
|
|
1436
|
+
description: >
|
|
1437
|
+
Deterministic projection from persisted tool events β works for ALL historical runs, never drifts from
|
|
1438
|
+
real execution. Job-level aggregation is client-side: `GET /v1/tasks?jobId=X` then fetch per-run
|
|
1439
|
+
artifacts (each carries taskId+jobId). Failed calls / unpaired starts emit nothing.
|
|
1440
|
+
responses:
|
|
1441
|
+
'200':
|
|
1442
|
+
description: Artifacts (possibly empty).
|
|
1443
|
+
content:
|
|
1444
|
+
application/json:
|
|
1445
|
+
schema: { $ref: '#/components/schemas/ArtifactList' }
|
|
1446
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1447
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
1448
|
+
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
1449
|
+
|
|
1450
|
+
/v1/tasks/{taskId}/stream:
|
|
1451
|
+
parameters:
|
|
1452
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
1453
|
+
- $ref: '#/components/parameters/TaskIdPath'
|
|
1454
|
+
get:
|
|
1455
|
+
tags: [trace]
|
|
1456
|
+
operationId: traceStream
|
|
1457
|
+
x-status: draft # trace relay (same source/semantics as runs.events); kept for the workspace live view.
|
|
1458
|
+
summary: Trace relay SSE (same source/semantics as runs.events).
|
|
1459
|
+
parameters:
|
|
1460
|
+
- $ref: '#/components/parameters/LastEventId'
|
|
1461
|
+
responses:
|
|
1462
|
+
'200':
|
|
1463
|
+
description: SSE stream of AgentEvents (resumable; same durable source as runs.events).
|
|
1464
|
+
content:
|
|
1465
|
+
text/event-stream:
|
|
1466
|
+
schema: { $ref: '#/components/schemas/AgentEvent' }
|
|
1467
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1468
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
1469
|
+
|
|
1470
|
+
/v1/usage:
|
|
1471
|
+
parameters:
|
|
1472
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
1473
|
+
get:
|
|
1474
|
+
tags: [usage]
|
|
1475
|
+
operationId: usageGet
|
|
1476
|
+
x-status: live # service GET /v1/usage (shape audited from source). SDK usage.get().
|
|
1477
|
+
summary: Per-principal cost/quota snapshot (the CLI `/cost` panel's real data).
|
|
1478
|
+
description: >
|
|
1479
|
+
READ-ONLY. Worker computes this from the REQUEST principal (crypto-verified on a direct door β never the
|
|
1480
|
+
spoofable header); the client never sends a scope. When no cost-quota window is configured β
|
|
1481
|
+
`{ enabled:false, windowSec, maxTaskCostUsd?, maxTaskTokens? }` (only the single-task ceilings); the caller
|
|
1482
|
+
renders "no quota data" and NEVER blocks on it (enforcement is 100% server-side; this only displays).
|
|
1483
|
+
responses:
|
|
1484
|
+
'200':
|
|
1485
|
+
description: Usage/quota snapshot (enabled=false when no window is configured).
|
|
1486
|
+
content:
|
|
1487
|
+
application/json:
|
|
1488
|
+
schema: { $ref: '#/components/schemas/UsageInfo' }
|
|
1489
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1490
|
+
|
|
1491
|
+
/v1/policy:
|
|
1492
|
+
parameters:
|
|
1493
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
1494
|
+
get:
|
|
1495
|
+
tags: [policy]
|
|
1496
|
+
operationId: policyGet
|
|
1497
|
+
x-status: live # service GET /v1/policy (shape audited from source). SDK policy.get().
|
|
1498
|
+
summary: Effective-governance snapshot (the CLI `/permissions` panel's real data; inspect-only).
|
|
1499
|
+
description: >
|
|
1500
|
+
READ-ONLY snapshot of the worker's effective autonomy band + command policy + approval conditions + limits
|
|
1501
|
+
(worker-GLOBAL config, same for every principal β a principal is OPTIONAL, only enforced under
|
|
1502
|
+
REQUIRE_PRINCIPAL). The CLI renders "what can run automatically, what needs approval, what the ceilings
|
|
1503
|
+
are" from this. π΄ The client NEVER re-decides/admits commands from this β command admission + approval
|
|
1504
|
+
enforcement is 100% server-side (in resolveSpec); this is pure inspect. The CLI's "permission modes" are
|
|
1505
|
+
TIGHTEN-ONLY client cosmetics and cannot loosen server gating.
|
|
1506
|
+
responses:
|
|
1507
|
+
'200':
|
|
1508
|
+
description: Effective policy snapshot.
|
|
1509
|
+
content:
|
|
1510
|
+
application/json:
|
|
1511
|
+
schema: { $ref: '#/components/schemas/PolicyInfo' }
|
|
1512
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1513
|
+
|
|
1514
|
+
/v1/models:
|
|
1515
|
+
parameters:
|
|
1516
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
1517
|
+
get:
|
|
1518
|
+
tags: [models]
|
|
1519
|
+
operationId: modelsList
|
|
1520
|
+
x-status: live # service GET /v1/models (server.ts:859-882). SDK models.list().
|
|
1521
|
+
summary: Non-secret model catalog (the `@model` picker / autocomplete data source).
|
|
1522
|
+
description: >
|
|
1523
|
+
READ-ONLY catalog of `@mention`-pickable models. The user selects a model PER-TASK by typing `@<name>` in
|
|
1524
|
+
the objective body (the service allowlist-parses it; the client never re-decides). Envelope `{ models, default }`
|
|
1525
|
+
(`default` = the deployment default model id, display-only β the internal `default` alias is hidden).
|
|
1526
|
+
π΄ NON-SECRET: only name/id/provider/reasoning/vision + the E4/E7 capability fields β NEVER baseUrl/apiKey/
|
|
1527
|
+
headers (the service strips them). Fail-safe: on error, render an empty picker (never block).
|
|
1528
|
+
responses:
|
|
1529
|
+
'200':
|
|
1530
|
+
description: Model catalog (possibly empty).
|
|
1531
|
+
content:
|
|
1532
|
+
application/json:
|
|
1533
|
+
schema:
|
|
1534
|
+
type: object
|
|
1535
|
+
required: [models, default]
|
|
1536
|
+
properties:
|
|
1537
|
+
models:
|
|
1538
|
+
type: array
|
|
1539
|
+
items: { $ref: '#/components/schemas/ModelInfo' }
|
|
1540
|
+
default: { type: string, description: "deployment default model id (display-only)" }
|
|
1541
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1542
|
+
|
|
1543
|
+
/v1/elicitations/{id}/respond:
|
|
1544
|
+
parameters:
|
|
1545
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
1546
|
+
- name: id
|
|
1547
|
+
in: path
|
|
1548
|
+
required: true
|
|
1549
|
+
schema: { type: string }
|
|
1550
|
+
description: the elicitationId from the live `elicitation` SSE frame.
|
|
1551
|
+
post:
|
|
1552
|
+
tags: [elicitations]
|
|
1553
|
+
operationId: elicitationRespond
|
|
1554
|
+
x-status: gated # service POST /v1/elicitations/:id/respond β present only under MCP_ELICITATION_ENABLED (else 501).
|
|
1555
|
+
summary: Answer an inbound-MCP elicitation (design/99 Β§E23, LIVE-only HITL).
|
|
1556
|
+
description: >
|
|
1557
|
+
Resolve a parked inbound-MCP elicitation with the user's form answer; the value flows back to the MCP server
|
|
1558
|
+
on the still-open task connection. π΄ LIVE-ONLY + SAME-REPLICA (the parked promise is in-memory): a respond
|
|
1559
|
+
on another replica / after answer / after the TTL finds nothing β 404. π΄ OWNER-GATED with a 404 (NO existence
|
|
1560
|
+
oracle): a non-owner AND an unknown id both get 404 β never an existence leak. A malformed body β 400
|
|
1561
|
+
(existence-independent). Not a submit / not idempotent β no client retry.
|
|
1562
|
+
requestBody:
|
|
1563
|
+
required: true
|
|
1564
|
+
content:
|
|
1565
|
+
application/json:
|
|
1566
|
+
schema: { $ref: '#/components/schemas/ElicitResponse' }
|
|
1567
|
+
responses:
|
|
1568
|
+
'200':
|
|
1569
|
+
description: Answer applied (the promise resolved).
|
|
1570
|
+
content:
|
|
1571
|
+
application/json:
|
|
1572
|
+
schema: { $ref: '#/components/schemas/ElicitRespondAck' }
|
|
1573
|
+
'400': { description: malformed body (action not accept/decline/cancel, or non-scalar content). }
|
|
1574
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1575
|
+
'404': { description: no pending elicitation for this id (non-owner, answered, expired, or wrong replica) β no existence oracle. }
|
|
1576
|
+
'501': { description: inbound MCP elicitation is not enabled on this worker (MCP_ELICITATION_ENABLED). }
|
|
1577
|
+
|
|
1578
|
+
/v1/questions/{id}/respond:
|
|
1579
|
+
parameters:
|
|
1580
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
1581
|
+
- name: id
|
|
1582
|
+
in: path
|
|
1583
|
+
required: true
|
|
1584
|
+
schema: { type: string }
|
|
1585
|
+
description: the questionId from the live `question` SSE frame.
|
|
1586
|
+
post:
|
|
1587
|
+
tags: [questions]
|
|
1588
|
+
operationId: questionRespond
|
|
1589
|
+
x-status: gated # service POST /v1/questions/:id/respond β present only under ASK_QUESTION_ENABLED (else 501).
|
|
1590
|
+
summary: Answer the agent's own AskUserQuestion (Β§4β£ tool-panorama, LIVE-only HITL).
|
|
1591
|
+
description: >
|
|
1592
|
+
Resolve a parked LIVE AskUserQuestion with the end user's selection; the value flows back to the model on the
|
|
1593
|
+
still-open task connection (the model's AskUserQuestion tool call returns it). The SIBLING of elicitationRespond
|
|
1594
|
+
(that is an inbound-MCP-server ask; this is the AGENT's OWN structured multiple-choice question to the user).
|
|
1595
|
+
π΄ LIVE-ONLY + SAME-REPLICA (the parked promise is in-memory): a respond on another replica / after answer /
|
|
1596
|
+
after the TTL (5 min) finds nothing β 404. π΄ OWNER-GATED with a 404 (NO existence oracle): a non-owner AND an
|
|
1597
|
+
unknown id both get 404 β never an existence leak. A malformed body β 400 (existence-independent). Not a submit /
|
|
1598
|
+
not idempotent (it resolves an in-flight promise exactly once) β no client retry.
|
|
1599
|
+
requestBody:
|
|
1600
|
+
required: true
|
|
1601
|
+
content:
|
|
1602
|
+
application/json:
|
|
1603
|
+
schema: { $ref: '#/components/schemas/QuestionAnswer' }
|
|
1604
|
+
responses:
|
|
1605
|
+
'200':
|
|
1606
|
+
description: Answer applied (the parked question promise resolved).
|
|
1607
|
+
content:
|
|
1608
|
+
application/json:
|
|
1609
|
+
schema: { $ref: '#/components/schemas/QuestionRespondAck' }
|
|
1610
|
+
'400': { description: 'malformed body (answers not an array of {header, selected:string[], note?}).' }
|
|
1611
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1612
|
+
'404': { description: no pending question for this id (non-owner, answered, expired, or wrong replica) β no existence oracle. }
|
|
1613
|
+
'501': { description: AskUserQuestion is not enabled on this worker (ASK_QUESTION_ENABLED). }
|
|
1614
|
+
|
|
1615
|
+
/v1/workflows:
|
|
1616
|
+
parameters:
|
|
1617
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
1618
|
+
get:
|
|
1619
|
+
tags: [workflows]
|
|
1620
|
+
operationId: workflowsList
|
|
1621
|
+
x-status: gated # design/97/98; SELF_ORCHESTRATION_ENABLED (default OFF β 501). Owner-scoped (scope = creating principal).
|
|
1622
|
+
summary: List S8 self-orchestration runs (owner-scoped summaries).
|
|
1623
|
+
description: >
|
|
1624
|
+
OWNER-SCOPED β returns ONLY the calling principal's own runs (scope = the run's creating principal,
|
|
1625
|
+
`default` when none). Envelope key = `workflows` (NOT a bare array). REPLICA-LOCAL: this lists the runs the
|
|
1626
|
+
run store on THIS replica knows (durable cross-replica history is a service follow-on). Gated off
|
|
1627
|
+
(SELF_ORCHESTRATION_ENABLED) β 501; the caller fail-safes to empty.
|
|
1628
|
+
parameters:
|
|
1629
|
+
- in: query
|
|
1630
|
+
name: status
|
|
1631
|
+
required: false
|
|
1632
|
+
schema: { $ref: '#/components/schemas/WorkflowRunStatus' }
|
|
1633
|
+
description: Filter by lifecycle status.
|
|
1634
|
+
- in: query
|
|
1635
|
+
name: limit
|
|
1636
|
+
required: false
|
|
1637
|
+
schema: { type: integer, minimum: 1, maximum: 100, default: 50 }
|
|
1638
|
+
description: Max rows (server clamps to 1..100; default 50).
|
|
1639
|
+
responses:
|
|
1640
|
+
'200':
|
|
1641
|
+
description: Owner's workflow-run summaries.
|
|
1642
|
+
content:
|
|
1643
|
+
application/json:
|
|
1644
|
+
schema:
|
|
1645
|
+
type: object
|
|
1646
|
+
required: [workflows]
|
|
1647
|
+
properties:
|
|
1648
|
+
workflows:
|
|
1649
|
+
type: array
|
|
1650
|
+
items: { $ref: '#/components/schemas/WorkflowRunSummary' }
|
|
1651
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1652
|
+
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
1653
|
+
|
|
1654
|
+
/v1/workflows/{workflowRunId}:
|
|
1655
|
+
parameters:
|
|
1656
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
1657
|
+
- $ref: '#/components/parameters/WorkflowRunIdPath'
|
|
1658
|
+
get:
|
|
1659
|
+
tags: [workflows]
|
|
1660
|
+
operationId: workflowsGet
|
|
1661
|
+
x-status: gated # design/97/98; SELF_ORCHESTRATION_ENABLED (default OFF β 501). Per-id owner gate (non-owner β 404).
|
|
1662
|
+
summary: Full S8 self-orchestration run (phases/agents/stats).
|
|
1663
|
+
description: >
|
|
1664
|
+
Full run detail. OWNER-GATED via the persisted run's scope β a non-owner or unknown id β 404 (NO existence
|
|
1665
|
+
oracle, parity with runs). Gated off β 501.
|
|
1666
|
+
responses:
|
|
1667
|
+
'200':
|
|
1668
|
+
description: Full workflow run.
|
|
1669
|
+
content:
|
|
1670
|
+
application/json:
|
|
1671
|
+
schema: { $ref: '#/components/schemas/WorkflowRun' }
|
|
1672
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1673
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
1674
|
+
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
1675
|
+
|
|
1676
|
+
/v1/workflows/{workflowRunId}/stream:
|
|
1677
|
+
parameters:
|
|
1678
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
1679
|
+
- $ref: '#/components/parameters/WorkflowRunIdPath'
|
|
1680
|
+
get:
|
|
1681
|
+
tags: [workflows]
|
|
1682
|
+
operationId: workflowsStream
|
|
1683
|
+
x-status: gated # design/97/98; SELF_ORCHESTRATION_ENABLED (default OFF β 501). In-process subscribeWorkflow; NOT resumable.
|
|
1684
|
+
summary: Live S8 run progress SSE (NON-resumable).
|
|
1685
|
+
description: >
|
|
1686
|
+
LIVE progress. FIRST frame is `event: meta` `{version, runId}`; each subsequent frame carries a core
|
|
1687
|
+
`WorkflowEvent` (its `type` becomes the SSE event name). π΄ NOT RESUMABLE β the source is REPLICA-LOCAL +
|
|
1688
|
+
in-process (no durable seq, no `Last-Event-ID`); a drop is a FULL restart (re-`GET /v1/workflows/:id` for
|
|
1689
|
+
the snapshot), not a resume. Owner-gated (non-owner β 404). Gated off β 501 (before any frame).
|
|
1690
|
+
responses:
|
|
1691
|
+
'200':
|
|
1692
|
+
description: SSE stream of workflow events (`meta` first, then core WorkflowEvents; non-resumable).
|
|
1693
|
+
content:
|
|
1694
|
+
text/event-stream:
|
|
1695
|
+
schema: { $ref: '#/components/schemas/WorkflowStreamEvent' }
|
|
1696
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1697
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
1698
|
+
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
1699
|
+
|
|
1700
|
+
/v1/fleet/stream:
|
|
1701
|
+
parameters:
|
|
1702
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
1703
|
+
get:
|
|
1704
|
+
tags: [fleet]
|
|
1705
|
+
operationId: fleetStream
|
|
1706
|
+
x-status: live # service 1bf7a5c β streamFleet (http/server.ts:4874) + fleet/fleet-bus.ts. Owner-gated; in-process bus; NOT resumable.
|
|
1707
|
+
summary: Live multi-row fleet view SSE (NON-resumable).
|
|
1708
|
+
description: >
|
|
1709
|
+
MF-Fleet (shell-host data contract). The LIVE fleet as an SSE PUSH stream: a SNAPSHOT on connect, then a
|
|
1710
|
+
per-row delta for every transition. Frame order: `event: meta` `{version, scoped}` β `event: snapshot`
|
|
1711
|
+
`{tasks, workflows, ts}` β then `event: task`/`event: workflow` UPSERTS (each `data: {row, ts}` is the
|
|
1712
|
+
POST-MERGE FULL row β replace by `row.id`, NOT a sparse patch) + `event: task_remove`/`event:
|
|
1713
|
+
workflow_remove` `{id, ts}` departures. Heartbeat `: hb` comment frames keep the connection alive.
|
|
1714
|
+
π΄ OWNER-SCOPED by the credential-derived principal (NOT a spoofable header) UNLESS fleet-wide (an operator
|
|
1715
|
+
/ deployment trace token sees every tenant's rows). The row `scope` is WIRE-STRIPPED after the owner-gate β
|
|
1716
|
+
it never appears on a row; the `meta.scoped` flag tells the consumer which view this is (true = owner-scoped).
|
|
1717
|
+
π΄ NOT RESUMABLE β replica-local + in-process (no durable seq / Last-Event-ID); a drop is a FULL restart
|
|
1718
|
+
(re-connect for a fresh snapshot), not a resume. No fleet wiring (`deps.fleetBus` absent) β 501 before any frame.
|
|
1719
|
+
responses:
|
|
1720
|
+
'200':
|
|
1721
|
+
description: SSE stream of fleet frames (`meta` first, then `snapshot`, then per-row upserts/removes; non-resumable).
|
|
1722
|
+
content:
|
|
1723
|
+
text/event-stream:
|
|
1724
|
+
schema: { $ref: '#/components/schemas/FleetFrame' }
|
|
1725
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1726
|
+
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
1727
|
+
|
|
1728
|
+
/v1/leader:
|
|
1729
|
+
parameters:
|
|
1730
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
1731
|
+
post:
|
|
1732
|
+
tags: [leader]
|
|
1733
|
+
operationId: leaderCreate
|
|
1734
|
+
x-status: gated # v2 pipeline; LEADER_ENABLED (default OFF) + REMOTE_EXEC=e2b. NOT the default door-B path.
|
|
1735
|
+
summary: Start a v2 leader run (gated; E2B + git push).
|
|
1736
|
+
description: >
|
|
1737
|
+
π΄ The v2 leader pipeline (E2B workers + git push), NOT door B's brain. Gated server-side:
|
|
1738
|
+
`LEADER_ENABLED` (default OFF) + `REMOTE_EXEC=e2b`. Mode is a SERVER deploy flag, not a client choice:
|
|
1739
|
+
`LEADER_FANOUT_ENABLED=true` (default) lets the router fan out to N isolated workers (= the value-HOLD
|
|
1740
|
+
correctness bet); `=false` pins a single worker. Either way it runs the E2B+git pipeline. Door B's brain
|
|
1741
|
+
is the strong single agent via `/v1/tasks`+`/v1/runs`+`scenario`. When disabled β 501.
|
|
1742
|
+
requestBody:
|
|
1743
|
+
required: true
|
|
1744
|
+
content:
|
|
1745
|
+
application/json:
|
|
1746
|
+
schema:
|
|
1747
|
+
type: object
|
|
1748
|
+
required: [objective]
|
|
1749
|
+
additionalProperties: true
|
|
1750
|
+
properties:
|
|
1751
|
+
objective: { type: string }
|
|
1752
|
+
sessionId: { type: string }
|
|
1753
|
+
jobId: { type: string, minLength: 1, maxLength: 64 }
|
|
1754
|
+
responses:
|
|
1755
|
+
'202':
|
|
1756
|
+
description: Leader run accepted.
|
|
1757
|
+
content:
|
|
1758
|
+
application/json:
|
|
1759
|
+
schema: { $ref: '#/components/schemas/LeaderReceipt' }
|
|
1760
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1761
|
+
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
1762
|
+
|
|
1763
|
+
/v1/leader/{leaderRunId}:
|
|
1764
|
+
parameters:
|
|
1765
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
1766
|
+
- in: path
|
|
1767
|
+
name: leaderRunId
|
|
1768
|
+
required: true
|
|
1769
|
+
schema: { type: string }
|
|
1770
|
+
get:
|
|
1771
|
+
tags: [leader]
|
|
1772
|
+
operationId: leaderGet
|
|
1773
|
+
x-status: gated
|
|
1774
|
+
summary: Read leader run state.
|
|
1775
|
+
responses:
|
|
1776
|
+
'200':
|
|
1777
|
+
description: Leader run state.
|
|
1778
|
+
content:
|
|
1779
|
+
application/json:
|
|
1780
|
+
schema: { $ref: '#/components/schemas/LeaderRecord' }
|
|
1781
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1782
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
1783
|
+
|
|
1784
|
+
/metrics/summary:
|
|
1785
|
+
get:
|
|
1786
|
+
tags: [metrics]
|
|
1787
|
+
operationId: metricsSummary
|
|
1788
|
+
x-status: draft # ops surface; uses a metricsToken (NOT a principal). Shape loose.
|
|
1789
|
+
summary: Worker health/cost summary (ops).
|
|
1790
|
+
description: Uses `metricsToken` (operator), not `x-agent-principal`. Shape is loose/ops-defined.
|
|
1791
|
+
security:
|
|
1792
|
+
- metricsToken: []
|
|
1793
|
+
responses:
|
|
1794
|
+
'200':
|
|
1795
|
+
description: Metrics summary (shape draft).
|
|
1796
|
+
content:
|
|
1797
|
+
application/json:
|
|
1798
|
+
schema: { type: object, additionalProperties: true }
|
|
1799
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1800
|
+
|
|
1801
|
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
1802
|
+
|
|
1803
|
+
components:
|
|
1804
|
+
|
|
1805
|
+
securitySchemes:
|
|
1806
|
+
bearerAuth:
|
|
1807
|
+
type: http
|
|
1808
|
+
scheme: bearer
|
|
1809
|
+
description: >
|
|
1810
|
+
Worker `SERVICE_AUTH_TOKEN`. SERVER-SIDE ONLY (the SDK browser guard refuses to construct a
|
|
1811
|
+
token-bearing client in a browser; the portal's browser calls its own BFF). Missing β 503; bad β 401.
|
|
1812
|
+
metricsToken:
|
|
1813
|
+
type: http
|
|
1814
|
+
scheme: bearer
|
|
1815
|
+
description: Operator metrics token (ops only; distinct from the service auth token and from any principal).
|
|
1816
|
+
|
|
1817
|
+
parameters:
|
|
1818
|
+
PrincipalHeader:
|
|
1819
|
+
in: header
|
|
1820
|
+
name: x-agent-principal
|
|
1821
|
+
required: true
|
|
1822
|
+
schema: { $ref: '#/components/schemas/Principal' }
|
|
1823
|
+
description: >
|
|
1824
|
+
Authenticated end-user identity (REQUIRED; principal-first, CORE-PRINCIPLES Β§3). Opaque to the service
|
|
1825
|
+
(memory scope + session owner key; format NOT validated server-side). The SDK normalizes; anonymous must
|
|
1826
|
+
be explicit (`anon:<id>`). On the multi-tenant doors this is set PER REQUEST.
|
|
1827
|
+
IdempotencyKey:
|
|
1828
|
+
in: header
|
|
1829
|
+
name: idempotency-key
|
|
1830
|
+
required: false
|
|
1831
|
+
schema: { type: string }
|
|
1832
|
+
description: >
|
|
1833
|
+
Submit-class dedupe key (the SDK auto-generates a UUID β retries are safe by default). In-process window
|
|
1834
|
+
= 15 min (per-instance). A submit is retried by the SDK ONLY when a key exists.
|
|
1835
|
+
LastEventId:
|
|
1836
|
+
in: header
|
|
1837
|
+
name: Last-Event-ID
|
|
1838
|
+
required: false
|
|
1839
|
+
schema: { type: string }
|
|
1840
|
+
description: Resume an SSE stream from after this event id (= the durable `task_event.seq`).
|
|
1841
|
+
TaskIdPath:
|
|
1842
|
+
in: path
|
|
1843
|
+
name: taskId
|
|
1844
|
+
required: true
|
|
1845
|
+
schema: { type: string }
|
|
1846
|
+
WorkflowRunIdPath:
|
|
1847
|
+
in: path
|
|
1848
|
+
name: workflowRunId
|
|
1849
|
+
required: true
|
|
1850
|
+
schema: { type: string }
|
|
1851
|
+
|
|
1852
|
+
responses:
|
|
1853
|
+
BadRequest:
|
|
1854
|
+
description: >
|
|
1855
|
+
Bad request β malformed input only (e.g. missing `objective`, or `jobId` present but empty or >64 chars,
|
|
1856
|
+
NOT silently truncated). NOTE: budget rejections are NOT 4xx β they are result-level
|
|
1857
|
+
(HTTP 200 + `TaskResult.errorCode`), see the TaskResult/RateLimited notes (pinned wire contract).
|
|
1858
|
+
content:
|
|
1859
|
+
application/json:
|
|
1860
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
1861
|
+
Unauthorized:
|
|
1862
|
+
description: Fail-closed auth β 401 (bad token) / 503 (missing token). Mapped to AuthError.
|
|
1863
|
+
content:
|
|
1864
|
+
application/json:
|
|
1865
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
1866
|
+
NotFound:
|
|
1867
|
+
description: >
|
|
1868
|
+
No such resource for this principal. Owner-scoped reads/cancel return 404 (not 403) for a non-owned or
|
|
1869
|
+
unknown id β existence is not leaked across principals.
|
|
1870
|
+
content:
|
|
1871
|
+
application/json:
|
|
1872
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
1873
|
+
Conflict:
|
|
1874
|
+
description: >
|
|
1875
|
+
409 β session already has an active run, OR an idempotency/session-CAS conflict. Body carries
|
|
1876
|
+
`activeTaskId` (the task holding the session lock). β οΈ The SDK surfaces this as a TYPED ConflictError and
|
|
1877
|
+
does NOT silently resolve it to that run: `activeTaskId` may be the caller's own first attempt OR an
|
|
1878
|
+
unrelated concurrent run on the same session β 409 alone cannot tell (the pinned wire contract Q6). Only triggers when
|
|
1879
|
+
a `sessionId` is present.
|
|
1880
|
+
content:
|
|
1881
|
+
application/json:
|
|
1882
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
1883
|
+
RateLimited:
|
|
1884
|
+
description: >
|
|
1885
|
+
429 β per-principal CUMULATIVE quota exceeded (`quotaExceeded`). `Retry-After` (seconds) honored by the
|
|
1886
|
+
SDK on retry. This is the ONLY budget/limit signal at the HTTP layer; per-task budget gates are
|
|
1887
|
+
result-level (200 + errorCode), NOT here (pinned wire contract).
|
|
1888
|
+
headers:
|
|
1889
|
+
Retry-After:
|
|
1890
|
+
schema: { type: integer }
|
|
1891
|
+
description: Seconds to wait before retrying.
|
|
1892
|
+
content:
|
|
1893
|
+
application/json:
|
|
1894
|
+
schema: { $ref: '#/components/schemas/QuotaError' }
|
|
1895
|
+
NotImplemented:
|
|
1896
|
+
description: 501 β capability not available (e.g. no TiDB run store; leader gated off; `wake` unsupported).
|
|
1897
|
+
content:
|
|
1898
|
+
application/json:
|
|
1899
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
1900
|
+
PayloadTooLarge:
|
|
1901
|
+
description: >
|
|
1902
|
+
413 β the request body exceeded a server cap (2c session-sync: a blob PUT past the 64 MiB per-blob cap, or a
|
|
1903
|
+
Phase-B NDJSON line past the per-line cap). Rejected mid-stream (no unbounded buffer). Not retryable verbatim.
|
|
1904
|
+
content:
|
|
1905
|
+
application/json:
|
|
1906
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
1907
|
+
UnprocessableEntity:
|
|
1908
|
+
description: >
|
|
1909
|
+
422 β a semantically-invalid submission the server understood but cannot apply (2c session-sync: a referenced
|
|
1910
|
+
blob is missing at import Phase A `missing_blob`, or the staged entry log failed the tree invariants
|
|
1911
|
+
`invalid_entries`). Fix the offending data + retry; deterministic, not a transient.
|
|
1912
|
+
content:
|
|
1913
|
+
application/json:
|
|
1914
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
1915
|
+
|
|
1916
|
+
schemas:
|
|
1917
|
+
|
|
1918
|
+
Principal:
|
|
1919
|
+
type: string
|
|
1920
|
+
description: >
|
|
1921
|
+
Opaque end-user identity. Convention (NOT enforced server-side): `user:` / `org:` / `ai:` / `anon:`.
|
|
1922
|
+
examples: ['user:42', 'org:acme', 'anon:7f3c']
|
|
1923
|
+
|
|
1924
|
+
Scenario:
|
|
1925
|
+
type: string
|
|
1926
|
+
description: >
|
|
1927
|
+
Primary routing axis (one image serves many; unknown β "default"). Selects model/tools/skills/prompt.
|
|
1928
|
+
examples: ['default', 'oa', 'code-review', 'team']
|
|
1929
|
+
|
|
1930
|
+
RunStatus:
|
|
1931
|
+
type: string
|
|
1932
|
+
enum: [running, completed, failed, suspended, needs_review]
|
|
1933
|
+
description: >
|
|
1934
|
+
`suspended` = HITL (an F4 policy gate or AskUserQuestion is waiting on a human) β NOT an error.
|
|
1935
|
+
`needs_review` (design/80 D-0) = durable TERMINAL of a dry-run/shadow review (POST-prediction),
|
|
1936
|
+
distinct from the mid-run `suspended` pause. A user-cancelled run is `failed` with `errorCode`
|
|
1937
|
+
"cancelled" (cancel is LIVE; `cancelling` is a cancel-ack field, NOT a member of this enum).
|
|
1938
|
+
|
|
1939
|
+
TaskStats:
|
|
1940
|
+
type: object
|
|
1941
|
+
properties:
|
|
1942
|
+
turns: { type: integer }
|
|
1943
|
+
tokens: { type: integer }
|
|
1944
|
+
promptTokens: { type: integer }
|
|
1945
|
+
cachedTokens: { type: integer }
|
|
1946
|
+
outputTokens: { type: integer }
|
|
1947
|
+
costUsd: { type: number }
|
|
1948
|
+
cacheHitRate: { type: number }
|
|
1949
|
+
required: [turns, tokens]
|
|
1950
|
+
|
|
1951
|
+
TaskRequest:
|
|
1952
|
+
type: object
|
|
1953
|
+
description: >
|
|
1954
|
+
Common request body. `[additionalProperties]` mirrors the service passthrough β scenario-specific fields
|
|
1955
|
+
(code-review's `repo`/`council`/`debate`/`rounds`, etc.) ride here.
|
|
1956
|
+
required: [objective]
|
|
1957
|
+
additionalProperties: true
|
|
1958
|
+
properties:
|
|
1959
|
+
objective:
|
|
1960
|
+
type: string
|
|
1961
|
+
description: The task. Put per-request context here (keep `systemPrompt` stable/cacheable).
|
|
1962
|
+
scenario: { $ref: '#/components/schemas/Scenario' }
|
|
1963
|
+
sessionId:
|
|
1964
|
+
type: string
|
|
1965
|
+
description: Continue a conversation (same sessionId across turns). Omit on first turn β service mints one.
|
|
1966
|
+
jobId:
|
|
1967
|
+
type: string
|
|
1968
|
+
minLength: 1
|
|
1969
|
+
maxLength: 64
|
|
1970
|
+
description: >
|
|
1971
|
+
Work-view correlation key (the anti-fragmentation substrate, DIRECTION Β§2). LIVE server-side
|
|
1972
|
+
(service 63b8696): persisted to `task_run.job_id`, groupable via `GET /v1/tasks?jobId=`.
|
|
1973
|
+
Set one jobId across a job's sub-runs so they group into one Task across BOTH doors. >64 chars or empty
|
|
1974
|
+
β 400 (not silently truncated). The SDK auto-generates one if absent.
|
|
1975
|
+
systemPrompt:
|
|
1976
|
+
type: string
|
|
1977
|
+
maxLength: 16384
|
|
1978
|
+
description: >
|
|
1979
|
+
Business/UX system prompt the CLIENT owns β STABLE per integrator version (cacheable prefix).
|
|
1980
|
+
Server cap 16384 chars β 400.
|
|
1981
|
+
skills:
|
|
1982
|
+
type: array
|
|
1983
|
+
maxItems: 10
|
|
1984
|
+
description: >
|
|
1985
|
+
Per-request user skills (LIVE, service 170c384) β progressive disclosure, same
|
|
1986
|
+
mechanism as scenario skills. Merge: scenario WINS on name collision (user's dropped, warn-logged).
|
|
1987
|
+
Caps server-enforced; violations 400 with the offending rule named.
|
|
1988
|
+
items: { $ref: '#/components/schemas/SkillSpec' }
|
|
1989
|
+
reasoningEffort:
|
|
1990
|
+
type: string
|
|
1991
|
+
enum: [off, minimal, low, medium, high, xhigh, max]
|
|
1992
|
+
description: >
|
|
1993
|
+
Per-turn reasoning strength (CC /effort parity). Mapped to TaskSpec.thinking; an UNKNOWN value
|
|
1994
|
+
is a 400 (fail-loud). Omit β engine default (the model/role default). The shell stamps the
|
|
1995
|
+
user's /effort pick here per turn.
|
|
1996
|
+
model:
|
|
1997
|
+
type: string
|
|
1998
|
+
description: >
|
|
1999
|
+
Per-turn MODEL (CC /model picker parity). Alias ('sonnet'/'opus') or full id. The service
|
|
2000
|
+
resolveTaskModel CATALOG-GATES it (body.model wins over @mention wins over the deployment default;
|
|
2001
|
+
out-of-roster falls back). Maps to core TaskSpec.model. Omit β deployment/role default.
|
|
2002
|
+
mcpServers:
|
|
2003
|
+
type: array
|
|
2004
|
+
maxItems: 32
|
|
2005
|
+
description: >
|
|
2006
|
+
Per-request MCP servers (CC .mcp.json parity). The trusted BFF/shell projects the local .mcp.json
|
|
2007
|
+
here so the engine mounts those servers' tools. HONORED only on a SINGLE-USER deployment
|
|
2008
|
+
(mcpInjectionHonored = requirePrincipal!==true); multi-tenant ignores it (fail-closed). Merge:
|
|
2009
|
+
deployment/center baseline wins on name collision (caller can ADD, never SHADOW).
|
|
2010
|
+
items: { $ref: '#/components/schemas/McpServerSpec' }
|
|
2011
|
+
resumeAt:
|
|
2012
|
+
type: string
|
|
2013
|
+
description: >
|
|
2014
|
+
CC Rewind parity (E18). Fork the conversation FROM this user-message SessionTreeEntry.id (non-destructive
|
|
2015
|
+
branch via setLeafId). With rewindFiles=true β "both" (also restore the working tree). Alone β
|
|
2016
|
+
"conversation". HONORED only where the deployment wired a resume-anchor store + getLeafId (capabilities.resumeAt);
|
|
2017
|
+
unknown anchor β 4xx, capability-off β 501. Cannot combine with a durable resume.
|
|
2018
|
+
rewindFiles:
|
|
2019
|
+
type: boolean
|
|
2020
|
+
description: >
|
|
2021
|
+
CC Rewind parity (E18). On a NORMAL turn β CAPTURE a working-tree snapshot at turn end (per-turn, by leaf;
|
|
2022
|
+
required so later rewinds have something to restore). On a resumeAt turn β RESTORE the tree to resumeAt's
|
|
2023
|
+
snapshot ("both"). Needs a fileSnapshotStore wired (capabilities.rewindFiles).
|
|
2024
|
+
rewindFilesTo:
|
|
2025
|
+
type: string
|
|
2026
|
+
description: >
|
|
2027
|
+
CC Rewind "code"-only (core 1.166.0 TaskSpec.rewindFilesTo). Restore the working tree to this user-message
|
|
2028
|
+
entryId's snapshot WITHOUT forking the conversation (no setLeafId). Use instead of resumeAt+rewindFiles when
|
|
2029
|
+
the user wants only files reverted. Target = a user-message SessionTreeEntry.id.
|
|
2030
|
+
permissionMode:
|
|
2031
|
+
type: string
|
|
2032
|
+
enum: [default, plan, acceptEdits, bypassPermissions]
|
|
2033
|
+
description: >
|
|
2034
|
+
CC permission-mode INTENT. The frontend (TUI shell / web portal) carries the RAW mode; the SERVICE
|
|
2035
|
+
INTERPRETS it (axis-aware, tighten-only): `plan` β mount present_plan (core enablePlanMode) + read-only
|
|
2036
|
+
hands (handsReadOnly) = CC EnterPlanMode read-only research; `acceptEdits`/`bypassPermissions` are
|
|
2037
|
+
loosening, not honorable remotely (coerced to default engine-side, enforced client-side). Carrying the
|
|
2038
|
+
intent (not pre-interpreted core fields) = one interpretation across frontends + service-owned governance.
|
|
2039
|
+
images:
|
|
2040
|
+
type: array
|
|
2041
|
+
items:
|
|
2042
|
+
oneOf:
|
|
2043
|
+
- type: object
|
|
2044
|
+
required: [data, mimeType]
|
|
2045
|
+
properties:
|
|
2046
|
+
data: { type: string }
|
|
2047
|
+
mimeType: { type: string }
|
|
2048
|
+
- type: object
|
|
2049
|
+
required: [url]
|
|
2050
|
+
properties:
|
|
2051
|
+
url: { type: string }
|
|
2052
|
+
maxCostUsd:
|
|
2053
|
+
type: number
|
|
2054
|
+
description: Caller-requested cost ceiling; server caps to the operator max (may ask less, never more).
|
|
2055
|
+
maxTokens: { type: integer, description: Caller-requested token ceiling; server-capped. }
|
|
2056
|
+
verify:
|
|
2057
|
+
type: boolean
|
|
2058
|
+
description: Adversarial verify gate (impl β independent read-only verify β fix loop). Not on `/v1/tasks/stream`.
|
|
2059
|
+
verifyRounds: { type: integer }
|
|
2060
|
+
cascade:
|
|
2061
|
+
type: boolean
|
|
2062
|
+
description: Quality-gate cascade (cheapβstrong). Mutually exclusive with `verify`. Not on the stream.
|
|
2063
|
+
suggestNextPrompts:
|
|
2064
|
+
description: >
|
|
2065
|
+
E12 (shell-host; service spec-fields.ts:9 / runs.ts:392, SHIPPED) β opt-in post-completion "what to ask
|
|
2066
|
+
next" suggestions. After a COMPLETED run, core runs an LLM pass and the service appends a `suggestions`
|
|
2067
|
+
event to the durable run-events tail. `true` = core defaults; the object form tunes count / generating
|
|
2068
|
+
role. π΄ Mutually exclusive with `verify`/`cascade` (those return a result, not a streamed run β 400).
|
|
2069
|
+
Only meaningful on `/v1/runs` (the sync `/v1/tasks` has no events tail). UNTRUSTED model text (redacted)
|
|
2070
|
+
β UI-only, never re-fed to a model.
|
|
2071
|
+
oneOf:
|
|
2072
|
+
- type: boolean
|
|
2073
|
+
- type: object
|
|
2074
|
+
properties:
|
|
2075
|
+
count: { type: integer }
|
|
2076
|
+
role: { type: string, description: A model role name. }
|
|
2077
|
+
|
|
2078
|
+
TaskResult:
|
|
2079
|
+
type: object
|
|
2080
|
+
description: Synchronous task result (`POST /v1/tasks`).
|
|
2081
|
+
required: [taskId, sessionId, status, stats]
|
|
2082
|
+
properties:
|
|
2083
|
+
taskId: { type: string }
|
|
2084
|
+
sessionId: { type: string }
|
|
2085
|
+
status: { $ref: '#/components/schemas/RunStatus' }
|
|
2086
|
+
result: { type: string }
|
|
2087
|
+
errorCode:
|
|
2088
|
+
type: string
|
|
2089
|
+
description: >
|
|
2090
|
+
Result-level terminal code (NOT an HTTP error). Includes `budget.precall` / `budget.exceeded`
|
|
2091
|
+
(cost gate, non-retryable) β branch on this, not on a 4xx (pinned wire contract).
|
|
2092
|
+
errorMessage: { type: string }
|
|
2093
|
+
stats: { $ref: '#/components/schemas/TaskStats' }
|
|
2094
|
+
verification:
|
|
2095
|
+
type: object
|
|
2096
|
+
description: Present when run behind the verify gate.
|
|
2097
|
+
properties:
|
|
2098
|
+
verdict: { type: string, enum: [PASS, FAIL, PARTIAL, unverified] }
|
|
2099
|
+
rounds: { type: integer }
|
|
2100
|
+
findings: { type: array, items: { type: string } }
|
|
2101
|
+
|
|
2102
|
+
RunReceipt:
|
|
2103
|
+
type: object
|
|
2104
|
+
description: Async run receipt (`POST /v1/runs` β 202). The capability token is NEVER on the wire.
|
|
2105
|
+
required: [taskId, sessionId, status]
|
|
2106
|
+
properties:
|
|
2107
|
+
taskId: { type: string }
|
|
2108
|
+
sessionId: { type: string }
|
|
2109
|
+
status: { $ref: '#/components/schemas/RunStatus' }
|
|
2110
|
+
|
|
2111
|
+
RunRecord:
|
|
2112
|
+
type: object
|
|
2113
|
+
description: >
|
|
2114
|
+
Run state (`GET /v1/runs/:id`) β live shape per the pinned wire contract Drift 2: `result` is the NESTED TaskResult
|
|
2115
|
+
object (output text in result.result, stats in result.stats; NO top-level stats), and the error text
|
|
2116
|
+
field is `error` (not errorMessage; errorCode is floated up from result.errorCode).
|
|
2117
|
+
required: [taskId, sessionId, status]
|
|
2118
|
+
properties:
|
|
2119
|
+
taskId: { type: string }
|
|
2120
|
+
sessionId: { type: string }
|
|
2121
|
+
status: { $ref: '#/components/schemas/RunStatus' }
|
|
2122
|
+
result: { $ref: '#/components/schemas/TaskResult' }
|
|
2123
|
+
errorCode:
|
|
2124
|
+
type: string
|
|
2125
|
+
description: Result-level terminal code (e.g. `budget.exceeded`, `cancelled`); NOT an HTTP error.
|
|
2126
|
+
error: { type: string }
|
|
2127
|
+
jobId:
|
|
2128
|
+
type: string
|
|
2129
|
+
description: Present when the run has a job correlation (omitted when null). LIVE.
|
|
2130
|
+
source:
|
|
2131
|
+
type: string
|
|
2132
|
+
description: >
|
|
2133
|
+
System attribution DERIVED FROM the authenticating credential (unforgeable; body injection ignored).
|
|
2134
|
+
e.g. oa | cc-mcp | portal. LIVE (service cf1be73).
|
|
2135
|
+
|
|
2136
|
+
CancelAck:
|
|
2137
|
+
type: object
|
|
2138
|
+
description: >
|
|
2139
|
+
Synchronous ack for `POST /v1/runs/:id/cancel`. `status` is "cancelling" (accepted) or a terminal status
|
|
2140
|
+
(idempotent no-op). NOTE: "cancelling" is NOT a RunStatus enum member β it only appears here.
|
|
2141
|
+
required: [taskId, status]
|
|
2142
|
+
properties:
|
|
2143
|
+
taskId: { type: string }
|
|
2144
|
+
status: { type: string }
|
|
2145
|
+
note: { type: string }
|
|
2146
|
+
|
|
2147
|
+
ResumeEvicted:
|
|
2148
|
+
type: object
|
|
2149
|
+
description: >
|
|
2150
|
+
416 body for an evicted SSE resume point (`GET /v1/runs/:id/events`). There is NO machine `code` field β
|
|
2151
|
+
match the 416 STATUS and read `retainedFrom`, then full-sync via `/v1/tasks/:id/turns` (pinned SSE contract).
|
|
2152
|
+
required: [error, retainedFrom]
|
|
2153
|
+
properties:
|
|
2154
|
+
error: { type: string }
|
|
2155
|
+
retainedFrom: { type: integer, description: Lowest event seq still retained; resume events from here. }
|
|
2156
|
+
|
|
2157
|
+
QuotaError:
|
|
2158
|
+
type: object
|
|
2159
|
+
description: 429 per-principal cumulative-quota body (`quotaExceeded`).
|
|
2160
|
+
additionalProperties: true
|
|
2161
|
+
properties:
|
|
2162
|
+
error: { type: string }
|
|
2163
|
+
usedMicroUsd: { type: integer }
|
|
2164
|
+
limitMicroUsd: { type: integer }
|
|
2165
|
+
retryAfterSec: { type: integer }
|
|
2166
|
+
|
|
2167
|
+
TraceTurn:
|
|
2168
|
+
type: object
|
|
2169
|
+
description: >
|
|
2170
|
+
Trace turn (true shape, service-pinned). role currently only "assistant" (user/system/tool reserved β
|
|
2171
|
+
the user/objective opening turn is NOT projected today). blocks is an OPEN set; tool-result.output
|
|
2172
|
+
is currently ALWAYS absent (session-storage join not wired β known gap).
|
|
2173
|
+
required: [seq, ts, role, blocks]
|
|
2174
|
+
additionalProperties: true
|
|
2175
|
+
properties:
|
|
2176
|
+
seq: { type: integer }
|
|
2177
|
+
ts: { type: string }
|
|
2178
|
+
role: { type: string }
|
|
2179
|
+
blocks:
|
|
2180
|
+
type: array
|
|
2181
|
+
items:
|
|
2182
|
+
type: object
|
|
2183
|
+
required: [type]
|
|
2184
|
+
additionalProperties: true
|
|
2185
|
+
properties:
|
|
2186
|
+
type: { type: string, description: 'thinking | content | tool-call | tool-result | (open)' }
|
|
2187
|
+
text: { type: string }
|
|
2188
|
+
id: { type: string, description: tool-call β matches approvals toolCallId. }
|
|
2189
|
+
name: { type: string }
|
|
2190
|
+
input: { description: tool-call β redacted args. }
|
|
2191
|
+
callId: { type: string }
|
|
2192
|
+
isError: { type: boolean }
|
|
2193
|
+
tokens:
|
|
2194
|
+
type: object
|
|
2195
|
+
properties:
|
|
2196
|
+
in: { type: integer }
|
|
2197
|
+
out: { type: integer }
|
|
2198
|
+
cacheRead: { type: integer }
|
|
2199
|
+
cacheWrite: { type: integer }
|
|
2200
|
+
|
|
2201
|
+
TurnList:
|
|
2202
|
+
type: object
|
|
2203
|
+
description: >
|
|
2204
|
+
Turn trace envelope (`GET /v1/tasks/:id/turns`). Key is `turns` (resource-named). `retainedFrom` marks
|
|
2205
|
+
the lowest retained seq. Turn ITEM shape is draft (co-design with artifacts, Q7).
|
|
2206
|
+
required: [turns]
|
|
2207
|
+
properties:
|
|
2208
|
+
turns:
|
|
2209
|
+
type: array
|
|
2210
|
+
items: { $ref: '#/components/schemas/TraceTurn' }
|
|
2211
|
+
nextCursor: { type: string }
|
|
2212
|
+
retainedFrom: { type: integer }
|
|
2213
|
+
|
|
2214
|
+
TaskList:
|
|
2215
|
+
type: object
|
|
2216
|
+
description: Paginated run summaries (`GET /v1/tasks`). Envelope key is `tasks` (resource-named, NOT `items`).
|
|
2217
|
+
required: [tasks]
|
|
2218
|
+
properties:
|
|
2219
|
+
tasks:
|
|
2220
|
+
type: array
|
|
2221
|
+
items: { $ref: '#/components/schemas/TaskSummary' }
|
|
2222
|
+
nextCursor: { type: string, description: Keyset cursor for the next page (absent at end). }
|
|
2223
|
+
|
|
2224
|
+
TaskSummary:
|
|
2225
|
+
type: object
|
|
2226
|
+
description: >
|
|
2227
|
+
Run list item (the live `runSummary` cheap summary β the pinned wire contract Drift 1). NOTE the wire names:
|
|
2228
|
+
`id` (not taskId), `startedAt` (not createdAt), `costMicroUsd` (micro-USD Γ1e6, not costUsd); no sessionId.
|
|
2229
|
+
required: [id, status, startedAt, lastStep]
|
|
2230
|
+
additionalProperties: true
|
|
2231
|
+
properties:
|
|
2232
|
+
id: { type: string }
|
|
2233
|
+
status: { $ref: '#/components/schemas/RunStatus' }
|
|
2234
|
+
scenario: { type: string }
|
|
2235
|
+
jobId: { type: string }
|
|
2236
|
+
source: { type: string, description: 'Credential-derived system attribution (service-pinned).' }
|
|
2237
|
+
objectivePreview: { type: string, maxLength: 120, description: 'Objective, secret-redacted then truncated (service-pinned; older rows absent).' }
|
|
2238
|
+
owner: { type: string, description: 'Run owner principal (service-pinned; operator view).' }
|
|
2239
|
+
startedAt: { type: string, format: date-time }
|
|
2240
|
+
endedAt: { type: string, format: date-time }
|
|
2241
|
+
turns: { type: integer }
|
|
2242
|
+
tokens: { type: integer }
|
|
2243
|
+
costMicroUsd: { type: integer }
|
|
2244
|
+
lastStep:
|
|
2245
|
+
type: object
|
|
2246
|
+
required: [type]
|
|
2247
|
+
properties:
|
|
2248
|
+
type: { type: string, enum: [thinking, tool-call, responding, idle] }
|
|
2249
|
+
label: { type: string }
|
|
2250
|
+
|
|
2251
|
+
Artifact:
|
|
2252
|
+
type: object
|
|
2253
|
+
description: >
|
|
2254
|
+
Workspace artifact β a deterministic PROJECTION of persisted tool_start/tool_end pairs (no new write
|
|
2255
|
+
path; args already redacted at persist time). Co-signed the pinned wire contract. kind is an OPEN set β render
|
|
2256
|
+
unknown kinds as a generic row, never crash. v1 emits "file" (successful write_file/edit_file, deduped
|
|
2257
|
+
by path) and "git-push" (successful bash containing git push); "diff" is reserved, not emitted.
|
|
2258
|
+
required: [id, taskId, kind, ref, createdAt]
|
|
2259
|
+
additionalProperties: true
|
|
2260
|
+
properties:
|
|
2261
|
+
id: { type: string, description: '"<taskId>:<n>" β stable within the task (UI key).' }
|
|
2262
|
+
taskId: { type: string }
|
|
2263
|
+
jobId: { type: string, description: Present when the run has a job (job-level aggregation key). }
|
|
2264
|
+
kind: { type: string, description: 'Open set; v1: "file" | "git-push" ("diff" reserved).' }
|
|
2265
|
+
ref: { type: string, description: 'file: workspace-relative path; git-push: refspec tail or command.' }
|
|
2266
|
+
summary: { type: string }
|
|
2267
|
+
createdAt: { type: string, format: date-time, description: ts of the last successful contributing call. }
|
|
2268
|
+
additions:
|
|
2269
|
+
type: integer
|
|
2270
|
+
description: file kind only β hunk-level line stats from logged args (NOT a git diff). the pinned wire contract.
|
|
2271
|
+
deletions: { type: integer }
|
|
2272
|
+
|
|
2273
|
+
ArtifactList:
|
|
2274
|
+
type: object
|
|
2275
|
+
required: [artifacts]
|
|
2276
|
+
properties:
|
|
2277
|
+
artifacts:
|
|
2278
|
+
type: array
|
|
2279
|
+
items: { $ref: '#/components/schemas/Artifact' }
|
|
2280
|
+
|
|
2281
|
+
LeaderReceipt:
|
|
2282
|
+
type: object
|
|
2283
|
+
required: [leaderRunId, status]
|
|
2284
|
+
properties:
|
|
2285
|
+
leaderRunId: { type: string }
|
|
2286
|
+
status: { type: string, enum: [running, completed, failed] }
|
|
2287
|
+
|
|
2288
|
+
LeaderRecord:
|
|
2289
|
+
type: object
|
|
2290
|
+
required: [id, status]
|
|
2291
|
+
properties:
|
|
2292
|
+
id: { type: string }
|
|
2293
|
+
status: { type: string, enum: [running, completed, failed] }
|
|
2294
|
+
result: {}
|
|
2295
|
+
error: { type: string }
|
|
2296
|
+
|
|
2297
|
+
ApprovalDecision:
|
|
2298
|
+
type: object
|
|
2299
|
+
description: HITL decision (`POST /v1/approvals/:sessionId/decide`).
|
|
2300
|
+
required: [decision]
|
|
2301
|
+
properties:
|
|
2302
|
+
decision: { type: string, enum: [approve, deny] }
|
|
2303
|
+
answer:
|
|
2304
|
+
description: >
|
|
2305
|
+
AskUserQuestion answer β TRUE WIRE SHAPE (worker-validated, 400 otherwise; found in prod
|
|
2306
|
+
integration, service-pinned): {answers:[{header, selected:string[], note?}]} where header matches
|
|
2307
|
+
the question's header. Omit for a plain F4 approve/deny.
|
|
2308
|
+
type: object
|
|
2309
|
+
properties:
|
|
2310
|
+
answers:
|
|
2311
|
+
type: array
|
|
2312
|
+
items:
|
|
2313
|
+
type: object
|
|
2314
|
+
required: [header, selected]
|
|
2315
|
+
properties:
|
|
2316
|
+
header: { type: string }
|
|
2317
|
+
selected: { type: array, items: { type: string } }
|
|
2318
|
+
note: { type: string }
|
|
2319
|
+
reason: { type: string }
|
|
2320
|
+
# design/80 D-1 β action binding (TOCTOU guard). Read the three off the PendingCheckpoint the human saw
|
|
2321
|
+
# and echo VERBATIM; server resolves THAT exact checkpoint, fail-closed on mismatch (approval_binding_mismatch).
|
|
2322
|
+
# Omit only against a pre-D-1 worker. boundInputHash is OPAQUE β never hashed/recomputed client-side.
|
|
2323
|
+
checkpointToken: { type: string, description: 'Single-use token of the exact checkpoint the human saw.' }
|
|
2324
|
+
boundCallId: { type: string, description: '= PendingCheckpoint.boundCallId (the bound pendingAction.toolCallId).' }
|
|
2325
|
+
boundInputHash: { type: string, description: 'Echo PendingCheckpoint.boundInputHash byte-for-byte; server-minted opaque, NEVER recompute.' }
|
|
2326
|
+
updatedInput:
|
|
2327
|
+
description: >
|
|
2328
|
+
Approve-with-edit (design/37 last-wins): operator's rewritten tool args, applied AFTER the binding
|
|
2329
|
+
check. `boundInputHash` STILL binds the ORIGINAL input the human saw β never hash updatedInput.
|
|
2330
|
+
|
|
2331
|
+
Capabilities:
|
|
2332
|
+
type: object
|
|
2333
|
+
description: >
|
|
2334
|
+
Worker capability map (`GET /v1/capabilities`). Open set β ignore unknown keys.
|
|
2335
|
+
Booleans share deps with the route gates (can't say yes and then 501).
|
|
2336
|
+
required: [version]
|
|
2337
|
+
additionalProperties: true
|
|
2338
|
+
properties:
|
|
2339
|
+
asyncRuns: { type: boolean }
|
|
2340
|
+
artifacts: { type: boolean }
|
|
2341
|
+
approvals: { type: boolean }
|
|
2342
|
+
leader: { type: boolean }
|
|
2343
|
+
version: { type: string }
|
|
2344
|
+
scenarios:
|
|
2345
|
+
type: array
|
|
2346
|
+
items: { type: string }
|
|
2347
|
+
pricingConfigured:
|
|
2348
|
+
type: boolean
|
|
2349
|
+
description: false β stats.costMicroUsd=0 may mean "no MODEL_COST_* configured", not "free".
|
|
2350
|
+
memory:
|
|
2351
|
+
type: boolean
|
|
2352
|
+
description: Memory transparency endpoints (GET/DELETE /v1/memory) available. the pinned wire contract.
|
|
2353
|
+
memoryWrite:
|
|
2354
|
+
type: boolean
|
|
2355
|
+
description: MF-30 β memory WRITE (append/edit/forget) available (deps.memory.append; server.ts:828).
|
|
2356
|
+
sessionSync:
|
|
2357
|
+
type: boolean
|
|
2358
|
+
description: >
|
|
2359
|
+
2c session-sync (P1d) β the `/v1/sessions/{sessionId}/sync/*` peer routes resolve (durable backend +
|
|
2360
|
+
entry-export seam + a file-snapshot store wired; server.ts:850). Gate the whole sync surface off this.
|
|
2361
|
+
|
|
2362
|
+
SkillSpec:
|
|
2363
|
+
type: object
|
|
2364
|
+
description: A per-request skill (passed as an object; core-native TaskSpec.skills shape).
|
|
2365
|
+
required: [name, description, content]
|
|
2366
|
+
properties:
|
|
2367
|
+
name: { type: string, maxLength: 64 }
|
|
2368
|
+
description: { type: string, maxLength: 1024 }
|
|
2369
|
+
content: { type: string, maxLength: 32768 }
|
|
2370
|
+
|
|
2371
|
+
McpServerSpec:
|
|
2372
|
+
type: object
|
|
2373
|
+
description: >
|
|
2374
|
+
A per-request MCP server (CC .mcp.json parity) β core-native McpServerSpec shape. stdio = a local
|
|
2375
|
+
command the worker spawns (single-user only); http = a remote streamable-HTTP server.
|
|
2376
|
+
required: [name, transport]
|
|
2377
|
+
properties:
|
|
2378
|
+
name: { type: string }
|
|
2379
|
+
transport:
|
|
2380
|
+
oneOf:
|
|
2381
|
+
- type: object
|
|
2382
|
+
required: [kind, command]
|
|
2383
|
+
properties:
|
|
2384
|
+
kind: { type: string, enum: [stdio] }
|
|
2385
|
+
command: { type: string }
|
|
2386
|
+
args: { type: array, items: { type: string } }
|
|
2387
|
+
env: { type: object, additionalProperties: { type: string } }
|
|
2388
|
+
- type: object
|
|
2389
|
+
required: [kind, url]
|
|
2390
|
+
properties:
|
|
2391
|
+
kind: { type: string, enum: [http] }
|
|
2392
|
+
url: { type: string }
|
|
2393
|
+
headers: { type: object, additionalProperties: { type: string } }
|
|
2394
|
+
principalHeader: { type: string }
|
|
2395
|
+
allowTools: { type: array, items: { type: string } }
|
|
2396
|
+
elicitation: { type: boolean }
|
|
2397
|
+
toolAxes:
|
|
2398
|
+
type: object
|
|
2399
|
+
additionalProperties:
|
|
2400
|
+
type: object
|
|
2401
|
+
properties:
|
|
2402
|
+
effect: { type: string, enum: [read, write] }
|
|
2403
|
+
egress: { type: boolean }
|
|
2404
|
+
irreversibility: { type: string, enum: [always, never] }
|
|
2405
|
+
|
|
2406
|
+
MemoryRecord:
|
|
2407
|
+
type: object
|
|
2408
|
+
description: The caller's own <user_memory> (owner-scoped by construction). content null = empty.
|
|
2409
|
+
required: [scope, content]
|
|
2410
|
+
properties:
|
|
2411
|
+
scope: { type: string }
|
|
2412
|
+
content: { type: ['string', 'null'] }
|
|
2413
|
+
|
|
2414
|
+
MemoryWriteAck:
|
|
2415
|
+
type: object
|
|
2416
|
+
description: MF-30 write ack β unified { ok, id, scope } for append/edit/forget (service; the handler
|
|
2417
|
+
always returns ok β the old src:2880 summary comment that dropped it was a doc-bug). id = the note (unchanged on edit).
|
|
2418
|
+
required: [ok, id, scope]
|
|
2419
|
+
properties:
|
|
2420
|
+
ok: { type: boolean }
|
|
2421
|
+
id: { type: string }
|
|
2422
|
+
scope: { type: string }
|
|
2423
|
+
|
|
2424
|
+
ModelInfo:
|
|
2425
|
+
type: object
|
|
2426
|
+
description: >
|
|
2427
|
+
A non-secret `@model` catalog entry (GET /v1/models; service server.ts:860-880). `name` = the `@handle` the
|
|
2428
|
+
user types in the objective body to pick this model per-task. π΄ NON-SECRET β name/capabilities only, never
|
|
2429
|
+
baseUrl/apiKey/headers (the service strips them). Open set (additive future fields).
|
|
2430
|
+
required: [name]
|
|
2431
|
+
additionalProperties: true
|
|
2432
|
+
properties:
|
|
2433
|
+
name: { type: string, description: 'the @mention handle (user-pickable model name).' }
|
|
2434
|
+
id: { type: string, description: 'underlying model id (display, e.g. deepseek-v4-pro).' }
|
|
2435
|
+
provider: { type: string }
|
|
2436
|
+
reasoning: { type: boolean, description: 'a reasoning-tier model (picker can badge it).' }
|
|
2437
|
+
vision: { type: boolean, description: 'accepts image input.' }
|
|
2438
|
+
contextWindow: { type: integer, description: 'E4 (SHIPPED) β context-window token DENOMINATOR for the %-used gauge; omitted when unknown.' }
|
|
2439
|
+
maxOutputTokens: { type: integer, description: 'E4 (SHIPPED) β single-call max output tokens; omitted when unknown.' }
|
|
2440
|
+
supportedEffortLevels:
|
|
2441
|
+
type: array
|
|
2442
|
+
items: { type: string }
|
|
2443
|
+
description: 'E7 (SHIPPED) β the /effort picker default set (minimal/low/medium/high); present ONLY for a reasoning model.'
|
|
2444
|
+
|
|
2445
|
+
ElicitResponse:
|
|
2446
|
+
type: object
|
|
2447
|
+
description: >
|
|
2448
|
+
The shell's answer to an inbound-MCP elicitation (= core McpElicitResponse). On `accept`, `content` is a flat
|
|
2449
|
+
record of scalars / string[] β an OPAQUE passthrough core NEVER validates against requestedSchema; the service
|
|
2450
|
+
validates only this OUTER shape. A non-accept carries no content.
|
|
2451
|
+
required: [action]
|
|
2452
|
+
properties:
|
|
2453
|
+
action: { type: string, enum: [accept, decline, cancel] }
|
|
2454
|
+
content:
|
|
2455
|
+
type: object
|
|
2456
|
+
description: 'accept only β submitted form values (flat scalars).'
|
|
2457
|
+
additionalProperties:
|
|
2458
|
+
oneOf:
|
|
2459
|
+
- { type: string }
|
|
2460
|
+
- { type: number }
|
|
2461
|
+
- { type: boolean }
|
|
2462
|
+
- { type: array, items: { type: string } }
|
|
2463
|
+
|
|
2464
|
+
ElicitRespondAck:
|
|
2465
|
+
type: object
|
|
2466
|
+
description: The 200 ack from a successful elicitation respond (service elicitation.ts:267).
|
|
2467
|
+
required: [elicitationId, delivery, action]
|
|
2468
|
+
properties:
|
|
2469
|
+
elicitationId: { type: string }
|
|
2470
|
+
delivery: { const: applied }
|
|
2471
|
+
action: { type: string, enum: [accept, decline, cancel] }
|
|
2472
|
+
|
|
2473
|
+
QuestionAnswer:
|
|
2474
|
+
type: object
|
|
2475
|
+
description: >
|
|
2476
|
+
The shell's answer to a live `question` frame (= core QuestionAnswer, ask-question.ts:69). The service validates
|
|
2477
|
+
only this OUTER shape (`answers[]` of `{header, selected:string[], note?}`); core owns the semantic fence
|
|
2478
|
+
(`selected β the offered options`, `note` wrapped in an untrusted-DATA fence).
|
|
2479
|
+
required: [answers]
|
|
2480
|
+
properties:
|
|
2481
|
+
answers:
|
|
2482
|
+
type: array
|
|
2483
|
+
items: { $ref: '#/components/schemas/QuestionAnswerItem' }
|
|
2484
|
+
|
|
2485
|
+
QuestionAnswerItem:
|
|
2486
|
+
type: object
|
|
2487
|
+
description: >
|
|
2488
|
+
One question's answer, keyed by the question's `header`. `selected` echoes the EXACT offered option label(s)
|
|
2489
|
+
(core rejects any not in the offered options); single-select β a one-element array, multiSelect β 0..n labels.
|
|
2490
|
+
required: [header, selected]
|
|
2491
|
+
properties:
|
|
2492
|
+
header: { type: string }
|
|
2493
|
+
selected: { type: array, items: { type: string } }
|
|
2494
|
+
note: { type: string, description: 'optional free-text / off-list ("Other") input; core untrusted-fences it.' }
|
|
2495
|
+
|
|
2496
|
+
QuestionRespondAck:
|
|
2497
|
+
type: object
|
|
2498
|
+
description: The 200 ack from a successful question respond (service question.ts:226).
|
|
2499
|
+
required: [questionId, delivery]
|
|
2500
|
+
properties:
|
|
2501
|
+
questionId: { type: string }
|
|
2502
|
+
delivery: { const: applied }
|
|
2503
|
+
|
|
2504
|
+
UsageInfo:
|
|
2505
|
+
type: object
|
|
2506
|
+
description: >
|
|
2507
|
+
Per-principal cost/quota snapshot (GET /v1/usage; shape audited from source). Computed from
|
|
2508
|
+
the REQUEST principal. When no quota window is configured β enabled=false + ONLY windowSec/maxTask*
|
|
2509
|
+
(the amount/limit fields are ABSENT) β render must tolerate the gap. Open set (additive future fields).
|
|
2510
|
+
required: [enabled]
|
|
2511
|
+
additionalProperties: true
|
|
2512
|
+
properties:
|
|
2513
|
+
enabled: { type: boolean, description: 'A cost-quota WINDOW is configured (false β single-task ceilings only).' }
|
|
2514
|
+
windowSec: { type: integer, description: 'Rolling quota window seconds (e.g. 86400 = daily).' }
|
|
2515
|
+
usedMicroUsd: { type: integer, description: 'Spent in the current window (micro-USD = 1e-6 USD).' }
|
|
2516
|
+
usedUsd: { type: number, description: 'Spent in the current window (USD; convenience projection).' }
|
|
2517
|
+
limitMicroUsd: { type: integer, description: 'Window quota ceiling (micro-USD).' }
|
|
2518
|
+
limitUsd: { type: number, description: 'Window quota ceiling (USD).' }
|
|
2519
|
+
remainingMicroUsd: { type: integer, description: 'Window remaining (limit - used, floored at 0).' }
|
|
2520
|
+
overLimit: { type: boolean, description: 'Window quota exceeded (new tasks blocked until retryAfterSec).' }
|
|
2521
|
+
retryAfterSec: { type: integer, description: 'Suggested wait when overLimit.' }
|
|
2522
|
+
maxTaskCostUsd: { type: number, description: 'Single-task cost hard ceiling (USD; independent of the window, always present).' }
|
|
2523
|
+
maxTaskTokens: { type: integer, description: 'Single-task token hard ceiling (independent of the window).' }
|
|
2524
|
+
|
|
2525
|
+
PolicyInfo:
|
|
2526
|
+
type: object
|
|
2527
|
+
description: >
|
|
2528
|
+
Effective-governance snapshot (GET /v1/policy; shape audited from source). Worker-GLOBAL
|
|
2529
|
+
config (same for every principal). π΄ INSPECT-ONLY β the client never re-decides/admits from this (command
|
|
2530
|
+
admission + approval enforcement is 100% server-side). Open set; nested shapes permissive (evolution-safe).
|
|
2531
|
+
additionalProperties: true
|
|
2532
|
+
properties:
|
|
2533
|
+
autonomy: { type: ['string', 'null'], description: 'Effective autonomy band (open string; null = unset/default).' }
|
|
2534
|
+
commandPolicy:
|
|
2535
|
+
type: array
|
|
2536
|
+
description: 'Command β decision table (internal shape permissive; read command/decision).'
|
|
2537
|
+
items:
|
|
2538
|
+
type: object
|
|
2539
|
+
additionalProperties: true
|
|
2540
|
+
properties:
|
|
2541
|
+
command: { type: string }
|
|
2542
|
+
decision: { type: string }
|
|
2543
|
+
approvalRequire:
|
|
2544
|
+
type: array
|
|
2545
|
+
description: 'Conditions that force human approval (open structure; passed through, not parsed).'
|
|
2546
|
+
items: {}
|
|
2547
|
+
limits:
|
|
2548
|
+
type: object
|
|
2549
|
+
description: 'Effective ceilings (single-task + principal-level + quota window). Tolerate missing fields.'
|
|
2550
|
+
additionalProperties: true
|
|
2551
|
+
properties:
|
|
2552
|
+
maxTaskCostUsd: { type: number }
|
|
2553
|
+
maxTaskTokens: { type: integer }
|
|
2554
|
+
maxPrincipalCostUsd: { type: number }
|
|
2555
|
+
costQuotaWindowSec: { type: integer }
|
|
2556
|
+
|
|
2557
|
+
WorkflowRunStatus:
|
|
2558
|
+
type: string
|
|
2559
|
+
description: >
|
|
2560
|
+
S8 self-orchestration run lifecycle (core WorkflowRunStatus, workflow-run-store.ts). Open set (oneOf NOT
|
|
2561
|
+
exhaustive β render an unknown future state generically).
|
|
2562
|
+
examples: [running, completed, failed]
|
|
2563
|
+
|
|
2564
|
+
WorkflowRunSummary:
|
|
2565
|
+
type: object
|
|
2566
|
+
description: >
|
|
2567
|
+
GET /v1/workflows list row = core summarizeWorkflowRun projection (owner-scoped; scope = the creating
|
|
2568
|
+
principal). All times are epoch MS. Open set.
|
|
2569
|
+
required: [id, scope, status, phaseCount, agentCount, tokens, startedAt, createdAt]
|
|
2570
|
+
additionalProperties: true
|
|
2571
|
+
properties:
|
|
2572
|
+
id: { type: string, description: 'Run id (= WorkflowRun.id).' }
|
|
2573
|
+
scope: { type: string, description: 'Tenant/group scope (= the creating principal).' }
|
|
2574
|
+
status: { $ref: '#/components/schemas/WorkflowRunStatus' }
|
|
2575
|
+
phaseCount: { type: integer, description: 'Phases recorded.' }
|
|
2576
|
+
agentCount: { type: integer, description: 'Agent-runs recorded.' }
|
|
2577
|
+
tokens: { type: integer, description: 'Total tokens = own + nested (folded in the summary; a triage sort key).' }
|
|
2578
|
+
startedAt: { type: integer, description: 'Start epoch ms.' }
|
|
2579
|
+
endedAt: { type: integer, description: 'End epoch ms (absent while running).' }
|
|
2580
|
+
createdAt: { type: integer, description: 'Record-creation epoch ms (listByScope sort key).' }
|
|
2581
|
+
|
|
2582
|
+
WorkflowRun:
|
|
2583
|
+
type: object
|
|
2584
|
+
description: >
|
|
2585
|
+
GET /v1/workflows/:id full run (core WorkflowRun). Non-owner β 404 (no existence oracle).
|
|
2586
|
+
π΄ phases/agents/stats nested shapes may evolve β permissive (read length/known fields, tolerate the rest).
|
|
2587
|
+
required: [id, scope, status, phases, agents, stats, startedAt, createdAt]
|
|
2588
|
+
additionalProperties: true
|
|
2589
|
+
properties:
|
|
2590
|
+
id: { type: string }
|
|
2591
|
+
scope: { type: string }
|
|
2592
|
+
status: { $ref: '#/components/schemas/WorkflowRunStatus' }
|
|
2593
|
+
phases: { type: array, description: 'Phase records (permissive).', items: {} }
|
|
2594
|
+
agents: { type: array, description: 'Agent-run records (permissive).', items: {} }
|
|
2595
|
+
stats:
|
|
2596
|
+
type: object
|
|
2597
|
+
description: 'Token stats β own at stats.tokens, nested at stats.nested.tokens (R-5 own/nested split).'
|
|
2598
|
+
additionalProperties: true
|
|
2599
|
+
properties:
|
|
2600
|
+
tokens: { type: integer }
|
|
2601
|
+
nested:
|
|
2602
|
+
type: object
|
|
2603
|
+
additionalProperties: true
|
|
2604
|
+
properties:
|
|
2605
|
+
tokens: { type: integer }
|
|
2606
|
+
startedAt: { type: integer }
|
|
2607
|
+
endedAt: { type: integer }
|
|
2608
|
+
createdAt: { type: integer }
|
|
2609
|
+
|
|
2610
|
+
WorkflowStreamEvent:
|
|
2611
|
+
type: object
|
|
2612
|
+
description: >
|
|
2613
|
+
One frame of GET /v1/workflows/:id/stream (streamWorkflowRun). FIRST frame is `event:meta`
|
|
2614
|
+
`{version, runId}`; subsequent frames carry a core WorkflowEvent (its `type` is the SSE event name). NOT
|
|
2615
|
+
resumable (replica-local, in-process; no id/Last-Event-ID). `data` is permissive (core-internal, evolves).
|
|
2616
|
+
NOTE: on the wire this is SSE framing (event name + JSON data), not a JSON body β schema describes one
|
|
2617
|
+
decoded frame.
|
|
2618
|
+
required: [event, data]
|
|
2619
|
+
additionalProperties: true
|
|
2620
|
+
properties:
|
|
2621
|
+
event: { type: string, description: 'SSE event name = "meta" for the opener, else the core WorkflowEvent type (open set).' }
|
|
2622
|
+
data: { type: object, additionalProperties: true, description: 'The frame JSON data: payload (core WorkflowEvent shape; permissive).' }
|
|
2623
|
+
|
|
2624
|
+
# ββ MF-Fleet (shell-host data contract) β GET /v1/fleet/stream frame vocabulary (service fleet/fleet-bus.ts) ββ
|
|
2625
|
+
FleetTaskStatus:
|
|
2626
|
+
type: string
|
|
2627
|
+
description: >
|
|
2628
|
+
The fleet vocabulary the shell renders (service FleetTaskStatus, fleet-bus.ts:27-36). The service maps its
|
|
2629
|
+
run/workflow status onto this NEUTRAL set: `awaiting approval` = a needs-review/plan-approval park,
|
|
2630
|
+
`waiting` = a durable suspend. CLOSED on the wire; render OPEN (branch known, fall back generically).
|
|
2631
|
+
enum: [queued, running, waiting, stopping, 'awaiting approval', idle, completed, failed, killed]
|
|
2632
|
+
FleetTaskRow:
|
|
2633
|
+
type: object
|
|
2634
|
+
description: >
|
|
2635
|
+
One active task row (service FleetTaskRow fleet-bus.ts:40-54) β MINUS the wire-STRIPPED `scope` (a
|
|
2636
|
+
tenant-isolation field the service removes AFTER the owner-gate; never rendered, never on this wire). A
|
|
2637
|
+
`task` frame carries the POST-MERGE FULL row β replace by `id`, NOT a sparse patch. A subagent child row
|
|
2638
|
+
sets `parentId` (E2 parentToolCallId) + an id of `"<runId> <taskId>"`.
|
|
2639
|
+
required: [id, name, status]
|
|
2640
|
+
additionalProperties: true
|
|
2641
|
+
properties:
|
|
2642
|
+
id: { type: string, description: 'Stable identity (durable runId/taskId; subagent child = "<runId> <taskId>"). Update/remove by this.' }
|
|
2643
|
+
name: { type: string }
|
|
2644
|
+
description: { type: string }
|
|
2645
|
+
parentId: { type: string, description: 'Subagent nesting (E2 parentToolCallId) β top-level when absent.' }
|
|
2646
|
+
status: { $ref: '#/components/schemas/FleetTaskStatus' }
|
|
2647
|
+
elapsedMs: { type: integer }
|
|
2648
|
+
tokens: { type: integer }
|
|
2649
|
+
tokenDir: { type: string, enum: [up, down], description: 'Token-meter direction for the row animation.' }
|
|
2650
|
+
queuedCount: { type: integer }
|
|
2651
|
+
awaitingPlanApproval: { type: boolean }
|
|
2652
|
+
FleetWorkflowRow:
|
|
2653
|
+
type: object
|
|
2654
|
+
description: >
|
|
2655
|
+
One workflow row (service FleetWorkflowRow fleet-bus.ts:57-69) β MINUS the wire-stripped `scope`. `status`
|
|
2656
|
+
is the NEUTRAL workflow run status (running|completed|failed), open on read.
|
|
2657
|
+
required: [id, name, status]
|
|
2658
|
+
additionalProperties: true
|
|
2659
|
+
properties:
|
|
2660
|
+
id: { type: string }
|
|
2661
|
+
name: { type: string }
|
|
2662
|
+
description: { type: string }
|
|
2663
|
+
status: { type: string, description: 'Neutral workflow run status (running|completed|failed; open).' }
|
|
2664
|
+
doneCount: { type: integer }
|
|
2665
|
+
totalCount: { type: integer }
|
|
2666
|
+
failedCount: { type: integer }
|
|
2667
|
+
elapsedMs: { type: integer }
|
|
2668
|
+
tokens: { type: integer }
|
|
2669
|
+
FleetFrame:
|
|
2670
|
+
description: >
|
|
2671
|
+
One decoded frame off GET /v1/fleet/stream (service streamFleet emit set, http/server.ts:4877-4908). On the
|
|
2672
|
+
wire this is SSE framing (the `type` here = the SSE `event` name; the object = the decoded `data:` JSON).
|
|
2673
|
+
Emit order: `meta` (FIRST) β `snapshot` (connect-time full set) β per-row `task`/`workflow` upserts +
|
|
2674
|
+
`task_remove`/`workflow_remove` departures. The SDK PRODUCES these frames; the consumer maintains the
|
|
2675
|
+
active-set state (update-in-place by `row.id`, remove by `id`). Heartbeat `: hb` comments are not frames.
|
|
2676
|
+
OPEN set β branch on `type`, ignore an unknown future frame.
|
|
2677
|
+
oneOf:
|
|
2678
|
+
- $ref: '#/components/schemas/FleetFrame_meta'
|
|
2679
|
+
- $ref: '#/components/schemas/FleetFrame_snapshot'
|
|
2680
|
+
- $ref: '#/components/schemas/FleetFrame_task'
|
|
2681
|
+
- $ref: '#/components/schemas/FleetFrame_task_remove'
|
|
2682
|
+
- $ref: '#/components/schemas/FleetFrame_workflow'
|
|
2683
|
+
- $ref: '#/components/schemas/FleetFrame_workflow_remove'
|
|
2684
|
+
discriminator:
|
|
2685
|
+
propertyName: type
|
|
2686
|
+
mapping:
|
|
2687
|
+
meta: '#/components/schemas/FleetFrame_meta'
|
|
2688
|
+
snapshot: '#/components/schemas/FleetFrame_snapshot'
|
|
2689
|
+
task: '#/components/schemas/FleetFrame_task'
|
|
2690
|
+
task_remove: '#/components/schemas/FleetFrame_task_remove'
|
|
2691
|
+
workflow: '#/components/schemas/FleetFrame_workflow'
|
|
2692
|
+
workflow_remove: '#/components/schemas/FleetFrame_workflow_remove'
|
|
2693
|
+
FleetFrame_meta:
|
|
2694
|
+
type: object
|
|
2695
|
+
description: 'FIRST frame β `{version, scoped}`. `scoped:true` β owner-scoped view; `false` β fleet-wide (operator/trace token).'
|
|
2696
|
+
required: [type, version, scoped]
|
|
2697
|
+
properties:
|
|
2698
|
+
type: { const: meta }
|
|
2699
|
+
version: { type: integer }
|
|
2700
|
+
scoped: { type: boolean }
|
|
2701
|
+
FleetFrame_snapshot:
|
|
2702
|
+
type: object
|
|
2703
|
+
description: 'Connect-time FULL state (the visible, scope-stripped rows). Seed the active-set Map from this.'
|
|
2704
|
+
required: [type, tasks, workflows, ts]
|
|
2705
|
+
properties:
|
|
2706
|
+
type: { const: snapshot }
|
|
2707
|
+
tasks: { type: array, items: { $ref: '#/components/schemas/FleetTaskRow' } }
|
|
2708
|
+
workflows: { type: array, items: { $ref: '#/components/schemas/FleetWorkflowRow' } }
|
|
2709
|
+
ts: { type: integer }
|
|
2710
|
+
FleetFrame_task:
|
|
2711
|
+
type: object
|
|
2712
|
+
description: 'A task-row UPSERT β `row` is the POST-MERGE FULL row β replace the Map entry by `row.id`.'
|
|
2713
|
+
required: [type, row, ts]
|
|
2714
|
+
properties:
|
|
2715
|
+
type: { const: task }
|
|
2716
|
+
row: { $ref: '#/components/schemas/FleetTaskRow' }
|
|
2717
|
+
ts: { type: integer }
|
|
2718
|
+
FleetFrame_task_remove:
|
|
2719
|
+
type: object
|
|
2720
|
+
description: 'A task row left the active set (terminal + swept) β delete `id` from the Map.'
|
|
2721
|
+
required: [type, id, ts]
|
|
2722
|
+
properties:
|
|
2723
|
+
type: { const: task_remove }
|
|
2724
|
+
id: { type: string }
|
|
2725
|
+
ts: { type: integer }
|
|
2726
|
+
FleetFrame_workflow:
|
|
2727
|
+
type: object
|
|
2728
|
+
description: 'A workflow-row UPSERT β `row` is the POST-MERGE FULL row β replace by `row.id`.'
|
|
2729
|
+
required: [type, row, ts]
|
|
2730
|
+
properties:
|
|
2731
|
+
type: { const: workflow }
|
|
2732
|
+
row: { $ref: '#/components/schemas/FleetWorkflowRow' }
|
|
2733
|
+
ts: { type: integer }
|
|
2734
|
+
FleetFrame_workflow_remove:
|
|
2735
|
+
type: object
|
|
2736
|
+
description: 'A workflow row left the active set β delete `id`.'
|
|
2737
|
+
required: [type, id, ts]
|
|
2738
|
+
properties:
|
|
2739
|
+
type: { const: workflow_remove }
|
|
2740
|
+
id: { type: string }
|
|
2741
|
+
ts: { type: integer }
|
|
2742
|
+
|
|
2743
|
+
TraceStreamEvent:
|
|
2744
|
+
type: object
|
|
2745
|
+
description: >
|
|
2746
|
+
One frame of GET /v1/tasks/:id/stream β the workspace LIVE timeline (streamTaskTrace + mapTraceEvent). A
|
|
2747
|
+
DISTINCT vocabulary from AgentEvent (the durable log is BLOCK-grained, each frame is a delta to append).
|
|
2748
|
+
Resumable (each `id:` = the durable task_event.seq; on 416 the server returns `{retainedFrom}` and the SDK
|
|
2749
|
+
resumes from there after a turns full-sync). Known events: meta / block-thinking-delta / block-content-delta
|
|
2750
|
+
/ tool-call / tool-result / turn / done / error (+ heartbeat keep-alives the SDK swallows). Open set.
|
|
2751
|
+
NOTE: SSE framing on the wire (event name + JSON data + optional id), not a JSON body.
|
|
2752
|
+
required: [event, data]
|
|
2753
|
+
additionalProperties: true
|
|
2754
|
+
properties:
|
|
2755
|
+
event: { type: string, description: 'SSE event name (open set; dispatch on this).' }
|
|
2756
|
+
id: { type: string, description: 'The durable seq (id: line) when present β feed back as Last-Event-ID to resume. meta/heartbeat carry none.' }
|
|
2757
|
+
data: { type: object, additionalProperties: true, description: 'The frame JSON data: payload (shape varies by event; permissive).' }
|
|
2758
|
+
|
|
2759
|
+
PendingList:
|
|
2760
|
+
type: object
|
|
2761
|
+
description: Envelope for pending HITL checkpoints (key `pending`, NOT a bare array β the pinned wire contract).
|
|
2762
|
+
required: [pending]
|
|
2763
|
+
properties:
|
|
2764
|
+
pending:
|
|
2765
|
+
type: array
|
|
2766
|
+
items: { $ref: '#/components/schemas/PendingCheckpoint' }
|
|
2767
|
+
|
|
2768
|
+
ExemptionList:
|
|
2769
|
+
type: object
|
|
2770
|
+
description: Envelope for a session's approval exemptions (; key `exemptions`, NOT a bare array).
|
|
2771
|
+
required: [exemptions]
|
|
2772
|
+
properties:
|
|
2773
|
+
exemptions:
|
|
2774
|
+
type: array
|
|
2775
|
+
items: { $ref: '#/components/schemas/ApprovalExemption' }
|
|
2776
|
+
|
|
2777
|
+
ApprovalExemption:
|
|
2778
|
+
type: object
|
|
2779
|
+
description: >
|
|
2780
|
+
One granted "don't ask again" exemption (service ApprovalExemptionRow). An APPROVAL
|
|
2781
|
+
MEMORY only β deny/neverAuto always outrank it; it never loosens the session-policy layer.
|
|
2782
|
+
required: [toolName, grantedBy, createdAt]
|
|
2783
|
+
properties:
|
|
2784
|
+
toolName: { type: string, description: Canonical tool name the exemption covers. }
|
|
2785
|
+
grantedBy:
|
|
2786
|
+
type: ['string', 'null']
|
|
2787
|
+
description: Principal that granted it; null = ownerless/anonymous grant.
|
|
2788
|
+
createdAt: { type: number, description: Epoch MS of the FIRST grant (re-grant refreshes grantedBy only). }
|
|
2789
|
+
|
|
2790
|
+
PendingCheckpoint:
|
|
2791
|
+
type: object
|
|
2792
|
+
description: >
|
|
2793
|
+
A pending HITL checkpoint (true shape). NEVER includes a capability token.
|
|
2794
|
+
β οΈ `createdAt`/`deadline` are EPOCH MILLISECONDS (numbers), not ISO strings.
|
|
2795
|
+
π΄ ASSISTANT-WIRE-CONTRACT Β§1/Β§7 (service main 6cd0164 / core 1.110.0): this MIRRORS the core
|
|
2796
|
+
`CheckpointSummary` β `token`/`gateKind`/`severity?`/`spentMicroUsd?` are Β§1 fields (the inbox endpoint
|
|
2797
|
+
projects the full set; legacy `/v1/approvals` may omit them, so they are optional here). Project ONLY
|
|
2798
|
+
the `severity` SCALAR β NEVER a `riskDescriptor` object.
|
|
2799
|
+
required: [sessionId, scope]
|
|
2800
|
+
additionalProperties: true
|
|
2801
|
+
properties:
|
|
2802
|
+
sessionId: { type: string, description: 'The decide handle: POST /v1/approvals/:sessionId/decide.' }
|
|
2803
|
+
token: { type: string, description: 'ASSISTANT Β§1 β resume capability token (opaque; echo, never parse).' }
|
|
2804
|
+
gateKind:
|
|
2805
|
+
type: string
|
|
2806
|
+
enum: [human, irreversible_ask, resource_limit, needs_review, plan_review, task_done]
|
|
2807
|
+
description: 'ASSISTANT Β§1 β which kind of pause (6-arm open set).'
|
|
2808
|
+
severity:
|
|
2809
|
+
type: integer
|
|
2810
|
+
enum: [1, 2, 3, 4, 5]
|
|
2811
|
+
description: >
|
|
2812
|
+
ASSISTANT Β§1 β deterministic risk tier = the inbox SORT KEY. PRESENT ONLY for human/irreversible_ask
|
|
2813
|
+
gates; ABSENT for resource_limit/needs_review/plan_review β the UI MUST tolerate undefined.
|
|
2814
|
+
spentMicroUsd: { type: number, description: 'ASSISTANT Β§1 β cumulative spend on the suspend chain.' }
|
|
2815
|
+
taskId: { type: ['string', 'null'], description: 'Suspended run holding the session claim (service-pinned) β join key to trace tool-call blocks + context link.' }
|
|
2816
|
+
scope: { type: string, description: 'Principal; "_" = submitted without one.' }
|
|
2817
|
+
toolName: { type: ['string', 'null'], description: Tool awaiting approval (AskUserQuestion = the question gate). }
|
|
2818
|
+
toolCallId: { type: ['string', 'null'] }
|
|
2819
|
+
input:
|
|
2820
|
+
description: >
|
|
2821
|
+
The pending tool call's args (post-hook), REDACTED + size-bounded β for a tool gate the write
|
|
2822
|
+
payload, for an AskUserQuestion the question itself. Lets the approval card render WITHOUT an N+1
|
|
2823
|
+
`trace.turns` fetch per item. Oversized payloads collapse to `{ truncated, bytes }`.
|
|
2824
|
+
`null` for checkpoints suspended before this field existed (consumer falls back to the trace join).
|
|
2825
|
+
# design/80 D-1 β the three binding values the human implicitly approves; the consumer reads them here
|
|
2826
|
+
# and echoes them VERBATIM into decide (TOCTOU guard). Absent on pre-D-1 checkpoints β omit from decide.
|
|
2827
|
+
checkpointToken: { type: string, description: 'Single-use token for the exact pending checkpoint the human saw.' }
|
|
2828
|
+
boundCallId: { type: string, description: '= toolCallId of the action the human saw (the bound call).' }
|
|
2829
|
+
boundInputHash: { type: string, description: 'Server-minted OPAQUE sha256 over the pending action args; echo verbatim, NEVER recompute. Binds the input the human SAW, not any updatedInput.' }
|
|
2830
|
+
createdAt: { type: integer, description: epoch ms. }
|
|
2831
|
+
deadline: { type: ['integer', 'null'], description: epoch ms; expired checkpoints are reaped. }
|
|
2832
|
+
|
|
2833
|
+
# ββ assistant-scheduler wire (ASSISTANT-WIRE-CONTRACT.md, service main 6cd0164 / core 1.110.0, LIVE) ββ
|
|
2834
|
+
CheckpointSummary:
|
|
2835
|
+
type: object
|
|
2836
|
+
description: >
|
|
2837
|
+
ASSISTANT-WIRE-CONTRACT Β§1 β core's lightweight projection of every PENDING checkpoint
|
|
2838
|
+
(`summarizeCheckpoint`, identical across all store backends). The shared inbox/overview row shape.
|
|
2839
|
+
Null-able fields are OMITTED when absent, not null. π΄ projects ONLY the `severity` SCALAR β NO
|
|
2840
|
+
`riskDescriptor` object.
|
|
2841
|
+
required: [token, sessionId, scope, gateKind]
|
|
2842
|
+
additionalProperties: true
|
|
2843
|
+
properties:
|
|
2844
|
+
token: { type: string, description: 'resume capability token (opaque; echo, never parse).' }
|
|
2845
|
+
sessionId: { type: string, description: 'the suspended task/session id.' }
|
|
2846
|
+
scope: { type: string, description: 'the multi-tenant owner scope.' }
|
|
2847
|
+
gateKind:
|
|
2848
|
+
type: string
|
|
2849
|
+
enum: [human, irreversible_ask, resource_limit, needs_review, plan_review, task_done]
|
|
2850
|
+
description: 'which kind of pause (6-arm open set).'
|
|
2851
|
+
severity:
|
|
2852
|
+
type: integer
|
|
2853
|
+
enum: [1, 2, 3, 4, 5]
|
|
2854
|
+
description: >
|
|
2855
|
+
deterministic risk tier = the inbox SORT KEY. PRESENT ONLY for human/irreversible_ask gates
|
|
2856
|
+
(they carry a RiskDescriptor); ABSENT for resource_limit/needs_review/plan_review β UI tolerates undefined.
|
|
2857
|
+
spentMicroUsd: { type: number, description: 'cumulative spend on the suspend chain (when a resource ledger is attached).' }
|
|
2858
|
+
deadline: { type: integer, description: 'awaiting-human SLA deadline (epoch ms).' }
|
|
2859
|
+
|
|
2860
|
+
InboxRow:
|
|
2861
|
+
description: >
|
|
2862
|
+
ASSISTANT-WIRE-CONTRACT Β§2 β an inbox row = a CheckpointSummary + an `objective` (enriched from the
|
|
2863
|
+
task ctx; `null` if unavailable).
|
|
2864
|
+
allOf:
|
|
2865
|
+
- $ref: '#/components/schemas/CheckpointSummary'
|
|
2866
|
+
- type: object
|
|
2867
|
+
properties:
|
|
2868
|
+
objective: { type: ['string', 'null'], description: 'task objective, null if unavailable.' }
|
|
2869
|
+
|
|
2870
|
+
InboxList:
|
|
2871
|
+
type: object
|
|
2872
|
+
description: 'ASSISTANT-WIRE-CONTRACT Β§2 β `GET /v1/assistant/inbox` envelope. Already severity-sorted by core.'
|
|
2873
|
+
required: [inbox]
|
|
2874
|
+
additionalProperties: true
|
|
2875
|
+
properties:
|
|
2876
|
+
inbox:
|
|
2877
|
+
type: array
|
|
2878
|
+
items: { $ref: '#/components/schemas/InboxRow' }
|
|
2879
|
+
|
|
2880
|
+
AssistantTaskRow:
|
|
2881
|
+
type: object
|
|
2882
|
+
description: >
|
|
2883
|
+
ASSISTANT-WIRE-CONTRACT Β§3 β a scheduler-overview row = run-store `listRuns(owner)` joined with the core
|
|
2884
|
+
scheduler seam. β οΈ `createdAt`/`updatedAt` are ISO date-time strings (unlike inbox/Β§1 epoch-ms).
|
|
2885
|
+
required: [taskId, sessionId, status, needsAttention, createdAt, updatedAt]
|
|
2886
|
+
additionalProperties: true
|
|
2887
|
+
properties:
|
|
2888
|
+
taskId: { type: string }
|
|
2889
|
+
sessionId: { type: string }
|
|
2890
|
+
status: { type: string, enum: [running, suspended] }
|
|
2891
|
+
needsAttention: { type: boolean, description: '= suspended on a HITL gate.' }
|
|
2892
|
+
gate:
|
|
2893
|
+
type: ['object', 'null']
|
|
2894
|
+
description: 'the active gate, or null. severity/spentMicroUsd/deadline may themselves be null on the wire.'
|
|
2895
|
+
additionalProperties: true
|
|
2896
|
+
properties:
|
|
2897
|
+
kind: { type: string, description: 'same open set as gateKind.' }
|
|
2898
|
+
severity: { type: ['integer', 'null'], enum: [1, 2, 3, 4, 5, null] }
|
|
2899
|
+
spentMicroUsd: { type: ['number', 'null'] }
|
|
2900
|
+
deadline: { type: ['integer', 'null'], description: epoch ms. }
|
|
2901
|
+
createdAt: { type: string, description: ISO date-time. }
|
|
2902
|
+
updatedAt: { type: string, description: ISO date-time. }
|
|
2903
|
+
|
|
2904
|
+
AssistantTaskList:
|
|
2905
|
+
type: object
|
|
2906
|
+
description: >
|
|
2907
|
+
ASSISTANT-WIRE-CONTRACT Β§3 β `GET /v1/assistant/tasks` envelope. Triage-ordered by core (needs-attention
|
|
2908
|
+
first, then severity DESC, then spend DESC). Empty `{tasks:[]}` when no TiDB run store.
|
|
2909
|
+
required: [tasks]
|
|
2910
|
+
additionalProperties: true
|
|
2911
|
+
properties:
|
|
2912
|
+
tasks:
|
|
2913
|
+
type: array
|
|
2914
|
+
items: { $ref: '#/components/schemas/AssistantTaskRow' }
|
|
2915
|
+
|
|
2916
|
+
PlanReviewRequest:
|
|
2917
|
+
type: object
|
|
2918
|
+
description: >
|
|
2919
|
+
ASSISTANT-WIRE-CONTRACT Β§4c β `POST /v1/assistant/tasks/:id/plan_review` body (3-state). `editedPlan` is
|
|
2920
|
+
REQUIRED iff `decision==="edit"` (FORBIDDEN otherwise β 400).
|
|
2921
|
+
required: [decision]
|
|
2922
|
+
additionalProperties: false
|
|
2923
|
+
properties:
|
|
2924
|
+
decision: { type: string, enum: [approve, edit, reject] }
|
|
2925
|
+
editedPlan: { type: string, description: 'the operator''s revised plan β required iff decision==="edit".' }
|
|
2926
|
+
reason: { type: string }
|
|
2927
|
+
|
|
2928
|
+
SessionRecord:
|
|
2929
|
+
type: object
|
|
2930
|
+
description: >
|
|
2931
|
+
Session audit (`GET /v1/sessions/:id`) β true shape. `messages` = current model context
|
|
2932
|
+
(older history folded into a summary message); can be large.
|
|
2933
|
+
required: [sessionId, createdAt, messages]
|
|
2934
|
+
additionalProperties: true
|
|
2935
|
+
properties:
|
|
2936
|
+
sessionId: { type: string }
|
|
2937
|
+
owner: { type: ['string', 'null'], description: Principal. }
|
|
2938
|
+
createdAt: { type: string, description: ISO timestamp (NOTE β unlike approvals' epoch-ms). }
|
|
2939
|
+
floorEntryId: { type: ['string', 'null'], description: Compaction window floor; null = never compacted. }
|
|
2940
|
+
thinkingLevel: { type: string }
|
|
2941
|
+
model:
|
|
2942
|
+
type: ['object', 'null']
|
|
2943
|
+
properties:
|
|
2944
|
+
provider: { type: string }
|
|
2945
|
+
modelId: { type: string }
|
|
2946
|
+
messages:
|
|
2947
|
+
type: array
|
|
2948
|
+
items: {}
|
|
2949
|
+
|
|
2950
|
+
SessionSummary:
|
|
2951
|
+
type: object
|
|
2952
|
+
description: >
|
|
2953
|
+
E16 (shell-host sessionPicker; service SessionSummary tidb-run-store.ts:42) β a `GET /v1/sessions` list row
|
|
2954
|
+
aggregating a session's run ledger (last activity + most-recent objective preview + status). owner-scoped.
|
|
2955
|
+
All timestamps ISO. `objectivePreview` is service-redacted + truncated (genuinely `null` when no run).
|
|
2956
|
+
required: [sessionId, owner, lastActivityAt, firstActivityAt, runCount, objectivePreview, lastStatus]
|
|
2957
|
+
additionalProperties: true
|
|
2958
|
+
properties:
|
|
2959
|
+
sessionId: { type: string }
|
|
2960
|
+
owner: { type: ['string', 'null'] }
|
|
2961
|
+
lastActivityAt: { type: string }
|
|
2962
|
+
firstActivityAt: { type: string }
|
|
2963
|
+
runCount: { type: integer }
|
|
2964
|
+
objectivePreview: { type: ['string', 'null'] }
|
|
2965
|
+
lastStatus: { type: string }
|
|
2966
|
+
|
|
2967
|
+
SessionListPage:
|
|
2968
|
+
type: object
|
|
2969
|
+
description: Keyset page envelope for `GET /v1/sessions`. `nextCursor` absent = last page.
|
|
2970
|
+
required: [sessions]
|
|
2971
|
+
properties:
|
|
2972
|
+
sessions:
|
|
2973
|
+
type: array
|
|
2974
|
+
items: { $ref: '#/components/schemas/SessionSummary' }
|
|
2975
|
+
nextCursor: { type: string, description: "Opaque keyset cursor (echo verbatim as ?cursor=)." }
|
|
2976
|
+
|
|
2977
|
+
SessionPermissionRules:
|
|
2978
|
+
type: object
|
|
2979
|
+
description: >
|
|
2980
|
+
E6 (shell-host permissionMode; core SessionPermissionRules session-policy-store.ts:23) β operator-tightened
|
|
2981
|
+
per-session tool-permission rules (core reads them at prepare-time, SUBTRACT-only). 5 optional `string[]`
|
|
2982
|
+
fields. tighten-only invariant is core-enforced (a loosen needs an operator).
|
|
2983
|
+
properties:
|
|
2984
|
+
toolAllow: { type: array, items: { type: string }, description: Narrowing allowlist (only these tools allowed). }
|
|
2985
|
+
toolDeny: { type: array, items: { type: string }, description: Always-denied tool names (deny wins). }
|
|
2986
|
+
allowDirs: { type: array, items: { type: string }, description: Write tools confined to these dirs; bash denied while set. }
|
|
2987
|
+
commandAllow: { type: array, items: { type: string }, description: "Only these bash command names (argv[0]) allowed." }
|
|
2988
|
+
commandDeny: { type: array, items: { type: string }, description: "Always-denied bash command names (argv[0])." }
|
|
2989
|
+
|
|
2990
|
+
StoredSessionRules:
|
|
2991
|
+
description: '{@link SessionPermissionRules} + the monotonic `rev` the store stamps (the OCC key).'
|
|
2992
|
+
allOf:
|
|
2993
|
+
- $ref: '#/components/schemas/SessionPermissionRules'
|
|
2994
|
+
- type: object
|
|
2995
|
+
required: [rev]
|
|
2996
|
+
properties:
|
|
2997
|
+
rev: { type: integer }
|
|
2998
|
+
|
|
2999
|
+
McpServerStatus:
|
|
3000
|
+
type: object
|
|
3001
|
+
description: >
|
|
3002
|
+
E9 (shell-host mcpStatus; core McpServerStatus core/mcp.ts:76) β one MCP server's status at materialization
|
|
3003
|
+
time. `status` is connected | failed (core never emits disabled). `error` (failed only) is service-redacted
|
|
3004
|
+
+ length-bounded.
|
|
3005
|
+
required: [name, status]
|
|
3006
|
+
additionalProperties: true
|
|
3007
|
+
properties:
|
|
3008
|
+
name: { type: string }
|
|
3009
|
+
status: { type: string, description: "connected | failed (open set)" }
|
|
3010
|
+
serverInfo:
|
|
3011
|
+
type: object
|
|
3012
|
+
properties:
|
|
3013
|
+
name: { type: string }
|
|
3014
|
+
version: { type: string }
|
|
3015
|
+
toolNames: { type: array, items: { type: string } }
|
|
3016
|
+
error: { type: string }
|
|
3017
|
+
|
|
3018
|
+
McpStatusPanel:
|
|
3019
|
+
type: object
|
|
3020
|
+
description: >
|
|
3021
|
+
`GET /v1/sessions/:id/mcp` envelope. `asOf` = THIS materialization moment (ISO). `degraded:true` (servers
|
|
3022
|
+
empty) β materialize timed out/failed (NOT "no MCP"). No MCP configured β `servers:[]` without `degraded`.
|
|
3023
|
+
required: [asOf, servers]
|
|
3024
|
+
additionalProperties: true
|
|
3025
|
+
properties:
|
|
3026
|
+
asOf: { type: string }
|
|
3027
|
+
servers:
|
|
3028
|
+
type: array
|
|
3029
|
+
items: { $ref: '#/components/schemas/McpServerStatus' }
|
|
3030
|
+
degraded: { type: boolean }
|
|
3031
|
+
|
|
3032
|
+
# ββ 2c session-sync (P1d) β the cloud-as-a-SYNC-PEER schemas ββββββββββββββββββββββββββββββββββββββββββββββ
|
|
3033
|
+
SyncEntry:
|
|
3034
|
+
type: object
|
|
3035
|
+
description: >
|
|
3036
|
+
ONE session-tree entry as it crosses the sync wire β OPAQUE to the SDK (verbatim, cross-backend-stable ids).
|
|
3037
|
+
The SDK reads only `.id` (for the Β§7 id-set classify) and pipes the rest through untouched; the cloud
|
|
3038
|
+
re-validates the whole tree (unique ids / parent-before-child / one-root / leaf-resolvable).
|
|
3039
|
+
required: [id]
|
|
3040
|
+
additionalProperties: true
|
|
3041
|
+
properties:
|
|
3042
|
+
id: { type: string, description: Stable entry id, unique within a session (the Β§7 divergence key). }
|
|
3043
|
+
parentId: { type: [string, 'null'] }
|
|
3044
|
+
type: { type: string, description: Entry discriminator (message / leaf / compaction / β¦) β opaque to the SDK. }
|
|
3045
|
+
|
|
3046
|
+
SyncSnapshot:
|
|
3047
|
+
type: object
|
|
3048
|
+
description: >
|
|
3049
|
+
One E19 file snapshot keyed by a `SessionTreeEntry.id`. `manifest` = `[relPath, blobHash]` tuples; the blob
|
|
3050
|
+
BYTES are NOT inlined (they ride the content-addressed blob routes).
|
|
3051
|
+
required: [key, manifest]
|
|
3052
|
+
properties:
|
|
3053
|
+
key: { type: string }
|
|
3054
|
+
manifest:
|
|
3055
|
+
type: array
|
|
3056
|
+
items:
|
|
3057
|
+
type: array
|
|
3058
|
+
minItems: 2
|
|
3059
|
+
maxItems: 2
|
|
3060
|
+
items: { type: string }
|
|
3061
|
+
description: A `[relPath, blobHash]` tuple.
|
|
3062
|
+
|
|
3063
|
+
SyncAnchor:
|
|
3064
|
+
type: object
|
|
3065
|
+
description: One E18 resume-at anchor. `owner` is RE-KEYED to the importing principal on a PUSH (Β§9).
|
|
3066
|
+
required: [eventId, entryId, owner]
|
|
3067
|
+
properties:
|
|
3068
|
+
eventId: { type: string }
|
|
3069
|
+
entryId: { type: string }
|
|
3070
|
+
owner: { type: [string, 'null'] }
|
|
3071
|
+
|
|
3072
|
+
SessionRulesRecord:
|
|
3073
|
+
type: object
|
|
3074
|
+
description: A (principal, rules) policy record β one row across ALL principals (E6 `listBySession`). Replayed verbatim on import (the cloud applies the tighten-only gate).
|
|
3075
|
+
required: [rules]
|
|
3076
|
+
properties:
|
|
3077
|
+
principal: { type: string, description: Absent = the session-default rules. }
|
|
3078
|
+
rules: { $ref: '#/components/schemas/StoredSessionRules' }
|
|
3079
|
+
|
|
3080
|
+
SessionManifest:
|
|
3081
|
+
type: object
|
|
3082
|
+
description: >
|
|
3083
|
+
2c session-sync `GET β¦/sync/manifest` payload (wrapped server-side as `{ manifest }`). The THIN cross-backend
|
|
3084
|
+
snapshot: entry IDS (oldest-first, NOT payloads) + per-snapshot relPathβblobHash + policy + anchors + leaf.
|
|
3085
|
+
required: [sessionId, entryIds, entryCount, leafId, snapshots, policy, anchors]
|
|
3086
|
+
properties:
|
|
3087
|
+
sessionId: { type: string }
|
|
3088
|
+
entryIds: { type: array, items: { type: string }, description: Oldest-first, verbatim cross-backend. }
|
|
3089
|
+
entryCount: { type: integer, description: '= entryIds.length (the `/sync/entries` trailer count).' }
|
|
3090
|
+
leafId: { type: [string, 'null'] }
|
|
3091
|
+
snapshots: { type: array, items: { $ref: '#/components/schemas/SyncSnapshot' } }
|
|
3092
|
+
policy: { type: array, items: { $ref: '#/components/schemas/SessionRulesRecord' } }
|
|
3093
|
+
anchors: { type: array, items: { $ref: '#/components/schemas/SyncAnchor' } }
|
|
3094
|
+
|
|
3095
|
+
SessionBundle:
|
|
3096
|
+
type: object
|
|
3097
|
+
description: >
|
|
3098
|
+
The portable state of ONE session, ready to replay into another backend. Blobs are NOT inlined (each
|
|
3099
|
+
snapshot carries only its `manifest`; the bytes stream through the content-addressed blob routes). The SDK
|
|
3100
|
+
assembles this from a manifest + the streamed entries for a PUSH.
|
|
3101
|
+
required: [sessionId, entries, snapshots, policy, anchors]
|
|
3102
|
+
properties:
|
|
3103
|
+
sessionId: { type: string }
|
|
3104
|
+
entries: { type: array, items: { $ref: '#/components/schemas/SyncEntry' } }
|
|
3105
|
+
snapshots: { type: array, items: { $ref: '#/components/schemas/SyncSnapshot' } }
|
|
3106
|
+
policy: { type: array, items: { $ref: '#/components/schemas/SessionRulesRecord' } }
|
|
3107
|
+
anchors: { type: array, items: { $ref: '#/components/schemas/SyncAnchor' } }
|
|
3108
|
+
|
|
3109
|
+
SyncRelation:
|
|
3110
|
+
description: >
|
|
3111
|
+
Β§7 β how a SOURCE log relates to a DESTINATION log, decided over the entry-ID SETS. A discriminated union on
|
|
3112
|
+
`relation`. `fresh`/`identical` carry nothing else; `fast_forward` the appended tail; `stale` the dst entries
|
|
3113
|
+
the src lacks; `fork` the common ancestor + each side's exclusive ids.
|
|
3114
|
+
oneOf:
|
|
3115
|
+
- type: object
|
|
3116
|
+
required: [relation]
|
|
3117
|
+
properties: { relation: { const: fresh } }
|
|
3118
|
+
- type: object
|
|
3119
|
+
required: [relation]
|
|
3120
|
+
properties: { relation: { const: identical } }
|
|
3121
|
+
- type: object
|
|
3122
|
+
required: [relation, newEntryIds]
|
|
3123
|
+
properties:
|
|
3124
|
+
relation: { const: fast_forward }
|
|
3125
|
+
newEntryIds: { type: array, items: { type: string } }
|
|
3126
|
+
- type: object
|
|
3127
|
+
required: [relation, dstAheadBy]
|
|
3128
|
+
properties:
|
|
3129
|
+
relation: { const: stale }
|
|
3130
|
+
dstAheadBy: { type: array, items: { type: string } }
|
|
3131
|
+
- type: object
|
|
3132
|
+
required: [relation, commonAncestor, srcExclusive, dstExclusive]
|
|
3133
|
+
properties:
|
|
3134
|
+
relation: { const: fork }
|
|
3135
|
+
commonAncestor: { type: [string, 'null'] }
|
|
3136
|
+
srcExclusive: { type: array, items: { type: string } }
|
|
3137
|
+
dstExclusive: { type: array, items: { type: string } }
|
|
3138
|
+
discriminator:
|
|
3139
|
+
propertyName: relation
|
|
3140
|
+
mapping:
|
|
3141
|
+
fresh: '#/components/schemas/SyncRelation'
|
|
3142
|
+
identical: '#/components/schemas/SyncRelation'
|
|
3143
|
+
fast_forward: '#/components/schemas/SyncRelation'
|
|
3144
|
+
stale: '#/components/schemas/SyncRelation'
|
|
3145
|
+
fork: '#/components/schemas/SyncRelation'
|
|
3146
|
+
|
|
3147
|
+
ImportStaged:
|
|
3148
|
+
type: object
|
|
3149
|
+
description: >
|
|
3150
|
+
`POST β¦/sync/import` Phase-A result. `identical` β no `stagingId` (skip Phase B); `fresh`/`fast_forward` β
|
|
3151
|
+
`{ stagingId, relation }`. `relation` is the BARE classifier tag (not the full object).
|
|
3152
|
+
required: [relation]
|
|
3153
|
+
properties:
|
|
3154
|
+
stagingId: { type: string, description: Present iff Phase B is needed (absent on `identical`). }
|
|
3155
|
+
relation: { type: string, enum: [fresh, identical, fast_forward], description: The Β§7 tag classified at Phase A. }
|
|
3156
|
+
|
|
3157
|
+
ImportCommitted:
|
|
3158
|
+
type: object
|
|
3159
|
+
description: '`POST β¦/sync/import/:stagingId/entries` (Phase B) commit result β the AUTHORITATIVE in-txn re-classify tag.'
|
|
3160
|
+
required: [relation]
|
|
3161
|
+
properties:
|
|
3162
|
+
relation: { type: string, enum: [fresh, identical, fast_forward], description: The committed Β§7 tag. }
|
|
3163
|
+
|
|
3164
|
+
SyncConflict:
|
|
3165
|
+
type: object
|
|
3166
|
+
description: >
|
|
3167
|
+
409 body for a 2c session-sync PUSH that would lose destination history. `errorCode:"conflict"` + the Β§7
|
|
3168
|
+
`relation` (`fork`/`stale`) carrying the exclusive entry sets β the SDK surfaces a typed SyncConflictError.
|
|
3169
|
+
The SAME envelope also carries an active-run / in-flight-import conflict WITHOUT a `relation` (ConflictError).
|
|
3170
|
+
additionalProperties: true
|
|
3171
|
+
properties:
|
|
3172
|
+
error: { type: string }
|
|
3173
|
+
errorCode: { type: string, enum: [conflict, session_active, import_in_flight] }
|
|
3174
|
+
relation: { $ref: '#/components/schemas/SyncRelation' }
|
|
3175
|
+
activeTaskId: { type: string, description: Present on `session_active` β the task holding the session lock. }
|
|
3176
|
+
|
|
3177
|
+
ErrorResponse:
|
|
3178
|
+
type: object
|
|
3179
|
+
description: >
|
|
3180
|
+
Typed-error source. The SDK maps (status, errorCode) β a semantic error class so callers branch on the
|
|
3181
|
+
error, not a string. Messages are secret-scrubbed before surfacing.
|
|
3182
|
+
additionalProperties: true
|
|
3183
|
+
properties:
|
|
3184
|
+
errorCode:
|
|
3185
|
+
type: string
|
|
3186
|
+
description: >
|
|
3187
|
+
HTTP-error typed code. Known: `conflict` (409 session active / CAS), `quotaExceeded` (429).
|
|
3188
|
+
NOTE: budget (`budget.precall`/`budget.exceeded`) and `cancelled` are RESULT-level codes on
|
|
3189
|
+
TaskResult/RunRecord, NOT HTTP errors (pinned wire contract).
|
|
3190
|
+
error: { type: string }
|
|
3191
|
+
errorMessage: { type: string }
|
|
3192
|
+
activeTaskId:
|
|
3193
|
+
type: string
|
|
3194
|
+
description: On a 409 β the task currently holding the session lock (see the Conflict response note).
|
|
3195
|
+
|
|
3196
|
+
# ββ Streaming event taxonomy (the UX ε½θ). Each SSE `data:` line is one JSON-serialized AgentEvent. ββ
|
|
3197
|
+
# TWO vocabularies (the pinned wire contract Drift 3):
|
|
3198
|
+
# live (POST /v1/tasks/stream): text_delta / reasoning_delta (delta = string fragment, core-native)
|
|
3199
|
+
# + tool_start/tool_end/turn_end/compacted/done
|
|
3200
|
+
# durable (GET /v1/runs/:id/events): text / reasoning (turn-AGGREGATED, no deltas)
|
|
3201
|
+
# + tool_start/tool_end/turn_end/compacted/suspended/done/failed
|
|
3202
|
+
AgentEvent:
|
|
3203
|
+
oneOf:
|
|
3204
|
+
- $ref: '#/components/schemas/Event_reasoning'
|
|
3205
|
+
- $ref: '#/components/schemas/Event_text'
|
|
3206
|
+
- $ref: '#/components/schemas/Event_reasoning_delta'
|
|
3207
|
+
- $ref: '#/components/schemas/Event_text_delta'
|
|
3208
|
+
- $ref: '#/components/schemas/Event_tool_start'
|
|
3209
|
+
- $ref: '#/components/schemas/Event_tool_end'
|
|
3210
|
+
- $ref: '#/components/schemas/Event_turn_end'
|
|
3211
|
+
- $ref: '#/components/schemas/Event_compacted'
|
|
3212
|
+
- $ref: '#/components/schemas/Event_suggestions'
|
|
3213
|
+
- $ref: '#/components/schemas/Event_status'
|
|
3214
|
+
- $ref: '#/components/schemas/Event_task_progress'
|
|
3215
|
+
- $ref: '#/components/schemas/Event_workspace_changed'
|
|
3216
|
+
- $ref: '#/components/schemas/Event_suspended'
|
|
3217
|
+
- $ref: '#/components/schemas/Event_done'
|
|
3218
|
+
- $ref: '#/components/schemas/Event_failed'
|
|
3219
|
+
discriminator:
|
|
3220
|
+
propertyName: type
|
|
3221
|
+
mapping:
|
|
3222
|
+
reasoning: '#/components/schemas/Event_reasoning'
|
|
3223
|
+
text: '#/components/schemas/Event_text'
|
|
3224
|
+
reasoning_delta: '#/components/schemas/Event_reasoning_delta'
|
|
3225
|
+
text_delta: '#/components/schemas/Event_text_delta'
|
|
3226
|
+
tool_start: '#/components/schemas/Event_tool_start'
|
|
3227
|
+
tool_end: '#/components/schemas/Event_tool_end'
|
|
3228
|
+
turn_end: '#/components/schemas/Event_turn_end'
|
|
3229
|
+
compacted: '#/components/schemas/Event_compacted'
|
|
3230
|
+
suggestions: '#/components/schemas/Event_suggestions'
|
|
3231
|
+
status: '#/components/schemas/Event_status'
|
|
3232
|
+
task_progress: '#/components/schemas/Event_task_progress'
|
|
3233
|
+
workspace_changed: '#/components/schemas/Event_workspace_changed'
|
|
3234
|
+
suspended: '#/components/schemas/Event_suspended'
|
|
3235
|
+
done: '#/components/schemas/Event_done'
|
|
3236
|
+
failed: '#/components/schemas/Event_failed'
|
|
3237
|
+
|
|
3238
|
+
# design/99 Β§E2 (SHIPPED, core TaskEventIdentity) β additive per-event identity on every CONTENT event (NOT on
|
|
3239
|
+
# the lifecycle arms turn_end/compacted/suspended/done/failed). eventId = uuidv7 dedup/resume handle (the durable
|
|
3240
|
+
# `text` re-stamps the turn's first text_delta eventId, runs.ts:250); parentToolCallId = sub-agent attribution.
|
|
3241
|
+
EventIdentity:
|
|
3242
|
+
type: object
|
|
3243
|
+
properties:
|
|
3244
|
+
eventId: { type: string }
|
|
3245
|
+
parentToolCallId: { type: string }
|
|
3246
|
+
Event_reasoning:
|
|
3247
|
+
type: object
|
|
3248
|
+
description: durable stream only β turn-aggregated thinking.
|
|
3249
|
+
allOf: [{ $ref: '#/components/schemas/EventIdentity' }]
|
|
3250
|
+
required: [type, text]
|
|
3251
|
+
properties:
|
|
3252
|
+
type: { const: reasoning }
|
|
3253
|
+
text: { type: string }
|
|
3254
|
+
Event_text:
|
|
3255
|
+
type: object
|
|
3256
|
+
description: durable stream only β turn-aggregated answer text (carries the E18 resume-handle eventId).
|
|
3257
|
+
allOf: [{ $ref: '#/components/schemas/EventIdentity' }]
|
|
3258
|
+
required: [type, text]
|
|
3259
|
+
properties:
|
|
3260
|
+
type: { const: text }
|
|
3261
|
+
text: { type: string }
|
|
3262
|
+
Event_reasoning_delta:
|
|
3263
|
+
type: object
|
|
3264
|
+
description: live stream only β per-token thinking fragment (`delta` is the string fragment).
|
|
3265
|
+
allOf: [{ $ref: '#/components/schemas/EventIdentity' }]
|
|
3266
|
+
required: [type, delta]
|
|
3267
|
+
properties:
|
|
3268
|
+
type: { const: reasoning_delta }
|
|
3269
|
+
delta: { type: string }
|
|
3270
|
+
Event_text_delta:
|
|
3271
|
+
type: object
|
|
3272
|
+
description: live stream only β per-token answer fragment.
|
|
3273
|
+
allOf: [{ $ref: '#/components/schemas/EventIdentity' }]
|
|
3274
|
+
required: [type, delta]
|
|
3275
|
+
properties:
|
|
3276
|
+
type: { const: text_delta }
|
|
3277
|
+
delta: { type: string }
|
|
3278
|
+
Event_tool_start:
|
|
3279
|
+
type: object
|
|
3280
|
+
allOf: [{ $ref: '#/components/schemas/EventIdentity' }]
|
|
3281
|
+
required: [type, toolCallId, toolName]
|
|
3282
|
+
properties:
|
|
3283
|
+
type: { const: tool_start }
|
|
3284
|
+
toolCallId: { type: string }
|
|
3285
|
+
toolName: { type: string }
|
|
3286
|
+
args: {}
|
|
3287
|
+
Event_tool_end:
|
|
3288
|
+
type: object
|
|
3289
|
+
description: >
|
|
3290
|
+
design/99 Β§E1 (SHIPPED) β `output` is the model-facing action-card BODY: NON-UNIFORM (a string, or a
|
|
3291
|
+
(TextContent|ImageContent)[] block array) and degrades to a single TRUNCATED string over the core size cap
|
|
3292
|
+
(`truncated: true`). Absent β the tool produced no body. π΄ UNTRUSTED RAW + observability-only: the service
|
|
3293
|
+
has redactDeep'd + size-bounded it; a consumer MAY further bound, MUST never re-feed it to a model.
|
|
3294
|
+
allOf: [{ $ref: '#/components/schemas/EventIdentity' }]
|
|
3295
|
+
required: [type, toolCallId, toolName, isError]
|
|
3296
|
+
properties:
|
|
3297
|
+
type: { const: tool_end }
|
|
3298
|
+
toolCallId: { type: string }
|
|
3299
|
+
toolName: { type: string }
|
|
3300
|
+
isError: { type: boolean }
|
|
3301
|
+
output: {} # NON-UNIFORM: string | (TextContent|ImageContent)[] | (truncated) string. Absent β no body.
|
|
3302
|
+
truncated: { type: boolean } # true β output was size-bounded to a truncated string.
|
|
3303
|
+
Event_turn_end:
|
|
3304
|
+
type: object
|
|
3305
|
+
required: [type]
|
|
3306
|
+
properties:
|
|
3307
|
+
type: { const: turn_end }
|
|
3308
|
+
usage:
|
|
3309
|
+
type: object
|
|
3310
|
+
description: Full live shape β the portal cost bar's data source.
|
|
3311
|
+
properties:
|
|
3312
|
+
inputTokens: { type: integer }
|
|
3313
|
+
outputTokens: { type: integer }
|
|
3314
|
+
cacheReadTokens: { type: integer }
|
|
3315
|
+
cacheWriteTokens: { type: integer }
|
|
3316
|
+
costMicroUsd: { type: integer }
|
|
3317
|
+
Event_compacted:
|
|
3318
|
+
type: object
|
|
3319
|
+
required: [type]
|
|
3320
|
+
properties:
|
|
3321
|
+
type: { const: compacted }
|
|
3322
|
+
tokensBefore: { type: integer }
|
|
3323
|
+
Event_suggestions:
|
|
3324
|
+
type: object
|
|
3325
|
+
description: >
|
|
3326
|
+
E12 (shell-host; service runs.ts:392 + server.ts:3633, opt-in via `TaskRequest.suggestNextPrompts`) β
|
|
3327
|
+
post-completion "what to ask next" prompt suggestions. Appended to the DURABLE run-events tail AFTER a
|
|
3328
|
+
`done` with `status:"completed"` (core's fire-and-forget LLM pass). π΄ UNTRUSTED model text, service-
|
|
3329
|
+
redacted, UI-ONLY β render as clickable next-prompt chips; NEVER re-feed to a model. Empty/absent β none.
|
|
3330
|
+
required: [type, suggestions]
|
|
3331
|
+
properties:
|
|
3332
|
+
type: { const: suggestions }
|
|
3333
|
+
suggestions: { type: array, items: { type: string } }
|
|
3334
|
+
Event_status:
|
|
3335
|
+
type: object
|
|
3336
|
+
description: >
|
|
3337
|
+
design/99 Β§E3/Β§E10 (SHIPPED, LIVE-only) β a brain-layer liveness status the brain would otherwise absorb
|
|
3338
|
+
silently (429 rate-limit / 5xx-or-network retry / reconnect / open circuit breaker). EPHEMERAL β never
|
|
3339
|
+
persisted/replayed on resume; provider-neutral (NO HTTP status / stop_reason). `phase` is core's closed
|
|
3340
|
+
BrainStatusPhase but render open (a deployment may add a phase on its own channel).
|
|
3341
|
+
allOf: [{ $ref: '#/components/schemas/EventIdentity' }]
|
|
3342
|
+
required: [type, phase]
|
|
3343
|
+
properties:
|
|
3344
|
+
type: { const: status }
|
|
3345
|
+
phase: { type: string, description: "rate_limited | retrying | reconnecting | circuit_open (open set)" }
|
|
3346
|
+
detail: { type: string, description: "neutral human hint (NO provider/HTTP detail)" }
|
|
3347
|
+
retryInSec: { type: number }
|
|
3348
|
+
Event_task_progress:
|
|
3349
|
+
type: object
|
|
3350
|
+
description: >
|
|
3351
|
+
design/99 MF-10 (core 1.147+, `task_progress` arm) β a SUBAGENT PROGRESS TICK: a delegated sub-run's usage
|
|
3352
|
+
ACCRUING at each turn boundary WHILE it runs (tokens/tool-uses climbing), so a parent UI animates the Task
|
|
3353
|
+
card rollup. Emitted ONLY for a subagent (a task carrying a `parentToolCallId`, via EventIdentity); a
|
|
3354
|
+
top-level run's own usage is the consumer's own (turn_end). `status` is the literal "running" (the terminal
|
|
3355
|
+
usage is the `done` result). LIVE/EPHEMERAL β NOT persisted, NOT replayed on resume.
|
|
3356
|
+
π΄ WIRE NOTE: core mints this arm, but as of service 1bf7a5c NO service projection forwards it onto a CLIENT
|
|
3357
|
+
wire (/v1/tasks/stream or /v1/runs/:id/events) β the service consumes it INTERNALLY (fleetRunPublisher) and
|
|
3358
|
+
re-expresses it as a fleet CHILD row on /v1/fleet/stream. So today MF-10 is observable via the fleet stream
|
|
3359
|
+
subagent child rows; this arm is the core-arm MIRROR and surfaces on the task/run streams ONLY IF a future
|
|
3360
|
+
service projection passes it through. Additive / tolerate-absent.
|
|
3361
|
+
allOf: [{ $ref: '#/components/schemas/EventIdentity' }]
|
|
3362
|
+
required: [type, taskId, usage, status]
|
|
3363
|
+
properties:
|
|
3364
|
+
type: { const: task_progress }
|
|
3365
|
+
taskId: { type: string, description: 'The subagent task id (the delegated sub-run).' }
|
|
3366
|
+
usage:
|
|
3367
|
+
type: object
|
|
3368
|
+
description: 'Cumulative usage as of this turn boundary (closed 3-field set, byte-aligned to core).'
|
|
3369
|
+
required: [totalTokens, toolUses, durationMs]
|
|
3370
|
+
properties:
|
|
3371
|
+
totalTokens: { type: integer }
|
|
3372
|
+
toolUses: { type: integer }
|
|
3373
|
+
durationMs: { type: integer }
|
|
3374
|
+
status: { const: running, description: 'Always "running" mid-run (the terminal usage is the done result).' }
|
|
3375
|
+
Event_workspace_changed:
|
|
3376
|
+
type: object
|
|
3377
|
+
description: >
|
|
3378
|
+
design/99 Β§E13 (SHIPPED, LIVE-only) β a `bash` `cd` moved the task's tracked working directory; carries the
|
|
3379
|
+
new RAW logical cwd so a shell UI updates its prompt/path. Only fires with a real shell mounted; the initial
|
|
3380
|
+
cwd is NOT announced (only subsequent changes). EPHEMERAL β not persisted; resets across suspend/resume.
|
|
3381
|
+
allOf: [{ $ref: '#/components/schemas/EventIdentity' }]
|
|
3382
|
+
required: [type, cwd]
|
|
3383
|
+
properties:
|
|
3384
|
+
type: { const: workspace_changed }
|
|
3385
|
+
cwd: { type: string }
|
|
3386
|
+
Event_suspended:
|
|
3387
|
+
type: object
|
|
3388
|
+
description: HITL β the run paused on an F4 gate or AskUserQuestion. Resolve via approvals.decide, then resume.
|
|
3389
|
+
required: [type]
|
|
3390
|
+
properties:
|
|
3391
|
+
type: { const: suspended }
|
|
3392
|
+
gate:
|
|
3393
|
+
description: >
|
|
3394
|
+
CheckpointGate β OPEN DISCRIMINATED SET (design/80 D-0), mirrors core src/core/checkpoint-store.ts.
|
|
3395
|
+
5 known kinds below; consumers MUST branch on `kind` and render an UNKNOWN kind generically (core
|
|
3396
|
+
can mint a kind this contract predates β a coreβwire contract test fails CI, not prod). Tool
|
|
3397
|
+
args/question text are NOT here β join the task's trace tool-call block via approvals' toolCallId.
|
|
3398
|
+
oneOf:
|
|
3399
|
+
- type: object # plain human approval (budget-auto-approvable)
|
|
3400
|
+
required: [kind, reason, toolName]
|
|
3401
|
+
properties: { kind: { const: human }, reason: { type: string }, toolName: { type: string } }
|
|
3402
|
+
- type: object # design/37 irreversibility / design/70 egress safety-tighten β NOT budgetable (load-bearing safety gate)
|
|
3403
|
+
required: [kind, reason, toolName]
|
|
3404
|
+
properties: { kind: { const: irreversible_ask }, reason: { type: string }, toolName: { type: string } }
|
|
3405
|
+
- type: object # design/74 resource (cost/token/time) slice boundary β resolve { decision: "continue" }
|
|
3406
|
+
required: [kind, reason]
|
|
3407
|
+
properties: { kind: { const: resource_limit }, reason: { type: string } }
|
|
3408
|
+
- type: object # design/76 dry-run/shadow β durable needs_review TERMINAL (POST-prediction review, disjoint from approval family)
|
|
3409
|
+
required: [kind, reason]
|
|
3410
|
+
properties: { kind: { const: needs_review }, reason: { type: string } }
|
|
3411
|
+
- type: object # design/38 Path A background sub-task handle (door B never sees it)
|
|
3412
|
+
required: [kind]
|
|
3413
|
+
properties: { kind: { const: task_done } }
|
|
3414
|
+
- type: object # OPEN SET β unknown future kind (e.g. design/80 D-B plan_review); render generically, never crash
|
|
3415
|
+
required: [kind]
|
|
3416
|
+
# π΄ the pinned wire contract: exclude the 5 KNOWN kinds so a known kind matches ONLY its const member above
|
|
3417
|
+
# (not also this fallback) β keeps `oneOf` unambiguous AND preserves each kind's shape validation.
|
|
3418
|
+
# This fallback matches ONLY a future kind this contract predates. Add new known kinds to the enum.
|
|
3419
|
+
properties: { kind: { type: string, not: { enum: [human, irreversible_ask, resource_limit, needs_review, task_done] } } }
|
|
3420
|
+
- type: 'null'
|
|
3421
|
+
Event_done:
|
|
3422
|
+
type: object
|
|
3423
|
+
description: Carries the full TaskResult (output string at .result, stats at .stats) β service Drift 4.
|
|
3424
|
+
required: [type, result]
|
|
3425
|
+
properties:
|
|
3426
|
+
type: { const: done }
|
|
3427
|
+
result: { $ref: '#/components/schemas/TaskResult' }
|
|
3428
|
+
Event_failed:
|
|
3429
|
+
type: object
|
|
3430
|
+
required: [type]
|
|
3431
|
+
properties:
|
|
3432
|
+
type: { const: failed }
|
|
3433
|
+
errorCode: { type: string }
|
|
3434
|
+
errorMessage: { type: string }
|