@sema-agent/sdk 2.1.3 → 2.2.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/resources/trace.d.ts +2 -1
- package/dist/resources/trace.d.ts.map +1 -1
- package/dist/resources/trace.js +2 -0
- package/dist/resources/trace.js.map +1 -1
- package/dist/resources/usage.d.ts +4 -1
- package/dist/resources/usage.d.ts.map +1 -1
- package/dist/resources/usage.js.map +1 -1
- package/dist/types.d.ts +49 -1
- package/dist/types.d.ts.map +1 -1
- package/openapi.yaml +1148 -203
- 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:
|
|
@@ -558,14 +608,9 @@ paths:
|
|
|
558
608
|
description: Accepted — compaction runs at the next turn boundary; watch the run stream for `compacted{trigger:"manual"}`.
|
|
559
609
|
content:
|
|
560
610
|
application/json:
|
|
561
|
-
schema
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
properties:
|
|
565
|
-
taskId: { type: string }
|
|
566
|
-
status: { type: string }
|
|
567
|
-
delivery: { type: string, enum: [accepted] }
|
|
568
|
-
note: { type: string }
|
|
611
|
+
# census 批2 第三段(2026-07-30):`CompactAck` 这个命名 schema 本文件早就有,path 却抄了一份
|
|
612
|
+
# 内联形(usage 三兄弟同病)——接上,封闭做在 schema 一处。
|
|
613
|
+
schema: { $ref: '#/components/schemas/CompactAck' }
|
|
569
614
|
'400': { $ref: '#/components/responses/BadRequest' }
|
|
570
615
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
571
616
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
@@ -574,6 +619,7 @@ paths:
|
|
|
574
619
|
content:
|
|
575
620
|
application/json:
|
|
576
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)
|
|
577
623
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
578
624
|
|
|
579
625
|
/v1/runs/{taskId}/detach:
|
|
@@ -608,14 +654,8 @@ paths:
|
|
|
608
654
|
description: Requested — honest about carrying no confirmation (the run stream carries the outcome).
|
|
609
655
|
content:
|
|
610
656
|
application/json:
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
required: [taskId, toolCallId, delivery]
|
|
614
|
-
properties:
|
|
615
|
-
taskId: { type: string }
|
|
616
|
-
toolCallId: { type: string }
|
|
617
|
-
delivery: { type: string, enum: [requested] }
|
|
618
|
-
note: { type: string }
|
|
657
|
+
# census 批2 第三段(2026-07-30):同 compact——`DetachAck` 在档,path 抄内联形;接上。
|
|
658
|
+
schema: { $ref: '#/components/schemas/DetachAck' }
|
|
619
659
|
'400': { $ref: '#/components/responses/BadRequest' }
|
|
620
660
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
621
661
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
@@ -624,6 +664,14 @@ paths:
|
|
|
624
664
|
content:
|
|
625
665
|
application/json:
|
|
626
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' }
|
|
627
675
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
628
676
|
|
|
629
677
|
/v1/runs/{taskId}/subagents/{target}/output:
|
|
@@ -635,6 +683,17 @@ paths:
|
|
|
635
683
|
required: true
|
|
636
684
|
schema: { type: string }
|
|
637
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.
|
|
638
697
|
get:
|
|
639
698
|
tags: [runs]
|
|
640
699
|
operationId: runsSubagentOutput
|
|
@@ -653,14 +712,9 @@ paths:
|
|
|
653
712
|
description: 'The registry projection. `content` = the child''s final assistant body (UNTRUSTED model output).'
|
|
654
713
|
content:
|
|
655
714
|
application/json:
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
properties:
|
|
660
|
-
taskId: { type: string }
|
|
661
|
-
target: { type: string }
|
|
662
|
-
content: { type: string }
|
|
663
|
-
output: { type: object, description: 'Registry details projection (status/kind/…), passed through verbatim.' }
|
|
715
|
+
# census 批2 第三段(2026-07-30):`SubagentOutputResult` 在档而 path 抄内联形(且抄漏了
|
|
716
|
+
# required 的 content/output)——接上。cursorSemantics 只有 generic 面铸,本面不铸(schema 注)。
|
|
717
|
+
schema: { $ref: '#/components/schemas/SubagentOutputResult' }
|
|
664
718
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
665
719
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
666
720
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
@@ -715,6 +769,7 @@ paths:
|
|
|
715
769
|
content:
|
|
716
770
|
application/json:
|
|
717
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)
|
|
718
773
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
719
774
|
|
|
720
775
|
/v1/runs/{taskId}/subagents/{target}/stream:
|
|
@@ -726,6 +781,17 @@ paths:
|
|
|
726
781
|
required: true
|
|
727
782
|
schema: { type: string }
|
|
728
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.
|
|
729
795
|
get:
|
|
730
796
|
tags: [runs]
|
|
731
797
|
operationId: runsSubagentStream
|
|
@@ -760,6 +826,17 @@ paths:
|
|
|
760
826
|
required: true
|
|
761
827
|
schema: { type: string }
|
|
762
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.
|
|
763
840
|
get:
|
|
764
841
|
tags: [runs]
|
|
765
842
|
operationId: runsTaskOutput
|
|
@@ -778,15 +855,9 @@ paths:
|
|
|
778
855
|
description: 'Registry projection passed through verbatim; `content` is UNTRUSTED tool/model output.'
|
|
779
856
|
content:
|
|
780
857
|
application/json:
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
properties:
|
|
785
|
-
taskId: { type: string }
|
|
786
|
-
target: { type: string }
|
|
787
|
-
content: { type: string }
|
|
788
|
-
output: { type: object }
|
|
789
|
-
cursorSemantics: { type: string, enum: [cursor, full], description: 'G14: whether THIS read consumed the cursor. Absent on error/not_ready or unknown kinds.' }
|
|
858
|
+
# census 批2 第三段(2026-07-30):内联形收编进已在档的 `SubagentOutputResult`(它本来就
|
|
859
|
+
# 自述覆盖本面,cursorSemantics 也在那边)。
|
|
860
|
+
schema: { $ref: '#/components/schemas/SubagentOutputResult' }
|
|
790
861
|
'400': { $ref: '#/components/responses/BadRequest' }
|
|
791
862
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
792
863
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
@@ -816,7 +887,18 @@ paths:
|
|
|
816
887
|
output verb.
|
|
817
888
|
responses:
|
|
818
889
|
'200':
|
|
819
|
-
|
|
890
|
+
# 🔴 census 批2 第三段(2026-07-30)spec 谎修正:这条 200 此前只有一句 description、**零 content
|
|
891
|
+
# 声明**(机器视角=「无 body」),而 server 真发 JSON(`routes/runs.ts:1155`,与 output 面同一个
|
|
892
|
+
# sendJson,含 g14 的条件 `cursorSemantics`)。补接同一个 schema。
|
|
893
|
+
description: 'Stopped — same projection shape as the output verb.'
|
|
894
|
+
content:
|
|
895
|
+
application/json:
|
|
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'
|
|
820
902
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
821
903
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
822
904
|
'409':
|
|
@@ -824,6 +906,14 @@ paths:
|
|
|
824
906
|
content:
|
|
825
907
|
application/json:
|
|
826
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' }
|
|
827
917
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
828
918
|
|
|
829
919
|
/v1/sessions:
|
|
@@ -851,6 +941,14 @@ paths:
|
|
|
851
941
|
name: owner
|
|
852
942
|
schema: { type: string }
|
|
853
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.
|
|
854
952
|
responses:
|
|
855
953
|
'200':
|
|
856
954
|
description: Keyset page of session summaries.
|
|
@@ -966,6 +1064,10 @@ paths:
|
|
|
966
1064
|
required: [deleted]
|
|
967
1065
|
properties:
|
|
968
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'
|
|
969
1071
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
970
1072
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
971
1073
|
'409': { $ref: '#/components/responses/Conflict' }
|
|
@@ -1081,6 +1183,14 @@ paths:
|
|
|
1081
1183
|
schema: { type: string }
|
|
1082
1184
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1083
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' }
|
|
1084
1194
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
1085
1195
|
'503': { description: 'Subscription connection cap exceeded — Retry-After set; fall back to polling /head.' }
|
|
1086
1196
|
|
|
@@ -1109,11 +1219,16 @@ paths:
|
|
|
1109
1219
|
application/json:
|
|
1110
1220
|
schema:
|
|
1111
1221
|
type: object
|
|
1222
|
+
# 封闭两层(census 批2 第三段,2026-07-30):铸造点 `routes/sessions.ts:81-85` 是两层字面量,
|
|
1223
|
+
# 唯一条件键 `mode`(`config.autonomy` 缺席即省略)。description 里许诺的 tools/agents/
|
|
1224
|
+
# commands/cwd 至今没铸——封闭把「将来长出来要先过 spec」变成机器事实。
|
|
1225
|
+
additionalProperties: false
|
|
1112
1226
|
required: [sessionId, model]
|
|
1113
1227
|
properties:
|
|
1114
1228
|
sessionId: { type: string }
|
|
1115
1229
|
model:
|
|
1116
1230
|
type: object
|
|
1231
|
+
additionalProperties: false
|
|
1117
1232
|
required: [provider, modelId]
|
|
1118
1233
|
properties:
|
|
1119
1234
|
provider: { type: string }
|
|
@@ -1226,6 +1341,8 @@ paths:
|
|
|
1226
1341
|
application/json:
|
|
1227
1342
|
schema:
|
|
1228
1343
|
type: object
|
|
1344
|
+
# 封闭(census 批2 第三段,2026-07-30):live 臂铸造点 `routes/notify-wake.ts:86` 是两键字面量。
|
|
1345
|
+
additionalProperties: false
|
|
1229
1346
|
required: [sessionId, delivery]
|
|
1230
1347
|
properties:
|
|
1231
1348
|
sessionId: { type: string }
|
|
@@ -1236,7 +1353,9 @@ paths:
|
|
|
1236
1353
|
application/json:
|
|
1237
1354
|
schema:
|
|
1238
1355
|
type: object
|
|
1239
|
-
required:
|
|
1356
|
+
# 封闭 + note 补 required:parked 臂铸造点 `routes/notify-wake.ts:104` 是三键字面量,note 无条件。
|
|
1357
|
+
additionalProperties: false
|
|
1358
|
+
required: [sessionId, delivery, note]
|
|
1240
1359
|
properties:
|
|
1241
1360
|
sessionId: { type: string }
|
|
1242
1361
|
delivery: { type: string, enum: [parked] }
|
|
@@ -1274,22 +1393,31 @@ paths:
|
|
|
1274
1393
|
properties:
|
|
1275
1394
|
message: { type: string, minLength: 1 }
|
|
1276
1395
|
responses:
|
|
1277
|
-
'
|
|
1278
|
-
|
|
1396
|
+
'200':
|
|
1397
|
+
# 🔴 census 批2 第三段(2026-07-30)spec 谎修正:此前这里写的是 **202** + required [taskId,…]。
|
|
1398
|
+
# 亲读真码:wake 的成功腿是 `driveResumeIntoRunLog` 的返回(`server.ts:2289` 与取消臂 `:2240`),
|
|
1399
|
+
# **恒为 200**(wake 是同步驱动到终局/再挂起,不是 accepted 回执),且 `taskId` 有条件
|
|
1400
|
+
# (无活跃行即省略——ApprovalDecisionResult 同一铸造点的同一注)。旧 202+required taskId 双谎。
|
|
1401
|
+
description: Woken — the resumed leg ran to its next settle (terminal, re-suspended, or needs_review).
|
|
1279
1402
|
content:
|
|
1280
1403
|
application/json:
|
|
1281
1404
|
schema:
|
|
1282
1405
|
type: object
|
|
1283
|
-
|
|
1406
|
+
additionalProperties: false
|
|
1407
|
+
required: [sessionId, status]
|
|
1284
1408
|
properties:
|
|
1285
|
-
taskId: { type: string }
|
|
1409
|
+
taskId: { type: string, description: 'The active run id; OMITTED when no active row.' }
|
|
1286
1410
|
sessionId: { type: string }
|
|
1287
|
-
status: { type: string }
|
|
1411
|
+
status: { type: string, description: 'The run''s resulting status (completed/failed/suspended/needs_review; blocked/timeout also occur — core TaskStatus is passed through verbatim).' }
|
|
1412
|
+
errorCode: { type: string, description: 'Failure code when the resumed leg failed (e.g. `cancelled`).' }
|
|
1413
|
+
errorMessage: { type: string, description: 'Human-readable failure detail (server-side secret-redacted).' }
|
|
1414
|
+
retriable: { type: boolean, description: 'true = the park is STILL pending (re-parked); refetch and retry.' }
|
|
1288
1415
|
'400': { $ref: '#/components/responses/BadRequest' }
|
|
1289
1416
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1290
1417
|
'404': { description: 'No parked checkpoint for this session (nothing to wake), or session not visible to the caller.' }
|
|
1291
1418
|
'409': { description: 'Pending gate (`wake.gate_pending` — decide it through its own entry) or resume context missing.' }
|
|
1292
1419
|
'422': { description: '`steering.invalid_content` — the message failed the steering content gate.' }
|
|
1420
|
+
'429': { $ref: '#/components/responses/RateLimited' }
|
|
1293
1421
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
1294
1422
|
|
|
1295
1423
|
/v1/sessions/{sessionId}/workspace:
|
|
@@ -1322,6 +1450,9 @@ paths:
|
|
|
1322
1450
|
application/json:
|
|
1323
1451
|
schema:
|
|
1324
1452
|
type: object
|
|
1453
|
+
# 封闭两层(census 批2 第三段,2026-07-30):铸造点 `routes/sessions.ts:621-626`(信封)与
|
|
1454
|
+
# `:616`(行)。条件键=信封 `latest`(零快照即省略)、行 `bytes`(blobSizes 面缺席=诚实省略)。
|
|
1455
|
+
additionalProperties: false
|
|
1325
1456
|
required: [sessionId, snapshots, total]
|
|
1326
1457
|
properties:
|
|
1327
1458
|
sessionId: { type: string }
|
|
@@ -1329,6 +1460,7 @@ paths:
|
|
|
1329
1460
|
type: array
|
|
1330
1461
|
items:
|
|
1331
1462
|
type: object
|
|
1463
|
+
additionalProperties: false
|
|
1332
1464
|
required: [key, files]
|
|
1333
1465
|
properties:
|
|
1334
1466
|
key: { type: string }
|
|
@@ -1378,6 +1510,9 @@ paths:
|
|
|
1378
1510
|
application/json:
|
|
1379
1511
|
schema:
|
|
1380
1512
|
type: object
|
|
1513
|
+
# 封闭两层(census 批2 第三段,2026-07-30):铸造点 `routes/sessions.ts:648-654`(信封)与
|
|
1514
|
+
# `:651`(行)。条件键=信封 `nextOffset`(尾页省略)、行 `size`(blobSizes 面缺席省略)。
|
|
1515
|
+
additionalProperties: false
|
|
1381
1516
|
required: [sessionId, key, entries, total]
|
|
1382
1517
|
properties:
|
|
1383
1518
|
sessionId: { type: string }
|
|
@@ -1386,6 +1521,7 @@ paths:
|
|
|
1386
1521
|
type: array
|
|
1387
1522
|
items:
|
|
1388
1523
|
type: object
|
|
1524
|
+
additionalProperties: false
|
|
1389
1525
|
required: [path, hash]
|
|
1390
1526
|
properties:
|
|
1391
1527
|
path: { type: string }
|
|
@@ -1516,6 +1652,10 @@ paths:
|
|
|
1516
1652
|
required: [rules]
|
|
1517
1653
|
properties:
|
|
1518
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'
|
|
1519
1659
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1520
1660
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
1521
1661
|
'409': { $ref: '#/components/responses/Conflict' }
|
|
@@ -1617,6 +1757,8 @@ paths:
|
|
|
1617
1757
|
application/json:
|
|
1618
1758
|
schema:
|
|
1619
1759
|
type: object
|
|
1760
|
+
# 封闭(census 批2 第三段,2026-07-30):铸造点 `routes/session-sync.ts:165` 是单键字面量。
|
|
1761
|
+
additionalProperties: false
|
|
1620
1762
|
required: [manifest]
|
|
1621
1763
|
properties:
|
|
1622
1764
|
manifest: { $ref: '#/components/schemas/SessionManifest' }
|
|
@@ -1793,7 +1935,10 @@ paths:
|
|
|
1793
1935
|
2c session-sync PUSH Phase A — a SMALL metadata body (`entryIds, snapshots, policy, anchors, resolution?`); the
|
|
1794
1936
|
entries stream in Phase B. The cloud classifies (§7), pre-checks blob presence (a missing referenced hash →
|
|
1795
1937
|
422 `missing_blob` — PUT it first), guards the import lease + any active run (409), then mints a stagingId.
|
|
1796
|
-
`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` →
|
|
1797
1942
|
`{ stagingId, relation }`. 🔴 a `fork`/`stale` without `resolution:"overwrite-dst"` → 409
|
|
1798
1943
|
`conflict` carrying the `relation` (the SDK surfaces SyncConflictError).
|
|
1799
1944
|
requestBody:
|
|
@@ -1828,6 +1973,7 @@ paths:
|
|
|
1828
1973
|
schema: { $ref: '#/components/schemas/SyncConflict' }
|
|
1829
1974
|
'422': { $ref: '#/components/responses/UnprocessableEntity' }
|
|
1830
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 起)
|
|
1831
1977
|
|
|
1832
1978
|
/v1/sessions/{sessionId}/sync/import/{stagingId}/entries:
|
|
1833
1979
|
parameters:
|
|
@@ -1884,6 +2030,15 @@ paths:
|
|
|
1884
2030
|
/v1/approvals:
|
|
1885
2031
|
parameters:
|
|
1886
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.
|
|
1887
2042
|
get:
|
|
1888
2043
|
tags: [approvals]
|
|
1889
2044
|
operationId: approvalsList
|
|
@@ -1905,14 +2060,27 @@ paths:
|
|
|
1905
2060
|
/v1/approvals/stream:
|
|
1906
2061
|
parameters:
|
|
1907
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.
|
|
1908
2072
|
get:
|
|
1909
2073
|
tags: [approvals]
|
|
1910
2074
|
operationId: approvalsStream
|
|
1911
2075
|
x-status: live # spec-path-gate 首批回填 2026-07-27(design/80 native push;SDK approvals.stream 消费)
|
|
1912
2076
|
summary: SSE — pending-approval deltas (subscribe once instead of polling GET /v1/approvals).
|
|
1913
2077
|
description: >
|
|
1914
|
-
Frames: `meta` ({type,version,mode:"approvals-delta",pollMs}) then `
|
|
1915
|
-
|
|
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
|
|
1916
2084
|
construction (polls the SHARED checkpoint table). 15-min cap + heartbeats; a DB blip retries, never
|
|
1917
2085
|
kills the stream. Scope = the caller's principal (operator/trace token = fleet-wide).
|
|
1918
2086
|
responses:
|
|
@@ -1950,6 +2118,18 @@ paths:
|
|
|
1950
2118
|
content:
|
|
1951
2119
|
application/json:
|
|
1952
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' }
|
|
1953
2133
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1954
2134
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
1955
2135
|
'409':
|
|
@@ -1959,17 +2139,19 @@ paths:
|
|
|
1959
2139
|
NOT match the current pending action (TOCTOU/swap). The SDK splits these by `errorCode` into
|
|
1960
2140
|
ConflictError vs ApprovalBindingMismatchError. binding_mismatch = SAFETY signal: refetch + re-present
|
|
1961
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.
|
|
1962
2147
|
content:
|
|
1963
2148
|
application/json:
|
|
1964
2149
|
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
content:
|
|
1971
|
-
application/json:
|
|
1972
|
-
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.
|
|
1973
2155
|
'413':
|
|
1974
2156
|
description: >
|
|
1975
2157
|
errorCode "reason_too_large" — `reason` exceeded 4096 chars (same cap on plan_review). REJECTED,
|
|
@@ -2005,6 +2187,41 @@ paths:
|
|
|
2005
2187
|
content:
|
|
2006
2188
|
application/json:
|
|
2007
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' }
|
|
2008
2225
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
2009
2226
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
2010
2227
|
'501':
|
|
@@ -2048,6 +2265,14 @@ paths:
|
|
|
2048
2265
|
required: [revoked]
|
|
2049
2266
|
properties:
|
|
2050
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' }
|
|
2051
2276
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
2052
2277
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
2053
2278
|
'501':
|
|
@@ -2241,6 +2466,7 @@ paths:
|
|
|
2241
2466
|
schema: { $ref: '#/components/schemas/TurnList' }
|
|
2242
2467
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
2243
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
|
|
2244
2470
|
|
|
2245
2471
|
/v1/tasks/source-summary:
|
|
2246
2472
|
parameters:
|
|
@@ -2262,18 +2488,27 @@ paths:
|
|
|
2262
2488
|
application/json:
|
|
2263
2489
|
schema:
|
|
2264
2490
|
type: object
|
|
2265
|
-
|
|
2491
|
+
# 🔴 封闭 + 两处 spec 谎修正(census 批2 第三段,2026-07-30),铸造点 `routes/trace-usage.ts:61`:
|
|
2492
|
+
# ① 顶层信封 server 真发 `{sinceSec, rows}` —— `sinceSec`(生效的窗口秒数,默认 86400)
|
|
2493
|
+
# 此前整键不在 spec 里;
|
|
2494
|
+
# ② 行 `source` 三个 store 实现(sql/pg/local 的 sourceSummary)签名都是 `string | null`
|
|
2495
|
+
# (无来源凭据的 run 记 null),spec 却写成裸 string。
|
|
2496
|
+
additionalProperties: false
|
|
2497
|
+
required: [sinceSec, rows]
|
|
2266
2498
|
properties:
|
|
2499
|
+
sinceSec: { type: integer, description: 'The effective window seconds (echo of ?sinceSec, default 86400).' }
|
|
2267
2500
|
rows:
|
|
2268
2501
|
type: array
|
|
2269
2502
|
items:
|
|
2270
2503
|
type: object
|
|
2504
|
+
additionalProperties: false
|
|
2271
2505
|
required: [source, status, count]
|
|
2272
2506
|
properties:
|
|
2273
|
-
source: { type: string }
|
|
2507
|
+
source: { type: [string, 'null'], description: 'Credential-derived system; null = runs submitted without a source.' }
|
|
2274
2508
|
status: { type: string }
|
|
2275
2509
|
count: { type: integer }
|
|
2276
2510
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
2511
|
+
'501': { $ref: '#/components/responses/NotImplemented' } # 无 durable run store(capability.run_store_required)——`routes/trace-usage.ts:56`,此前漏登记
|
|
2277
2512
|
|
|
2278
2513
|
/v1/tasks/{taskId}/artifacts:
|
|
2279
2514
|
parameters:
|
|
@@ -2327,6 +2562,7 @@ paths:
|
|
|
2327
2562
|
schema: { $ref: '#/components/schemas/TraceStreamEvent' }
|
|
2328
2563
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
2329
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
|
|
2330
2566
|
|
|
2331
2567
|
/v1/usage:
|
|
2332
2568
|
parameters:
|
|
@@ -2359,7 +2595,10 @@ paths:
|
|
|
2359
2595
|
summary: Windowed usage totals (usage-analytics face).
|
|
2360
2596
|
description: >
|
|
2361
2597
|
READ-ONLY analytics aggregate over the caller's principal (operator/trace token = fleet-wide). Query:
|
|
2362
|
-
`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
|
|
2363
2602
|
object (additive fields ride; consumers branch on known keys only).
|
|
2364
2603
|
responses:
|
|
2365
2604
|
'200':
|
|
@@ -2441,8 +2680,9 @@ paths:
|
|
|
2441
2680
|
summary: Non-secret model catalog (the `@model` picker / autocomplete data source).
|
|
2442
2681
|
description: >
|
|
2443
2682
|
READ-ONLY catalog of `@mention`-pickable models. The user selects a model PER-TASK by typing `@<name>` in
|
|
2444
|
-
the objective body (the service allowlist-parses it; the client never re-decides). Envelope
|
|
2445
|
-
(`default` = the deployment default model id, display-only — the
|
|
2683
|
+
the objective body (the service allowlist-parses it; the client never re-decides). Envelope
|
|
2684
|
+
`{ models, default, defaultModel }` (`default` = the deployment default model id, display-only — the
|
|
2685
|
+
internal `default` alias is hidden; `defaultModel` = the same default as `{id, name?}` in one piece).
|
|
2446
2686
|
🔴 NON-SECRET: only name/id/provider/reasoning/vision + the E4/E7 capability fields — NEVER baseUrl/apiKey/
|
|
2447
2687
|
headers (the service strips them). Fail-safe: on error, render an empty picker (never block).
|
|
2448
2688
|
responses:
|
|
@@ -2452,12 +2692,24 @@ paths:
|
|
|
2452
2692
|
application/json:
|
|
2453
2693
|
schema:
|
|
2454
2694
|
type: object
|
|
2455
|
-
|
|
2695
|
+
# 🔴 封闭 + `defaultModel` 补登记(census 批2 第三段,2026-07-30):铸造点
|
|
2696
|
+
# `routes/capabilities.ts:322` 三键字面量。`defaultModel`([865]③ id/name 撕裂消解,server
|
|
2697
|
+
# 已发多版)SDK `models.list()` 的返回类型早就带,**spec 是唯一没登记的一侧** —— 生成式
|
|
2698
|
+
# 消费方对它全瞎。`name` 条件(目录无命中即省略),`id` 恒在。
|
|
2699
|
+
additionalProperties: false
|
|
2700
|
+
required: [models, default, defaultModel]
|
|
2456
2701
|
properties:
|
|
2457
2702
|
models:
|
|
2458
2703
|
type: array
|
|
2459
2704
|
items: { $ref: '#/components/schemas/ModelInfo' }
|
|
2460
|
-
default: { type: string, description: "deployment default model id (display-only)" }
|
|
2705
|
+
default: { type: string, description: "deployment default model id (display-only; legacy id-form, byte-stable)" }
|
|
2706
|
+
defaultModel:
|
|
2707
|
+
type: object
|
|
2708
|
+
additionalProperties: false
|
|
2709
|
+
required: [id]
|
|
2710
|
+
properties:
|
|
2711
|
+
id: { type: string }
|
|
2712
|
+
name: { type: string, description: 'the catalog @handle of the default model; omitted when the catalog has no match (env single-model lane).' }
|
|
2461
2713
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
2462
2714
|
|
|
2463
2715
|
/v1/elicitations/{id}/respond:
|
|
@@ -2562,7 +2814,9 @@ paths:
|
|
|
2562
2814
|
description: Ack (`decision` echoed).
|
|
2563
2815
|
content:
|
|
2564
2816
|
application/json:
|
|
2565
|
-
|
|
2817
|
+
# census 批2 第三段(2026-07-30):这条 200 原本是裸 `{type: object, additionalProperties: true}`,
|
|
2818
|
+
# 而 `ToolApprovalRespondAck` 本文件里早就有、path 从来没引用(usage 三兄弟同款脱钩)。接上。
|
|
2819
|
+
schema: { $ref: '#/components/schemas/ToolApprovalRespondAck' }
|
|
2566
2820
|
'400': { description: Malformed body (existence-independent). }
|
|
2567
2821
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
2568
2822
|
'404': { description: Unknown/settled/expired/foreign approval (no existence oracle). }
|
|
@@ -2591,7 +2845,9 @@ paths:
|
|
|
2591
2845
|
application/json:
|
|
2592
2846
|
schema: { type: object, additionalProperties: true }
|
|
2593
2847
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
2594
|
-
|
|
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.
|
|
2595
2851
|
|
|
2596
2852
|
/v1/workflows:
|
|
2597
2853
|
parameters:
|
|
@@ -2617,6 +2873,13 @@ paths:
|
|
|
2617
2873
|
required: false
|
|
2618
2874
|
schema: { type: integer, minimum: 1, maximum: 100, default: 50 }
|
|
2619
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.
|
|
2620
2883
|
responses:
|
|
2621
2884
|
'200':
|
|
2622
2885
|
description: Owner's workflow-run summaries.
|
|
@@ -2625,6 +2888,7 @@ paths:
|
|
|
2625
2888
|
schema:
|
|
2626
2889
|
type: object
|
|
2627
2890
|
required: [workflows]
|
|
2891
|
+
additionalProperties: false
|
|
2628
2892
|
properties:
|
|
2629
2893
|
workflows:
|
|
2630
2894
|
type: array
|
|
@@ -2701,6 +2965,13 @@ paths:
|
|
|
2701
2965
|
application/json:
|
|
2702
2966
|
schema: { $ref: '#/components/schemas/AttachmentInfo' }
|
|
2703
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' }
|
|
2704
2975
|
'413': { description: 'attachment.too_large' }
|
|
2705
2976
|
'415': { description: 'attachment.mime_not_allowed' }
|
|
2706
2977
|
'501': { description: 'attachment store not configured' }
|
|
@@ -2718,7 +2989,15 @@ paths:
|
|
|
2718
2989
|
summary: Download an attachment's bytes (owner-gated, no oracle — unknown and foreign both 404).
|
|
2719
2990
|
responses:
|
|
2720
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' }
|
|
2721
2999
|
'404': { description: 'unknown or not owned' }
|
|
3000
|
+
'501': { description: '(declared 2026-07-31) errorCode "capability.attachment_store_required" — no attachment store configured.' }
|
|
2722
3001
|
delete:
|
|
2723
3002
|
tags: [tasks]
|
|
2724
3003
|
operationId: deleteAttachment
|
|
@@ -2726,10 +3005,33 @@ paths:
|
|
|
2726
3005
|
summary: Delete an attachment (owner-gated; 204 on success).
|
|
2727
3006
|
responses:
|
|
2728
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' }
|
|
2729
3015
|
'404': { description: 'unknown or not owned' }
|
|
3016
|
+
'501': { description: '(declared 2026-07-31) errorCode "capability.attachment_store_required" — no attachment store configured.' }
|
|
2730
3017
|
/v1/fleet/stream:
|
|
2731
3018
|
parameters:
|
|
2732
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.
|
|
2733
3035
|
get:
|
|
2734
3036
|
tags: [fleet]
|
|
2735
3037
|
operationId: fleetStream
|
|
@@ -2768,7 +3070,10 @@ paths:
|
|
|
2768
3070
|
`LEADER_ENABLED` (default OFF) + `REMOTE_EXEC=e2b`. Mode is a SERVER deploy flag, not a client choice:
|
|
2769
3071
|
`LEADER_FANOUT_ENABLED=true` (default) lets the router fan out to N isolated workers (= the value-HOLD
|
|
2770
3072
|
correctness bet); `=false` pins a single worker. Either way it runs the E2B+git pipeline. Door B's brain
|
|
2771
|
-
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.)
|
|
2772
3077
|
requestBody:
|
|
2773
3078
|
required: true
|
|
2774
3079
|
content:
|
|
@@ -2788,7 +3093,13 @@ paths:
|
|
|
2788
3093
|
application/json:
|
|
2789
3094
|
schema: { $ref: '#/components/schemas/LeaderReceipt' }
|
|
2790
3095
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
2791
|
-
'
|
|
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' }
|
|
2792
3103
|
|
|
2793
3104
|
/v1/leader/{leaderRunId}:
|
|
2794
3105
|
parameters:
|
|
@@ -2843,7 +3154,7 @@ paths:
|
|
|
2843
3154
|
schema:
|
|
2844
3155
|
type: object
|
|
2845
3156
|
required: [images]
|
|
2846
|
-
additionalProperties:
|
|
3157
|
+
additionalProperties: false
|
|
2847
3158
|
properties:
|
|
2848
3159
|
images:
|
|
2849
3160
|
type: array
|
|
@@ -2861,9 +3172,17 @@ paths:
|
|
|
2861
3172
|
summary: 'OPERATOR: register one image-index entry → { id }.'
|
|
2862
3173
|
requestBody: { required: true, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
|
|
2863
3174
|
responses:
|
|
2864
|
-
'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 } } } }
|
|
2865
3176
|
'400': { $ref: '#/components/responses/BadRequest' }
|
|
2866
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' }
|
|
2867
3186
|
|
|
2868
3187
|
/v1/images/{profile}:
|
|
2869
3188
|
parameters:
|
|
@@ -2875,7 +3194,9 @@ paths:
|
|
|
2875
3194
|
x-status: live
|
|
2876
3195
|
summary: 'Newest PUBLISHED entry for a profile → { image }.'
|
|
2877
3196
|
responses:
|
|
2878
|
-
|
|
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' } } } } } }
|
|
2879
3200
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
2880
3201
|
|
|
2881
3202
|
/v1/images/digests/{digest}:
|
|
@@ -2888,7 +3209,8 @@ paths:
|
|
|
2888
3209
|
x-status: live
|
|
2889
3210
|
summary: 'Entry by immutable digest → { image }.'
|
|
2890
3211
|
responses:
|
|
2891
|
-
|
|
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' } } } } } }
|
|
2892
3214
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
2893
3215
|
|
|
2894
3216
|
/v1/images/bakes:
|
|
@@ -2904,11 +3226,31 @@ paths:
|
|
|
2904
3226
|
busy ⇒ 409 {activeBakeId?, eventsUrl?}; submission rate-limit ⇒ 429 + retryAfterSec.
|
|
2905
3227
|
requestBody: { required: true, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
|
|
2906
3228
|
responses:
|
|
2907
|
-
'202': { description: '{ bakeId, eventsUrl, status, state
|
|
3229
|
+
'202': { description: '{ bakeId, eventsUrl, status, state }.', content: { application/json: { schema: { $ref: '#/components/schemas/BakeSubmitAck' } } } }
|
|
2908
3230
|
'400': { $ref: '#/components/responses/BadRequest' }
|
|
2909
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' }
|
|
2910
3237
|
'409': { description: 'Busy — { activeBakeId?, eventsUrl? }.' }
|
|
2911
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' }
|
|
2912
3254
|
|
|
2913
3255
|
/v1/images/bakes/claim:
|
|
2914
3256
|
parameters:
|
|
@@ -2919,9 +3261,16 @@ paths:
|
|
|
2919
3261
|
x-status: live # bake-runner credential face (machine-to-machine).
|
|
2920
3262
|
summary: 'RUNNER: claim the oldest queued bake (discover+CAS in one step; 204 = queue empty).'
|
|
2921
3263
|
responses:
|
|
2922
|
-
'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' } } } }
|
|
2923
3265
|
'204': { description: 'Queue empty.' }
|
|
2924
|
-
'
|
|
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' }
|
|
2925
3274
|
|
|
2926
3275
|
/v1/images/bakes/{bakeId}:
|
|
2927
3276
|
parameters:
|
|
@@ -2931,9 +3280,14 @@ paths:
|
|
|
2931
3280
|
tags: [sandbox]
|
|
2932
3281
|
operationId: bakesGet
|
|
2933
3282
|
x-status: live
|
|
2934
|
-
summary: 'One bake row → { bake } (
|
|
3283
|
+
summary: 'One bake row → { bake } (status/state/profile/bands/logs/digest…).'
|
|
2935
3284
|
responses:
|
|
2936
|
-
'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' }
|
|
2937
3291
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
2938
3292
|
|
|
2939
3293
|
/v1/images/bakes/{bakeId}/cancel:
|
|
@@ -2944,9 +3298,14 @@ paths:
|
|
|
2944
3298
|
tags: [sandbox]
|
|
2945
3299
|
operationId: bakesCancel
|
|
2946
3300
|
x-status: live
|
|
2947
|
-
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).'
|
|
2948
3302
|
responses:
|
|
2949
|
-
'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' }
|
|
2950
3309
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
2951
3310
|
|
|
2952
3311
|
/v1/images/bakes/{bakeId}/claim:
|
|
@@ -2959,7 +3318,12 @@ paths:
|
|
|
2959
3318
|
x-status: live
|
|
2960
3319
|
summary: 'RUNNER: claim THIS queued bake (single-flight CAS; losing the race ⇒ 409).'
|
|
2961
3320
|
responses:
|
|
2962
|
-
'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' }
|
|
2963
3327
|
'409': { description: 'Claim race lost / not queued.' }
|
|
2964
3328
|
|
|
2965
3329
|
/v1/images/bakes/{bakeId}/ingest:
|
|
@@ -2971,10 +3335,19 @@ paths:
|
|
|
2971
3335
|
operationId: bakesIngest
|
|
2972
3336
|
x-status: live
|
|
2973
3337
|
summary: 'RUNNER: push one build.sh event line + lease heartbeat (x-bake-ingest-secret header from the claim).'
|
|
2974
|
-
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.'
|
|
2975
3339
|
requestBody: { required: true, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
|
|
2976
3340
|
responses:
|
|
2977
|
-
'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' }
|
|
2978
3351
|
'409': { description: 'Lease invalid / claimed by another runner.' }
|
|
2979
3352
|
|
|
2980
3353
|
/v1/images/bakes/{bakeId}/events:
|
|
@@ -2985,9 +3358,14 @@ paths:
|
|
|
2985
3358
|
tags: [sandbox]
|
|
2986
3359
|
operationId: bakesEvents
|
|
2987
3360
|
x-status: live
|
|
2988
|
-
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).'
|
|
2989
3362
|
responses:
|
|
2990
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' }
|
|
2991
3369
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
2992
3370
|
|
|
2993
3371
|
/v1/images/select:
|
|
@@ -3061,14 +3439,19 @@ paths:
|
|
|
3061
3439
|
The approval row carries the FULL tool-call args (commands/paths/contents of a high-risk op), so the
|
|
3062
3440
|
by-id read enforces the same tenant boundary as the list: operators read across tenants; everyone else
|
|
3063
3441
|
reads ONLY their own scope (the guard fires at the SQL layer — no cross-tenant args even if an id
|
|
3064
|
-
leaks). Non-owner → 404 (never 403, no existence oracle).
|
|
3065
|
-
|
|
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.
|
|
3066
3449
|
responses:
|
|
3067
3450
|
'200':
|
|
3068
3451
|
description: The approval row (full args projection).
|
|
3069
3452
|
content:
|
|
3070
3453
|
application/json:
|
|
3071
|
-
schema: {
|
|
3454
|
+
schema: { $ref: '#/components/schemas/ApprovalRow' }
|
|
3072
3455
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
3073
3456
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
3074
3457
|
|
|
@@ -3099,11 +3482,14 @@ paths:
|
|
|
3099
3482
|
application/json:
|
|
3100
3483
|
schema:
|
|
3101
3484
|
type: object
|
|
3485
|
+
# 🔴 census 批2 五段:CLOSED — exact literal `{ scope, exportedAt, entries }`
|
|
3486
|
+
# (memory-policy.ts:58), no fourth key.
|
|
3102
3487
|
required: [scope, exportedAt, entries]
|
|
3488
|
+
additionalProperties: false
|
|
3103
3489
|
properties:
|
|
3104
3490
|
scope: { type: string }
|
|
3105
3491
|
exportedAt: { type: string, format: date-time }
|
|
3106
|
-
entries: { type: array, items: {
|
|
3492
|
+
entries: { type: array, items: { $ref: '#/components/schemas/MemoryEntry' } }
|
|
3107
3493
|
'400': { $ref: '#/components/responses/BadRequest' }
|
|
3108
3494
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
3109
3495
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
@@ -3139,7 +3525,7 @@ paths:
|
|
|
3139
3525
|
description: 'The sync outcome (core reconcile/nextSyncBaseline projection).'
|
|
3140
3526
|
content:
|
|
3141
3527
|
application/json:
|
|
3142
|
-
schema: {
|
|
3528
|
+
schema: { $ref: '#/components/schemas/MemorySyncResponse' }
|
|
3143
3529
|
'400': { $ref: '#/components/responses/BadRequest' }
|
|
3144
3530
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
3145
3531
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
@@ -3175,7 +3561,15 @@ paths:
|
|
|
3175
3561
|
description: Aggregation rows.
|
|
3176
3562
|
content:
|
|
3177
3563
|
application/json:
|
|
3178
|
-
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' } }
|
|
3179
3573
|
'403': { description: 'Multi-tenant worker: outcomes view is operator-only.' }
|
|
3180
3574
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
3181
3575
|
|
|
@@ -3217,9 +3611,13 @@ paths:
|
|
|
3217
3611
|
application/json:
|
|
3218
3612
|
schema:
|
|
3219
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.
|
|
3220
3617
|
required: [links]
|
|
3618
|
+
additionalProperties: false
|
|
3221
3619
|
properties:
|
|
3222
|
-
links: { type: array, items: {
|
|
3620
|
+
links: { type: array, items: { $ref: '#/components/schemas/SendfileLinkRow' } }
|
|
3223
3621
|
nextBefore: { type: string }
|
|
3224
3622
|
'400': { $ref: '#/components/responses/BadRequest' }
|
|
3225
3623
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
@@ -3261,9 +3659,18 @@ paths:
|
|
|
3261
3659
|
schema:
|
|
3262
3660
|
type: object
|
|
3263
3661
|
required: [runId, entries]
|
|
3662
|
+
additionalProperties: false
|
|
3264
3663
|
properties:
|
|
3265
3664
|
runId: { type: string }
|
|
3266
|
-
|
|
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'
|
|
3267
3674
|
nextOffset: { type: integer }
|
|
3268
3675
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
3269
3676
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
@@ -3304,6 +3711,14 @@ paths:
|
|
|
3304
3711
|
responses:
|
|
3305
3712
|
'200':
|
|
3306
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' }
|
|
3307
3722
|
'400': { $ref: '#/components/responses/BadRequest' }
|
|
3308
3723
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
3309
3724
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
@@ -3314,6 +3729,14 @@ paths:
|
|
|
3314
3729
|
content:
|
|
3315
3730
|
application/json:
|
|
3316
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' }
|
|
3317
3740
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
3318
3741
|
|
|
3319
3742
|
/metrics/summary:
|
|
@@ -3333,7 +3756,12 @@ paths:
|
|
|
3333
3756
|
description: Metrics summary (shape draft).
|
|
3334
3757
|
content:
|
|
3335
3758
|
application/json:
|
|
3336
|
-
|
|
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' }
|
|
3337
3765
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
3338
3766
|
|
|
3339
3767
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -3401,6 +3829,15 @@ components:
|
|
|
3401
3829
|
content:
|
|
3402
3830
|
application/json:
|
|
3403
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' }
|
|
3404
3841
|
NotFound:
|
|
3405
3842
|
description: >
|
|
3406
3843
|
No such resource for this principal. Owner-scoped reads/cancel return 404 (not 403) for a non-owned or
|
|
@@ -3477,8 +3914,11 @@ components:
|
|
|
3477
3914
|
`Capabilities.scenarios` instead. `source`/`builtin` = builtin vs center-config overlay; `toolset` names
|
|
3478
3915
|
the tool bundle while `tools` are the resolved tool names; `promptSummary` is a human-readable prompt
|
|
3479
3916
|
DIGEST (never the full prompt); `enabled` = a center scenario can be declared-but-disabled.
|
|
3917
|
+
# 封闭(census 批2 第三段,2026-07-30):路由 `routes/capabilities.ts:38` 原样发 `deps.scenarioDetails[name]`,
|
|
3918
|
+
# 其类型 `ScenarioDetail`(capabilities/scenarios.ts:353)恰是这 8 个必填键;builtin 工厂 `mk`(:379)与
|
|
3919
|
+
# center overlay 都按该接口铸,无第 9 键来源。
|
|
3480
3920
|
required: [name, source, builtin, summary, toolset, tools, promptSummary, enabled]
|
|
3481
|
-
additionalProperties:
|
|
3921
|
+
additionalProperties: false
|
|
3482
3922
|
properties:
|
|
3483
3923
|
name: { type: string }
|
|
3484
3924
|
source: { type: string, enum: [builtin, center] }
|
|
@@ -4077,11 +4517,15 @@ components:
|
|
|
4077
4517
|
ResumeEvicted:
|
|
4078
4518
|
type: object
|
|
4079
4519
|
description: >
|
|
4080
|
-
416 body for an evicted SSE resume point (`GET /v1/runs/:id/events`).
|
|
4081
|
-
|
|
4082
|
-
|
|
4520
|
+
416 body for an evicted SSE resume point (`GET /v1/runs/:id/events`). Carries the machine code
|
|
4521
|
+
`errorCode: "limit.retention_evicted"`; matching the 416 STATUS alone is equally valid. Read
|
|
4522
|
+
`retainedFrom`, then full-sync via `/v1/tasks/:id/turns` (pinned SSE contract).
|
|
4523
|
+
(Corrected 2026-07-31 — see the 416 response note on that path: the "no machine code" claim predates
|
|
4524
|
+
the site's move onto the shared `sendError`.)
|
|
4525
|
+
required: [error, errorCode, retainedFrom]
|
|
4083
4526
|
properties:
|
|
4084
4527
|
error: { type: string }
|
|
4528
|
+
errorCode: { type: string, enum: [limit.retention_evicted] }
|
|
4085
4529
|
retainedFrom: { type: integer, description: Lowest event seq still retained; resume events from here. }
|
|
4086
4530
|
|
|
4087
4531
|
ImageIndexEntry:
|
|
@@ -4097,7 +4541,7 @@ components:
|
|
|
4097
4541
|
[id, profile, bands, repo, tag, digest, toolchainVersions, capabilities, podContract, sizeBytes, status,
|
|
4098
4542
|
visibility, tenantId, manifestSha, recipeGitSha, generatorVersion, buildDate, supersedes, signed,
|
|
4099
4543
|
createdAt, updatedAt]
|
|
4100
|
-
additionalProperties:
|
|
4544
|
+
additionalProperties: false
|
|
4101
4545
|
properties:
|
|
4102
4546
|
id: { type: string, description: 'deterministically derived from (repo, digest) — there is no explicit-id upsert path, so an id collision ⟺ a (repo, digest) collision.' }
|
|
4103
4547
|
profile: { type: string }
|
|
@@ -4111,26 +4555,8 @@ components:
|
|
|
4111
4555
|
back to bind an image (bind by `profile`; the server re-resolves + re-admits under a trusted
|
|
4112
4556
|
principal, because trusting a caller-supplied digest would fail OPEN).
|
|
4113
4557
|
toolchainVersions: { type: object, additionalProperties: { type: string } }
|
|
4114
|
-
capabilities:
|
|
4115
|
-
|
|
4116
|
-
additionalProperties: true
|
|
4117
|
-
properties:
|
|
4118
|
-
browser: { type: boolean }
|
|
4119
|
-
db: { type: boolean }
|
|
4120
|
-
nestedBuild: { type: boolean, description: 'can it build container images (back-compat boolean).' }
|
|
4121
|
-
nestedBuildMode:
|
|
4122
|
-
type: string
|
|
4123
|
-
enum: [none, docker-cli-only, rootless-buildkit, privileged-dind]
|
|
4124
|
-
description: 'HOW it nests (the posture). Refines `nestedBuild`; absent ⇒ fall back to the boolean.'
|
|
4125
|
-
podContract:
|
|
4126
|
-
type: object
|
|
4127
|
-
additionalProperties: true
|
|
4128
|
-
properties:
|
|
4129
|
-
devShmMB: { type: integer }
|
|
4130
|
-
minMemMB: { type: integer }
|
|
4131
|
-
minCpu: { type: string }
|
|
4132
|
-
readyTimeoutSec: { type: integer }
|
|
4133
|
-
securityContext: { type: object, additionalProperties: true }
|
|
4558
|
+
capabilities: { $ref: '#/components/schemas/ImageCapabilities' }
|
|
4559
|
+
podContract: { $ref: '#/components/schemas/ImagePodContract' }
|
|
4134
4560
|
sizeBytes: { type: ['integer', 'null'] }
|
|
4135
4561
|
status: { type: string, enum: [building, published, deprecated, failed] }
|
|
4136
4562
|
visibility: { type: string, enum: [public, tenant] }
|
|
@@ -4144,14 +4570,47 @@ components:
|
|
|
4144
4570
|
createdAt: { type: string, description: ISO date-time. }
|
|
4145
4571
|
updatedAt: { type: string, description: ISO date-time. }
|
|
4146
4572
|
|
|
4573
|
+
ImageCapabilities:
|
|
4574
|
+
# 抽成具名 schema(census 批2 四段)——此前在 ImageIndexEntry/ImageSelectResult 两处内联重复,
|
|
4575
|
+
# ImageSelectResult 那份还漏了 `nestedBuildMode`(server 侧两处读的是同一个 ImageCapabilities,
|
|
4576
|
+
# entry.capabilities 逐字透传;两侧同缺,不是刻意窄化)。server store-contracts.ts 全字段皆 optional。
|
|
4577
|
+
type: object
|
|
4578
|
+
description: What a sandbox image can do (server `ImageCapabilities`, plugins/store-contracts.ts).
|
|
4579
|
+
additionalProperties: false
|
|
4580
|
+
properties:
|
|
4581
|
+
browser: { type: boolean }
|
|
4582
|
+
db: { type: boolean }
|
|
4583
|
+
nestedBuild: { type: boolean, description: 'can it build container images (back-compat boolean).' }
|
|
4584
|
+
nestedBuildMode:
|
|
4585
|
+
type: string
|
|
4586
|
+
enum: [none, docker-cli-only, rootless-buildkit, privileged-dind]
|
|
4587
|
+
description: 'HOW it nests (the posture). Refines `nestedBuild`; absent ⇒ fall back to the boolean.'
|
|
4588
|
+
|
|
4589
|
+
ImagePodContract:
|
|
4590
|
+
# 抽成具名 schema(census 批2 四段)——同上,ImageSelectResult 那份此前连字段都没声明(裸 additionalProperties:true)。
|
|
4591
|
+
type: object
|
|
4592
|
+
description: Pod-shape hints for scheduling a sandbox off this image (server `ImagePodContract`).
|
|
4593
|
+
additionalProperties: false
|
|
4594
|
+
properties:
|
|
4595
|
+
devShmMB: { type: integer }
|
|
4596
|
+
minMemMB: { type: integer }
|
|
4597
|
+
minCpu: { type: string }
|
|
4598
|
+
readyTimeoutSec: { type: integer }
|
|
4599
|
+
securityContext: { type: object, additionalProperties: true, description: 'k8s-shaped, genuinely arbitrary — Record<string,unknown> server-side.' }
|
|
4600
|
+
|
|
4147
4601
|
ImageSelectResult:
|
|
4148
|
-
# 新增(2026-07-25 回填批)
|
|
4602
|
+
# 新增(2026-07-25 回填批)。🔴 census 批2 四段:closed against the real handler (images.ts:407-415) — ALL
|
|
4603
|
+
# seven keys are unconditionally assigned every 200 (required tightened from none); `manifestSha` can be
|
|
4604
|
+
# null (ImageIndexEntry.manifestSha is `string | null`, this was typed non-nullable — a spec lie); and
|
|
4605
|
+
# `podContract`/`capabilities` now share the SAME closed schemas as ImageIndexEntry (no more drift/no
|
|
4606
|
+
# more missing `nestedBuildMode`).
|
|
4149
4607
|
type: object
|
|
4150
4608
|
description: >
|
|
4151
4609
|
`POST /v1/images/select` resolution result — a PREVIEW of "which digest/capabilities does this profile
|
|
4152
4610
|
resolve to" for the UI. 🔴 NOT a binding credential: to actually bind, send `sandboxImageProfile` on the
|
|
4153
4611
|
task request and let the server resolve under a trusted principal.
|
|
4154
|
-
|
|
4612
|
+
required: [profile, digest, repo, ref, podContract, capabilities, manifestSha]
|
|
4613
|
+
additionalProperties: false
|
|
4155
4614
|
properties:
|
|
4156
4615
|
profile: { type: string }
|
|
4157
4616
|
digest: { type: string }
|
|
@@ -4161,15 +4620,9 @@ components:
|
|
|
4161
4620
|
description: >
|
|
4162
4621
|
`${repo}@${digest}` — always present on a 200. This is the value a caller threads into the kata
|
|
4163
4622
|
adapter's per-sandbox image (the server handler says so itself), i.e. it is load-bearing, not decorative.
|
|
4164
|
-
podContract: {
|
|
4165
|
-
capabilities:
|
|
4166
|
-
|
|
4167
|
-
additionalProperties: true
|
|
4168
|
-
properties:
|
|
4169
|
-
browser: { type: boolean }
|
|
4170
|
-
db: { type: boolean }
|
|
4171
|
-
nestedBuild: { type: boolean }
|
|
4172
|
-
manifestSha: { type: string }
|
|
4623
|
+
podContract: { $ref: '#/components/schemas/ImagePodContract' }
|
|
4624
|
+
capabilities: { $ref: '#/components/schemas/ImageCapabilities' }
|
|
4625
|
+
manifestSha: { type: ['string', 'null'], description: 'the source entry''s manifestSha — nullable (ImageIndexEntry.manifestSha is string|null; was wrongly non-nullable here).' }
|
|
4173
4626
|
|
|
4174
4627
|
QuotaError:
|
|
4175
4628
|
type: object
|
|
@@ -4311,17 +4764,32 @@ components:
|
|
|
4311
4764
|
|
|
4312
4765
|
LeaderReceipt:
|
|
4313
4766
|
type: object
|
|
4767
|
+
description: >
|
|
4768
|
+
202 receipt of POST /v1/leader (server leader/endpoint.ts:130 — exact literal
|
|
4769
|
+
`{ leaderRunId, status: "running" }`; the POST ack is always the "running" value, the other
|
|
4770
|
+
`status` enum members only ever appear on the GET twin below).
|
|
4771
|
+
# 🔴 census 批2 五段:CLOSED — the POST handler's literal never carries a third key.
|
|
4314
4772
|
required: [leaderRunId, status]
|
|
4773
|
+
additionalProperties: false
|
|
4315
4774
|
properties:
|
|
4316
4775
|
leaderRunId: { type: string }
|
|
4317
4776
|
status: { type: string, enum: [running, completed, failed] }
|
|
4318
4777
|
|
|
4319
4778
|
LeaderRecord:
|
|
4320
4779
|
type: object
|
|
4780
|
+
description: >
|
|
4781
|
+
200 body of GET /v1/leader/{leaderRunId} (server leader/endpoint.ts:142-150 — exact literal
|
|
4782
|
+
`{ id, status, ...(result?{result}:{}), ...(error?{error}:{}) }`).
|
|
4783
|
+
# 🔴 census 批2 五段:两处修——① CLOSED(result/error 是 OMIT-when-absent 的可选键,不是额外键);
|
|
4784
|
+
# ② `status` 的第四值 `needs_human`(LEADER-REPAIRLOOP-INTEGRATION §5/§10.6 的第三终态,
|
|
4785
|
+
# `statusForLeaderResult` 在 candidate_only/needs_human_oracle/conflict 三个 repairTerminal 上产出)
|
|
4786
|
+
# 此前 SPEC LIES BY OMISSION——TS 侧 `LeaderRecord.status` 早就带这个第四值(types.ts:1607,亲验
|
|
4787
|
+
# 已在场),只有 spec 的 enum 一直缺它,是个只有场景真触发才会现形的假红洞(一个真实合法帧被判违约)。
|
|
4321
4788
|
required: [id, status]
|
|
4789
|
+
additionalProperties: false
|
|
4322
4790
|
properties:
|
|
4323
4791
|
id: { type: string }
|
|
4324
|
-
status: { type: string, enum: [running, completed, failed] }
|
|
4792
|
+
status: { type: string, enum: [running, completed, failed, needs_human] }
|
|
4325
4793
|
result: {}
|
|
4326
4794
|
error: { type: string }
|
|
4327
4795
|
|
|
@@ -4360,6 +4828,30 @@ components:
|
|
|
4360
4828
|
Approve-with-edit (design/37 last-wins): operator's rewritten tool args, applied AFTER the binding
|
|
4361
4829
|
check. `boundInputHash` STILL binds the ORIGINAL input the human saw — never hash updatedInput.
|
|
4362
4830
|
|
|
4831
|
+
ApprovalRow:
|
|
4832
|
+
type: object
|
|
4833
|
+
description: >
|
|
4834
|
+
The legacy D-lane approval row of GET /v1/approvals/{approvalId} (server `ApprovalRow`,
|
|
4835
|
+
plugins/approval-store-sql.ts:43 — the row is `sendJson`'d verbatim, approvals-assistant.ts:668).
|
|
4836
|
+
Present only on deployments running the legacy `approvalStore` leg (no `checkpointStore` wired);
|
|
4837
|
+
the durable F4 lane has no by-id GET (only list + decide).
|
|
4838
|
+
# 🔴 census 批2 五段:CLOSED — byte-identical to the server row type, no extra keys.
|
|
4839
|
+
required: [id, taskId, sessionId, owner, scope, toolName, args, status, reason, decidedBy, createdAt, decidedAt]
|
|
4840
|
+
additionalProperties: false
|
|
4841
|
+
properties:
|
|
4842
|
+
id: { type: string }
|
|
4843
|
+
taskId: { type: [string, 'null'] }
|
|
4844
|
+
sessionId: { type: [string, 'null'] }
|
|
4845
|
+
owner: { type: [string, 'null'] }
|
|
4846
|
+
scope: { type: [string, 'null'], description: 'Single-DB fleet scope guard (tenant identity, owner-sourced).' }
|
|
4847
|
+
toolName: { type: string }
|
|
4848
|
+
args: { description: 'The reviewed tool-call args — full fidelity, not redacted (this IS the review surface).' }
|
|
4849
|
+
status: { type: string, enum: [pending, approved, denied, expired] }
|
|
4850
|
+
reason: { type: [string, 'null'] }
|
|
4851
|
+
decidedBy: { type: [string, 'null'] }
|
|
4852
|
+
createdAt: { type: string }
|
|
4853
|
+
decidedAt: { type: [string, 'null'] }
|
|
4854
|
+
|
|
4363
4855
|
Capabilities:
|
|
4364
4856
|
type: object
|
|
4365
4857
|
description: >
|
|
@@ -4555,14 +5047,160 @@ components:
|
|
|
4555
5047
|
egress: { type: boolean }
|
|
4556
5048
|
irreversibility: { type: string, enum: [always, never] }
|
|
4557
5049
|
|
|
5050
|
+
MemoryEntryFrontmatter:
|
|
5051
|
+
type: object
|
|
5052
|
+
description: >
|
|
5053
|
+
core `MemoryEntryFrontmatter` (@sema-agent/core memory-engine/types.d.ts). All fields optional —
|
|
5054
|
+
an entry with none of them is a bare note.
|
|
5055
|
+
additionalProperties: false
|
|
5056
|
+
properties:
|
|
5057
|
+
name: { type: string }
|
|
5058
|
+
description: { type: string }
|
|
5059
|
+
type: { type: string }
|
|
5060
|
+
deleted: { type: boolean }
|
|
5061
|
+
provenance:
|
|
5062
|
+
type: object
|
|
5063
|
+
required: [kind, path, contentHash, ingestedAt]
|
|
5064
|
+
additionalProperties: false
|
|
5065
|
+
properties:
|
|
5066
|
+
kind: { type: string, enum: [repo_file] }
|
|
5067
|
+
path: { type: string }
|
|
5068
|
+
contentHash: { type: string }
|
|
5069
|
+
ingestedAt: { type: integer }
|
|
5070
|
+
trust: { type: string, enum: [untrusted] }
|
|
5071
|
+
extra: { type: array, items: { type: string } }
|
|
5072
|
+
|
|
5073
|
+
MemoryEntry:
|
|
5074
|
+
type: object
|
|
5075
|
+
description: >
|
|
5076
|
+
core `MemoryEntry` (@sema-agent/core memory-engine/types.d.ts) — one memory-engine (DB twins)
|
|
5077
|
+
note. Used verbatim by both GET /v1/memory/export's `entries` and POST /v1/memory/sync/{scope}'s
|
|
5078
|
+
`serverEntries` (server never re-shapes it — memory-policy.ts:57 / memory-sync.ts:79 pass the core
|
|
5079
|
+
array straight to `sendJson`).
|
|
5080
|
+
required: [id, slug, frontmatter, body, rev, scope]
|
|
5081
|
+
additionalProperties: false
|
|
5082
|
+
properties:
|
|
5083
|
+
id: { type: string }
|
|
5084
|
+
slug: { type: string }
|
|
5085
|
+
frontmatter: { $ref: '#/components/schemas/MemoryEntryFrontmatter' }
|
|
5086
|
+
body: { type: string }
|
|
5087
|
+
rev: { type: string }
|
|
5088
|
+
scope: { type: string }
|
|
5089
|
+
|
|
5090
|
+
MemorySyncResponse:
|
|
5091
|
+
type: object
|
|
5092
|
+
description: >
|
|
5093
|
+
200 body of POST /v1/memory/sync/{scope} — core `MemorySyncResponse` (server memory-sync.ts:74-86),
|
|
5094
|
+
`sendJson`'d verbatim (memory-policy.ts:107). `pullTruncated` is OMIT-when-absent (present+`true`
|
|
5095
|
+
only when `serverEntries` was cut by `?pull.limit`).
|
|
5096
|
+
required: [applied, conflicts, serverEntries, serverDeletes, cursor]
|
|
5097
|
+
additionalProperties: false
|
|
5098
|
+
properties:
|
|
5099
|
+
applied:
|
|
5100
|
+
type: array
|
|
5101
|
+
description: The center's own-round applied ops (backend `PatchReport.applied` verbatim; empty on an idempotent replay).
|
|
5102
|
+
items:
|
|
5103
|
+
type: object
|
|
5104
|
+
required: [op, id]
|
|
5105
|
+
additionalProperties: false
|
|
5106
|
+
properties:
|
|
5107
|
+
op: { type: string, enum: [add, update, delete] }
|
|
5108
|
+
id: { type: string }
|
|
5109
|
+
slug: { type: string }
|
|
5110
|
+
conflicts:
|
|
5111
|
+
type: array
|
|
5112
|
+
items:
|
|
5113
|
+
type: object
|
|
5114
|
+
required: [id, reason]
|
|
5115
|
+
additionalProperties: false
|
|
5116
|
+
properties:
|
|
5117
|
+
id: { type: string }
|
|
5118
|
+
reason: { type: string }
|
|
5119
|
+
baseRev: { type: string }
|
|
5120
|
+
currentRev: { type: string }
|
|
5121
|
+
serverEntries:
|
|
5122
|
+
type: array
|
|
5123
|
+
description: Pull half — entries new/changed at the center since the caller's baseRevs.
|
|
5124
|
+
items: { $ref: '#/components/schemas/MemoryEntry' }
|
|
5125
|
+
serverDeletes:
|
|
5126
|
+
type: array
|
|
5127
|
+
items:
|
|
5128
|
+
type: object
|
|
5129
|
+
required: [id, baseRev]
|
|
5130
|
+
additionalProperties: false
|
|
5131
|
+
properties:
|
|
5132
|
+
id: { type: string }
|
|
5133
|
+
baseRev: { type: string }
|
|
5134
|
+
cursor:
|
|
5135
|
+
type: object
|
|
5136
|
+
required: [peer, baseRevs, updatedAtMs]
|
|
5137
|
+
additionalProperties: false
|
|
5138
|
+
properties:
|
|
5139
|
+
peer: { type: string }
|
|
5140
|
+
baseRevs: { type: object, additionalProperties: { type: string } }
|
|
5141
|
+
updatedAtMs: { type: integer }
|
|
5142
|
+
pullTruncated: { type: boolean, enum: [true] }
|
|
5143
|
+
|
|
5144
|
+
MetricsSummaryOps:
|
|
5145
|
+
type: object
|
|
5146
|
+
description: >
|
|
5147
|
+
200 body of GET /metrics/summary — `{ model, ...MetricsSummary }` (server.ts:794; server
|
|
5148
|
+
observability/metrics.ts `MetricsSummary` interface + `summarize()`, all keys unconditional).
|
|
5149
|
+
Ops/observability face (`x-sdk: none` on the path) — closed here for census fidelity, but this
|
|
5150
|
+
does NOT change its SDK-wrapping status.
|
|
5151
|
+
required:
|
|
5152
|
+
[model, runsActive, tasks, tokensTotal, costUsd, costUsdByModel, costUnknown, unpricedCalls,
|
|
5153
|
+
taskDurationAvgSec, brainFirstTokenAvgMs, brainCallAvgMs, cacheHitRateAvg, promptCacheLowHit,
|
|
5154
|
+
rateLimited, costQuotaRejected, budgetExceeded, degraded, degenerate, memoryConsolidationOps,
|
|
5155
|
+
planCacheRecurrence, cascade, verifications, councilRuns, toolErrors]
|
|
5156
|
+
additionalProperties: false
|
|
5157
|
+
properties:
|
|
5158
|
+
model: { type: string }
|
|
5159
|
+
runsActive: { type: integer }
|
|
5160
|
+
tasks: { type: object, additionalProperties: { type: integer } }
|
|
5161
|
+
tokensTotal: { type: integer }
|
|
5162
|
+
costUsd: { type: number }
|
|
5163
|
+
costUsdByModel: { type: object, additionalProperties: { type: number } }
|
|
5164
|
+
# D5 ([2122]): true ⇒ the window saw a cost-unknown brain.call (unpriced deployment) — costUsd/
|
|
5165
|
+
# costUsdByModel are then a LOWER BOUND, not an exact total.
|
|
5166
|
+
costUnknown: { type: boolean }
|
|
5167
|
+
unpricedCalls: { type: integer }
|
|
5168
|
+
taskDurationAvgSec: { type: [number, 'null'] }
|
|
5169
|
+
brainFirstTokenAvgMs: { type: [number, 'null'] }
|
|
5170
|
+
brainCallAvgMs: { type: [number, 'null'] }
|
|
5171
|
+
cacheHitRateAvg: { type: [number, 'null'] }
|
|
5172
|
+
promptCacheLowHit: { type: integer }
|
|
5173
|
+
rateLimited: { type: integer }
|
|
5174
|
+
costQuotaRejected: { type: integer }
|
|
5175
|
+
budgetExceeded: { type: object, additionalProperties: { type: integer } }
|
|
5176
|
+
degraded: { type: object, additionalProperties: { type: integer } }
|
|
5177
|
+
degenerate: { type: object, additionalProperties: { type: integer } }
|
|
5178
|
+
memoryConsolidationOps: { type: object, additionalProperties: { type: integer } }
|
|
5179
|
+
planCacheRecurrence:
|
|
5180
|
+
type: object
|
|
5181
|
+
required: [tasks, hits, hitRate]
|
|
5182
|
+
additionalProperties: false
|
|
5183
|
+
properties:
|
|
5184
|
+
tasks: { type: integer }
|
|
5185
|
+
hits: { type: integer }
|
|
5186
|
+
hitRate: { type: [number, 'null'] }
|
|
5187
|
+
cascade: { type: object, additionalProperties: { type: integer } }
|
|
5188
|
+
verifications: { type: object, additionalProperties: { type: integer } }
|
|
5189
|
+
councilRuns: { type: object, additionalProperties: { type: integer } }
|
|
5190
|
+
toolErrors: { type: object, additionalProperties: { type: integer } }
|
|
5191
|
+
|
|
4558
5192
|
ModelInfo:
|
|
4559
5193
|
type: object
|
|
4560
5194
|
description: >
|
|
4561
5195
|
A non-secret `@model` catalog entry (GET /v1/models; service src/http/routes/capabilities.ts). `name` = the `@handle` the
|
|
4562
5196
|
user types in the objective body to pick this model per-task. 🔴 NON-SECRET — name/capabilities only, never
|
|
4563
|
-
baseUrl/apiKey/headers (the service strips them).
|
|
4564
|
-
required:
|
|
4565
|
-
|
|
5197
|
+
baseUrl/apiKey/headers (the service strips them).
|
|
5198
|
+
# 🔴 封闭 + required 补铸造点(census 批2 第三段,2026-07-30):铸造点 `routes/capabilities.ts:295-313`
|
|
5199
|
+
# 是逐键字面量投影(白名单,不是透传——「open set」旧注不成立,新键要先过这道 spec)。core Model 类型
|
|
5200
|
+
# 里 id/provider/reasoning 都是必填、`vision` 由 `input` 恒算 ⇒ 五键无条件;contextWindow/maxOutputTokens
|
|
5201
|
+
# 0/缺省即省略,supportedEffortLevels 仅 reasoning 模型。
|
|
5202
|
+
required: [name, id, provider, reasoning, vision]
|
|
5203
|
+
additionalProperties: false
|
|
4566
5204
|
properties:
|
|
4567
5205
|
name: { type: string, description: 'the @mention handle (user-pickable model name).' }
|
|
4568
5206
|
id: { type: string, description: 'underlying model id (display, e.g. deepseek-v4-pro).' }
|
|
@@ -4598,6 +5236,8 @@ components:
|
|
|
4598
5236
|
ElicitRespondAck:
|
|
4599
5237
|
type: object
|
|
4600
5238
|
description: The 200 ack from a successful elicitation respond (service elicitation.ts:267).
|
|
5239
|
+
# 封闭(census 批2 第三段,2026-07-30):铸造点是三键字面量,三键全部无条件。
|
|
5240
|
+
additionalProperties: false
|
|
4601
5241
|
required: [elicitationId, delivery, action]
|
|
4602
5242
|
properties:
|
|
4603
5243
|
elicitationId: { type: string }
|
|
@@ -4630,6 +5270,8 @@ components:
|
|
|
4630
5270
|
QuestionRespondAck:
|
|
4631
5271
|
type: object
|
|
4632
5272
|
description: The 200 ack from a successful question respond (service question.ts:226).
|
|
5273
|
+
# 封闭(census 批2 第三段,2026-07-30):铸造点是两键字面量。
|
|
5274
|
+
additionalProperties: false
|
|
4633
5275
|
required: [questionId, delivery]
|
|
4634
5276
|
properties:
|
|
4635
5277
|
questionId: { type: string }
|
|
@@ -4713,14 +5355,19 @@ components:
|
|
|
4713
5355
|
type: object
|
|
4714
5356
|
description: >
|
|
4715
5357
|
GET /v1/workflows list row = core summarizeWorkflowRun projection (owner-scoped; scope = the creating
|
|
4716
|
-
principal). All times are epoch MS.
|
|
5358
|
+
principal). All times are epoch MS.
|
|
5359
|
+
🔴 census 批2 四段(2026-07-30):CLOSED against core's real `summarizeWorkflowRun` (sema-core
|
|
5360
|
+
src/core/workflow-run-store.ts:79-97) — `originatingSessionId`/`agentFailures` were emitted on the
|
|
5361
|
+
wire (both conditional-spread, present only when defined) but absent from this schema (two-side same-gap).
|
|
4717
5362
|
required: [id, scope, status, phaseCount, agentCount, tokens, startedAt, createdAt]
|
|
4718
|
-
additionalProperties:
|
|
5363
|
+
additionalProperties: false
|
|
4719
5364
|
properties:
|
|
4720
5365
|
id: { type: string, description: 'Run id (= WorkflowRun.id).' }
|
|
4721
5366
|
scope: { type: string, description: 'Tenant/group scope (= the creating principal).' }
|
|
5367
|
+
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.' }
|
|
4722
5368
|
name: { type: string, description: "The workflow script's declared `meta.name`." }
|
|
4723
5369
|
description: { type: string, description: "The workflow script's declared `meta.description` (one-liner)." }
|
|
5370
|
+
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).' }
|
|
4724
5371
|
currentPhase: { type: string, description: 'Title of the phase currently executing (absent once terminal).' }
|
|
4725
5372
|
status: { $ref: '#/components/schemas/WorkflowRunStatus' }
|
|
4726
5373
|
phaseCount: { type: integer, description: 'Phases recorded.' }
|
|
@@ -4740,25 +5387,40 @@ components:
|
|
|
4740
5387
|
🔴 `arg` is a SHORT primary-arg SUMMARY (command→name, path→basename, url→origin+path), secret-scrubbed
|
|
4741
5388
|
and truncated (~80 code points) by core. It exists for the monitor's `Read(path)` / `Bash(grep …)`
|
|
4742
5389
|
display — it is NOT the full args. RENDER it; NEVER re-feed it to a model.
|
|
4743
|
-
|
|
5390
|
+
🔴 census 批2 四段:CLOSED against core's real `ToolActivity` (sema-core src/core/types.ts:2332-2348) —
|
|
5391
|
+
`at` (the [843]④a epoch-ms stamp, carried verbatim by both the start and end beat) was on the wire
|
|
5392
|
+
but absent from this schema.
|
|
5393
|
+
required: [phase, toolCallId, toolName]
|
|
5394
|
+
additionalProperties: false
|
|
4744
5395
|
properties:
|
|
4745
5396
|
phase: { type: string, enum: [start, end] }
|
|
4746
5397
|
toolCallId: { type: string }
|
|
4747
5398
|
toolName: { type: string, description: 'the monitor renders `${toolName}(${arg})`.' }
|
|
5399
|
+
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).' }
|
|
4748
5400
|
arg: { type: string, description: 'SHORT, secret-scrubbed, ~80-code-point summary (set on `phase:"start"`). UNTRUSTED display text.' }
|
|
4749
5401
|
isError: { type: boolean, description: 'set on `phase:"end"` — whether the tool call errored.' }
|
|
4750
5402
|
|
|
4751
5403
|
WorkflowAgentRow:
|
|
4752
5404
|
# 新增(2026-07-25 回填批)—— 取代 `WorkflowRun.agents` 的 `items: {}`(零形状)。
|
|
5405
|
+
# 🔴 census 批2 四段:CLOSED against the REAL wire projection — server's `summarizeWorkflowDetail`
|
|
5406
|
+
# (src/http/routes/workflows.ts:358-394), not core's raw `WorkflowAgentRun`. The two differ: the server
|
|
5407
|
+
# ADDS `displayStatus`/`callKey`/`groupId`/`lastActivityAt`/`durationMs`/`queuedAt`/`startedAt`/`endedAt`/
|
|
5408
|
+
# `replayed` (all present on the wire but previously undeclared — same-side gap, no server change needed)
|
|
5409
|
+
# and DROPS core's `errorCode`/`errorMessage`/`attempts`/`lastAttemptReason`/`sessionId` (never projected
|
|
5410
|
+
# by this route — genuinely absent from the wire, not a spec lie).
|
|
4753
5411
|
type: object
|
|
4754
5412
|
description: >
|
|
4755
|
-
One agent-run row in a workflow's detail
|
|
4756
|
-
|
|
4757
|
-
additionalProperties:
|
|
5413
|
+
One agent-run row in a workflow's detail (server projection, not the raw core record).
|
|
5414
|
+
required: [label, status, displayStatus, callKey, queuedAt]
|
|
5415
|
+
additionalProperties: false
|
|
4758
5416
|
properties:
|
|
4759
|
-
label: { type: string, description: 'display label.' }
|
|
5417
|
+
label: { type: string, description: 'display label (redacted — LLM-authored).' }
|
|
4760
5418
|
status: { type: string, description: 'lifecycle status (the input vocabulary the display derivation reads).' }
|
|
4761
|
-
|
|
5419
|
+
displayStatus: { type: string, description: 'core deriveAgentDisplayStatus: running|queued|done|failed|interrupted — the canonical glyph vocabulary, one source of truth shared with the shell.' }
|
|
5420
|
+
taskStatus: { type: string, description: 'the underlying task''s terminal TaskStatus (open vocabulary; core enum verbatim).' }
|
|
5421
|
+
callKey: { type: string, description: 'the stable deterministic identity of this ctx.agent call (resume-journal key).' }
|
|
5422
|
+
groupId: { type: string, description: 'the nesting ctx.workflow sub-group this agent ran under; absent = top-level.' }
|
|
5423
|
+
phase: { type: string, description: 'the phase title this agent ran under (redacted — LLM-authored); absent = unphased.' }
|
|
4762
5424
|
model: { type: string, description: 'per-agent model DISPLAY label (e.g. "Opus 4.8 (1M context)"), not an id.' }
|
|
4763
5425
|
tokens: { type: integer }
|
|
4764
5426
|
turns: { type: integer }
|
|
@@ -4767,44 +5429,149 @@ components:
|
|
|
4767
5429
|
type: array
|
|
4768
5430
|
description: 'the "Activity" tail — the LAST-N tool-call beats (bounded).'
|
|
4769
5431
|
items: { $ref: '#/components/schemas/WorkflowActivityBeat' }
|
|
5432
|
+
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.' }
|
|
5433
|
+
durationMs: { type: integer, description: 'endedAt − startedAt; absent while queued/running or if either end is unset (a queued agent has no running duration).' }
|
|
5434
|
+
queuedAt: { type: integer, description: 'when the agent was ENQUEUED (before waiting on a concurrency slot). Always set.' }
|
|
5435
|
+
startedAt: { type: integer, description: 'when the agent actually started running (post-queue). Absent while queued, or if aborted/finalized before it ran.' }
|
|
5436
|
+
endedAt: { type: integer }
|
|
5437
|
+
replayed: { type: boolean, description: 'set when this agent''s result was REPLAYED from a resume journal rather than freshly run.' }
|
|
4770
5438
|
prompt: { type: string, description: 'what the worker was ASKED (core-redacted + bounded). UNTRUSTED display text.' }
|
|
4771
5439
|
output: { type: string, description: 'the worker''s final OUTPUT (core-redacted + bounded). UNTRUSTED display text.' }
|
|
4772
5440
|
|
|
4773
|
-
|
|
5441
|
+
WorkflowPhaseProgress:
|
|
5442
|
+
# 新(census 批2 四段):此前 `WorkflowRun.phases` 是 `items: {}`(零形状)。真形 = server
|
|
5443
|
+
# `summarizeWorkflowDetail`'s phases.map projection (workflows.ts:335-346) — a DERIVED progress view,
|
|
5444
|
+
# not core's raw `WorkflowPhase` (drops `detail`/`model`/(phase-level) `agentFailures`; adds `done`/`total`).
|
|
4774
5445
|
type: object
|
|
4775
|
-
description:
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
required: [id, scope, status, phases, agents, stats, startedAt, createdAt]
|
|
4779
|
-
additionalProperties: true
|
|
5446
|
+
description: One phase's progress in a workflow's detail (server-derived — done/total of the agents grouped under it).
|
|
5447
|
+
required: [title, status, startedAt, done, total]
|
|
5448
|
+
additionalProperties: false
|
|
4780
5449
|
properties:
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
|
|
5450
|
+
title: { type: string, description: 'phase title (redacted — LLM-authored).' }
|
|
5451
|
+
status: { type: string, description: 'core WorkflowItemStatus, plus "pending" for a meta-preregistered phase not yet adopted.' }
|
|
5452
|
+
startedAt: { type: integer, description: 'adoption time for a pre-registered phase (0 while still pending).' }
|
|
5453
|
+
endedAt: { type: integer }
|
|
5454
|
+
durationMs: { type: integer, description: 'endedAt − startedAt; absent until the phase ends.' }
|
|
5455
|
+
done: { type: integer, description: 'agents grouped under this phase whose status is a terminal (completed/failed).' }
|
|
5456
|
+
total: { type: integer, description: 'agents grouped under this phase.' }
|
|
5457
|
+
|
|
5458
|
+
WorkflowGroupNode:
|
|
5459
|
+
# 新(census 批2 四段):`WorkflowRun.groups` was undeclared entirely. Real shape = server's rebuilt nested
|
|
5460
|
+
# tree (workflows.ts:399-441, GroupNode) — recursive, acyclic (a dangling/cyclic parentGroupId is grafted
|
|
5461
|
+
# onto the root by the server, so this shape never actually cycles on the wire).
|
|
5462
|
+
type: object
|
|
5463
|
+
description: One node of the workflow's nested `ctx.workflow` group tree (rebuilt server-side from `parentGroupId`).
|
|
5464
|
+
required: [groupId, status, startedAt, agentCallKeys, children]
|
|
5465
|
+
additionalProperties: false
|
|
5466
|
+
properties:
|
|
5467
|
+
groupId: { type: string }
|
|
5468
|
+
parentGroupId: { type: string, description: 'absent = top-level (a child of the implicit root).' }
|
|
5469
|
+
status: { type: string, description: 'core WorkflowItemStatus (running/completed/failed).' }
|
|
5470
|
+
startedAt: { type: integer }
|
|
5471
|
+
endedAt: { type: integer }
|
|
5472
|
+
durationMs: { type: integer, description: 'endedAt − startedAt; absent until the group ends.' }
|
|
5473
|
+
agentCallKeys: { type: array, items: { type: string }, description: 'callKeys of the agents directly under this group.' }
|
|
5474
|
+
children: { type: array, items: { $ref: '#/components/schemas/WorkflowGroupNode' }, description: 'nested child groups (recursive).' }
|
|
5475
|
+
|
|
5476
|
+
WorkflowRunStats:
|
|
5477
|
+
# 新(census 批2 四段):此前 inline `stats` 只钉了 `tokens`/`nested.tokens` 两键;真形 = core
|
|
5478
|
+
# `WorkflowRunStats`(sema-core src/orchestration/workflow-types.ts:105-110)——own/nested 各自还有
|
|
5479
|
+
# `turns`/`costMicroUsd`,nested 另有 `tasks`。四键(own turns/costMicroUsd + nested 两键)此前两侧同缺。
|
|
5480
|
+
type: object
|
|
5481
|
+
description: 'Cumulative workflow usage. `own` (top-level) and `nested` (delegated sub-agents) are kept SEPARATE (R-5) — total spend = tokens + nested.tokens.'
|
|
5482
|
+
required: [tokens, turns, costMicroUsd, nested]
|
|
5483
|
+
additionalProperties: false
|
|
5484
|
+
properties:
|
|
5485
|
+
tokens: { type: integer }
|
|
5486
|
+
turns: { type: integer }
|
|
5487
|
+
costMicroUsd: { type: integer }
|
|
5488
|
+
nested:
|
|
4795
5489
|
type: object
|
|
4796
|
-
|
|
4797
|
-
additionalProperties:
|
|
5490
|
+
required: [tokens, turns, tasks, costMicroUsd]
|
|
5491
|
+
additionalProperties: false
|
|
4798
5492
|
properties:
|
|
4799
5493
|
tokens: { type: integer }
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
5494
|
+
turns: { type: integer }
|
|
5495
|
+
tasks: { type: integer }
|
|
5496
|
+
costMicroUsd: { type: integer }
|
|
5497
|
+
|
|
5498
|
+
WorkflowRun:
|
|
5499
|
+
# 🔴 census 批2 四段:CLOSED against the REAL wire projection — server's `summarizeWorkflowDetail`
|
|
5500
|
+
# (src/http/routes/workflows.ts:317-475), which is a DERIVED view over core's `WorkflowRun`, not the raw
|
|
5501
|
+
# record. This schema previously mirrored (a stale subset of) the core type; the actual wire adds
|
|
5502
|
+
# `durationMs`/`unphased`/`groups` and drops core's `sourceTaskId`/`originatingSessionId`/`effectiveArgs`/
|
|
5503
|
+
# `resultFull`/`completionId`/`resume`/`journalSkips` (never projected by this route — genuinely absent
|
|
5504
|
+
# from the wire, not a spec lie). `agentFailures`/`rev`/`error`/`result` WERE on the wire but undeclared
|
|
5505
|
+
# (two-side same-gap, this segment's fix).
|
|
5506
|
+
type: object
|
|
5507
|
+
description: 'GET /v1/workflows/:id full run detail (server-derived — Phases/agents/groups panels). Non-owner → 404 (no existence oracle).'
|
|
5508
|
+
required: [id, scope, status, stats, startedAt, createdAt, phases, agents, groups]
|
|
5509
|
+
additionalProperties: false
|
|
5510
|
+
properties:
|
|
5511
|
+
id: { type: string }
|
|
5512
|
+
scope: { type: string }
|
|
5513
|
+
name: { type: string, description: "The workflow script's declared `meta.name` (redacted)." }
|
|
5514
|
+
description: { type: string, description: "The workflow script's declared `meta.description` (redacted, one-liner)." }
|
|
5515
|
+
status: { $ref: '#/components/schemas/WorkflowRunStatus' }
|
|
5516
|
+
agentFailures: { type: integer, description: 'run-level count of agents whose status ended failed — stamped at run end, only when > 0.' }
|
|
5517
|
+
stats: { $ref: '#/components/schemas/WorkflowRunStats' }
|
|
4805
5518
|
startedAt: { type: integer }
|
|
4806
5519
|
endedAt: { type: integer }
|
|
4807
5520
|
createdAt: { type: integer }
|
|
5521
|
+
durationMs: { type: integer, description: 'endedAt − startedAt; absent while the run is still running.' }
|
|
5522
|
+
rev: { type: integer, description: 'store optimistic-concurrency revision; unset for a pure in-memory run.' }
|
|
5523
|
+
error: { type: string, description: 'set when status === "failed": the error the script threw.' }
|
|
5524
|
+
result: { type: string, description: 'the script''s RETURN VALUE (bounded + redacted). Absent on failed/pre-1.234 runs.' }
|
|
5525
|
+
phases:
|
|
5526
|
+
type: array
|
|
5527
|
+
description: 'Phase progress records.'
|
|
5528
|
+
items: { $ref: '#/components/schemas/WorkflowPhaseProgress' }
|
|
5529
|
+
unphased:
|
|
5530
|
+
type: object
|
|
5531
|
+
description: 'done/total of agents with no phase (or a stray phase matching no registered entry). Present only when non-empty.'
|
|
5532
|
+
required: [done, total]
|
|
5533
|
+
additionalProperties: false
|
|
5534
|
+
properties:
|
|
5535
|
+
done: { type: integer }
|
|
5536
|
+
total: { type: integer }
|
|
5537
|
+
agents:
|
|
5538
|
+
type: array
|
|
5539
|
+
description: 'Agent-run records.'
|
|
5540
|
+
items: { $ref: '#/components/schemas/WorkflowAgentRow' }
|
|
5541
|
+
groups:
|
|
5542
|
+
type: array
|
|
5543
|
+
description: 'The nested ctx.workflow group tree (roots only — children nest recursively). Empty when the script used no nesting.'
|
|
5544
|
+
items: { $ref: '#/components/schemas/WorkflowGroupNode' }
|
|
5545
|
+
|
|
5546
|
+
WorkflowJournalEntry:
|
|
5547
|
+
# 新(census 批2 四段):GET /v1/workflows/:id/journal row, the non-truncated arm (server `projectResult`,
|
|
5548
|
+
# workflows.ts:127-134) — one agent()-call's cached TaskResult, bounded + redacted.
|
|
5549
|
+
type: object
|
|
5550
|
+
description: One journal entry — a completed agent() call's projected TaskResult (bounded + redacted).
|
|
5551
|
+
required: [callKey, ordinal, status]
|
|
5552
|
+
additionalProperties: false
|
|
5553
|
+
properties:
|
|
5554
|
+
callKey: { type: string }
|
|
5555
|
+
ordinal: { type: integer, description: 'callKeyOrdinal(callKey) — the stable resume/display order.' }
|
|
5556
|
+
status: { type: string, description: 'the TaskResult''s terminal status (open vocabulary; core enum verbatim).' }
|
|
5557
|
+
error: { type: string, description: 'first line of errorMessage, clipped to 300 chars (redacted). Present only when the result carried an errorMessage.' }
|
|
5558
|
+
result: { type: string, description: 'the result text, clipped to 2000 chars (redacted). Present only when the result carried one.' }
|
|
5559
|
+
tokens: { type: integer, description: 'present only when the result carried stats.' }
|
|
5560
|
+
turns: { type: integer, description: 'present only when the result carried stats.' }
|
|
5561
|
+
|
|
5562
|
+
WorkflowJournalEntryTruncated:
|
|
5563
|
+
# 新(census 批2 四段):the truncated arm (workflows.ts:141-142/159-162) — an honest stub for a row whose
|
|
5564
|
+
# stored TaskResult exceeded the per-row 64KiB gate (SQL stores) or byte length (file/in-memory
|
|
5565
|
+
# fallback): the server never pulls the oversized payload into process memory.
|
|
5566
|
+
type: object
|
|
5567
|
+
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.
|
|
5568
|
+
required: [callKey, ordinal, truncated, resultBytes]
|
|
5569
|
+
additionalProperties: false
|
|
5570
|
+
properties:
|
|
5571
|
+
callKey: { type: string }
|
|
5572
|
+
ordinal: { type: integer }
|
|
5573
|
+
truncated: { type: boolean, enum: [true] }
|
|
5574
|
+
resultBytes: { type: integer, description: 'the stored TaskResult''s byte length (so a UI can at least show the size).' }
|
|
4808
5575
|
|
|
4809
5576
|
WorkflowStreamEvent:
|
|
4810
5577
|
type: object
|
|
@@ -4831,8 +5598,11 @@ components:
|
|
|
4831
5598
|
AttachmentInfo:
|
|
4832
5599
|
type: object
|
|
4833
5600
|
description: 'Upload receipt (POST /v1/attachments 201). `name` is the SANITIZED basename the server will materialize under `attachments/`.'
|
|
5601
|
+
# 🔴 census 批2 五段(2026-07-30):CLOSED against the real emitter — `sendJson(res, 201, { id, name,
|
|
5602
|
+
# mime, sha256, sizeBytes })` (server attachments.ts:90) is an EXACT 5-key object literal, never a
|
|
5603
|
+
# superset.
|
|
4834
5604
|
required: [id, name, mime, sha256, sizeBytes]
|
|
4835
|
-
additionalProperties:
|
|
5605
|
+
additionalProperties: false
|
|
4836
5606
|
properties:
|
|
4837
5607
|
id: { type: string }
|
|
4838
5608
|
name: { type: string }
|
|
@@ -5623,13 +6393,17 @@ components:
|
|
|
5623
6393
|
E9 (shell-host mcpStatus; core McpServerStatus core/mcp.ts:76) — one MCP server's status at materialization
|
|
5624
6394
|
time. `status` is connected | failed (core never emits disabled). `error` (failed only) is service-redacted
|
|
5625
6395
|
+ length-bounded.
|
|
6396
|
+
# 封闭三层(census 批2 第三段,2026-07-30):行铸造点 `routes/sessions.ts:157-165` 逐键条件展开这 5 键;
|
|
6397
|
+
# `serverInfo` 不是透传——core 自己投影成两键字面量(core dist mcp.js:986 `{name, version}`)。
|
|
5626
6398
|
required: [name, status]
|
|
5627
|
-
additionalProperties:
|
|
6399
|
+
additionalProperties: false
|
|
5628
6400
|
properties:
|
|
5629
6401
|
name: { type: string }
|
|
5630
6402
|
status: { type: string, description: "connected | failed (open set)" }
|
|
5631
6403
|
serverInfo:
|
|
5632
6404
|
type: object
|
|
6405
|
+
additionalProperties: false
|
|
6406
|
+
required: [name, version]
|
|
5633
6407
|
properties:
|
|
5634
6408
|
name: { type: string }
|
|
5635
6409
|
version: { type: string }
|
|
@@ -5641,8 +6415,10 @@ components:
|
|
|
5641
6415
|
description: >
|
|
5642
6416
|
`GET /v1/sessions/:id/mcp` envelope. `asOf` = THIS materialization moment (ISO). `degraded:true` (servers
|
|
5643
6417
|
empty) ⇒ materialize timed out/failed (NOT "no MCP"). No MCP configured ⇒ `servers:[]` without `degraded`.
|
|
6418
|
+
# 封闭(census 批2 第三段,2026-07-30):三个铸造臂(`routes/sessions.ts:134` 空面板 / `:152` 超时
|
|
6419
|
+
# degraded / `:155-166` 正常)键集并集恰是这 3 键;degraded 只在超时臂铸。
|
|
5644
6420
|
required: [asOf, servers]
|
|
5645
|
-
additionalProperties:
|
|
6421
|
+
additionalProperties: false
|
|
5646
6422
|
properties:
|
|
5647
6423
|
asOf: { type: string }
|
|
5648
6424
|
servers:
|
|
@@ -5669,6 +6445,9 @@ components:
|
|
|
5669
6445
|
description: >
|
|
5670
6446
|
One E19 file snapshot keyed by a `SessionTreeEntry.id`. `manifest` = `[relPath, blobHash]` tuples; the blob
|
|
5671
6447
|
BYTES are NOT inlined (they ride the content-addressed blob routes).
|
|
6448
|
+
# 封闭(census 批2 第三段,2026-07-30):server 侧类型+铸造点(`session-sync.ts:206` PULL 出 /
|
|
6449
|
+
# `routes/session-sync.ts:53` PUSH 校验形)都恰是这两键。
|
|
6450
|
+
additionalProperties: false
|
|
5672
6451
|
required: [key, manifest]
|
|
5673
6452
|
properties:
|
|
5674
6453
|
key: { type: string }
|
|
@@ -5684,6 +6463,8 @@ components:
|
|
|
5684
6463
|
SyncAnchor:
|
|
5685
6464
|
type: object
|
|
5686
6465
|
description: One E18 resume-at anchor. `owner` is RE-KEYED to the importing principal on a PUSH (§9).
|
|
6466
|
+
# 封闭(census 批2 第三段,2026-07-30):server 侧行类型 `session-sync.ts:52` 恰是这三键(owner 可 null 不可缺)。
|
|
6467
|
+
additionalProperties: false
|
|
5687
6468
|
required: [eventId, entryId, owner]
|
|
5688
6469
|
properties:
|
|
5689
6470
|
eventId: { type: string }
|
|
@@ -5693,6 +6474,9 @@ components:
|
|
|
5693
6474
|
SessionRulesRecord:
|
|
5694
6475
|
type: object
|
|
5695
6476
|
description: A (principal, rules) policy record — one row across ALL principals (E6 `listBySession`). Replayed verbatim on import (the cloud applies the tighten-only gate).
|
|
6477
|
+
# 封闭(census 批2 第三段,2026-07-30):core `SessionRulesRecord`(session-policy-store.d.ts:19)恰是
|
|
6478
|
+
# 这两键;`principal: undefined`(会话默认规则)在 JSON 里=整键省略。
|
|
6479
|
+
additionalProperties: false
|
|
5696
6480
|
required: [rules]
|
|
5697
6481
|
properties:
|
|
5698
6482
|
principal: { type: string, description: Absent = the session-default rules. }
|
|
@@ -5703,6 +6487,9 @@ components:
|
|
|
5703
6487
|
description: >
|
|
5704
6488
|
2c session-sync `GET …/sync/manifest` payload (wrapped server-side as `{ manifest }`). The THIN cross-backend
|
|
5705
6489
|
snapshot: entry IDS (oldest-first, NOT payloads) + per-snapshot relPath→blobHash + policy + anchors + leaf.
|
|
6490
|
+
# 封闭(census 批2 第三段,2026-07-30):铸造点 `session-sync.ts:226`(exportSessionManifest 尾部
|
|
6491
|
+
# 字面量)恰是这 7 键,全部无条件(leafId 缺席位=null 不省略)。
|
|
6492
|
+
additionalProperties: false
|
|
5706
6493
|
required: [sessionId, entryIds, entryCount, leafId, snapshots, policy, anchors]
|
|
5707
6494
|
properties:
|
|
5708
6495
|
sessionId: { type: string }
|
|
@@ -5732,24 +6519,31 @@ components:
|
|
|
5732
6519
|
§7 — how a SOURCE log relates to a DESTINATION log, decided over the entry-ID SETS. A discriminated union on
|
|
5733
6520
|
`relation`. `fresh`/`identical` carry nothing else; `fast_forward` the appended tail; `stale` the dst entries
|
|
5734
6521
|
the src lacks; `fork` the common ancestor + each side's exclusive ids.
|
|
6522
|
+
# 五臂全封闭(census 批2 第三段,2026-07-30):铸造点=分类器 `session-sync-kernel.ts:126-160`,
|
|
6523
|
+
# 五个 return 全是字面量,臂形与 core 导出的 `SyncRelation` 判别联合逐键一致。
|
|
5735
6524
|
oneOf:
|
|
5736
6525
|
- type: object
|
|
6526
|
+
additionalProperties: false
|
|
5737
6527
|
required: [relation]
|
|
5738
6528
|
properties: { relation: { const: fresh } }
|
|
5739
6529
|
- type: object
|
|
6530
|
+
additionalProperties: false
|
|
5740
6531
|
required: [relation]
|
|
5741
6532
|
properties: { relation: { const: identical } }
|
|
5742
6533
|
- type: object
|
|
6534
|
+
additionalProperties: false
|
|
5743
6535
|
required: [relation, newEntryIds]
|
|
5744
6536
|
properties:
|
|
5745
6537
|
relation: { const: fast_forward }
|
|
5746
6538
|
newEntryIds: { type: array, items: { type: string } }
|
|
5747
6539
|
- type: object
|
|
6540
|
+
additionalProperties: false
|
|
5748
6541
|
required: [relation, dstAheadBy]
|
|
5749
6542
|
properties:
|
|
5750
6543
|
relation: { const: stale }
|
|
5751
6544
|
dstAheadBy: { type: array, items: { type: string } }
|
|
5752
6545
|
- type: object
|
|
6546
|
+
additionalProperties: false
|
|
5753
6547
|
required: [relation, commonAncestor, srcExclusive, dstExclusive]
|
|
5754
6548
|
properties:
|
|
5755
6549
|
relation: { const: fork }
|
|
@@ -5770,6 +6564,9 @@ components:
|
|
|
5770
6564
|
description: >
|
|
5771
6565
|
`POST …/sync/import` Phase-A result. `identical` → no `stagingId` (skip Phase B); `fresh`/`fast_forward` →
|
|
5772
6566
|
`{ stagingId, relation }`. `relation` is the BARE classifier tag (not the full object).
|
|
6567
|
+
# 封闭(census 批2 第三段,2026-07-30):两个铸造臂——identical 臂 `routes/session-sync.ts:385-389`
|
|
6568
|
+
# (relation+basis+payloadVerified,无 stagingId)/ staged 臂 `:472`(stagingId+relation)。
|
|
6569
|
+
additionalProperties: false
|
|
5773
6570
|
required: [relation]
|
|
5774
6571
|
properties:
|
|
5775
6572
|
stagingId: { type: string, description: Present iff Phase B is needed (absent on `identical`). }
|
|
@@ -5777,24 +6574,32 @@ components:
|
|
|
5777
6574
|
# 🔴 server ≥1.277 —— 把分类**判据**写进响应,好让消费方不能把 200 `identical` 读成"内容已同步"。
|
|
5778
6575
|
basis:
|
|
5779
6576
|
type: string
|
|
5780
|
-
|
|
5781
|
-
|
|
6577
|
+
# 🔴 census 批2 第三段(2026-07-30)spec 谎修正:`entry-ids+digest` 不是"将来值"——1.277.0 落
|
|
6578
|
+
# digest 半场的**同一车**里铸造点就是 `basis: payloadVerified ? "entry-ids+digest" : "entry-ids"`
|
|
6579
|
+
# (routes/session-sync.ts:387),旧注却把它写成 x-open-enum 的假想例。补进闭集;x-open-enum 保留
|
|
6580
|
+
# (真正的将来值仍按未知值降级)。
|
|
6581
|
+
enum: [entry-ids, entry-ids+digest]
|
|
6582
|
+
x-open-enum: true
|
|
5782
6583
|
description: >
|
|
5783
|
-
The classification BASIS.
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
6584
|
+
The classification BASIS. `entry-ids`: §7 compared only the **entry id set** — entry ids are uuidv7,
|
|
6585
|
+
NOT content-addressed, so "same ids, different payload" is structurally possible and the classifier is
|
|
6586
|
+
blind to payload AND parent structure; on `identical` the entries are skipped ENTIRELY while the
|
|
6587
|
+
destination keeps its own content. `entry-ids+digest` (server ≥1.277): the caller ALSO supplied a
|
|
6588
|
+
comparable `logDigest` and it matched — payload equality was really verified.
|
|
5787
6589
|
payloadVerified:
|
|
5788
6590
|
type: boolean
|
|
5789
6591
|
description: >
|
|
5790
|
-
Whether the server verified PAYLOAD equality.
|
|
5791
|
-
|
|
6592
|
+
Whether the server verified PAYLOAD equality. `true` ⇔ `basis:"entry-ids+digest"` (the caller sent a
|
|
6593
|
+
comparable `logDigest` that matched); `false` = id-sets only — a consumer MUST distinguish "entry ids
|
|
6594
|
+
match (content NOT verified)" from "content is in sync" in its own wording.
|
|
5792
6595
|
⚠️ ABSENT ≠ verified: a server <1.277 omits both fields and its basis was id-sets all the same —
|
|
5793
6596
|
so test `payloadVerified !== true`, never `=== false`.
|
|
5794
6597
|
|
|
5795
6598
|
ImportCommitted:
|
|
5796
6599
|
type: object
|
|
5797
6600
|
description: '`POST …/sync/import/:stagingId/entries` (Phase B) commit result — the AUTHORITATIVE in-txn re-classify tag.'
|
|
6601
|
+
# 封闭(census 批2 第三段,2026-07-30):铸造点 `routes/session-sync.ts:608`(`{ relation: committed.relation }`)单键字面量。
|
|
6602
|
+
additionalProperties: false
|
|
5798
6603
|
required: [relation]
|
|
5799
6604
|
properties:
|
|
5800
6605
|
relation: { type: string, enum: [fresh, identical, fast_forward], description: The committed §7 tag. }
|
|
@@ -6859,6 +7664,24 @@ components:
|
|
|
6859
7664
|
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".'
|
|
6860
7665
|
detail: { type: string, description: 'Redacted supplement (e.g. why skipped). Additive / tolerate-absent.' }
|
|
6861
7666
|
|
|
7667
|
+
BakeStatus:
|
|
7668
|
+
# 新(census 批2 四段)——server `BakeStatus`(plugins/store-contracts.ts:83), the COARSE lifecycle
|
|
7669
|
+
# bakeView/claimResponse/bakesCreate all key on.
|
|
7670
|
+
type: string
|
|
7671
|
+
enum: [queued, running, done, failed]
|
|
7672
|
+
|
|
7673
|
+
BakeState:
|
|
7674
|
+
# 新(census 批2 四段)——server `BakeState`(store-contracts.ts:85-93), build.sh's finer 8-value state;
|
|
7675
|
+
# null before the runner's first `state` line lands.
|
|
7676
|
+
type: [string, 'null']
|
|
7677
|
+
enum: [PENDING, BUILDING, PUSHING, VERIFYING, REGISTERING, COMPLETE, FAILED, CANCELLED, null]
|
|
7678
|
+
|
|
7679
|
+
BakeErrorCode:
|
|
7680
|
+
# 新(census 批2 四段)——server `BakeErrorCode`(store-contracts.ts:96), the closed structured terminal
|
|
7681
|
+
# code set (§P2.14 #2); null = success or an uncategorized failure.
|
|
7682
|
+
type: [string, 'null']
|
|
7683
|
+
enum: [band-conflict, duplicate, disk, timeout, cancelled, null]
|
|
7684
|
+
|
|
6862
7685
|
BakeClaim:
|
|
6863
7686
|
type: object
|
|
6864
7687
|
description: >
|
|
@@ -6867,8 +7690,11 @@ components:
|
|
|
6867
7690
|
secret (minted at claim, echoed on every ingest via the x-bake-ingest-secret header) + the
|
|
6868
7691
|
profile/bands the runner needs for its profile-aware hard deadline. The runner executes `argv`
|
|
6869
7692
|
VERBATIM — it never assembles its own command line. 204 (no body) = queue empty.
|
|
6870
|
-
|
|
6871
|
-
|
|
7693
|
+
🔴 census 批2 四段:CLOSED + wired to both claim paths (this schema existed but neither path
|
|
7694
|
+
`$ref`'d it — path↔schema 脱钩; both declared a bare open `{}` instead). `logs`/`bands` are
|
|
7695
|
+
unconditionally assigned by claimResponse() but were absent from `required` — same-side gap.
|
|
7696
|
+
required: [bakeId, argv, push, dryRun, logs, profile, bands, ingestSecret, leaseUntil]
|
|
7697
|
+
additionalProperties: false
|
|
6872
7698
|
properties:
|
|
6873
7699
|
bakeId: { type: string }
|
|
6874
7700
|
argv:
|
|
@@ -6877,23 +7703,128 @@ components:
|
|
|
6877
7703
|
description: 'The server-assembled, whitelist-vetted build command — execute verbatim, never self-assemble.'
|
|
6878
7704
|
push: { type: boolean }
|
|
6879
7705
|
dryRun: { type: boolean }
|
|
6880
|
-
logs: { description: 'Log routing config, passed through as stored
|
|
7706
|
+
logs: { type: boolean, description: 'Log routing config, passed through as stored.' }
|
|
6881
7707
|
profile: { type: string }
|
|
6882
|
-
bands: { description: 'Profile bands for the runner''s profile-aware hard deadline (opaque passthrough).' }
|
|
7708
|
+
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.' }
|
|
6883
7709
|
ingestSecret: { type: string, description: 'Per-bake credential — the ONLY place it leaves image-api; echo it on every ingest.' }
|
|
6884
7710
|
leaseUntil:
|
|
6885
7711
|
description: 'Lease expiry (epoch ms or ISO timestamp depending on store backend).'
|
|
6886
7712
|
oneOf: [{ type: number }, { type: string }]
|
|
6887
7713
|
|
|
7714
|
+
BakeRecordView:
|
|
7715
|
+
# 新(census 批2 四段)——GET /v1/images/bakes/{bakeId}'s `{ bake }` envelope. Exact key set = server
|
|
7716
|
+
# `bakeView()` (images.ts:527-551): the durable BakeRecord MINUS the runner-internal secret/lease fields
|
|
7717
|
+
# (ingestSecret/runnerId/leaseUntil/cancelRequested/argv — an ops surface, not the claim credential).
|
|
7718
|
+
type: object
|
|
7719
|
+
description: The operator poll view of one bake (server bakeView projection over the durable BakeRecord).
|
|
7720
|
+
required:
|
|
7721
|
+
[bakeId, status, state, profile, bands, baseRef, push, dryRun, logs, digest, repo, ref, indexId,
|
|
7722
|
+
exitCode, error, errorCode, manifestSha, tag, requestedBy, createdAt, updatedAt]
|
|
7723
|
+
additionalProperties: false
|
|
7724
|
+
properties:
|
|
7725
|
+
bakeId: { type: string }
|
|
7726
|
+
status: { $ref: '#/components/schemas/BakeStatus' }
|
|
7727
|
+
state: { $ref: '#/components/schemas/BakeState' }
|
|
7728
|
+
profile: { type: string }
|
|
7729
|
+
bands: { type: [array, 'null'], items: { type: string } }
|
|
7730
|
+
baseRef: { type: [string, 'null'] }
|
|
7731
|
+
push: { type: boolean }
|
|
7732
|
+
dryRun: { type: boolean }
|
|
7733
|
+
logs: { type: boolean }
|
|
7734
|
+
digest: { type: [string, 'null'] }
|
|
7735
|
+
repo: { type: [string, 'null'] }
|
|
7736
|
+
ref: { type: [string, 'null'] }
|
|
7737
|
+
indexId: { type: [string, 'null'] }
|
|
7738
|
+
exitCode: { type: [integer, 'null'] }
|
|
7739
|
+
error: { type: [string, 'null'] }
|
|
7740
|
+
errorCode: { $ref: '#/components/schemas/BakeErrorCode' }
|
|
7741
|
+
manifestSha: { type: [string, 'null'] }
|
|
7742
|
+
tag: { type: [string, 'null'] }
|
|
7743
|
+
requestedBy: { type: [string, 'null'] }
|
|
7744
|
+
createdAt: { type: string, description: ISO date-time. }
|
|
7745
|
+
updatedAt: { type: string, description: ISO date-time. }
|
|
7746
|
+
|
|
7747
|
+
BakeSubmitAck:
|
|
7748
|
+
# 新(census 批2 四段)——POST /v1/images/bakes 202 body. All three code paths (attach-to-in-flight,
|
|
7749
|
+
# atomic-admission-created, dryRun) produce this SAME shape (images.ts:193/206/213/217).
|
|
7750
|
+
type: object
|
|
7751
|
+
description: Acknowledgement of a submitted/attached-to bake.
|
|
7752
|
+
required: [bakeId, eventsUrl, status, state]
|
|
7753
|
+
additionalProperties: false
|
|
7754
|
+
properties:
|
|
7755
|
+
bakeId: { type: string }
|
|
7756
|
+
eventsUrl: { type: string, description: '`/v1/images/bakes/{bakeId}/events` — the SSE resource to attach to.' }
|
|
7757
|
+
status: { $ref: '#/components/schemas/BakeStatus' }
|
|
7758
|
+
state: { $ref: '#/components/schemas/BakeState' }
|
|
7759
|
+
|
|
7760
|
+
BakeCancelAck:
|
|
7761
|
+
# 新(census 批2 四段)——POST /v1/images/bakes/{bakeId}/cancel 202 body (images.ts:239-241). `note` is
|
|
7762
|
+
# ALWAYS present (a ternary VALUE, not a conditional key) — the spec previously implied it was optional.
|
|
7763
|
+
type: object
|
|
7764
|
+
description: Cooperative-cancel acknowledgement (a durable flag; the runner kills the build at its next heartbeat).
|
|
7765
|
+
required: [bakeId, cancelRequested, note]
|
|
7766
|
+
additionalProperties: false
|
|
7767
|
+
properties:
|
|
7768
|
+
bakeId: { type: string }
|
|
7769
|
+
cancelRequested: { type: boolean, enum: [true] }
|
|
7770
|
+
note: { type: string, description: 'always one of two fixed strings: "cancel requested — …" or "bake already terminal — cancel is a no-op".' }
|
|
7771
|
+
|
|
7772
|
+
BakeIngestAck:
|
|
7773
|
+
# 新(census 批2 四段)——POST /v1/images/bakes/{bakeId}/ingest 200 body. 🔴 FOUR distinct shapes share
|
|
7774
|
+
# this one status code (images.ts:302-334): a heartbeat ack carries no `seq`; a non-terminal frame ack
|
|
7775
|
+
# carries `seq` but no `terminal`; a terminal `done` carries `terminal`+`indexId` (success) OR
|
|
7776
|
+
# `terminal`+`needsFlip` (the bounded-retry-exhausted non-throw path, still 200 — the runner re-POSTs the
|
|
7777
|
+
# idempotent done to re-drive the flip). `cancelRequested`+`leaseValid` ride on every non-heartbeat shape
|
|
7778
|
+
# (merged in at the send site); the heartbeat shape sets them directly instead.
|
|
7779
|
+
description: One ingest acknowledgement (four shapes keyed by which fields are present).
|
|
7780
|
+
oneOf:
|
|
7781
|
+
- type: object
|
|
7782
|
+
description: Heartbeat ack (lease renewed; no event appended).
|
|
7783
|
+
required: [accepted, leaseValid, cancelRequested]
|
|
7784
|
+
additionalProperties: false
|
|
7785
|
+
properties:
|
|
7786
|
+
accepted: { type: boolean, enum: [true] }
|
|
7787
|
+
leaseValid: { type: boolean }
|
|
7788
|
+
cancelRequested: { type: boolean }
|
|
7789
|
+
- type: object
|
|
7790
|
+
description: Non-terminal build.sh line appended (resolved/state/step/image/manifest/log).
|
|
7791
|
+
required: [accepted, seq, cancelRequested, leaseValid]
|
|
7792
|
+
additionalProperties: false
|
|
7793
|
+
properties:
|
|
7794
|
+
accepted: { type: boolean, enum: [true] }
|
|
7795
|
+
seq: { type: integer }
|
|
7796
|
+
cancelRequested: { type: boolean }
|
|
7797
|
+
leaseValid: { type: boolean, enum: [true] }
|
|
7798
|
+
- type: object
|
|
7799
|
+
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).
|
|
7800
|
+
required: [accepted, terminal, indexId, cancelRequested, leaseValid]
|
|
7801
|
+
additionalProperties: false
|
|
7802
|
+
properties:
|
|
7803
|
+
accepted: { type: boolean, enum: [true] }
|
|
7804
|
+
terminal: { type: boolean }
|
|
7805
|
+
indexId: { type: [string, 'null'] }
|
|
7806
|
+
cancelRequested: { type: boolean }
|
|
7807
|
+
leaseValid: { type: boolean, enum: [true] }
|
|
7808
|
+
- type: object
|
|
7809
|
+
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.
|
|
7810
|
+
required: [accepted, terminal, needsFlip, cancelRequested, leaseValid]
|
|
7811
|
+
additionalProperties: false
|
|
7812
|
+
properties:
|
|
7813
|
+
accepted: { type: boolean, enum: [true] }
|
|
7814
|
+
terminal: { type: boolean, enum: [false] }
|
|
7815
|
+
needsFlip: { type: boolean, enum: [true] }
|
|
7816
|
+
cancelRequested: { type: boolean }
|
|
7817
|
+
leaseValid: { type: boolean, enum: [true] }
|
|
7818
|
+
|
|
6888
7819
|
OutcomeRow:
|
|
6889
7820
|
type: object
|
|
6890
7821
|
description: >
|
|
6891
7822
|
One task-outcome ledger row of GET /v1/outcomes (envelope key `outcomes`; design/73 §7 read-only
|
|
6892
7823
|
base). Shape = the SQL ledgers' per-(taskSignature, model) summary projection (server
|
|
6893
|
-
|
|
6894
|
-
|
|
7824
|
+
outcome-ledger-sql.ts `summary()` — the only queryable sink; a File sink 501s honestly).
|
|
7825
|
+
# 🔴 census 批2 五段:CLOSED — the exact 5-key `.map()` projection, no sixth key.
|
|
6895
7826
|
required: [taskSignature, model, n, passRate, meanCostMicroUsd]
|
|
6896
|
-
additionalProperties:
|
|
7827
|
+
additionalProperties: false
|
|
6897
7828
|
properties:
|
|
6898
7829
|
taskSignature: { type: string }
|
|
6899
7830
|
model: { type: string }
|
|
@@ -6908,8 +7839,10 @@ components:
|
|
|
6908
7839
|
nextBefore?}`; server src/http/routes/sessions-list.ts — exact projection). A principal caller is PINNED to its
|
|
6909
7840
|
own scope (?scope= ignored); a fleet credential may pass ?scope= (default = the single-user
|
|
6910
7841
|
sentinel "_"). Page with `before` = a previous page's `nextBefore` (uuidv7; malformed → 400).
|
|
7842
|
+
# 🔴 census 批2 五段:CLOSED — exact projection (sessions-list.ts:94-105); taskId/sessionId are the
|
|
7843
|
+
# only OMIT-when-absent keys, no others.
|
|
6911
7844
|
required: [id, filename, size, ttlSec, track, bucket, key, createdAt]
|
|
6912
|
-
additionalProperties:
|
|
7845
|
+
additionalProperties: false
|
|
6913
7846
|
properties:
|
|
6914
7847
|
id: { type: string, description: 'uuidv7 link id — also the `before` pagination cursor.' }
|
|
6915
7848
|
filename: { type: string }
|
|
@@ -6946,8 +7879,11 @@ components:
|
|
|
6946
7879
|
TaskOutput-tool text (the child's final assistant body once terminal) — UNTRUSTED model output,
|
|
6947
7880
|
same posture as a run `result`. `output` = the engine registry's UnifiedTaskOutput projection
|
|
6948
7881
|
passed through VERBATIM.
|
|
7882
|
+
# 封闭(census 批2 第三段,2026-07-30):两个铸造点(`routes/runs.ts:934` 窄面 4 键 / `:1155` generic
|
|
7883
|
+
# 面 4 键 + 条件 `...g14`)顶层键集就是这 5 个。`output` 子对象**保持开**——它是 registry 投影的
|
|
7884
|
+
# verbatim 透传(deps 签名 `details: unknown`),不是 server 铸的形。
|
|
6949
7885
|
required: [taskId, target, content, output]
|
|
6950
|
-
additionalProperties:
|
|
7886
|
+
additionalProperties: false
|
|
6951
7887
|
properties:
|
|
6952
7888
|
taskId: { type: string }
|
|
6953
7889
|
target: { type: string }
|
|
@@ -6983,12 +7919,14 @@ components:
|
|
|
6983
7919
|
exact literal: `status:"running"`, `delivery:"accepted"`). ACCEPTANCE, not an outcome: compaction
|
|
6984
7920
|
runs at the next turn boundary and a `compacted{trigger:"manual"}` event rides the run stream ONLY
|
|
6985
7921
|
if anything was summarized (mooted/failed → no event).
|
|
6986
|
-
required:
|
|
6987
|
-
|
|
7922
|
+
# 封闭 + required 补铸造点(census 批2 第三段,2026-07-30):唯一铸造点 `routes/runs.ts:793` 是
|
|
7923
|
+
# 四键字面量,四键**全部无条件**——此前 required 只列 taskId,把恒在键写成了可缺。
|
|
7924
|
+
required: [taskId, status, delivery, note]
|
|
7925
|
+
additionalProperties: false
|
|
6988
7926
|
properties:
|
|
6989
7927
|
taskId: { type: string }
|
|
6990
7928
|
status: { type: string, description: 'Literal "running" on today''s server.' }
|
|
6991
|
-
delivery: { type: string,
|
|
7929
|
+
delivery: { type: string, enum: [accepted] }
|
|
6992
7930
|
note: { type: string, description: 'The "compaction will run at the next turn boundary…" copy.' }
|
|
6993
7931
|
|
|
6994
7932
|
DetachAck:
|
|
@@ -6998,12 +7936,14 @@ components:
|
|
|
6998
7936
|
`toolCallId` echoed, `delivery:"requested"`). Fire-and-forget: acceptance only — the
|
|
6999
7937
|
authoritative outcome is the tool call's own `tool_end.structured {detached:true, task_id}`;
|
|
7000
7938
|
a non-detachable env makes the request a fail-safe no-op.
|
|
7001
|
-
required:
|
|
7002
|
-
|
|
7939
|
+
# 封闭 + required 补铸造点(census 批2 第三段,2026-07-30):唯一铸造点 `routes/runs.ts:848` 是
|
|
7940
|
+
# 四键字面量,四键全部无条件(toolCallId=请求回显,body 校验过必在)。
|
|
7941
|
+
required: [taskId, toolCallId, delivery, note]
|
|
7942
|
+
additionalProperties: false
|
|
7003
7943
|
properties:
|
|
7004
7944
|
taskId: { type: string }
|
|
7005
7945
|
toolCallId: { type: string, description: 'Echoed from the request.' }
|
|
7006
|
-
delivery: { type: string,
|
|
7946
|
+
delivery: { type: string, enum: [requested] }
|
|
7007
7947
|
note: { type: string, description: 'The fail-safe no-op copy.' }
|
|
7008
7948
|
|
|
7009
7949
|
SideQueryRequest:
|
|
@@ -7087,8 +8027,10 @@ components:
|
|
|
7087
8027
|
The 200 ack of POST /v1/tool-approvals/{approvalId}/respond (server tool-approval.ts:521 — exact
|
|
7088
8028
|
shape, `decision` echoed). Owner-gated with a 404 and NO existence oracle: non-owner and
|
|
7089
8029
|
unknown/settled/expired/wrong-replica ids are indistinguishable.
|
|
8030
|
+
# 封闭(census 批2 第三段,2026-07-30):铸造点 `tool-approval.ts:521` 三恒在键 + 两条件键
|
|
8031
|
+
# (rememberApplied/updatedInputForwarded 的条件展开就在同一行字面量里),键集恰是这 5 个。
|
|
7090
8032
|
required: [approvalId, delivery, decision]
|
|
7091
|
-
additionalProperties:
|
|
8033
|
+
additionalProperties: false
|
|
7092
8034
|
properties:
|
|
7093
8035
|
approvalId: { type: string }
|
|
7094
8036
|
delivery: { type: string, enum: [applied] }
|
|
@@ -7139,7 +8081,7 @@ components:
|
|
|
7139
8081
|
costUsd: { type: number }
|
|
7140
8082
|
estimated:
|
|
7141
8083
|
type: boolean
|
|
7142
|
-
description: 'true =
|
|
8084
|
+
description: 'true = partly-estimated window, two axes: token view (rows lacking the per-model echo fell back to stats.tokens, counted into tokensOut) AND cost view (server >=3.6.0: unpriced rows carry NO costMicroUsd and fold as 0, so costUsd is a lower bound — render as ">= $X", not "~"). Honest flag, contract-required.'
|
|
7143
8085
|
|
|
7144
8086
|
UsageWindowBase:
|
|
7145
8087
|
type: object
|
|
@@ -7256,13 +8198,16 @@ components:
|
|
|
7256
8198
|
The 200 receipt of POST /v1/workflows/{runId}/agents/{label}/steer (server src/http/routes/workflows.ts —
|
|
7257
8199
|
exact literal: `status:"running"`, `delivery:"applied"`, `marker` = the engine-minted delivery
|
|
7258
8200
|
marker; NO `note`, unlike the run-subagent verb).
|
|
7259
|
-
|
|
7260
|
-
|
|
8201
|
+
# 🔴 census 批2 五段:CLOSED — all 5 keys are unconditional in the literal
|
|
8202
|
+
# `{ runId: wfRunId, label, status: "running", delivery: "applied", marker }` (workflows.ts:279);
|
|
8203
|
+
# this schema previously existed but no path `$ref`'d it (see the path fix above).
|
|
8204
|
+
required: [runId, label, status, delivery, marker]
|
|
8205
|
+
additionalProperties: false
|
|
7261
8206
|
properties:
|
|
7262
8207
|
runId: { type: string }
|
|
7263
8208
|
label: { type: string }
|
|
7264
|
-
status: { type: string, description: 'Literal "running" on today''s server.' }
|
|
7265
|
-
delivery: { type: string, description: 'Literal "applied" on today''s server.' }
|
|
8209
|
+
status: { type: string, const: running, description: 'Literal "running" on today''s server.' }
|
|
8210
|
+
delivery: { type: string, const: applied, description: 'Literal "applied" on today''s server.' }
|
|
7266
8211
|
marker: { type: string, description: 'Engine-minted delivery marker.' }
|
|
7267
8212
|
|
|
7268
8213
|
WorkspaceSnapshotRow:
|