@sema-agent/sdk 2.1.4 → 2.3.0
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/dist/errors.d.ts +16 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +11 -3
- package/dist/errors.js.map +1 -1
- package/dist/types.d.ts +55 -0
- package/dist/types.d.ts.map +1 -1
- package/openapi.yaml +1008 -133
- package/package.json +1 -1
package/openapi.yaml
CHANGED
|
@@ -210,6 +210,7 @@ paths:
|
|
|
210
210
|
schema: { $ref: '#/components/schemas/TaskResult' }
|
|
211
211
|
'400': { $ref: '#/components/responses/BadRequest' }
|
|
212
212
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
213
|
+
'403': { $ref: '#/components/responses/Forbidden' } # (declared 2026-08-01) 带 `sessionId` 复用他人会话:归属不匹配 ⇒ 403(自公开快照起点即有,spec 此前漏登记)。server 3.15.0 起多一个触发条件:多租部署上 session store 无归属判别能力时,复用**任何** caller 自报的 sessionId 都 403(含本 worker 自己刚铸的 —— 无 register seam 时归属从未被记下)。core 2.11.0 起默认内存店自带该能力 ⇒ 该触发条件在默认部署上不再出现。
|
|
213
214
|
'409': { $ref: '#/components/responses/Conflict' }
|
|
214
215
|
'429': { $ref: '#/components/responses/RateLimited' }
|
|
215
216
|
'503': { $ref: '#/components/responses/Unauthorized' }
|
|
@@ -262,7 +263,16 @@ paths:
|
|
|
262
263
|
application/json:
|
|
263
264
|
schema: { $ref: '#/components/schemas/TaskList' }
|
|
264
265
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
265
|
-
|
|
266
|
+
# 🔴 429 REMOVED 2026-07-31 — `routes/trace-usage.ts` has zero rate/quota/lease gates in the whole file
|
|
267
|
+
# (the only gates on this route are the fleet-wide 401 and the run-store 501 below); nothing can emit it.
|
|
268
|
+
'501':
|
|
269
|
+
description: >-
|
|
270
|
+
errorCode "capability.run_store_required" — the trace API needs a durable run store
|
|
271
|
+
(`DB_BACKEND=mysql|pg`). Added 2026-07-31: the guard has always been there (it fires before the
|
|
272
|
+
list/turns/stream/artifacts dispatch) but no trace path declared the code.
|
|
273
|
+
content:
|
|
274
|
+
application/json:
|
|
275
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
266
276
|
|
|
267
277
|
/v1/tasks/stream:
|
|
268
278
|
parameters:
|
|
@@ -300,6 +310,7 @@ paths:
|
|
|
300
310
|
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
301
311
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
302
312
|
'429': { $ref: '#/components/responses/RateLimited' }
|
|
313
|
+
'503': { $ref: '#/components/responses/Unauthorized' } # (declared 2026-07-31) this path is in server.ts's isBillableSubmitPath set — same three 503 doors as POST /v1/tasks and /v1/runs (no service token / draining / model-roster-pending)
|
|
303
314
|
|
|
304
315
|
/v1/runs:
|
|
305
316
|
parameters:
|
|
@@ -381,8 +392,12 @@ paths:
|
|
|
381
392
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
382
393
|
'416':
|
|
383
394
|
description: >
|
|
384
|
-
Resume point evicted past retention.
|
|
385
|
-
|
|
395
|
+
Resume point evicted past retention. Body carries `errorCode: "limit.retention_evicted"`
|
|
396
|
+
(matching the 416 STATUS alone also works); read `retainedFrom`, full-sync via
|
|
397
|
+
`GET /v1/tasks/:id/turns`, then resume from `retainedFrom`.
|
|
398
|
+
(Corrected 2026-07-31: this said "there is NO `code` field". It was true before the server moved
|
|
399
|
+
this site onto the shared `sendError`, which emits `errorCode` unconditionally — a stale denial
|
|
400
|
+
that steered consumers away from a field that is in fact on the wire.)
|
|
386
401
|
content:
|
|
387
402
|
application/json:
|
|
388
403
|
schema: { $ref: '#/components/schemas/ResumeEvicted' }
|
|
@@ -401,8 +416,12 @@ paths:
|
|
|
401
416
|
(non-owner → 404, no existence leak; missing principal → 401). All responses are synchronous ACKs; actual
|
|
402
417
|
termination is async (same-replica immediate; cross-replica ≈30s via the heartbeat tick). A cancelled run
|
|
403
418
|
settles to `status` failed + `errorCode` "cancelled" (NO new status — distinguish via errorCode; the UI
|
|
404
|
-
shows "cancelled", not an error). A SUSPENDED
|
|
405
|
-
|
|
419
|
+
shows "cancelled", not an error). A SUSPENDED (or `needs_review`) run IS cancellable here — the server
|
|
420
|
+
settles its pending checkpoint (CAS `expire`, the reaper's "≈ deny" terminal — deliberately not a
|
|
421
|
+
resume, since cancel means the run dies) and THEN terminalizes the row, releasing the session claim;
|
|
422
|
+
the ack is 202. (Corrected 2026-07-31: this paragraph used to say "409 — cancel it by DENYING its
|
|
423
|
+
approval instead". That was the pre-[868] behavior and it dead-ended when the deny itself was lost,
|
|
424
|
+
leaving the session locked with no release path.) LIVE (service ced3d88).
|
|
406
425
|
responses:
|
|
407
426
|
'202':
|
|
408
427
|
description: >
|
|
@@ -414,7 +433,17 @@ paths:
|
|
|
414
433
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
415
434
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
416
435
|
'409':
|
|
417
|
-
description:
|
|
436
|
+
description: >-
|
|
437
|
+
Concurrency loss on the suspended path only: someone else settled the pending checkpoint first
|
|
438
|
+
(an approve/deny won the CAS). Re-read the run — it is probably already resuming or terminal.
|
|
439
|
+
NOT "suspended runs are uncancellable" — those are cancelled here and 202.
|
|
440
|
+
content:
|
|
441
|
+
application/json:
|
|
442
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
443
|
+
'500':
|
|
444
|
+
description: >-
|
|
445
|
+
errorCode "internal.cancel_not_terminalized" — the checkpoint was settled but the run row could
|
|
446
|
+
not be terminalized after retries. Retry cancel; a stuck row is reaped after the stale window.
|
|
418
447
|
content:
|
|
419
448
|
application/json:
|
|
420
449
|
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
@@ -433,8 +462,12 @@ paths:
|
|
|
433
462
|
design/80 D-A. The core of *supervision* (vs mere approve/deny): inject direction into a running run.
|
|
434
463
|
AT-MOST-ONCE — steer is NOT idempotent (no retry). `trusted` is NOT a body field — the server derives it
|
|
435
464
|
from the authenticated operator (the BFF's operator role); a network steer defaults to untrusted. A
|
|
436
|
-
durable-`suspended` run →
|
|
437
|
-
|
|
465
|
+
durable-`suspended` run → the steer is PARKED on the pending checkpoint and answered **202**
|
|
466
|
+
`{status:"suspended", delivery:"queued"}`; it is injected when the run resumes. (Corrected 2026-07-31:
|
|
467
|
+
this paragraph used to say "409 — steer is not queued; resolve its approval instead", which the server
|
|
468
|
+
stopped doing — `routes/runs.ts` parks it. 409 now only means the park LOST: the checkpoint resolved or
|
|
469
|
+
expired between the read and the CAS.) A worker with no checkpoint store answers 501 instead.
|
|
470
|
+
The pendingSteer freeze / close-tag guard is server-side; the client only sends text.
|
|
438
471
|
requestBody:
|
|
439
472
|
required: true
|
|
440
473
|
content:
|
|
@@ -444,6 +477,14 @@ paths:
|
|
|
444
477
|
required: [text]
|
|
445
478
|
properties:
|
|
446
479
|
text: { type: string, description: 'The steering direction (untrusted DATA; the server fences it).' }
|
|
480
|
+
priority:
|
|
481
|
+
type: string
|
|
482
|
+
enum: [now, next, later]
|
|
483
|
+
description: >-
|
|
484
|
+
🔴 Declared 2026-07-31 (always read + enum-validated + echoed, never documented). The
|
|
485
|
+
handler 400s `request.field_invalid` on any other value and echoes the accepted one back
|
|
486
|
+
in the 200/202 body. It was mentioned only in prose under `Capabilities.fleet`, never in
|
|
487
|
+
this operation's own schema.
|
|
447
488
|
mode:
|
|
448
489
|
type: string
|
|
449
490
|
enum: [all, one-at-a-time]
|
|
@@ -454,11 +495,18 @@ paths:
|
|
|
454
495
|
per-call; the per-call mapping is pending D-A core.
|
|
455
496
|
responses:
|
|
456
497
|
'200': { description: Steer accepted and applied. }
|
|
457
|
-
'202':
|
|
498
|
+
'202':
|
|
499
|
+
description: >-
|
|
500
|
+
Steer accepted and QUEUED. Two shapes share this code: a live run queues to the next turn-boundary
|
|
501
|
+
drain; a durable-`suspended` run parks on its pending checkpoint
|
|
502
|
+
(`{status:"suspended", delivery:"queued"}`) and drains on resume.
|
|
458
503
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
459
504
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
460
505
|
'409':
|
|
461
|
-
description:
|
|
506
|
+
description: >-
|
|
507
|
+
errorCode "steering.not_running" — the run is terminal, is running on ANOTHER replica
|
|
508
|
+
(cross-replica live-steer is not wired), or the durable park lost its CAS (the checkpoint
|
|
509
|
+
resolved/expired in between). NOT the ordinary suspended case — that one parks and 202s.
|
|
462
510
|
content:
|
|
463
511
|
application/json:
|
|
464
512
|
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
@@ -475,6 +523,7 @@ paths:
|
|
|
475
523
|
content:
|
|
476
524
|
application/json:
|
|
477
525
|
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
526
|
+
'429': { $ref: '#/components/responses/RateLimited' } # (declared 2026-07-31) rateLimited||quotaExceeded||leaseDenied all gate this verb (routes/runs.ts)
|
|
478
527
|
|
|
479
528
|
/v1/runs/{taskId}/subagents/{target}/steer:
|
|
480
529
|
parameters:
|
|
@@ -524,6 +573,7 @@ paths:
|
|
|
524
573
|
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
525
574
|
'413':
|
|
526
575
|
description: 'errorCode "steer.content_too_large" — content over the per-steer cap; shorten and resend (retrying verbatim never succeeds).'
|
|
576
|
+
'429': { $ref: '#/components/responses/RateLimited' } # (declared 2026-07-31) rateLimited||quotaExceeded||leaseDenied all gate this verb (routes/runs.ts)
|
|
527
577
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
528
578
|
|
|
529
579
|
/v1/runs/{taskId}/compact:
|
|
@@ -569,6 +619,7 @@ paths:
|
|
|
569
619
|
content:
|
|
570
620
|
application/json:
|
|
571
621
|
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
622
|
+
'429': { $ref: '#/components/responses/RateLimited' } # (declared 2026-07-31) rateLimited||quotaExceeded||leaseDenied all gate this verb (routes/runs.ts)
|
|
572
623
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
573
624
|
|
|
574
625
|
/v1/runs/{taskId}/detach:
|
|
@@ -613,6 +664,14 @@ paths:
|
|
|
613
664
|
content:
|
|
614
665
|
application/json:
|
|
615
666
|
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
667
|
+
'429':
|
|
668
|
+
description: >-
|
|
669
|
+
(declared 2026-07-31) errorCode "limit.rate_exceeded" only — `routes/runs.ts` gates this verb with
|
|
670
|
+
`rateLimited(req,res)` alone (no quota/lease gate; mutating but runs no model, parity with cancel).
|
|
671
|
+
`Retry-After` (seconds) set.
|
|
672
|
+
content:
|
|
673
|
+
application/json:
|
|
674
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
616
675
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
617
676
|
|
|
618
677
|
/v1/runs/{taskId}/subagents/{target}/output:
|
|
@@ -624,6 +683,17 @@ paths:
|
|
|
624
683
|
required: true
|
|
625
684
|
schema: { type: string }
|
|
626
685
|
description: 'Background-agent handle (`a*` short form) under this parent run.'
|
|
686
|
+
- name: session
|
|
687
|
+
in: query
|
|
688
|
+
required: false
|
|
689
|
+
schema: { type: string }
|
|
690
|
+
description: >-
|
|
691
|
+
🔴 Declared 2026-07-31 (it was ALWAYS read, never documented). For a session-bound run, a
|
|
692
|
+
non-operator caller MUST send the session id here — the handler compares it to the run's
|
|
693
|
+
`sessionId` and answers **404 "run not found"** on mismatch or absence, byte-identical to the
|
|
694
|
+
unknown-run 404. So a consumer following the old spec (principal + path params only) hit a
|
|
695
|
+
silent, indistinguishable 404. Operator/trace callers bypass this, as they do the owner gate;
|
|
696
|
+
a run with no sessionId is not conversation-bound and needs no assertion.
|
|
627
697
|
get:
|
|
628
698
|
tags: [runs]
|
|
629
699
|
operationId: runsSubagentOutput
|
|
@@ -699,6 +769,7 @@ paths:
|
|
|
699
769
|
content:
|
|
700
770
|
application/json:
|
|
701
771
|
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
772
|
+
'429': { $ref: '#/components/responses/RateLimited' } # (declared 2026-07-31) rateLimited||quotaExceeded||leaseDenied all gate this verb (routes/runs.ts) — BILLABLE (starts model work)
|
|
702
773
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
703
774
|
|
|
704
775
|
/v1/runs/{taskId}/subagents/{target}/stream:
|
|
@@ -710,6 +781,17 @@ paths:
|
|
|
710
781
|
required: true
|
|
711
782
|
schema: { type: string }
|
|
712
783
|
description: 'Background-agent handle (`a*` short form) under this parent run.'
|
|
784
|
+
- name: session
|
|
785
|
+
in: query
|
|
786
|
+
required: false
|
|
787
|
+
schema: { type: string }
|
|
788
|
+
description: >-
|
|
789
|
+
🔴 Declared 2026-07-31 (it was ALWAYS read, never documented). For a session-bound run, a
|
|
790
|
+
non-operator caller MUST send the session id here — the handler compares it to the run's
|
|
791
|
+
`sessionId` and answers **404 "run not found"** on mismatch or absence, byte-identical to the
|
|
792
|
+
unknown-run 404. So a consumer following the old spec (principal + path params only) hit a
|
|
793
|
+
silent, indistinguishable 404. Operator/trace callers bypass this, as they do the owner gate;
|
|
794
|
+
a run with no sessionId is not conversation-bound and needs no assertion.
|
|
713
795
|
get:
|
|
714
796
|
tags: [runs]
|
|
715
797
|
operationId: runsSubagentStream
|
|
@@ -744,6 +826,17 @@ paths:
|
|
|
744
826
|
required: true
|
|
745
827
|
schema: { type: string }
|
|
746
828
|
description: 'EXACT task handle only (`b*` background bash, monitor, background agent) — agent-name / legacy-shellId resolution is deliberately not on the wire (fail-closed 404).'
|
|
829
|
+
- name: session
|
|
830
|
+
in: query
|
|
831
|
+
required: false
|
|
832
|
+
schema: { type: string }
|
|
833
|
+
description: >-
|
|
834
|
+
🔴 Declared 2026-07-31 (it was ALWAYS read, never documented). For a session-bound run, a
|
|
835
|
+
non-operator caller MUST send the session id here — the handler compares it to the run's
|
|
836
|
+
`sessionId` and answers **404 "run not found"** on mismatch or absence, byte-identical to the
|
|
837
|
+
unknown-run 404. So a consumer following the old spec (principal + path params only) hit a
|
|
838
|
+
silent, indistinguishable 404. Operator/trace callers bypass this, as they do the owner gate;
|
|
839
|
+
a run with no sessionId is not conversation-bound and needs no assertion.
|
|
747
840
|
get:
|
|
748
841
|
tags: [runs]
|
|
749
842
|
operationId: runsTaskOutput
|
|
@@ -801,6 +894,11 @@ paths:
|
|
|
801
894
|
content:
|
|
802
895
|
application/json:
|
|
803
896
|
schema: { $ref: '#/components/schemas/SubagentOutputResult' }
|
|
897
|
+
'400':
|
|
898
|
+
# (declared 2026-07-31) shares the path-decode + `?filter=` validation of the sibling output verb
|
|
899
|
+
# (both ride `taskVerbMatch` in routes/runs.ts) — request.path_malformed (malformed subagent-path
|
|
900
|
+
# percent-encoding) and request.param_unsupported (`?filter=` is refused on the wire, GET and stop alike).
|
|
901
|
+
$ref: '#/components/responses/BadRequest'
|
|
804
902
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
805
903
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
806
904
|
'409':
|
|
@@ -808,6 +906,14 @@ paths:
|
|
|
808
906
|
content:
|
|
809
907
|
application/json:
|
|
810
908
|
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
909
|
+
'429':
|
|
910
|
+
description: >-
|
|
911
|
+
(declared 2026-07-31) errorCode "limit.rate_exceeded" only — `routes/runs.ts` gates this verb with
|
|
912
|
+
`rateLimited(req,res)` alone (mutating but runs no model, parity with detach). `Retry-After`
|
|
913
|
+
(seconds) set.
|
|
914
|
+
content:
|
|
915
|
+
application/json:
|
|
916
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
811
917
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
812
918
|
|
|
813
919
|
/v1/sessions:
|
|
@@ -835,6 +941,14 @@ paths:
|
|
|
835
941
|
name: owner
|
|
836
942
|
schema: { type: string }
|
|
837
943
|
description: Fleet-wide credential only — scope the page to a tenant. Ignored for a principal caller.
|
|
944
|
+
- in: query
|
|
945
|
+
name: q
|
|
946
|
+
required: false
|
|
947
|
+
schema: { type: string, maxLength: 256 }
|
|
948
|
+
description: >-
|
|
949
|
+
🔴 Declared 2026-07-31 (always read, never documented). Case-sensitive SUBSTRING filter over the
|
|
950
|
+
row's objective preview; the server trims and caps it at 256 chars. A consumer following the old
|
|
951
|
+
spec had no way to know the search face existed.
|
|
838
952
|
responses:
|
|
839
953
|
'200':
|
|
840
954
|
description: Keyset page of session summaries.
|
|
@@ -950,6 +1064,10 @@ paths:
|
|
|
950
1064
|
required: [deleted]
|
|
951
1065
|
properties:
|
|
952
1066
|
deleted: { type: boolean }
|
|
1067
|
+
'400':
|
|
1068
|
+
# (declared 2026-07-31) errorCode "request.id_invalid" — the path sessionId must decode to a
|
|
1069
|
+
# well-formed uuidv7 (`routes/sessions.ts`); a malformed id never reaches the store.
|
|
1070
|
+
$ref: '#/components/responses/BadRequest'
|
|
953
1071
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
954
1072
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
955
1073
|
'409': { $ref: '#/components/responses/Conflict' }
|
|
@@ -1065,6 +1183,14 @@ paths:
|
|
|
1065
1183
|
schema: { type: string }
|
|
1066
1184
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1067
1185
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
1186
|
+
'500':
|
|
1187
|
+
description: >-
|
|
1188
|
+
(declared 2026-07-31) errorCode "internal.error" — `routes/sessions.ts`'s catch around the owner
|
|
1189
|
+
lookup / subscribe setup, sent only when it fires BEFORE the SSE `200` headers went out
|
|
1190
|
+
(`!res.headersSent`); once streaming has started a failure just ends the connection instead.
|
|
1191
|
+
content:
|
|
1192
|
+
application/json:
|
|
1193
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
1068
1194
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
1069
1195
|
'503': { description: 'Subscription connection cap exceeded — Retry-After set; fall back to polling /head.' }
|
|
1070
1196
|
|
|
@@ -1526,6 +1652,10 @@ paths:
|
|
|
1526
1652
|
required: [rules]
|
|
1527
1653
|
properties:
|
|
1528
1654
|
rules: { $ref: '#/components/schemas/StoredSessionRules' }
|
|
1655
|
+
'400':
|
|
1656
|
+
# (declared 2026-07-31) errorCode "request.id_invalid" — GET and PUT share the SAME id-shape check
|
|
1657
|
+
# in `routes/sessions.ts` (uuidv7 required) ahead of the method branch; only PUT had this declared.
|
|
1658
|
+
$ref: '#/components/responses/BadRequest'
|
|
1529
1659
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1530
1660
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
1531
1661
|
'409': { $ref: '#/components/responses/Conflict' }
|
|
@@ -1805,7 +1935,10 @@ paths:
|
|
|
1805
1935
|
2c session-sync PUSH Phase A — a SMALL metadata body (`entryIds, snapshots, policy, anchors, resolution?`); the
|
|
1806
1936
|
entries stream in Phase B. The cloud classifies (§7), pre-checks blob presence (a missing referenced hash →
|
|
1807
1937
|
422 `missing_blob` — PUT it first), guards the import lease + any active run (409), then mints a stagingId.
|
|
1808
|
-
`identical` → `{ relation:"identical" }` with NO stagingId (skip Phase B);
|
|
1938
|
+
`identical` → `{ relation:"identical", basis, payloadVerified }` with NO stagingId (skip Phase B);
|
|
1939
|
+
`basis` is `"entry-ids+digest"` when the caller supplied a comparable digest and it matched, else
|
|
1940
|
+
`"entry-ids"` (id-set equality only) — see the `ImportStaged` schema, which has carried these two
|
|
1941
|
+
keys all along; only this prose said "one key". `fresh`/`fast_forward` →
|
|
1809
1942
|
`{ stagingId, relation }`. 🔴 a `fork`/`stale` without `resolution:"overwrite-dst"` → 409
|
|
1810
1943
|
`conflict` carrying the `relation` (the SDK surfaces SyncConflictError).
|
|
1811
1944
|
requestBody:
|
|
@@ -1840,6 +1973,7 @@ paths:
|
|
|
1840
1973
|
schema: { $ref: '#/components/schemas/SyncConflict' }
|
|
1841
1974
|
'422': { $ref: '#/components/responses/UnprocessableEntity' }
|
|
1842
1975
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
1976
|
+
'503': { $ref: '#/components/responses/Unauthorized' } # (declared 2026-08-01) E6 兄弟守卫:无 service token + 未开 ALLOW_UNAUTHED_WRITES 时,这条 import 提交腿与 `PUT …/policy` 写的是同一个 policy store ⇒ 同门同码 `auth.service_token_required`(server 3.15.0 起)
|
|
1843
1977
|
|
|
1844
1978
|
/v1/sessions/{sessionId}/sync/import/{stagingId}/entries:
|
|
1845
1979
|
parameters:
|
|
@@ -1896,6 +2030,15 @@ paths:
|
|
|
1896
2030
|
/v1/approvals:
|
|
1897
2031
|
parameters:
|
|
1898
2032
|
- $ref: '#/components/parameters/PrincipalHeader'
|
|
2033
|
+
- name: owner
|
|
2034
|
+
in: query
|
|
2035
|
+
required: false
|
|
2036
|
+
schema: { type: string }
|
|
2037
|
+
description: >-
|
|
2038
|
+
🔴 Declared 2026-07-31 (always read, never documented). OPERATOR-only tenant scoping: an operator
|
|
2039
|
+
caller narrows the view to one owner with this key; for a non-operator the server forces the scope
|
|
2040
|
+
to the caller's own principal and ignores it. The sibling `/v1/assistant/inbox` has declared the
|
|
2041
|
+
same-named param all along — this face just never caught up.
|
|
1899
2042
|
get:
|
|
1900
2043
|
tags: [approvals]
|
|
1901
2044
|
operationId: approvalsList
|
|
@@ -1917,14 +2060,27 @@ paths:
|
|
|
1917
2060
|
/v1/approvals/stream:
|
|
1918
2061
|
parameters:
|
|
1919
2062
|
- $ref: '#/components/parameters/PrincipalHeader'
|
|
2063
|
+
- name: owner
|
|
2064
|
+
in: query
|
|
2065
|
+
required: false
|
|
2066
|
+
schema: { type: string }
|
|
2067
|
+
description: >-
|
|
2068
|
+
🔴 Declared 2026-07-31 (always read, never documented). OPERATOR-only tenant scoping: an operator
|
|
2069
|
+
caller narrows the view to one owner with this key; for a non-operator the server forces the scope
|
|
2070
|
+
to the caller's own principal and ignores it. The sibling `/v1/assistant/inbox` has declared the
|
|
2071
|
+
same-named param all along — this face just never caught up.
|
|
1920
2072
|
get:
|
|
1921
2073
|
tags: [approvals]
|
|
1922
2074
|
operationId: approvalsStream
|
|
1923
2075
|
x-status: live # spec-path-gate 首批回填 2026-07-27(design/80 native push;SDK approvals.stream 消费)
|
|
1924
2076
|
summary: SSE — pending-approval deltas (subscribe once instead of polling GET /v1/approvals).
|
|
1925
2077
|
description: >
|
|
1926
|
-
Frames: `meta` ({type,version,mode:"approvals-delta",pollMs}) then `
|
|
1927
|
-
|
|
2078
|
+
Frames: `meta` ({type,version,mode:"approvals-delta",pollMs}), then `synced` ({type,count}) once the
|
|
2079
|
+
first full snapshot has been emitted, then `pending`/`resolved` deltas; plus `heartbeat` ({type}) on the
|
|
2080
|
+
keep-alive tick and a terminal `error` ({type,errorCode:"STREAM_MAX_DURATION",…}) when the 15-min cap
|
|
2081
|
+
fires. (Corrected 2026-07-31: this list used to name only meta/pending/resolved — a consumer written to
|
|
2082
|
+
it silently drops three frame kinds it really receives.) Each data payload mirrors its SSE event name in
|
|
2083
|
+
`data.type` — proxy-safe dispatch. Cross-replica by
|
|
1928
2084
|
construction (polls the SHARED checkpoint table). 15-min cap + heartbeats; a DB blip retries, never
|
|
1929
2085
|
kills the stream. Scope = the caller's principal (operator/trace token = fleet-wide).
|
|
1930
2086
|
responses:
|
|
@@ -1962,6 +2118,18 @@ paths:
|
|
|
1962
2118
|
content:
|
|
1963
2119
|
application/json:
|
|
1964
2120
|
schema: { $ref: '#/components/schemas/ApprovalDecisionResult' }
|
|
2121
|
+
'400':
|
|
2122
|
+
description: >-
|
|
2123
|
+
(declared 2026-07-31) `routes/approvals-assistant.ts` has 9+ distinct 400 sites on this verb, all
|
|
2124
|
+
`ErrorResponse`-shaped: "request.path_malformed" (sessionId %-decode) · "request.body_shape"
|
|
2125
|
+
(`decision` missing/invalid, or a malformed `answer`) · "request.field_invalid" (`reason` not a
|
|
2126
|
+
string; `remember` not `"session"`; `checkpointToken`/`boundCallId`/`boundInputHash` not a string)
|
|
2127
|
+
· "request.field_conflict" (`remember` without `decision:"approve"`) · "remember_not_in_proof" /
|
|
2128
|
+
"updated_input_not_in_proof" (`remember` / `updatedInput` on a direct-door worker — not covered by
|
|
2129
|
+
the decision proof) · "remember_requires_binding" (`remember` without the D-1 binding echo).
|
|
2130
|
+
content:
|
|
2131
|
+
application/json:
|
|
2132
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
1965
2133
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1966
2134
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
1967
2135
|
'409':
|
|
@@ -1971,17 +2139,19 @@ paths:
|
|
|
1971
2139
|
NOT match the current pending action (TOCTOU/swap). The SDK splits these by `errorCode` into
|
|
1972
2140
|
ConflictError vs ApprovalBindingMismatchError. binding_mismatch = SAFETY signal: refetch + re-present
|
|
1973
2141
|
to the human, NEVER auto-retry.
|
|
2142
|
+
THIRD member (moved here from a documented-but-nonexistent 410, 2026-07-31): `approval_stale`
|
|
2143
|
+
(`errorCode`; `terminal:"resolved"` when attributable, else the key is absent) — the checkpoint is
|
|
2144
|
+
already terminal (someone resolved it / abandonment-TTL GC / SLA resolve-deny). Client action:
|
|
2145
|
+
refetch the inbox; this pending is gone. SDK → ApprovalStaleError. Note the bare CAS 409 has NO
|
|
2146
|
+
`errorCode`, which is exactly how the three are told apart.
|
|
1974
2147
|
content:
|
|
1975
2148
|
application/json:
|
|
1976
2149
|
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
content:
|
|
1983
|
-
application/json:
|
|
1984
|
-
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
2150
|
+
# 🔴 REMOVED 2026-07-31 — the 410 that never was. `grep -rn "\b410\b" src/` across the whole server
|
|
2151
|
+
# is ZERO hits: `approval_stale` ships as a **409** carrying `errorCode: "approval_stale"` (the CAS
|
|
2152
|
+
# 409 stays bare → ConflictError, so the two 409s never collide). The `terminal` enum values
|
|
2153
|
+
# `expired_abort`/`expired_sla` are likewise never assigned — only `"resolved"`, or the key is absent.
|
|
2154
|
+
# A consumer branching on 410 to detect staleness had a branch that could not fire; see the 409 below.
|
|
1985
2155
|
'413':
|
|
1986
2156
|
description: >
|
|
1987
2157
|
errorCode "reason_too_large" — `reason` exceeded 4096 chars (same cap on plan_review). REJECTED,
|
|
@@ -2017,6 +2187,41 @@ paths:
|
|
|
2017
2187
|
content:
|
|
2018
2188
|
application/json:
|
|
2019
2189
|
schema: { $ref: '#/components/schemas/ExemptionList' }
|
|
2190
|
+
'400':
|
|
2191
|
+
description: >-
|
|
2192
|
+
(declared 2026-07-31) errorCode "request.path_malformed" — the sessionId (or a present `:toolName`
|
|
2193
|
+
segment) carried a malformed %-sequence (`decodeURIComponent` threw), shared decode gate in
|
|
2194
|
+
`routes/approvals-assistant.ts` ahead of GET/DELETE.
|
|
2195
|
+
content:
|
|
2196
|
+
application/json:
|
|
2197
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
2198
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
2199
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
2200
|
+
'501':
|
|
2201
|
+
description: Worker has no exemption store backend (DB_BACKEND / LOCAL lane required).
|
|
2202
|
+
content:
|
|
2203
|
+
application/json:
|
|
2204
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
2205
|
+
delete:
|
|
2206
|
+
tags: [approvals]
|
|
2207
|
+
operationId: approvalsExemptionsRouteUnsupported
|
|
2208
|
+
x-status: live # (declared 2026-07-31) same `em` regex as the sibling per-tool DELETE also matches this shorter path (no `:toolName`) — approvals-assistant.ts.
|
|
2209
|
+
summary: 'DELETE without a tool name — always 400 (use /exemptions/{toolName} instead).'
|
|
2210
|
+
description: >
|
|
2211
|
+
The route regex behind `GET /v1/approvals/{sessionId}/exemptions` also accepts `DELETE` on this
|
|
2212
|
+
SAME (shorter) path — `routes/approvals-assistant.ts` runs the identical store/decode/owner gates
|
|
2213
|
+
as the GET above (so a bad store → 501, a malformed %-encoded id → 400, an unknown/non-owned session
|
|
2214
|
+
→ 404 all still apply first), then — because this path has no `:toolName` segment to match — it
|
|
2215
|
+
deterministically hits `request.route_unsupported` ("DELETE needs /exemptions/:toolName"). This
|
|
2216
|
+
operation therefore never returns a 2xx; callers should use `DELETE /v1/approvals/{sessionId}/exemptions/{toolName}`.
|
|
2217
|
+
responses:
|
|
2218
|
+
'400':
|
|
2219
|
+
description: >-
|
|
2220
|
+
errorCode "request.route_unsupported" — "DELETE needs /exemptions/:toolName" (deterministic once
|
|
2221
|
+
past the store/decode/owner gates below); OR "request.path_malformed" from the shared decode gate.
|
|
2222
|
+
content:
|
|
2223
|
+
application/json:
|
|
2224
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
2020
2225
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
2021
2226
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
2022
2227
|
'501':
|
|
@@ -2060,6 +2265,14 @@ paths:
|
|
|
2060
2265
|
required: [revoked]
|
|
2061
2266
|
properties:
|
|
2062
2267
|
revoked: { const: true }
|
|
2268
|
+
'400':
|
|
2269
|
+
description: >-
|
|
2270
|
+
(declared 2026-07-31) errorCode "request.path_malformed" — sessionId or toolName carried a
|
|
2271
|
+
malformed %-sequence (shared decode gate in `routes/approvals-assistant.ts`, same as the sibling
|
|
2272
|
+
GET .../exemptions).
|
|
2273
|
+
content:
|
|
2274
|
+
application/json:
|
|
2275
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
2063
2276
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
2064
2277
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
2065
2278
|
'501':
|
|
@@ -2253,6 +2466,7 @@ paths:
|
|
|
2253
2466
|
schema: { $ref: '#/components/schemas/TurnList' }
|
|
2254
2467
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
2255
2468
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
2469
|
+
'501': { $ref: '#/components/responses/NotImplemented' } # (declared 2026-07-31) `routes/trace-usage.ts`'s `!deps.runStore` guard fires before the /turns|/stream|/artifacts sub-dispatch — capability.run_store_required
|
|
2256
2470
|
|
|
2257
2471
|
/v1/tasks/source-summary:
|
|
2258
2472
|
parameters:
|
|
@@ -2348,6 +2562,7 @@ paths:
|
|
|
2348
2562
|
schema: { $ref: '#/components/schemas/TraceStreamEvent' }
|
|
2349
2563
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
2350
2564
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
2565
|
+
'501': { $ref: '#/components/responses/NotImplemented' } # (declared 2026-07-31) `routes/trace-usage.ts`'s `!deps.runStore` guard fires before the /turns|/stream|/artifacts sub-dispatch — capability.run_store_required
|
|
2351
2566
|
|
|
2352
2567
|
/v1/usage:
|
|
2353
2568
|
parameters:
|
|
@@ -2380,7 +2595,10 @@ paths:
|
|
|
2380
2595
|
summary: Windowed usage totals (usage-analytics face).
|
|
2381
2596
|
description: >
|
|
2382
2597
|
READ-ONLY analytics aggregate over the caller's principal (operator/trace token = fleet-wide). Query:
|
|
2383
|
-
`from`/`to` (ISO), `
|
|
2598
|
+
`from`/`to` (ISO), `principal` (fleet-wide callers only — the handler reads this key; a JWT subject is
|
|
2599
|
+
forced to itself and the param is ignored). (Corrected 2026-07-31: this said `owner`, which the handler
|
|
2600
|
+
never reads — a hand-rolled caller sending `?owner=` got UNFILTERED results with no error. The shipped
|
|
2601
|
+
SDK client has always sent `principal`.) Response is the analytics aggregate — an OPEN
|
|
2384
2602
|
object (additive fields ride; consumers branch on known keys only).
|
|
2385
2603
|
responses:
|
|
2386
2604
|
'200':
|
|
@@ -2627,7 +2845,9 @@ paths:
|
|
|
2627
2845
|
application/json:
|
|
2628
2846
|
schema: { type: object, additionalProperties: true }
|
|
2629
2847
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
2630
|
-
|
|
2848
|
+
# 🔴 REMOVED 2026-07-31 — `routes/side-query.ts` has no 501 branch at all, and
|
|
2849
|
+
# `routes/capabilities.ts` reports `sideQuery: true` as a hardcoded constant (no deployment flag
|
|
2850
|
+
# gates this route). The documented "not enabled on this worker" case is unreachable.
|
|
2631
2851
|
|
|
2632
2852
|
/v1/workflows:
|
|
2633
2853
|
parameters:
|
|
@@ -2653,6 +2873,13 @@ paths:
|
|
|
2653
2873
|
required: false
|
|
2654
2874
|
schema: { type: integer, minimum: 1, maximum: 100, default: 50 }
|
|
2655
2875
|
description: Max rows (server clamps to 1..100; default 50).
|
|
2876
|
+
- in: query
|
|
2877
|
+
name: session
|
|
2878
|
+
required: false
|
|
2879
|
+
schema: { type: string }
|
|
2880
|
+
description: >-
|
|
2881
|
+
🔴 Declared 2026-07-31 (always read, never documented). Filters the rows to one session — a
|
|
2882
|
+
consumer following the old spec (status/limit only) silently got the whole scope back.
|
|
2656
2883
|
responses:
|
|
2657
2884
|
'200':
|
|
2658
2885
|
description: Owner's workflow-run summaries.
|
|
@@ -2661,6 +2888,7 @@ paths:
|
|
|
2661
2888
|
schema:
|
|
2662
2889
|
type: object
|
|
2663
2890
|
required: [workflows]
|
|
2891
|
+
additionalProperties: false
|
|
2664
2892
|
properties:
|
|
2665
2893
|
workflows:
|
|
2666
2894
|
type: array
|
|
@@ -2737,6 +2965,13 @@ paths:
|
|
|
2737
2965
|
application/json:
|
|
2738
2966
|
schema: { $ref: '#/components/schemas/AttachmentInfo' }
|
|
2739
2967
|
'400': { description: 'missing/unsafe name, malformed mime' }
|
|
2968
|
+
'401':
|
|
2969
|
+
description: >-
|
|
2970
|
+
(declared 2026-07-31) errorCode "auth.principal_required" — `routes/attachments.ts` requires a
|
|
2971
|
+
principal ahead of the method dispatch when `requirePrincipal` is on.
|
|
2972
|
+
content:
|
|
2973
|
+
application/json:
|
|
2974
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
2740
2975
|
'413': { description: 'attachment.too_large' }
|
|
2741
2976
|
'415': { description: 'attachment.mime_not_allowed' }
|
|
2742
2977
|
'501': { description: 'attachment store not configured' }
|
|
@@ -2754,7 +2989,15 @@ paths:
|
|
|
2754
2989
|
summary: Download an attachment's bytes (owner-gated, no oracle — unknown and foreign both 404).
|
|
2755
2990
|
responses:
|
|
2756
2991
|
'200': { description: 'bytes; content-type = stored mime; content-disposition carries the sanitized name.' }
|
|
2992
|
+
'401':
|
|
2993
|
+
description: >-
|
|
2994
|
+
(declared 2026-07-31) errorCode "auth.principal_required" — `routes/attachments.ts` gates the
|
|
2995
|
+
store-presence check and the principal requirement ahead of the GET/DELETE method dispatch.
|
|
2996
|
+
content:
|
|
2997
|
+
application/json:
|
|
2998
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
2757
2999
|
'404': { description: 'unknown or not owned' }
|
|
3000
|
+
'501': { description: '(declared 2026-07-31) errorCode "capability.attachment_store_required" — no attachment store configured.' }
|
|
2758
3001
|
delete:
|
|
2759
3002
|
tags: [tasks]
|
|
2760
3003
|
operationId: deleteAttachment
|
|
@@ -2762,10 +3005,33 @@ paths:
|
|
|
2762
3005
|
summary: Delete an attachment (owner-gated; 204 on success).
|
|
2763
3006
|
responses:
|
|
2764
3007
|
'204': { description: 'deleted' }
|
|
3008
|
+
'401':
|
|
3009
|
+
description: >-
|
|
3010
|
+
(declared 2026-07-31) errorCode "auth.principal_required" — same shared gate as the GET (store
|
|
3011
|
+
presence + principal requirement) ahead of the method dispatch.
|
|
3012
|
+
content:
|
|
3013
|
+
application/json:
|
|
3014
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
2765
3015
|
'404': { description: 'unknown or not owned' }
|
|
3016
|
+
'501': { description: '(declared 2026-07-31) errorCode "capability.attachment_store_required" — no attachment store configured.' }
|
|
2766
3017
|
/v1/fleet/stream:
|
|
2767
3018
|
parameters:
|
|
2768
3019
|
- $ref: '#/components/parameters/PrincipalHeader'
|
|
3020
|
+
- name: session
|
|
3021
|
+
in: query
|
|
3022
|
+
required: false
|
|
3023
|
+
schema: { type: string }
|
|
3024
|
+
description: >-
|
|
3025
|
+
🔴 Declared 2026-07-31 (always read, never documented). Scopes row visibility / content scrubbing
|
|
3026
|
+
to one session.
|
|
3027
|
+
- name: observe
|
|
3028
|
+
in: query
|
|
3029
|
+
required: false
|
|
3030
|
+
schema: { type: string, enum: ['1'] }
|
|
3031
|
+
description: >-
|
|
3032
|
+
🔴 Declared 2026-07-31 (always read, never documented). `observe=1` opens the stream WITHOUT the
|
|
3033
|
+
durable notification park-fencing (the workflow-completion inbox is not passed) — an observer view
|
|
3034
|
+
that must not consume another consumer's parked notifications.
|
|
2769
3035
|
get:
|
|
2770
3036
|
tags: [fleet]
|
|
2771
3037
|
operationId: fleetStream
|
|
@@ -2804,7 +3070,10 @@ paths:
|
|
|
2804
3070
|
`LEADER_ENABLED` (default OFF) + `REMOTE_EXEC=e2b`. Mode is a SERVER deploy flag, not a client choice:
|
|
2805
3071
|
`LEADER_FANOUT_ENABLED=true` (default) lets the router fan out to N isolated workers (= the value-HOLD
|
|
2806
3072
|
correctness bet); `=false` pins a single worker. Either way it runs the E2B+git pipeline. Door B's brain
|
|
2807
|
-
is the strong single agent via `/v1/tasks`+`/v1/runs`+`scenario`. When disabled
|
|
3073
|
+
is the strong single agent via `/v1/tasks`+`/v1/runs`+`scenario`. When disabled (`LEADER_ENABLED` off)
|
|
3074
|
+
the route is simply NOT REGISTERED → the generic **404** `not_found.route`, not 501.
|
|
3075
|
+
(Corrected 2026-07-31: "When disabled → 501" was never true — no code path emits 501 here; a consumer
|
|
3076
|
+
branching on 501 to detect "leader unavailable" never fires.)
|
|
2808
3077
|
requestBody:
|
|
2809
3078
|
required: true
|
|
2810
3079
|
content:
|
|
@@ -2824,7 +3093,13 @@ paths:
|
|
|
2824
3093
|
application/json:
|
|
2825
3094
|
schema: { $ref: '#/components/schemas/LeaderReceipt' }
|
|
2826
3095
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
2827
|
-
'
|
|
3096
|
+
'404':
|
|
3097
|
+
description: >-
|
|
3098
|
+
errorCode "not_found.route" — the leader lane is disabled on this worker, so the route is not
|
|
3099
|
+
registered at all. (Was documented as 501 until 2026-07-31; no code path ever emitted that.)
|
|
3100
|
+
content:
|
|
3101
|
+
application/json:
|
|
3102
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
2828
3103
|
|
|
2829
3104
|
/v1/leader/{leaderRunId}:
|
|
2830
3105
|
parameters:
|
|
@@ -2879,7 +3154,7 @@ paths:
|
|
|
2879
3154
|
schema:
|
|
2880
3155
|
type: object
|
|
2881
3156
|
required: [images]
|
|
2882
|
-
additionalProperties:
|
|
3157
|
+
additionalProperties: false
|
|
2883
3158
|
properties:
|
|
2884
3159
|
images:
|
|
2885
3160
|
type: array
|
|
@@ -2897,9 +3172,17 @@ paths:
|
|
|
2897
3172
|
summary: 'OPERATOR: register one image-index entry → { id }.'
|
|
2898
3173
|
requestBody: { required: true, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
|
|
2899
3174
|
responses:
|
|
2900
|
-
'200': { description: '{ id }.', content: { application/json: { schema: { type: object, required: [id], properties: { id: { type: string } }, additionalProperties:
|
|
3175
|
+
'200': { description: '{ id }.', content: { application/json: { schema: { type: object, required: [id], properties: { id: { type: string } }, additionalProperties: false } } } }
|
|
2901
3176
|
'400': { $ref: '#/components/responses/BadRequest' }
|
|
2902
3177
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
3178
|
+
'403':
|
|
3179
|
+
description: >-
|
|
3180
|
+
(declared 2026-07-31) errorCode "auth.operator_only" — EXPLICIT operator gate (not the bare
|
|
3181
|
+
isOperator, whose empty-OPERATOR_PRINCIPALS "true-for-all" would make this index-mutating door
|
|
3182
|
+
world-writable).
|
|
3183
|
+
content:
|
|
3184
|
+
application/json:
|
|
3185
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
2903
3186
|
|
|
2904
3187
|
/v1/images/{profile}:
|
|
2905
3188
|
parameters:
|
|
@@ -2911,7 +3194,9 @@ paths:
|
|
|
2911
3194
|
x-status: live
|
|
2912
3195
|
summary: 'Newest PUBLISHED entry for a profile → { image }.'
|
|
2913
3196
|
responses:
|
|
2914
|
-
|
|
3197
|
+
# 🔴 census 批2 四段:this arm was a bare open `{}` (no `image` key even declared) — the handler's real
|
|
3198
|
+
# body is `{ image: entry }` (images.ts:478-484), entry = the SAME ImageIndexEntry as the list route.
|
|
3199
|
+
'200': { description: '{ image: entry }.', content: { application/json: { schema: { type: object, required: [image], additionalProperties: false, properties: { image: { $ref: '#/components/schemas/ImageIndexEntry' } } } } } }
|
|
2915
3200
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
2916
3201
|
|
|
2917
3202
|
/v1/images/digests/{digest}:
|
|
@@ -2924,7 +3209,8 @@ paths:
|
|
|
2924
3209
|
x-status: live
|
|
2925
3210
|
summary: 'Entry by immutable digest → { image }.'
|
|
2926
3211
|
responses:
|
|
2927
|
-
|
|
3212
|
+
# 🔴 census 批2 四段:same open-`{}` gap as imagesByProfile — real body `{ image: entry }` (images.ts:466-472).
|
|
3213
|
+
'200': { description: '{ image: entry }.', content: { application/json: { schema: { type: object, required: [image], additionalProperties: false, properties: { image: { $ref: '#/components/schemas/ImageIndexEntry' } } } } } }
|
|
2928
3214
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
2929
3215
|
|
|
2930
3216
|
/v1/images/bakes:
|
|
@@ -2940,11 +3226,31 @@ paths:
|
|
|
2940
3226
|
busy ⇒ 409 {activeBakeId?, eventsUrl?}; submission rate-limit ⇒ 429 + retryAfterSec.
|
|
2941
3227
|
requestBody: { required: true, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
|
|
2942
3228
|
responses:
|
|
2943
|
-
'202': { description: '{ bakeId, eventsUrl, status, state
|
|
3229
|
+
'202': { description: '{ bakeId, eventsUrl, status, state }.', content: { application/json: { schema: { $ref: '#/components/schemas/BakeSubmitAck' } } } }
|
|
2944
3230
|
'400': { $ref: '#/components/responses/BadRequest' }
|
|
2945
3231
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
3232
|
+
'403':
|
|
3233
|
+
description: (declared 2026-07-31) errorCode "auth.operator_only" — EXPLICIT operator gate (§P2.4a).
|
|
3234
|
+
content:
|
|
3235
|
+
application/json:
|
|
3236
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
2946
3237
|
'409': { description: 'Busy — { activeBakeId?, eventsUrl? }.' }
|
|
2947
3238
|
'429': { description: 'Submission rate-limited (retryAfterSec).' }
|
|
3239
|
+
'501':
|
|
3240
|
+
description: >-
|
|
3241
|
+
(declared 2026-07-31) errorCode "capability.unavailable" — the requested bake shape is not open
|
|
3242
|
+
yet (custom/tenant P4), from the input-whitelist validator (`validateBake`).
|
|
3243
|
+
content:
|
|
3244
|
+
application/json:
|
|
3245
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
3246
|
+
'503':
|
|
3247
|
+
description: >-
|
|
3248
|
+
(declared 2026-07-31) errorCode "state.no_sandbox_base" — a real (non-dryRun) build has no
|
|
3249
|
+
concrete `sandbox-base` to build FROM (set `SANDBOX_BASE_REF` or publish one); a `dryRun` request
|
|
3250
|
+
is resolve-only and is exempt.
|
|
3251
|
+
content:
|
|
3252
|
+
application/json:
|
|
3253
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
2948
3254
|
|
|
2949
3255
|
/v1/images/bakes/claim:
|
|
2950
3256
|
parameters:
|
|
@@ -2955,9 +3261,16 @@ paths:
|
|
|
2955
3261
|
x-status: live # bake-runner credential face (machine-to-machine).
|
|
2956
3262
|
summary: 'RUNNER: claim the oldest queued bake (discover+CAS in one step; 204 = queue empty).'
|
|
2957
3263
|
responses:
|
|
2958
|
-
'200': { description: 'BakeClaim { bakeId, argv, push, dryRun, logs, profile, bands, ingestSecret, leaseUntil }.', content: { application/json: { schema: {
|
|
3264
|
+
'200': { description: 'BakeClaim { bakeId, argv, push, dryRun, logs, profile, bands, ingestSecret, leaseUntil }.', content: { application/json: { schema: { $ref: '#/components/schemas/BakeClaim' } } } }
|
|
2959
3265
|
'204': { description: 'Queue empty.' }
|
|
2960
|
-
'
|
|
3266
|
+
'403':
|
|
3267
|
+
description: >-
|
|
3268
|
+
errorCode "auth.bake_runner_only" — the caller is not the bake runner. (Was documented as 401
|
|
3269
|
+
until 2026-07-31; the handler has always sent 403 with this code, so a consumer keyed on 401
|
|
3270
|
+
never matched.)
|
|
3271
|
+
content:
|
|
3272
|
+
application/json:
|
|
3273
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
2961
3274
|
|
|
2962
3275
|
/v1/images/bakes/{bakeId}:
|
|
2963
3276
|
parameters:
|
|
@@ -2967,9 +3280,14 @@ paths:
|
|
|
2967
3280
|
tags: [sandbox]
|
|
2968
3281
|
operationId: bakesGet
|
|
2969
3282
|
x-status: live
|
|
2970
|
-
summary: 'One bake row → { bake } (
|
|
3283
|
+
summary: 'One bake row → { bake } (status/state/profile/bands/logs/digest…).'
|
|
2971
3284
|
responses:
|
|
2972
|
-
'200': { description: '{ bake }.', content: { application/json: { schema: { type: object, additionalProperties:
|
|
3285
|
+
'200': { description: '{ bake }.', content: { application/json: { schema: { type: object, required: [bake], additionalProperties: false, properties: { bake: { $ref: '#/components/schemas/BakeRecordView' } } } } } }
|
|
3286
|
+
'403':
|
|
3287
|
+
description: (declared 2026-07-31) errorCode "auth.operator_only" — EXPLICIT operator gate.
|
|
3288
|
+
content:
|
|
3289
|
+
application/json:
|
|
3290
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
2973
3291
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
2974
3292
|
|
|
2975
3293
|
/v1/images/bakes/{bakeId}/cancel:
|
|
@@ -2980,9 +3298,14 @@ paths:
|
|
|
2980
3298
|
tags: [sandbox]
|
|
2981
3299
|
operationId: bakesCancel
|
|
2982
3300
|
x-status: live
|
|
2983
|
-
summary: 'OPERATOR: request cancel → 202 { bakeId, cancelRequested: true, note
|
|
3301
|
+
summary: 'OPERATOR: request cancel → 202 { bakeId, cancelRequested: true, note } (flag only; the runner kills the build on its next heartbeat; terminal rows = honest no-op note).'
|
|
2984
3302
|
responses:
|
|
2985
|
-
'202': { description: '{ bakeId, cancelRequested, note
|
|
3303
|
+
'202': { description: '{ bakeId, cancelRequested, note }.', content: { application/json: { schema: { $ref: '#/components/schemas/BakeCancelAck' } } } }
|
|
3304
|
+
'403':
|
|
3305
|
+
description: (declared 2026-07-31) errorCode "auth.operator_only" — EXPLICIT operator gate.
|
|
3306
|
+
content:
|
|
3307
|
+
application/json:
|
|
3308
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
2986
3309
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
2987
3310
|
|
|
2988
3311
|
/v1/images/bakes/{bakeId}/claim:
|
|
@@ -2995,7 +3318,12 @@ paths:
|
|
|
2995
3318
|
x-status: live
|
|
2996
3319
|
summary: 'RUNNER: claim THIS queued bake (single-flight CAS; losing the race ⇒ 409).'
|
|
2997
3320
|
responses:
|
|
2998
|
-
'200': { description: 'BakeClaim (same shape as claim-next).', content: { application/json: { schema: {
|
|
3321
|
+
'200': { description: 'BakeClaim (same shape as claim-next).', content: { application/json: { schema: { $ref: '#/components/schemas/BakeClaim' } } } }
|
|
3322
|
+
'403':
|
|
3323
|
+
description: (declared 2026-07-31) errorCode "auth.bake_runner_only" — the caller is not the bake runner (same code as the sibling claim-next).
|
|
3324
|
+
content:
|
|
3325
|
+
application/json:
|
|
3326
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
2999
3327
|
'409': { description: 'Claim race lost / not queued.' }
|
|
3000
3328
|
|
|
3001
3329
|
/v1/images/bakes/{bakeId}/ingest:
|
|
@@ -3007,10 +3335,19 @@ paths:
|
|
|
3007
3335
|
operationId: bakesIngest
|
|
3008
3336
|
x-status: live
|
|
3009
3337
|
summary: 'RUNNER: push one build.sh event line + lease heartbeat (x-bake-ingest-secret header from the claim).'
|
|
3010
|
-
description: 'line = {event, …}; event=heartbeat renews the lease only.
|
|
3338
|
+
description: 'line = {event, …}; event=heartbeat renews the lease only. 409 = lease lost/stolen; cancelRequested=true ⇒ the runner should kill the build.'
|
|
3011
3339
|
requestBody: { required: true, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
|
|
3012
3340
|
responses:
|
|
3013
|
-
'200': { description: '
|
|
3341
|
+
'200': { description: 'One of four shapes (heartbeat / non-terminal append / terminal done-success / terminal done-needs-flip) — see BakeIngestAck.', content: { application/json: { schema: { $ref: '#/components/schemas/BakeIngestAck' } } } }
|
|
3342
|
+
'403':
|
|
3343
|
+
description: >-
|
|
3344
|
+
(declared 2026-07-31) two distinct causes share this status: errorCode "auth.bake_runner_only"
|
|
3345
|
+
(the caller's credential isn't the configured bake-runner principal) and
|
|
3346
|
+
"auth.ingest_secret_invalid" (the `x-bake-ingest-secret` header is missing or doesn't match the
|
|
3347
|
+
per-bake secret minted at claim).
|
|
3348
|
+
content:
|
|
3349
|
+
application/json:
|
|
3350
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
3014
3351
|
'409': { description: 'Lease invalid / claimed by another runner.' }
|
|
3015
3352
|
|
|
3016
3353
|
/v1/images/bakes/{bakeId}/events:
|
|
@@ -3021,9 +3358,14 @@ paths:
|
|
|
3021
3358
|
tags: [sandbox]
|
|
3022
3359
|
operationId: bakesEvents
|
|
3023
3360
|
x-status: live
|
|
3024
|
-
summary: 'Bake live SSE (run-trace-SSE framing; RESUMABLE — `id:` lines carry the event seq, Last-Event-ID resumes).'
|
|
3361
|
+
summary: 'OPERATOR: Bake live SSE (run-trace-SSE framing; RESUMABLE — `id:` lines carry the event seq, Last-Event-ID resumes).'
|
|
3025
3362
|
responses:
|
|
3026
3363
|
'200': { description: 'SSE stream.', content: { text/event-stream: { schema: { type: string } } } }
|
|
3364
|
+
'403':
|
|
3365
|
+
description: (declared 2026-07-31) errorCode "auth.operator_only" — EXPLICIT operator gate (this summary previously did not mention the restriction at all).
|
|
3366
|
+
content:
|
|
3367
|
+
application/json:
|
|
3368
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
3027
3369
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
3028
3370
|
|
|
3029
3371
|
/v1/images/select:
|
|
@@ -3097,14 +3439,19 @@ paths:
|
|
|
3097
3439
|
The approval row carries the FULL tool-call args (commands/paths/contents of a high-risk op), so the
|
|
3098
3440
|
by-id read enforces the same tenant boundary as the list: operators read across tenants; everyone else
|
|
3099
3441
|
reads ONLY their own scope (the guard fires at the SQL layer — no cross-tenant args even if an id
|
|
3100
|
-
leaks). Non-owner → 404 (never 403, no existence oracle).
|
|
3101
|
-
|
|
3442
|
+
leaks). Non-owner → 404 (never 403, no existence oracle).
|
|
3443
|
+
🔴 AVAILABILITY (corrected 2026-07-31): present only on deployments that have the D-lane approval store
|
|
3444
|
+
**AND NOT the checkpoint store**. When `DURABLE_APPROVAL` is on, the checkpoint lane takes precedence
|
|
3445
|
+
for the whole `/v1/approvals*` prefix and this by-id route answers 404 `not_found.route` — deliberate,
|
|
3446
|
+
not a bug; decide via `/v1/approvals/{sessionId}/decide` and list via `GET /v1/approvals`.
|
|
3447
|
+
The old wording ("present only on deployments with the D-lane approval store") named one condition of
|
|
3448
|
+
two, so a both-stores worker looked like it should serve this route.
|
|
3102
3449
|
responses:
|
|
3103
3450
|
'200':
|
|
3104
3451
|
description: The approval row (full args projection).
|
|
3105
3452
|
content:
|
|
3106
3453
|
application/json:
|
|
3107
|
-
schema: {
|
|
3454
|
+
schema: { $ref: '#/components/schemas/ApprovalRow' }
|
|
3108
3455
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
3109
3456
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
3110
3457
|
|
|
@@ -3135,11 +3482,14 @@ paths:
|
|
|
3135
3482
|
application/json:
|
|
3136
3483
|
schema:
|
|
3137
3484
|
type: object
|
|
3485
|
+
# 🔴 census 批2 五段:CLOSED — exact literal `{ scope, exportedAt, entries }`
|
|
3486
|
+
# (memory-policy.ts:58), no fourth key.
|
|
3138
3487
|
required: [scope, exportedAt, entries]
|
|
3488
|
+
additionalProperties: false
|
|
3139
3489
|
properties:
|
|
3140
3490
|
scope: { type: string }
|
|
3141
3491
|
exportedAt: { type: string, format: date-time }
|
|
3142
|
-
entries: { type: array, items: {
|
|
3492
|
+
entries: { type: array, items: { $ref: '#/components/schemas/MemoryEntry' } }
|
|
3143
3493
|
'400': { $ref: '#/components/responses/BadRequest' }
|
|
3144
3494
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
3145
3495
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
@@ -3175,7 +3525,7 @@ paths:
|
|
|
3175
3525
|
description: 'The sync outcome (core reconcile/nextSyncBaseline projection).'
|
|
3176
3526
|
content:
|
|
3177
3527
|
application/json:
|
|
3178
|
-
schema: {
|
|
3528
|
+
schema: { $ref: '#/components/schemas/MemorySyncResponse' }
|
|
3179
3529
|
'400': { $ref: '#/components/responses/BadRequest' }
|
|
3180
3530
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
3181
3531
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
@@ -3211,7 +3561,15 @@ paths:
|
|
|
3211
3561
|
description: Aggregation rows.
|
|
3212
3562
|
content:
|
|
3213
3563
|
application/json:
|
|
3214
|
-
schema:
|
|
3564
|
+
schema:
|
|
3565
|
+
type: object
|
|
3566
|
+
# 🔴 census 批2 五段:path↔schema 脱钩修(same class as the earlier BakeClaim/usage-triplet
|
|
3567
|
+
# finds) — `OutcomeRow` already existed named+correct (observability.ts:51's exact
|
|
3568
|
+
# `{ outcomes: rows }`) but this path never `$ref`'d it, declaring a bare open `{}` instead.
|
|
3569
|
+
required: [outcomes]
|
|
3570
|
+
additionalProperties: false
|
|
3571
|
+
properties:
|
|
3572
|
+
outcomes: { type: array, items: { $ref: '#/components/schemas/OutcomeRow' } }
|
|
3215
3573
|
'403': { description: 'Multi-tenant worker: outcomes view is operator-only.' }
|
|
3216
3574
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
3217
3575
|
|
|
@@ -3253,9 +3611,13 @@ paths:
|
|
|
3253
3611
|
application/json:
|
|
3254
3612
|
schema:
|
|
3255
3613
|
type: object
|
|
3614
|
+
# 🔴 census 批2 五段:path↔schema 脱钩修 (same class as /v1/outcomes above) — `SendfileLinkRow`
|
|
3615
|
+
# already existed named+correct but this path's `links` items were a bare open `{}` instead
|
|
3616
|
+
# of `$ref`ing it.
|
|
3256
3617
|
required: [links]
|
|
3618
|
+
additionalProperties: false
|
|
3257
3619
|
properties:
|
|
3258
|
-
links: { type: array, items: {
|
|
3620
|
+
links: { type: array, items: { $ref: '#/components/schemas/SendfileLinkRow' } }
|
|
3259
3621
|
nextBefore: { type: string }
|
|
3260
3622
|
'400': { $ref: '#/components/responses/BadRequest' }
|
|
3261
3623
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
@@ -3297,9 +3659,18 @@ paths:
|
|
|
3297
3659
|
schema:
|
|
3298
3660
|
type: object
|
|
3299
3661
|
required: [runId, entries]
|
|
3662
|
+
additionalProperties: false
|
|
3300
3663
|
properties:
|
|
3301
3664
|
runId: { type: string }
|
|
3302
|
-
|
|
3665
|
+
# 🔴 census 批2 四段:CLOSED against server's real `projectResult`/truncated-row shapes
|
|
3666
|
+
# (workflows.ts:127-165) — a row is EITHER the projected result OR an honest truncated stub
|
|
3667
|
+
# (a row whose stored TaskResult exceeded the 64KiB per-row gate never enters process memory).
|
|
3668
|
+
entries:
|
|
3669
|
+
type: array
|
|
3670
|
+
items:
|
|
3671
|
+
oneOf:
|
|
3672
|
+
- $ref: '#/components/schemas/WorkflowJournalEntry'
|
|
3673
|
+
- $ref: '#/components/schemas/WorkflowJournalEntryTruncated'
|
|
3303
3674
|
nextOffset: { type: integer }
|
|
3304
3675
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
3305
3676
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
@@ -3340,6 +3711,14 @@ paths:
|
|
|
3340
3711
|
responses:
|
|
3341
3712
|
'200':
|
|
3342
3713
|
description: 'Injected. Body `{ runId, label, status: "running", delivery: "applied", marker }`.'
|
|
3714
|
+
# 🔴 census 批2 五段:spec-谎修——this 200 declared a bare `description` with NO `content`/`schema`
|
|
3715
|
+
# at all (classify() reads it as "no-json", invisible to both the open- and closed-op ledgers)
|
|
3716
|
+
# while the server genuinely sends a JSON body (workflows.ts:279). The named schema
|
|
3717
|
+
# `WorkflowAgentSteerReceipt` already existed (path↔schema decoupling, same class as the earlier
|
|
3718
|
+
# BakeClaim/usage-triplet finds) — wiring it up here, not inventing a new one.
|
|
3719
|
+
content:
|
|
3720
|
+
application/json:
|
|
3721
|
+
schema: { $ref: '#/components/schemas/WorkflowAgentSteerReceipt' }
|
|
3343
3722
|
'400': { $ref: '#/components/responses/BadRequest' }
|
|
3344
3723
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
3345
3724
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
@@ -3350,6 +3729,14 @@ paths:
|
|
|
3350
3729
|
content:
|
|
3351
3730
|
application/json:
|
|
3352
3731
|
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
3732
|
+
'413':
|
|
3733
|
+
description: >-
|
|
3734
|
+
(declared 2026-07-31) errorCode "steer.content_too_large" — content exceeds the server's
|
|
3735
|
+
per-steer request cap (`STEER_IN_MAX_REQUEST_CHARS`, same family as the run/subagent steer caps);
|
|
3736
|
+
REJECTED, never truncated. Shorten and resend.
|
|
3737
|
+
content:
|
|
3738
|
+
application/json:
|
|
3739
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
3353
3740
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
3354
3741
|
|
|
3355
3742
|
/metrics/summary:
|
|
@@ -3369,7 +3756,12 @@ paths:
|
|
|
3369
3756
|
description: Metrics summary (shape draft).
|
|
3370
3757
|
content:
|
|
3371
3758
|
application/json:
|
|
3372
|
-
|
|
3759
|
+
# 🔴 census 批2 五段:CLOSED against the real emitter — `sendJson(res, 200, { model,
|
|
3760
|
+
# ...deps.metrics.summarize() })` (server.ts:794) is a fixed key set (server observability/
|
|
3761
|
+
# metrics.ts `MetricsSummary`), not an ops free-for-all. Left `x-status: draft` / `x-sdk: none`
|
|
3762
|
+
# untouched (still deliberately outside the SDK's wrapped surface) — closing the SHAPE and
|
|
3763
|
+
# keeping it un-wrapped are independent axes.
|
|
3764
|
+
schema: { $ref: '#/components/schemas/MetricsSummaryOps' }
|
|
3373
3765
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
3374
3766
|
|
|
3375
3767
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -3437,6 +3829,15 @@ components:
|
|
|
3437
3829
|
content:
|
|
3438
3830
|
application/json:
|
|
3439
3831
|
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
3832
|
+
Forbidden:
|
|
3833
|
+
description: >
|
|
3834
|
+
Authorization denied on a resource the caller is authenticated for — 403. Distinct from Unauthorized
|
|
3835
|
+
(401/503 = who are you / this worker refuses to serve): here the identity is established and rejected.
|
|
3836
|
+
Today's only producer is session-ownership: reusing a `sessionId` that belongs to another principal.
|
|
3837
|
+
Mapped to AuthError.
|
|
3838
|
+
content:
|
|
3839
|
+
application/json:
|
|
3840
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
3440
3841
|
NotFound:
|
|
3441
3842
|
description: >
|
|
3442
3843
|
No such resource for this principal. Owner-scoped reads/cancel return 404 (not 403) for a non-owned or
|
|
@@ -3959,6 +4360,27 @@ components:
|
|
|
3959
4360
|
sessionId: { type: string }
|
|
3960
4361
|
status: { $ref: '#/components/schemas/RunStatus' }
|
|
3961
4362
|
result: { type: string }
|
|
4363
|
+
activeTaskId:
|
|
4364
|
+
type: [string, 'null']
|
|
4365
|
+
description: >
|
|
4366
|
+
[2255]① reject-shape material (server >=3.21, SSE lane): on a `conflict.session_active_run`
|
|
4367
|
+
rejection the stream's terminal `done` frame carries the occupying run's taskId. On wire since
|
|
4368
|
+
[1833] G10; registered here late. ADDITIVE / tolerate-absent — normal terminal frames omit it.
|
|
4369
|
+
activeTaskStatus:
|
|
4370
|
+
type: string
|
|
4371
|
+
description: >
|
|
4372
|
+
Status of the occupying run (`running` | `suspended` | `needs_review`) on a session-active-run
|
|
4373
|
+
rejection. Splits the exit families: running => steer/cancel; parked => decide (see `pendingGate`) / cancel.
|
|
4374
|
+
pendingGate:
|
|
4375
|
+
type: object
|
|
4376
|
+
required: [kind, decidePath]
|
|
4377
|
+
additionalProperties: false
|
|
4378
|
+
description: >
|
|
4379
|
+
Present when the occupying run is parked on a pending durable gate: `decidePath` is THE ONE correct
|
|
4380
|
+
resume entry for its `kind` (gate-kind routed; sessionId-addressed — checkpoint tokens never appear on the wire).
|
|
4381
|
+
properties:
|
|
4382
|
+
kind: { type: string }
|
|
4383
|
+
decidePath: { type: string }
|
|
3962
4384
|
model:
|
|
3963
4385
|
type: string
|
|
3964
4386
|
description: >
|
|
@@ -4116,11 +4538,15 @@ components:
|
|
|
4116
4538
|
ResumeEvicted:
|
|
4117
4539
|
type: object
|
|
4118
4540
|
description: >
|
|
4119
|
-
416 body for an evicted SSE resume point (`GET /v1/runs/:id/events`).
|
|
4120
|
-
|
|
4121
|
-
|
|
4541
|
+
416 body for an evicted SSE resume point (`GET /v1/runs/:id/events`). Carries the machine code
|
|
4542
|
+
`errorCode: "limit.retention_evicted"`; matching the 416 STATUS alone is equally valid. Read
|
|
4543
|
+
`retainedFrom`, then full-sync via `/v1/tasks/:id/turns` (pinned SSE contract).
|
|
4544
|
+
(Corrected 2026-07-31 — see the 416 response note on that path: the "no machine code" claim predates
|
|
4545
|
+
the site's move onto the shared `sendError`.)
|
|
4546
|
+
required: [error, errorCode, retainedFrom]
|
|
4122
4547
|
properties:
|
|
4123
4548
|
error: { type: string }
|
|
4549
|
+
errorCode: { type: string, enum: [limit.retention_evicted] }
|
|
4124
4550
|
retainedFrom: { type: integer, description: Lowest event seq still retained; resume events from here. }
|
|
4125
4551
|
|
|
4126
4552
|
ImageIndexEntry:
|
|
@@ -4136,7 +4562,7 @@ components:
|
|
|
4136
4562
|
[id, profile, bands, repo, tag, digest, toolchainVersions, capabilities, podContract, sizeBytes, status,
|
|
4137
4563
|
visibility, tenantId, manifestSha, recipeGitSha, generatorVersion, buildDate, supersedes, signed,
|
|
4138
4564
|
createdAt, updatedAt]
|
|
4139
|
-
additionalProperties:
|
|
4565
|
+
additionalProperties: false
|
|
4140
4566
|
properties:
|
|
4141
4567
|
id: { type: string, description: 'deterministically derived from (repo, digest) — there is no explicit-id upsert path, so an id collision ⟺ a (repo, digest) collision.' }
|
|
4142
4568
|
profile: { type: string }
|
|
@@ -4150,26 +4576,8 @@ components:
|
|
|
4150
4576
|
back to bind an image (bind by `profile`; the server re-resolves + re-admits under a trusted
|
|
4151
4577
|
principal, because trusting a caller-supplied digest would fail OPEN).
|
|
4152
4578
|
toolchainVersions: { type: object, additionalProperties: { type: string } }
|
|
4153
|
-
capabilities:
|
|
4154
|
-
|
|
4155
|
-
additionalProperties: true
|
|
4156
|
-
properties:
|
|
4157
|
-
browser: { type: boolean }
|
|
4158
|
-
db: { type: boolean }
|
|
4159
|
-
nestedBuild: { type: boolean, description: 'can it build container images (back-compat boolean).' }
|
|
4160
|
-
nestedBuildMode:
|
|
4161
|
-
type: string
|
|
4162
|
-
enum: [none, docker-cli-only, rootless-buildkit, privileged-dind]
|
|
4163
|
-
description: 'HOW it nests (the posture). Refines `nestedBuild`; absent ⇒ fall back to the boolean.'
|
|
4164
|
-
podContract:
|
|
4165
|
-
type: object
|
|
4166
|
-
additionalProperties: true
|
|
4167
|
-
properties:
|
|
4168
|
-
devShmMB: { type: integer }
|
|
4169
|
-
minMemMB: { type: integer }
|
|
4170
|
-
minCpu: { type: string }
|
|
4171
|
-
readyTimeoutSec: { type: integer }
|
|
4172
|
-
securityContext: { type: object, additionalProperties: true }
|
|
4579
|
+
capabilities: { $ref: '#/components/schemas/ImageCapabilities' }
|
|
4580
|
+
podContract: { $ref: '#/components/schemas/ImagePodContract' }
|
|
4173
4581
|
sizeBytes: { type: ['integer', 'null'] }
|
|
4174
4582
|
status: { type: string, enum: [building, published, deprecated, failed] }
|
|
4175
4583
|
visibility: { type: string, enum: [public, tenant] }
|
|
@@ -4183,14 +4591,47 @@ components:
|
|
|
4183
4591
|
createdAt: { type: string, description: ISO date-time. }
|
|
4184
4592
|
updatedAt: { type: string, description: ISO date-time. }
|
|
4185
4593
|
|
|
4594
|
+
ImageCapabilities:
|
|
4595
|
+
# 抽成具名 schema(census 批2 四段)——此前在 ImageIndexEntry/ImageSelectResult 两处内联重复,
|
|
4596
|
+
# ImageSelectResult 那份还漏了 `nestedBuildMode`(server 侧两处读的是同一个 ImageCapabilities,
|
|
4597
|
+
# entry.capabilities 逐字透传;两侧同缺,不是刻意窄化)。server store-contracts.ts 全字段皆 optional。
|
|
4598
|
+
type: object
|
|
4599
|
+
description: What a sandbox image can do (server `ImageCapabilities`, plugins/store-contracts.ts).
|
|
4600
|
+
additionalProperties: false
|
|
4601
|
+
properties:
|
|
4602
|
+
browser: { type: boolean }
|
|
4603
|
+
db: { type: boolean }
|
|
4604
|
+
nestedBuild: { type: boolean, description: 'can it build container images (back-compat boolean).' }
|
|
4605
|
+
nestedBuildMode:
|
|
4606
|
+
type: string
|
|
4607
|
+
enum: [none, docker-cli-only, rootless-buildkit, privileged-dind]
|
|
4608
|
+
description: 'HOW it nests (the posture). Refines `nestedBuild`; absent ⇒ fall back to the boolean.'
|
|
4609
|
+
|
|
4610
|
+
ImagePodContract:
|
|
4611
|
+
# 抽成具名 schema(census 批2 四段)——同上,ImageSelectResult 那份此前连字段都没声明(裸 additionalProperties:true)。
|
|
4612
|
+
type: object
|
|
4613
|
+
description: Pod-shape hints for scheduling a sandbox off this image (server `ImagePodContract`).
|
|
4614
|
+
additionalProperties: false
|
|
4615
|
+
properties:
|
|
4616
|
+
devShmMB: { type: integer }
|
|
4617
|
+
minMemMB: { type: integer }
|
|
4618
|
+
minCpu: { type: string }
|
|
4619
|
+
readyTimeoutSec: { type: integer }
|
|
4620
|
+
securityContext: { type: object, additionalProperties: true, description: 'k8s-shaped, genuinely arbitrary — Record<string,unknown> server-side.' }
|
|
4621
|
+
|
|
4186
4622
|
ImageSelectResult:
|
|
4187
|
-
# 新增(2026-07-25 回填批)
|
|
4623
|
+
# 新增(2026-07-25 回填批)。🔴 census 批2 四段:closed against the real handler (images.ts:407-415) — ALL
|
|
4624
|
+
# seven keys are unconditionally assigned every 200 (required tightened from none); `manifestSha` can be
|
|
4625
|
+
# null (ImageIndexEntry.manifestSha is `string | null`, this was typed non-nullable — a spec lie); and
|
|
4626
|
+
# `podContract`/`capabilities` now share the SAME closed schemas as ImageIndexEntry (no more drift/no
|
|
4627
|
+
# more missing `nestedBuildMode`).
|
|
4188
4628
|
type: object
|
|
4189
4629
|
description: >
|
|
4190
4630
|
`POST /v1/images/select` resolution result — a PREVIEW of "which digest/capabilities does this profile
|
|
4191
4631
|
resolve to" for the UI. 🔴 NOT a binding credential: to actually bind, send `sandboxImageProfile` on the
|
|
4192
4632
|
task request and let the server resolve under a trusted principal.
|
|
4193
|
-
|
|
4633
|
+
required: [profile, digest, repo, ref, podContract, capabilities, manifestSha]
|
|
4634
|
+
additionalProperties: false
|
|
4194
4635
|
properties:
|
|
4195
4636
|
profile: { type: string }
|
|
4196
4637
|
digest: { type: string }
|
|
@@ -4200,15 +4641,9 @@ components:
|
|
|
4200
4641
|
description: >
|
|
4201
4642
|
`${repo}@${digest}` — always present on a 200. This is the value a caller threads into the kata
|
|
4202
4643
|
adapter's per-sandbox image (the server handler says so itself), i.e. it is load-bearing, not decorative.
|
|
4203
|
-
podContract: {
|
|
4204
|
-
capabilities:
|
|
4205
|
-
|
|
4206
|
-
additionalProperties: true
|
|
4207
|
-
properties:
|
|
4208
|
-
browser: { type: boolean }
|
|
4209
|
-
db: { type: boolean }
|
|
4210
|
-
nestedBuild: { type: boolean }
|
|
4211
|
-
manifestSha: { type: string }
|
|
4644
|
+
podContract: { $ref: '#/components/schemas/ImagePodContract' }
|
|
4645
|
+
capabilities: { $ref: '#/components/schemas/ImageCapabilities' }
|
|
4646
|
+
manifestSha: { type: ['string', 'null'], description: 'the source entry''s manifestSha — nullable (ImageIndexEntry.manifestSha is string|null; was wrongly non-nullable here).' }
|
|
4212
4647
|
|
|
4213
4648
|
QuotaError:
|
|
4214
4649
|
type: object
|
|
@@ -4350,17 +4785,32 @@ components:
|
|
|
4350
4785
|
|
|
4351
4786
|
LeaderReceipt:
|
|
4352
4787
|
type: object
|
|
4788
|
+
description: >
|
|
4789
|
+
202 receipt of POST /v1/leader (server leader/endpoint.ts:130 — exact literal
|
|
4790
|
+
`{ leaderRunId, status: "running" }`; the POST ack is always the "running" value, the other
|
|
4791
|
+
`status` enum members only ever appear on the GET twin below).
|
|
4792
|
+
# 🔴 census 批2 五段:CLOSED — the POST handler's literal never carries a third key.
|
|
4353
4793
|
required: [leaderRunId, status]
|
|
4794
|
+
additionalProperties: false
|
|
4354
4795
|
properties:
|
|
4355
4796
|
leaderRunId: { type: string }
|
|
4356
4797
|
status: { type: string, enum: [running, completed, failed] }
|
|
4357
4798
|
|
|
4358
4799
|
LeaderRecord:
|
|
4359
4800
|
type: object
|
|
4801
|
+
description: >
|
|
4802
|
+
200 body of GET /v1/leader/{leaderRunId} (server leader/endpoint.ts:142-150 — exact literal
|
|
4803
|
+
`{ id, status, ...(result?{result}:{}), ...(error?{error}:{}) }`).
|
|
4804
|
+
# 🔴 census 批2 五段:两处修——① CLOSED(result/error 是 OMIT-when-absent 的可选键,不是额外键);
|
|
4805
|
+
# ② `status` 的第四值 `needs_human`(LEADER-REPAIRLOOP-INTEGRATION §5/§10.6 的第三终态,
|
|
4806
|
+
# `statusForLeaderResult` 在 candidate_only/needs_human_oracle/conflict 三个 repairTerminal 上产出)
|
|
4807
|
+
# 此前 SPEC LIES BY OMISSION——TS 侧 `LeaderRecord.status` 早就带这个第四值(types.ts:1607,亲验
|
|
4808
|
+
# 已在场),只有 spec 的 enum 一直缺它,是个只有场景真触发才会现形的假红洞(一个真实合法帧被判违约)。
|
|
4360
4809
|
required: [id, status]
|
|
4810
|
+
additionalProperties: false
|
|
4361
4811
|
properties:
|
|
4362
4812
|
id: { type: string }
|
|
4363
|
-
status: { type: string, enum: [running, completed, failed] }
|
|
4813
|
+
status: { type: string, enum: [running, completed, failed, needs_human] }
|
|
4364
4814
|
result: {}
|
|
4365
4815
|
error: { type: string }
|
|
4366
4816
|
|
|
@@ -4399,6 +4849,30 @@ components:
|
|
|
4399
4849
|
Approve-with-edit (design/37 last-wins): operator's rewritten tool args, applied AFTER the binding
|
|
4400
4850
|
check. `boundInputHash` STILL binds the ORIGINAL input the human saw — never hash updatedInput.
|
|
4401
4851
|
|
|
4852
|
+
ApprovalRow:
|
|
4853
|
+
type: object
|
|
4854
|
+
description: >
|
|
4855
|
+
The legacy D-lane approval row of GET /v1/approvals/{approvalId} (server `ApprovalRow`,
|
|
4856
|
+
plugins/approval-store-sql.ts:43 — the row is `sendJson`'d verbatim, approvals-assistant.ts:668).
|
|
4857
|
+
Present only on deployments running the legacy `approvalStore` leg (no `checkpointStore` wired);
|
|
4858
|
+
the durable F4 lane has no by-id GET (only list + decide).
|
|
4859
|
+
# 🔴 census 批2 五段:CLOSED — byte-identical to the server row type, no extra keys.
|
|
4860
|
+
required: [id, taskId, sessionId, owner, scope, toolName, args, status, reason, decidedBy, createdAt, decidedAt]
|
|
4861
|
+
additionalProperties: false
|
|
4862
|
+
properties:
|
|
4863
|
+
id: { type: string }
|
|
4864
|
+
taskId: { type: [string, 'null'] }
|
|
4865
|
+
sessionId: { type: [string, 'null'] }
|
|
4866
|
+
owner: { type: [string, 'null'] }
|
|
4867
|
+
scope: { type: [string, 'null'], description: 'Single-DB fleet scope guard (tenant identity, owner-sourced).' }
|
|
4868
|
+
toolName: { type: string }
|
|
4869
|
+
args: { description: 'The reviewed tool-call args — full fidelity, not redacted (this IS the review surface).' }
|
|
4870
|
+
status: { type: string, enum: [pending, approved, denied, expired] }
|
|
4871
|
+
reason: { type: [string, 'null'] }
|
|
4872
|
+
decidedBy: { type: [string, 'null'] }
|
|
4873
|
+
createdAt: { type: string }
|
|
4874
|
+
decidedAt: { type: [string, 'null'] }
|
|
4875
|
+
|
|
4402
4876
|
Capabilities:
|
|
4403
4877
|
type: object
|
|
4404
4878
|
description: >
|
|
@@ -4594,6 +5068,148 @@ components:
|
|
|
4594
5068
|
egress: { type: boolean }
|
|
4595
5069
|
irreversibility: { type: string, enum: [always, never] }
|
|
4596
5070
|
|
|
5071
|
+
MemoryEntryFrontmatter:
|
|
5072
|
+
type: object
|
|
5073
|
+
description: >
|
|
5074
|
+
core `MemoryEntryFrontmatter` (@sema-agent/core memory-engine/types.d.ts). All fields optional —
|
|
5075
|
+
an entry with none of them is a bare note.
|
|
5076
|
+
additionalProperties: false
|
|
5077
|
+
properties:
|
|
5078
|
+
name: { type: string }
|
|
5079
|
+
description: { type: string }
|
|
5080
|
+
type: { type: string }
|
|
5081
|
+
deleted: { type: boolean }
|
|
5082
|
+
provenance:
|
|
5083
|
+
type: object
|
|
5084
|
+
required: [kind, path, contentHash, ingestedAt]
|
|
5085
|
+
additionalProperties: false
|
|
5086
|
+
properties:
|
|
5087
|
+
kind: { type: string, enum: [repo_file] }
|
|
5088
|
+
path: { type: string }
|
|
5089
|
+
contentHash: { type: string }
|
|
5090
|
+
ingestedAt: { type: integer }
|
|
5091
|
+
trust: { type: string, enum: [untrusted] }
|
|
5092
|
+
extra: { type: array, items: { type: string } }
|
|
5093
|
+
|
|
5094
|
+
MemoryEntry:
|
|
5095
|
+
type: object
|
|
5096
|
+
description: >
|
|
5097
|
+
core `MemoryEntry` (@sema-agent/core memory-engine/types.d.ts) — one memory-engine (DB twins)
|
|
5098
|
+
note. Used verbatim by both GET /v1/memory/export's `entries` and POST /v1/memory/sync/{scope}'s
|
|
5099
|
+
`serverEntries` (server never re-shapes it — memory-policy.ts:57 / memory-sync.ts:79 pass the core
|
|
5100
|
+
array straight to `sendJson`).
|
|
5101
|
+
required: [id, slug, frontmatter, body, rev, scope]
|
|
5102
|
+
additionalProperties: false
|
|
5103
|
+
properties:
|
|
5104
|
+
id: { type: string }
|
|
5105
|
+
slug: { type: string }
|
|
5106
|
+
frontmatter: { $ref: '#/components/schemas/MemoryEntryFrontmatter' }
|
|
5107
|
+
body: { type: string }
|
|
5108
|
+
rev: { type: string }
|
|
5109
|
+
scope: { type: string }
|
|
5110
|
+
|
|
5111
|
+
MemorySyncResponse:
|
|
5112
|
+
type: object
|
|
5113
|
+
description: >
|
|
5114
|
+
200 body of POST /v1/memory/sync/{scope} — core `MemorySyncResponse` (server memory-sync.ts:74-86),
|
|
5115
|
+
`sendJson`'d verbatim (memory-policy.ts:107). `pullTruncated` is OMIT-when-absent (present+`true`
|
|
5116
|
+
only when `serverEntries` was cut by `?pull.limit`).
|
|
5117
|
+
required: [applied, conflicts, serverEntries, serverDeletes, cursor]
|
|
5118
|
+
additionalProperties: false
|
|
5119
|
+
properties:
|
|
5120
|
+
applied:
|
|
5121
|
+
type: array
|
|
5122
|
+
description: The center's own-round applied ops (backend `PatchReport.applied` verbatim; empty on an idempotent replay).
|
|
5123
|
+
items:
|
|
5124
|
+
type: object
|
|
5125
|
+
required: [op, id]
|
|
5126
|
+
additionalProperties: false
|
|
5127
|
+
properties:
|
|
5128
|
+
op: { type: string, enum: [add, update, delete] }
|
|
5129
|
+
id: { type: string }
|
|
5130
|
+
slug: { type: string }
|
|
5131
|
+
conflicts:
|
|
5132
|
+
type: array
|
|
5133
|
+
items:
|
|
5134
|
+
type: object
|
|
5135
|
+
required: [id, reason]
|
|
5136
|
+
additionalProperties: false
|
|
5137
|
+
properties:
|
|
5138
|
+
id: { type: string }
|
|
5139
|
+
reason: { type: string }
|
|
5140
|
+
baseRev: { type: string }
|
|
5141
|
+
currentRev: { type: string }
|
|
5142
|
+
serverEntries:
|
|
5143
|
+
type: array
|
|
5144
|
+
description: Pull half — entries new/changed at the center since the caller's baseRevs.
|
|
5145
|
+
items: { $ref: '#/components/schemas/MemoryEntry' }
|
|
5146
|
+
serverDeletes:
|
|
5147
|
+
type: array
|
|
5148
|
+
items:
|
|
5149
|
+
type: object
|
|
5150
|
+
required: [id, baseRev]
|
|
5151
|
+
additionalProperties: false
|
|
5152
|
+
properties:
|
|
5153
|
+
id: { type: string }
|
|
5154
|
+
baseRev: { type: string }
|
|
5155
|
+
cursor:
|
|
5156
|
+
type: object
|
|
5157
|
+
required: [peer, baseRevs, updatedAtMs]
|
|
5158
|
+
additionalProperties: false
|
|
5159
|
+
properties:
|
|
5160
|
+
peer: { type: string }
|
|
5161
|
+
baseRevs: { type: object, additionalProperties: { type: string } }
|
|
5162
|
+
updatedAtMs: { type: integer }
|
|
5163
|
+
pullTruncated: { type: boolean, enum: [true] }
|
|
5164
|
+
|
|
5165
|
+
MetricsSummaryOps:
|
|
5166
|
+
type: object
|
|
5167
|
+
description: >
|
|
5168
|
+
200 body of GET /metrics/summary — `{ model, ...MetricsSummary }` (server.ts:794; server
|
|
5169
|
+
observability/metrics.ts `MetricsSummary` interface + `summarize()`, all keys unconditional).
|
|
5170
|
+
Ops/observability face (`x-sdk: none` on the path) — closed here for census fidelity, but this
|
|
5171
|
+
does NOT change its SDK-wrapping status.
|
|
5172
|
+
required:
|
|
5173
|
+
[model, runsActive, tasks, tokensTotal, costUsd, costUsdByModel, costUnknown, unpricedCalls,
|
|
5174
|
+
taskDurationAvgSec, brainFirstTokenAvgMs, brainCallAvgMs, cacheHitRateAvg, promptCacheLowHit,
|
|
5175
|
+
rateLimited, costQuotaRejected, budgetExceeded, degraded, degenerate, memoryConsolidationOps,
|
|
5176
|
+
planCacheRecurrence, cascade, verifications, councilRuns, toolErrors]
|
|
5177
|
+
additionalProperties: false
|
|
5178
|
+
properties:
|
|
5179
|
+
model: { type: string }
|
|
5180
|
+
runsActive: { type: integer }
|
|
5181
|
+
tasks: { type: object, additionalProperties: { type: integer } }
|
|
5182
|
+
tokensTotal: { type: integer }
|
|
5183
|
+
costUsd: { type: number }
|
|
5184
|
+
costUsdByModel: { type: object, additionalProperties: { type: number } }
|
|
5185
|
+
# D5 ([2122]): true ⇒ the window saw a cost-unknown brain.call (unpriced deployment) — costUsd/
|
|
5186
|
+
# costUsdByModel are then a LOWER BOUND, not an exact total.
|
|
5187
|
+
costUnknown: { type: boolean }
|
|
5188
|
+
unpricedCalls: { type: integer }
|
|
5189
|
+
taskDurationAvgSec: { type: [number, 'null'] }
|
|
5190
|
+
brainFirstTokenAvgMs: { type: [number, 'null'] }
|
|
5191
|
+
brainCallAvgMs: { type: [number, 'null'] }
|
|
5192
|
+
cacheHitRateAvg: { type: [number, 'null'] }
|
|
5193
|
+
promptCacheLowHit: { type: integer }
|
|
5194
|
+
rateLimited: { type: integer }
|
|
5195
|
+
costQuotaRejected: { type: integer }
|
|
5196
|
+
budgetExceeded: { type: object, additionalProperties: { type: integer } }
|
|
5197
|
+
degraded: { type: object, additionalProperties: { type: integer } }
|
|
5198
|
+
degenerate: { type: object, additionalProperties: { type: integer } }
|
|
5199
|
+
memoryConsolidationOps: { type: object, additionalProperties: { type: integer } }
|
|
5200
|
+
planCacheRecurrence:
|
|
5201
|
+
type: object
|
|
5202
|
+
required: [tasks, hits, hitRate]
|
|
5203
|
+
additionalProperties: false
|
|
5204
|
+
properties:
|
|
5205
|
+
tasks: { type: integer }
|
|
5206
|
+
hits: { type: integer }
|
|
5207
|
+
hitRate: { type: [number, 'null'] }
|
|
5208
|
+
cascade: { type: object, additionalProperties: { type: integer } }
|
|
5209
|
+
verifications: { type: object, additionalProperties: { type: integer } }
|
|
5210
|
+
councilRuns: { type: object, additionalProperties: { type: integer } }
|
|
5211
|
+
toolErrors: { type: object, additionalProperties: { type: integer } }
|
|
5212
|
+
|
|
4597
5213
|
ModelInfo:
|
|
4598
5214
|
type: object
|
|
4599
5215
|
description: >
|
|
@@ -4760,14 +5376,19 @@ components:
|
|
|
4760
5376
|
type: object
|
|
4761
5377
|
description: >
|
|
4762
5378
|
GET /v1/workflows list row = core summarizeWorkflowRun projection (owner-scoped; scope = the creating
|
|
4763
|
-
principal). All times are epoch MS.
|
|
5379
|
+
principal). All times are epoch MS.
|
|
5380
|
+
🔴 census 批2 四段(2026-07-30):CLOSED against core's real `summarizeWorkflowRun` (sema-core
|
|
5381
|
+
src/core/workflow-run-store.ts:79-97) — `originatingSessionId`/`agentFailures` were emitted on the
|
|
5382
|
+
wire (both conditional-spread, present only when defined) but absent from this schema (two-side same-gap).
|
|
4764
5383
|
required: [id, scope, status, phaseCount, agentCount, tokens, startedAt, createdAt]
|
|
4765
|
-
additionalProperties:
|
|
5384
|
+
additionalProperties: false
|
|
4766
5385
|
properties:
|
|
4767
5386
|
id: { type: string, description: 'Run id (= WorkflowRun.id).' }
|
|
4768
5387
|
scope: { type: string, description: 'Tenant/group scope (= the creating principal).' }
|
|
5388
|
+
originatingSessionId: { type: string, description: 'γ 批([1510]): the originating session id, so a LIST-face session filter needs no N+1 `get`. Absent for a direct runWorkflow call / sessionless deployment.' }
|
|
4769
5389
|
name: { type: string, description: "The workflow script's declared `meta.name`." }
|
|
4770
5390
|
description: { type: string, description: "The workflow script's declared `meta.description` (one-liner)." }
|
|
5391
|
+
agentFailures: { type: integer, description: 'Count of agent-runs that ended failed — present only when > 0 (a "completed, with failures" flag without an N+1 get).' }
|
|
4771
5392
|
currentPhase: { type: string, description: 'Title of the phase currently executing (absent once terminal).' }
|
|
4772
5393
|
status: { $ref: '#/components/schemas/WorkflowRunStatus' }
|
|
4773
5394
|
phaseCount: { type: integer, description: 'Phases recorded.' }
|
|
@@ -4787,25 +5408,40 @@ components:
|
|
|
4787
5408
|
🔴 `arg` is a SHORT primary-arg SUMMARY (command→name, path→basename, url→origin+path), secret-scrubbed
|
|
4788
5409
|
and truncated (~80 code points) by core. It exists for the monitor's `Read(path)` / `Bash(grep …)`
|
|
4789
5410
|
display — it is NOT the full args. RENDER it; NEVER re-feed it to a model.
|
|
4790
|
-
|
|
5411
|
+
🔴 census 批2 四段:CLOSED against core's real `ToolActivity` (sema-core src/core/types.ts:2332-2348) —
|
|
5412
|
+
`at` (the [843]④a epoch-ms stamp, carried verbatim by both the start and end beat) was on the wire
|
|
5413
|
+
but absent from this schema.
|
|
5414
|
+
required: [phase, toolCallId, toolName]
|
|
5415
|
+
additionalProperties: false
|
|
4791
5416
|
properties:
|
|
4792
5417
|
phase: { type: string, enum: [start, end] }
|
|
4793
5418
|
toolCallId: { type: string }
|
|
4794
5419
|
toolName: { type: string, description: 'the monitor renders `${toolName}(${arg})`.' }
|
|
5420
|
+
at: { type: integer, description: '[843]④a: epoch ms, stamped on both the start and end beat — a consumer derives per-call duration (end.at − start.at) and inter-call idle. Additive/optional: older producers lack it (absence ≠ 0).' }
|
|
4795
5421
|
arg: { type: string, description: 'SHORT, secret-scrubbed, ~80-code-point summary (set on `phase:"start"`). UNTRUSTED display text.' }
|
|
4796
5422
|
isError: { type: boolean, description: 'set on `phase:"end"` — whether the tool call errored.' }
|
|
4797
5423
|
|
|
4798
5424
|
WorkflowAgentRow:
|
|
4799
5425
|
# 新增(2026-07-25 回填批)—— 取代 `WorkflowRun.agents` 的 `items: {}`(零形状)。
|
|
5426
|
+
# 🔴 census 批2 四段:CLOSED against the REAL wire projection — server's `summarizeWorkflowDetail`
|
|
5427
|
+
# (src/http/routes/workflows.ts:358-394), not core's raw `WorkflowAgentRun`. The two differ: the server
|
|
5428
|
+
# ADDS `displayStatus`/`callKey`/`groupId`/`lastActivityAt`/`durationMs`/`queuedAt`/`startedAt`/`endedAt`/
|
|
5429
|
+
# `replayed` (all present on the wire but previously undeclared — same-side gap, no server change needed)
|
|
5430
|
+
# and DROPS core's `errorCode`/`errorMessage`/`attempts`/`lastAttemptReason`/`sessionId` (never projected
|
|
5431
|
+
# by this route — genuinely absent from the wire, not a spec lie).
|
|
4800
5432
|
type: object
|
|
4801
5433
|
description: >
|
|
4802
|
-
One agent-run row in a workflow's detail
|
|
4803
|
-
|
|
4804
|
-
additionalProperties:
|
|
5434
|
+
One agent-run row in a workflow's detail (server projection, not the raw core record).
|
|
5435
|
+
required: [label, status, displayStatus, callKey, queuedAt]
|
|
5436
|
+
additionalProperties: false
|
|
4805
5437
|
properties:
|
|
4806
|
-
label: { type: string, description: 'display label.' }
|
|
5438
|
+
label: { type: string, description: 'display label (redacted — LLM-authored).' }
|
|
4807
5439
|
status: { type: string, description: 'lifecycle status (the input vocabulary the display derivation reads).' }
|
|
4808
|
-
|
|
5440
|
+
displayStatus: { type: string, description: 'core deriveAgentDisplayStatus: running|queued|done|failed|interrupted — the canonical glyph vocabulary, one source of truth shared with the shell.' }
|
|
5441
|
+
taskStatus: { type: string, description: 'the underlying task''s terminal TaskStatus (open vocabulary; core enum verbatim).' }
|
|
5442
|
+
callKey: { type: string, description: 'the stable deterministic identity of this ctx.agent call (resume-journal key).' }
|
|
5443
|
+
groupId: { type: string, description: 'the nesting ctx.workflow sub-group this agent ran under; absent = top-level.' }
|
|
5444
|
+
phase: { type: string, description: 'the phase title this agent ran under (redacted — LLM-authored); absent = unphased.' }
|
|
4809
5445
|
model: { type: string, description: 'per-agent model DISPLAY label (e.g. "Opus 4.8 (1M context)"), not an id.' }
|
|
4810
5446
|
tokens: { type: integer }
|
|
4811
5447
|
turns: { type: integer }
|
|
@@ -4814,44 +5450,149 @@ components:
|
|
|
4814
5450
|
type: array
|
|
4815
5451
|
description: 'the "Activity" tail — the LAST-N tool-call beats (bounded).'
|
|
4816
5452
|
items: { $ref: '#/components/schemas/WorkflowActivityBeat' }
|
|
5453
|
+
lastActivityAt: { type: integer, description: 'the tail activity beat''s `at` timestamp — a historical view, so the shell derives idleness against its own clock rather than a served idleMs.' }
|
|
5454
|
+
durationMs: { type: integer, description: 'endedAt − startedAt; absent while queued/running or if either end is unset (a queued agent has no running duration).' }
|
|
5455
|
+
queuedAt: { type: integer, description: 'when the agent was ENQUEUED (before waiting on a concurrency slot). Always set.' }
|
|
5456
|
+
startedAt: { type: integer, description: 'when the agent actually started running (post-queue). Absent while queued, or if aborted/finalized before it ran.' }
|
|
5457
|
+
endedAt: { type: integer }
|
|
5458
|
+
replayed: { type: boolean, description: 'set when this agent''s result was REPLAYED from a resume journal rather than freshly run.' }
|
|
4817
5459
|
prompt: { type: string, description: 'what the worker was ASKED (core-redacted + bounded). UNTRUSTED display text.' }
|
|
4818
5460
|
output: { type: string, description: 'the worker''s final OUTPUT (core-redacted + bounded). UNTRUSTED display text.' }
|
|
4819
5461
|
|
|
4820
|
-
|
|
5462
|
+
WorkflowPhaseProgress:
|
|
5463
|
+
# 新(census 批2 四段):此前 `WorkflowRun.phases` 是 `items: {}`(零形状)。真形 = server
|
|
5464
|
+
# `summarizeWorkflowDetail`'s phases.map projection (workflows.ts:335-346) — a DERIVED progress view,
|
|
5465
|
+
# not core's raw `WorkflowPhase` (drops `detail`/`model`/(phase-level) `agentFailures`; adds `done`/`total`).
|
|
4821
5466
|
type: object
|
|
4822
|
-
description:
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
required: [id, scope, status, phases, agents, stats, startedAt, createdAt]
|
|
4826
|
-
additionalProperties: true
|
|
5467
|
+
description: One phase's progress in a workflow's detail (server-derived — done/total of the agents grouped under it).
|
|
5468
|
+
required: [title, status, startedAt, done, total]
|
|
5469
|
+
additionalProperties: false
|
|
4827
5470
|
properties:
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
5471
|
+
title: { type: string, description: 'phase title (redacted — LLM-authored).' }
|
|
5472
|
+
status: { type: string, description: 'core WorkflowItemStatus, plus "pending" for a meta-preregistered phase not yet adopted.' }
|
|
5473
|
+
startedAt: { type: integer, description: 'adoption time for a pre-registered phase (0 while still pending).' }
|
|
5474
|
+
endedAt: { type: integer }
|
|
5475
|
+
durationMs: { type: integer, description: 'endedAt − startedAt; absent until the phase ends.' }
|
|
5476
|
+
done: { type: integer, description: 'agents grouped under this phase whose status is a terminal (completed/failed).' }
|
|
5477
|
+
total: { type: integer, description: 'agents grouped under this phase.' }
|
|
5478
|
+
|
|
5479
|
+
WorkflowGroupNode:
|
|
5480
|
+
# 新(census 批2 四段):`WorkflowRun.groups` was undeclared entirely. Real shape = server's rebuilt nested
|
|
5481
|
+
# tree (workflows.ts:399-441, GroupNode) — recursive, acyclic (a dangling/cyclic parentGroupId is grafted
|
|
5482
|
+
# onto the root by the server, so this shape never actually cycles on the wire).
|
|
5483
|
+
type: object
|
|
5484
|
+
description: One node of the workflow's nested `ctx.workflow` group tree (rebuilt server-side from `parentGroupId`).
|
|
5485
|
+
required: [groupId, status, startedAt, agentCallKeys, children]
|
|
5486
|
+
additionalProperties: false
|
|
5487
|
+
properties:
|
|
5488
|
+
groupId: { type: string }
|
|
5489
|
+
parentGroupId: { type: string, description: 'absent = top-level (a child of the implicit root).' }
|
|
5490
|
+
status: { type: string, description: 'core WorkflowItemStatus (running/completed/failed).' }
|
|
5491
|
+
startedAt: { type: integer }
|
|
5492
|
+
endedAt: { type: integer }
|
|
5493
|
+
durationMs: { type: integer, description: 'endedAt − startedAt; absent until the group ends.' }
|
|
5494
|
+
agentCallKeys: { type: array, items: { type: string }, description: 'callKeys of the agents directly under this group.' }
|
|
5495
|
+
children: { type: array, items: { $ref: '#/components/schemas/WorkflowGroupNode' }, description: 'nested child groups (recursive).' }
|
|
5496
|
+
|
|
5497
|
+
WorkflowRunStats:
|
|
5498
|
+
# 新(census 批2 四段):此前 inline `stats` 只钉了 `tokens`/`nested.tokens` 两键;真形 = core
|
|
5499
|
+
# `WorkflowRunStats`(sema-core src/orchestration/workflow-types.ts:105-110)——own/nested 各自还有
|
|
5500
|
+
# `turns`/`costMicroUsd`,nested 另有 `tasks`。四键(own turns/costMicroUsd + nested 两键)此前两侧同缺。
|
|
5501
|
+
type: object
|
|
5502
|
+
description: 'Cumulative workflow usage. `own` (top-level) and `nested` (delegated sub-agents) are kept SEPARATE (R-5) — total spend = tokens + nested.tokens.'
|
|
5503
|
+
required: [tokens, turns, costMicroUsd, nested]
|
|
5504
|
+
additionalProperties: false
|
|
5505
|
+
properties:
|
|
5506
|
+
tokens: { type: integer }
|
|
5507
|
+
turns: { type: integer }
|
|
5508
|
+
costMicroUsd: { type: integer }
|
|
5509
|
+
nested:
|
|
4842
5510
|
type: object
|
|
4843
|
-
|
|
4844
|
-
additionalProperties:
|
|
5511
|
+
required: [tokens, turns, tasks, costMicroUsd]
|
|
5512
|
+
additionalProperties: false
|
|
4845
5513
|
properties:
|
|
4846
5514
|
tokens: { type: integer }
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
5515
|
+
turns: { type: integer }
|
|
5516
|
+
tasks: { type: integer }
|
|
5517
|
+
costMicroUsd: { type: integer }
|
|
5518
|
+
|
|
5519
|
+
WorkflowRun:
|
|
5520
|
+
# 🔴 census 批2 四段:CLOSED against the REAL wire projection — server's `summarizeWorkflowDetail`
|
|
5521
|
+
# (src/http/routes/workflows.ts:317-475), which is a DERIVED view over core's `WorkflowRun`, not the raw
|
|
5522
|
+
# record. This schema previously mirrored (a stale subset of) the core type; the actual wire adds
|
|
5523
|
+
# `durationMs`/`unphased`/`groups` and drops core's `sourceTaskId`/`originatingSessionId`/`effectiveArgs`/
|
|
5524
|
+
# `resultFull`/`completionId`/`resume`/`journalSkips` (never projected by this route — genuinely absent
|
|
5525
|
+
# from the wire, not a spec lie). `agentFailures`/`rev`/`error`/`result` WERE on the wire but undeclared
|
|
5526
|
+
# (two-side same-gap, this segment's fix).
|
|
5527
|
+
type: object
|
|
5528
|
+
description: 'GET /v1/workflows/:id full run detail (server-derived — Phases/agents/groups panels). Non-owner → 404 (no existence oracle).'
|
|
5529
|
+
required: [id, scope, status, stats, startedAt, createdAt, phases, agents, groups]
|
|
5530
|
+
additionalProperties: false
|
|
5531
|
+
properties:
|
|
5532
|
+
id: { type: string }
|
|
5533
|
+
scope: { type: string }
|
|
5534
|
+
name: { type: string, description: "The workflow script's declared `meta.name` (redacted)." }
|
|
5535
|
+
description: { type: string, description: "The workflow script's declared `meta.description` (redacted, one-liner)." }
|
|
5536
|
+
status: { $ref: '#/components/schemas/WorkflowRunStatus' }
|
|
5537
|
+
agentFailures: { type: integer, description: 'run-level count of agents whose status ended failed — stamped at run end, only when > 0.' }
|
|
5538
|
+
stats: { $ref: '#/components/schemas/WorkflowRunStats' }
|
|
4852
5539
|
startedAt: { type: integer }
|
|
4853
5540
|
endedAt: { type: integer }
|
|
4854
5541
|
createdAt: { type: integer }
|
|
5542
|
+
durationMs: { type: integer, description: 'endedAt − startedAt; absent while the run is still running.' }
|
|
5543
|
+
rev: { type: integer, description: 'store optimistic-concurrency revision; unset for a pure in-memory run.' }
|
|
5544
|
+
error: { type: string, description: 'set when status === "failed": the error the script threw.' }
|
|
5545
|
+
result: { type: string, description: 'the script''s RETURN VALUE (bounded + redacted). Absent on failed/pre-1.234 runs.' }
|
|
5546
|
+
phases:
|
|
5547
|
+
type: array
|
|
5548
|
+
description: 'Phase progress records.'
|
|
5549
|
+
items: { $ref: '#/components/schemas/WorkflowPhaseProgress' }
|
|
5550
|
+
unphased:
|
|
5551
|
+
type: object
|
|
5552
|
+
description: 'done/total of agents with no phase (or a stray phase matching no registered entry). Present only when non-empty.'
|
|
5553
|
+
required: [done, total]
|
|
5554
|
+
additionalProperties: false
|
|
5555
|
+
properties:
|
|
5556
|
+
done: { type: integer }
|
|
5557
|
+
total: { type: integer }
|
|
5558
|
+
agents:
|
|
5559
|
+
type: array
|
|
5560
|
+
description: 'Agent-run records.'
|
|
5561
|
+
items: { $ref: '#/components/schemas/WorkflowAgentRow' }
|
|
5562
|
+
groups:
|
|
5563
|
+
type: array
|
|
5564
|
+
description: 'The nested ctx.workflow group tree (roots only — children nest recursively). Empty when the script used no nesting.'
|
|
5565
|
+
items: { $ref: '#/components/schemas/WorkflowGroupNode' }
|
|
5566
|
+
|
|
5567
|
+
WorkflowJournalEntry:
|
|
5568
|
+
# 新(census 批2 四段):GET /v1/workflows/:id/journal row, the non-truncated arm (server `projectResult`,
|
|
5569
|
+
# workflows.ts:127-134) — one agent()-call's cached TaskResult, bounded + redacted.
|
|
5570
|
+
type: object
|
|
5571
|
+
description: One journal entry — a completed agent() call's projected TaskResult (bounded + redacted).
|
|
5572
|
+
required: [callKey, ordinal, status]
|
|
5573
|
+
additionalProperties: false
|
|
5574
|
+
properties:
|
|
5575
|
+
callKey: { type: string }
|
|
5576
|
+
ordinal: { type: integer, description: 'callKeyOrdinal(callKey) — the stable resume/display order.' }
|
|
5577
|
+
status: { type: string, description: 'the TaskResult''s terminal status (open vocabulary; core enum verbatim).' }
|
|
5578
|
+
error: { type: string, description: 'first line of errorMessage, clipped to 300 chars (redacted). Present only when the result carried an errorMessage.' }
|
|
5579
|
+
result: { type: string, description: 'the result text, clipped to 2000 chars (redacted). Present only when the result carried one.' }
|
|
5580
|
+
tokens: { type: integer, description: 'present only when the result carried stats.' }
|
|
5581
|
+
turns: { type: integer, description: 'present only when the result carried stats.' }
|
|
5582
|
+
|
|
5583
|
+
WorkflowJournalEntryTruncated:
|
|
5584
|
+
# 新(census 批2 四段):the truncated arm (workflows.ts:141-142/159-162) — an honest stub for a row whose
|
|
5585
|
+
# stored TaskResult exceeded the per-row 64KiB gate (SQL stores) or byte length (file/in-memory
|
|
5586
|
+
# fallback): the server never pulls the oversized payload into process memory.
|
|
5587
|
+
type: object
|
|
5588
|
+
description: A journal row too large to project — an honest "too big to show" stub, never a silent 2000-char truncation of the real result.
|
|
5589
|
+
required: [callKey, ordinal, truncated, resultBytes]
|
|
5590
|
+
additionalProperties: false
|
|
5591
|
+
properties:
|
|
5592
|
+
callKey: { type: string }
|
|
5593
|
+
ordinal: { type: integer }
|
|
5594
|
+
truncated: { type: boolean, enum: [true] }
|
|
5595
|
+
resultBytes: { type: integer, description: 'the stored TaskResult''s byte length (so a UI can at least show the size).' }
|
|
4855
5596
|
|
|
4856
5597
|
WorkflowStreamEvent:
|
|
4857
5598
|
type: object
|
|
@@ -4878,8 +5619,11 @@ components:
|
|
|
4878
5619
|
AttachmentInfo:
|
|
4879
5620
|
type: object
|
|
4880
5621
|
description: 'Upload receipt (POST /v1/attachments 201). `name` is the SANITIZED basename the server will materialize under `attachments/`.'
|
|
5622
|
+
# 🔴 census 批2 五段(2026-07-30):CLOSED against the real emitter — `sendJson(res, 201, { id, name,
|
|
5623
|
+
# mime, sha256, sizeBytes })` (server attachments.ts:90) is an EXACT 5-key object literal, never a
|
|
5624
|
+
# superset.
|
|
4881
5625
|
required: [id, name, mime, sha256, sizeBytes]
|
|
4882
|
-
additionalProperties:
|
|
5626
|
+
additionalProperties: false
|
|
4883
5627
|
properties:
|
|
4884
5628
|
id: { type: string }
|
|
4885
5629
|
name: { type: string }
|
|
@@ -6941,6 +7685,24 @@ components:
|
|
|
6941
7685
|
description: 'Machine-branchable cause. CLOSED on the server side (fleet-bus.ts HookNotice.reason) but OPEN ON READ — branch known values, render anything else as the generic "could not evaluate".'
|
|
6942
7686
|
detail: { type: string, description: 'Redacted supplement (e.g. why skipped). Additive / tolerate-absent.' }
|
|
6943
7687
|
|
|
7688
|
+
BakeStatus:
|
|
7689
|
+
# 新(census 批2 四段)——server `BakeStatus`(plugins/store-contracts.ts:83), the COARSE lifecycle
|
|
7690
|
+
# bakeView/claimResponse/bakesCreate all key on.
|
|
7691
|
+
type: string
|
|
7692
|
+
enum: [queued, running, done, failed]
|
|
7693
|
+
|
|
7694
|
+
BakeState:
|
|
7695
|
+
# 新(census 批2 四段)——server `BakeState`(store-contracts.ts:85-93), build.sh's finer 8-value state;
|
|
7696
|
+
# null before the runner's first `state` line lands.
|
|
7697
|
+
type: [string, 'null']
|
|
7698
|
+
enum: [PENDING, BUILDING, PUSHING, VERIFYING, REGISTERING, COMPLETE, FAILED, CANCELLED, null]
|
|
7699
|
+
|
|
7700
|
+
BakeErrorCode:
|
|
7701
|
+
# 新(census 批2 四段)——server `BakeErrorCode`(store-contracts.ts:96), the closed structured terminal
|
|
7702
|
+
# code set (§P2.14 #2); null = success or an uncategorized failure.
|
|
7703
|
+
type: [string, 'null']
|
|
7704
|
+
enum: [band-conflict, duplicate, disk, timeout, cancelled, null]
|
|
7705
|
+
|
|
6944
7706
|
BakeClaim:
|
|
6945
7707
|
type: object
|
|
6946
7708
|
description: >
|
|
@@ -6949,8 +7711,11 @@ components:
|
|
|
6949
7711
|
secret (minted at claim, echoed on every ingest via the x-bake-ingest-secret header) + the
|
|
6950
7712
|
profile/bands the runner needs for its profile-aware hard deadline. The runner executes `argv`
|
|
6951
7713
|
VERBATIM — it never assembles its own command line. 204 (no body) = queue empty.
|
|
6952
|
-
|
|
6953
|
-
|
|
7714
|
+
🔴 census 批2 四段:CLOSED + wired to both claim paths (this schema existed but neither path
|
|
7715
|
+
`$ref`'d it — path↔schema 脱钩; both declared a bare open `{}` instead). `logs`/`bands` are
|
|
7716
|
+
unconditionally assigned by claimResponse() but were absent from `required` — same-side gap.
|
|
7717
|
+
required: [bakeId, argv, push, dryRun, logs, profile, bands, ingestSecret, leaseUntil]
|
|
7718
|
+
additionalProperties: false
|
|
6954
7719
|
properties:
|
|
6955
7720
|
bakeId: { type: string }
|
|
6956
7721
|
argv:
|
|
@@ -6959,23 +7724,128 @@ components:
|
|
|
6959
7724
|
description: 'The server-assembled, whitelist-vetted build command — execute verbatim, never self-assemble.'
|
|
6960
7725
|
push: { type: boolean }
|
|
6961
7726
|
dryRun: { type: boolean }
|
|
6962
|
-
logs: { description: 'Log routing config, passed through as stored
|
|
7727
|
+
logs: { type: boolean, description: 'Log routing config, passed through as stored.' }
|
|
6963
7728
|
profile: { type: string }
|
|
6964
|
-
bands: { description: 'Profile bands for the runner''s profile-aware hard deadline (opaque passthrough).' }
|
|
7729
|
+
bands: { type: [array, 'null'], items: { type: string }, description: 'Profile bands for the runner''s profile-aware hard deadline (opaque passthrough); null when the bake carries none.' }
|
|
6965
7730
|
ingestSecret: { type: string, description: 'Per-bake credential — the ONLY place it leaves image-api; echo it on every ingest.' }
|
|
6966
7731
|
leaseUntil:
|
|
6967
7732
|
description: 'Lease expiry (epoch ms or ISO timestamp depending on store backend).'
|
|
6968
7733
|
oneOf: [{ type: number }, { type: string }]
|
|
6969
7734
|
|
|
7735
|
+
BakeRecordView:
|
|
7736
|
+
# 新(census 批2 四段)——GET /v1/images/bakes/{bakeId}'s `{ bake }` envelope. Exact key set = server
|
|
7737
|
+
# `bakeView()` (images.ts:527-551): the durable BakeRecord MINUS the runner-internal secret/lease fields
|
|
7738
|
+
# (ingestSecret/runnerId/leaseUntil/cancelRequested/argv — an ops surface, not the claim credential).
|
|
7739
|
+
type: object
|
|
7740
|
+
description: The operator poll view of one bake (server bakeView projection over the durable BakeRecord).
|
|
7741
|
+
required:
|
|
7742
|
+
[bakeId, status, state, profile, bands, baseRef, push, dryRun, logs, digest, repo, ref, indexId,
|
|
7743
|
+
exitCode, error, errorCode, manifestSha, tag, requestedBy, createdAt, updatedAt]
|
|
7744
|
+
additionalProperties: false
|
|
7745
|
+
properties:
|
|
7746
|
+
bakeId: { type: string }
|
|
7747
|
+
status: { $ref: '#/components/schemas/BakeStatus' }
|
|
7748
|
+
state: { $ref: '#/components/schemas/BakeState' }
|
|
7749
|
+
profile: { type: string }
|
|
7750
|
+
bands: { type: [array, 'null'], items: { type: string } }
|
|
7751
|
+
baseRef: { type: [string, 'null'] }
|
|
7752
|
+
push: { type: boolean }
|
|
7753
|
+
dryRun: { type: boolean }
|
|
7754
|
+
logs: { type: boolean }
|
|
7755
|
+
digest: { type: [string, 'null'] }
|
|
7756
|
+
repo: { type: [string, 'null'] }
|
|
7757
|
+
ref: { type: [string, 'null'] }
|
|
7758
|
+
indexId: { type: [string, 'null'] }
|
|
7759
|
+
exitCode: { type: [integer, 'null'] }
|
|
7760
|
+
error: { type: [string, 'null'] }
|
|
7761
|
+
errorCode: { $ref: '#/components/schemas/BakeErrorCode' }
|
|
7762
|
+
manifestSha: { type: [string, 'null'] }
|
|
7763
|
+
tag: { type: [string, 'null'] }
|
|
7764
|
+
requestedBy: { type: [string, 'null'] }
|
|
7765
|
+
createdAt: { type: string, description: ISO date-time. }
|
|
7766
|
+
updatedAt: { type: string, description: ISO date-time. }
|
|
7767
|
+
|
|
7768
|
+
BakeSubmitAck:
|
|
7769
|
+
# 新(census 批2 四段)——POST /v1/images/bakes 202 body. All three code paths (attach-to-in-flight,
|
|
7770
|
+
# atomic-admission-created, dryRun) produce this SAME shape (images.ts:193/206/213/217).
|
|
7771
|
+
type: object
|
|
7772
|
+
description: Acknowledgement of a submitted/attached-to bake.
|
|
7773
|
+
required: [bakeId, eventsUrl, status, state]
|
|
7774
|
+
additionalProperties: false
|
|
7775
|
+
properties:
|
|
7776
|
+
bakeId: { type: string }
|
|
7777
|
+
eventsUrl: { type: string, description: '`/v1/images/bakes/{bakeId}/events` — the SSE resource to attach to.' }
|
|
7778
|
+
status: { $ref: '#/components/schemas/BakeStatus' }
|
|
7779
|
+
state: { $ref: '#/components/schemas/BakeState' }
|
|
7780
|
+
|
|
7781
|
+
BakeCancelAck:
|
|
7782
|
+
# 新(census 批2 四段)——POST /v1/images/bakes/{bakeId}/cancel 202 body (images.ts:239-241). `note` is
|
|
7783
|
+
# ALWAYS present (a ternary VALUE, not a conditional key) — the spec previously implied it was optional.
|
|
7784
|
+
type: object
|
|
7785
|
+
description: Cooperative-cancel acknowledgement (a durable flag; the runner kills the build at its next heartbeat).
|
|
7786
|
+
required: [bakeId, cancelRequested, note]
|
|
7787
|
+
additionalProperties: false
|
|
7788
|
+
properties:
|
|
7789
|
+
bakeId: { type: string }
|
|
7790
|
+
cancelRequested: { type: boolean, enum: [true] }
|
|
7791
|
+
note: { type: string, description: 'always one of two fixed strings: "cancel requested — …" or "bake already terminal — cancel is a no-op".' }
|
|
7792
|
+
|
|
7793
|
+
BakeIngestAck:
|
|
7794
|
+
# 新(census 批2 四段)——POST /v1/images/bakes/{bakeId}/ingest 200 body. 🔴 FOUR distinct shapes share
|
|
7795
|
+
# this one status code (images.ts:302-334): a heartbeat ack carries no `seq`; a non-terminal frame ack
|
|
7796
|
+
# carries `seq` but no `terminal`; a terminal `done` carries `terminal`+`indexId` (success) OR
|
|
7797
|
+
# `terminal`+`needsFlip` (the bounded-retry-exhausted non-throw path, still 200 — the runner re-POSTs the
|
|
7798
|
+
# idempotent done to re-drive the flip). `cancelRequested`+`leaseValid` ride on every non-heartbeat shape
|
|
7799
|
+
# (merged in at the send site); the heartbeat shape sets them directly instead.
|
|
7800
|
+
description: One ingest acknowledgement (four shapes keyed by which fields are present).
|
|
7801
|
+
oneOf:
|
|
7802
|
+
- type: object
|
|
7803
|
+
description: Heartbeat ack (lease renewed; no event appended).
|
|
7804
|
+
required: [accepted, leaseValid, cancelRequested]
|
|
7805
|
+
additionalProperties: false
|
|
7806
|
+
properties:
|
|
7807
|
+
accepted: { type: boolean, enum: [true] }
|
|
7808
|
+
leaseValid: { type: boolean }
|
|
7809
|
+
cancelRequested: { type: boolean }
|
|
7810
|
+
- type: object
|
|
7811
|
+
description: Non-terminal build.sh line appended (resolved/state/step/image/manifest/log).
|
|
7812
|
+
required: [accepted, seq, cancelRequested, leaseValid]
|
|
7813
|
+
additionalProperties: false
|
|
7814
|
+
properties:
|
|
7815
|
+
accepted: { type: boolean, enum: [true] }
|
|
7816
|
+
seq: { type: integer }
|
|
7817
|
+
cancelRequested: { type: boolean }
|
|
7818
|
+
leaseValid: { type: boolean, enum: [true] }
|
|
7819
|
+
- type: object
|
|
7820
|
+
description: Terminal `done` — the coarse-status CAS was won and the register step succeeded (or the bake never registers, e.g. a non-COMPLETE terminal).
|
|
7821
|
+
required: [accepted, terminal, indexId, cancelRequested, leaseValid]
|
|
7822
|
+
additionalProperties: false
|
|
7823
|
+
properties:
|
|
7824
|
+
accepted: { type: boolean, enum: [true] }
|
|
7825
|
+
terminal: { type: boolean }
|
|
7826
|
+
indexId: { type: [string, 'null'] }
|
|
7827
|
+
cancelRequested: { type: boolean }
|
|
7828
|
+
leaseValid: { type: boolean, enum: [true] }
|
|
7829
|
+
- type: object
|
|
7830
|
+
description: Terminal `done` — the bounded setTerminal retry was exhausted; a non-terminal 200 asks the runner to re-POST the (lineOrd-idempotent) done to re-drive the flip.
|
|
7831
|
+
required: [accepted, terminal, needsFlip, cancelRequested, leaseValid]
|
|
7832
|
+
additionalProperties: false
|
|
7833
|
+
properties:
|
|
7834
|
+
accepted: { type: boolean, enum: [true] }
|
|
7835
|
+
terminal: { type: boolean, enum: [false] }
|
|
7836
|
+
needsFlip: { type: boolean, enum: [true] }
|
|
7837
|
+
cancelRequested: { type: boolean }
|
|
7838
|
+
leaseValid: { type: boolean, enum: [true] }
|
|
7839
|
+
|
|
6970
7840
|
OutcomeRow:
|
|
6971
7841
|
type: object
|
|
6972
7842
|
description: >
|
|
6973
7843
|
One task-outcome ledger row of GET /v1/outcomes (envelope key `outcomes`; design/73 §7 read-only
|
|
6974
7844
|
base). Shape = the SQL ledgers' per-(taskSignature, model) summary projection (server
|
|
6975
|
-
|
|
6976
|
-
|
|
7845
|
+
outcome-ledger-sql.ts `summary()` — the only queryable sink; a File sink 501s honestly).
|
|
7846
|
+
# 🔴 census 批2 五段:CLOSED — the exact 5-key `.map()` projection, no sixth key.
|
|
6977
7847
|
required: [taskSignature, model, n, passRate, meanCostMicroUsd]
|
|
6978
|
-
additionalProperties:
|
|
7848
|
+
additionalProperties: false
|
|
6979
7849
|
properties:
|
|
6980
7850
|
taskSignature: { type: string }
|
|
6981
7851
|
model: { type: string }
|
|
@@ -6990,8 +7860,10 @@ components:
|
|
|
6990
7860
|
nextBefore?}`; server src/http/routes/sessions-list.ts — exact projection). A principal caller is PINNED to its
|
|
6991
7861
|
own scope (?scope= ignored); a fleet credential may pass ?scope= (default = the single-user
|
|
6992
7862
|
sentinel "_"). Page with `before` = a previous page's `nextBefore` (uuidv7; malformed → 400).
|
|
7863
|
+
# 🔴 census 批2 五段:CLOSED — exact projection (sessions-list.ts:94-105); taskId/sessionId are the
|
|
7864
|
+
# only OMIT-when-absent keys, no others.
|
|
6993
7865
|
required: [id, filename, size, ttlSec, track, bucket, key, createdAt]
|
|
6994
|
-
additionalProperties:
|
|
7866
|
+
additionalProperties: false
|
|
6995
7867
|
properties:
|
|
6996
7868
|
id: { type: string, description: 'uuidv7 link id — also the `before` pagination cursor.' }
|
|
6997
7869
|
filename: { type: string }
|
|
@@ -7347,13 +8219,16 @@ components:
|
|
|
7347
8219
|
The 200 receipt of POST /v1/workflows/{runId}/agents/{label}/steer (server src/http/routes/workflows.ts —
|
|
7348
8220
|
exact literal: `status:"running"`, `delivery:"applied"`, `marker` = the engine-minted delivery
|
|
7349
8221
|
marker; NO `note`, unlike the run-subagent verb).
|
|
7350
|
-
|
|
7351
|
-
|
|
8222
|
+
# 🔴 census 批2 五段:CLOSED — all 5 keys are unconditional in the literal
|
|
8223
|
+
# `{ runId: wfRunId, label, status: "running", delivery: "applied", marker }` (workflows.ts:279);
|
|
8224
|
+
# this schema previously existed but no path `$ref`'d it (see the path fix above).
|
|
8225
|
+
required: [runId, label, status, delivery, marker]
|
|
8226
|
+
additionalProperties: false
|
|
7352
8227
|
properties:
|
|
7353
8228
|
runId: { type: string }
|
|
7354
8229
|
label: { type: string }
|
|
7355
|
-
status: { type: string, description: 'Literal "running" on today''s server.' }
|
|
7356
|
-
delivery: { type: string, description: 'Literal "applied" on today''s server.' }
|
|
8230
|
+
status: { type: string, const: running, description: 'Literal "running" on today''s server.' }
|
|
8231
|
+
delivery: { type: string, const: applied, description: 'Literal "applied" on today''s server.' }
|
|
7357
8232
|
marker: { type: string, description: 'Engine-minted delivery marker.' }
|
|
7358
8233
|
|
|
7359
8234
|
WorkspaceSnapshotRow:
|