@sema-agent/sdk 7.2.0-rc.1 → 7.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -0
- package/dist/errors.d.ts +25 -2
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +43 -2
- package/dist/errors.js.map +1 -1
- package/dist/events.d.ts +70 -4
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/resources/approvals.d.ts +17 -4
- package/dist/resources/approvals.d.ts.map +1 -1
- package/dist/resources/approvals.js +14 -1
- package/dist/resources/approvals.js.map +1 -1
- package/dist/resources/runs.d.ts +57 -0
- package/dist/resources/runs.d.ts.map +1 -1
- package/dist/resources/runs.js +62 -0
- package/dist/resources/runs.js.map +1 -1
- package/dist/resources/session-sync.d.ts +25 -3
- package/dist/resources/session-sync.d.ts.map +1 -1
- package/dist/resources/session-sync.js +35 -3
- package/dist/resources/session-sync.js.map +1 -1
- package/dist/resources/sessions.d.ts +10 -4
- package/dist/resources/sessions.d.ts.map +1 -1
- package/dist/resources/sessions.js.map +1 -1
- package/dist/resources/tool-approvals.d.ts +22 -1
- package/dist/resources/tool-approvals.d.ts.map +1 -1
- package/dist/resources/tool-approvals.js +3 -0
- package/dist/resources/tool-approvals.js.map +1 -1
- package/dist/resources/workspace.d.ts +7 -1
- package/dist/resources/workspace.d.ts.map +1 -1
- package/dist/resources/workspace.js.map +1 -1
- package/dist/types.d.ts +95 -6
- package/dist/types.d.ts.map +1 -1
- package/openapi.yaml +522 -67
- package/package.json +1 -1
package/openapi.yaml
CHANGED
|
@@ -487,10 +487,12 @@ paths:
|
|
|
487
487
|
# `steering.duplicate_input_id` 语义**完全依赖这个头** —— 不声明它,就是声明了一条谁也走不到的臂。
|
|
488
488
|
# ⚠️ 与 submit-class(`/v1/tasks`、`/v1/runs`)不同,这里的键**不是重试许可证**:steer 是
|
|
489
489
|
# AT-MOST-ONCE,SDK 不自动铸键也不因为有键就重试;它纯粹是服务端的去重令牌。
|
|
490
|
-
# ⚠️⚠️
|
|
491
|
-
#
|
|
492
|
-
# `live.steer(text, { trusted })
|
|
493
|
-
#
|
|
490
|
+
# ⚠️⚠️ **作用域(A-075.78 doc-rot 修,2026-08-30 亲读 server runs.ts live 腿实证)**:自 server 7.23.0
|
|
491
|
+
# (core 5.35.0 #257)起,派生的 `inputId` **两腿都传** —— parked 腿经 `setPendingSteer`(7.3.0 起就有),
|
|
492
|
+
# live 腿经 `live.steer(text, { trusted, inputId })`。此前这里写「live 腿不传 inputId,200 applied 腿
|
|
493
|
+
# 不去重」——那句自 7.23.0 起过期(SDK `resources/runs.ts` 的 SCOPE 段早已翻面,本注迟到)。两腿判重
|
|
494
|
+
# 窗口强度不同(parked=durable 行;live=流对象生命周期,不跨重启/副本/resume);对 7.19.0–7.22.0 的
|
|
495
|
+
# 老 server 旧限定仍逐字成立 —— 细节见下面 409 描述的 SCOPE 段。
|
|
494
496
|
- $ref: '#/components/parameters/IdempotencyKey'
|
|
495
497
|
post:
|
|
496
498
|
tags: [runs]
|
|
@@ -590,13 +592,19 @@ paths:
|
|
|
590
592
|
sent is already parked with DIFFERENT steering content (same key + same content is an idempotent
|
|
591
593
|
no-op and 202s). The engine refuses rather than silently picking a side. Reissue with a fresh
|
|
592
594
|
key — this is a new instruction, not a retry.
|
|
593
|
-
🔴 SCOPE (#193 车8
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
595
|
+
🔴 SCOPE (#193 车8 → corrected A-075.78, 2026-08-30, server live leg re-read): since server
|
|
596
|
+
7.23.0 (core 5.35.0 #257) the key dedupes BOTH legs — the parked path threads the derived
|
|
597
|
+
`inputId` into core's `setPendingSteer` (as it has since 7.3.0), and the live path threads the
|
|
598
|
+
SAME id into `TaskStream.steer(text, {trusted, inputId})`, so a lost 200 retried with the same
|
|
599
|
+
key is an idempotent no-op (core injects nothing, no second `human_input` frame) and same-key+
|
|
600
|
+
different-text 409s `steering.duplicate_input_id` on the live leg exactly like the parked one.
|
|
601
|
+
⚠️ The two dedup windows differ in STRENGTH (core contract, stated rather than papered over):
|
|
602
|
+
parked = DURABLE (the checkpoint row); live = the stream object's lifetime — one run leg in one
|
|
603
|
+
process, never spanning a restart/replica/resume; an id already delivered by the parked leg is
|
|
604
|
+
NOT in the live domain (cross-leg idempotency is the deployment's half).
|
|
605
|
+
⚠️ Against a server 7.19.0–7.22.0 the OLD caveat applies verbatim: only the parked legs dedupe —
|
|
606
|
+
a lost 200 retried with the same key injects TWICE with byte-identical receipts; treat a lost 200
|
|
607
|
+
there as "outcome unknown", never "safe to retry".
|
|
600
608
|
content:
|
|
601
609
|
application/json:
|
|
602
610
|
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
@@ -640,6 +648,127 @@ paths:
|
|
|
640
648
|
application/json:
|
|
641
649
|
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
642
650
|
|
|
651
|
+
/v1/runs/{taskId}/interrupt:
|
|
652
|
+
parameters:
|
|
653
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
654
|
+
- $ref: '#/components/parameters/TaskIdPath'
|
|
655
|
+
# server 收法与 steer 逐字同配方(scoped 口径 + sha256 定长 + `idem-` 前缀)收成 core `inputId`,
|
|
656
|
+
# 🔴 且 text 形 **202 回执的 `messageId` ≡ 这个 inputId** —— `task.turn_interrupted` 帧的
|
|
657
|
+
# `detail.inputId` 用同一个值,回执与帧对得上(并发两刀分得清哪刀是谁的)。与 steer 的缺席臂刻意
|
|
658
|
+
# 不同:本 op **无 key 也现铸 uuidv7 并下传**(每请求各是各的指令,零去重域,重试语义与旧形一致)。
|
|
659
|
+
# bare 形上本头是 no-op(没有输入帧可去重;halt 天然幂等,重复调用引擎自答 turnCut:false)。
|
|
660
|
+
# ⚠️ 键不是重试许可证(steer 同纪律):AT-MOST-ONCE,SDK 不自动铸也不因有键而重试。
|
|
661
|
+
- $ref: '#/components/parameters/IdempotencyKey'
|
|
662
|
+
post:
|
|
663
|
+
tags: [runs]
|
|
664
|
+
operationId: runsInterrupt
|
|
665
|
+
x-status: live # core #504 / server 7.52.0 F1 全弧(A-075.78 SDK 半场,2026-08-30)。
|
|
666
|
+
summary: Interrupt the in-flight TURN of a durable run — cut (+ optionally steer); bare form = halt.
|
|
667
|
+
description: >
|
|
668
|
+
core #504 — the TURN-level verb that run-level cancel is not: finished tools keep their REAL results,
|
|
669
|
+
un-started ones settle as interrupted pairs, in-flight tools are NOT collateral. TWO shapes,
|
|
670
|
+
discriminated by `text` presence.
|
|
671
|
+
WITH text = cut + steer: the in-flight turn is cut and the text goes to the FRONT of the queue — the
|
|
672
|
+
`"now"` tier is the VERB's semantics, not a body field (the steer op's `priority` stays advisory) —
|
|
673
|
+
and the run CONTINUES. 202 `{taskId, status, delivery:"queued-immediate", messageId, note}`;
|
|
674
|
+
`messageId` ≡ the engine inputId, and a real cut is proven ONLY by a `task.turn_interrupted` event on
|
|
675
|
+
the run stream whose `detail.inputId` matches it (core: no false interrupt claims — every real cut
|
|
676
|
+
emits one, an idle cut emits none and the text lands at the next turn boundary).
|
|
677
|
+
BARE form (body `{}` / empty / literal null) = halt (the CC Esc semantic, cut + STOP): the same cut,
|
|
678
|
+
but the run CLOSES at this human-made boundary as `status:"completed"` with
|
|
679
|
+
`TaskResult.haltedByUser:true`; the session continues normally (submit the next objective on the same
|
|
680
|
+
session). 202 `{taskId, status, turnCut, note}` — deliberately NO `delivery`/`messageId` (no input
|
|
681
|
+
frame exists to correlate; the frame's `detail.cause:"user_halt"` arm carries no inputId). A bare body
|
|
682
|
+
with EXTRA keys is a 400 — a typo'd `{txt:…}` must not silently become a verb that stops the run.
|
|
683
|
+
LIVE-ONLY, no park fallback (the root difference from steer — a turn is an in-process concept, a
|
|
684
|
+
parked run has no in-flight turn to cut). Authorization = the cancel door (owner-or-explicit-operator:
|
|
685
|
+
whoever may collaterally cancel the whole run holds the strictly smaller power to cut one turn).
|
|
686
|
+
requestBody:
|
|
687
|
+
required: false
|
|
688
|
+
content:
|
|
689
|
+
application/json:
|
|
690
|
+
schema:
|
|
691
|
+
type: object
|
|
692
|
+
# text 形对多余键宽容(冻结契约,steer 同族);bare 形零键纪律由 400 执法(见下)。
|
|
693
|
+
additionalProperties: true
|
|
694
|
+
properties:
|
|
695
|
+
text:
|
|
696
|
+
type: string
|
|
697
|
+
description: >-
|
|
698
|
+
Optional. Present ⇒ cut + steer (non-empty required; present-but-empty/non-string = 400).
|
|
699
|
+
Absent ⇒ bare halt — the body must then be EXACTLY `{}` (zero keys), empty, or null.
|
|
700
|
+
responses:
|
|
701
|
+
'202':
|
|
702
|
+
description: >-
|
|
703
|
+
Accepted — two shapes share this code; branch on which arm's keys are present (`delivery` vs
|
|
704
|
+
`turnCut`), NOT on the status code. See InterruptReceipt.
|
|
705
|
+
content:
|
|
706
|
+
application/json:
|
|
707
|
+
schema: { $ref: '#/components/schemas/InterruptReceipt' }
|
|
708
|
+
'400':
|
|
709
|
+
description: >-
|
|
710
|
+
errorCode "request.invalid_json" — the body did not parse as JSON;
|
|
711
|
+
errorCode "request.body_shape" — a scalar/array body, a bare body with EXTRA keys, or `text`
|
|
712
|
+
present but empty/non-string (the message is the literal accepted shape: `{}` for bare halt, or
|
|
713
|
+
`{ text: string (non-empty) }` for cut + steer). Malformed is NEVER silently folded into halt —
|
|
714
|
+
that would swap the caller's verb for one that stops the run.
|
|
715
|
+
content:
|
|
716
|
+
application/json:
|
|
717
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
718
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
719
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
720
|
+
'409':
|
|
721
|
+
description: >-
|
|
722
|
+
errorCode "interrupt.nothing_in_flight" — the run is PARKED on a pending decision: no in-flight
|
|
723
|
+
turn to cut, and deliberately NO park fallback — to deliver input to a parked run use
|
|
724
|
+
POST /v1/runs/{taskId}/steer (it parks onto the checkpoint and injects on resume).
|
|
725
|
+
errorCode "interrupt.not_held" — the run is running but THIS replica holds no live interruptible
|
|
726
|
+
turn face: live on ANOTHER replica (stated only when the row's instanceId PROVES it — the message
|
|
727
|
+
then says to resend there), or a verify/cascade execution lane which exposes no stream (those legs
|
|
728
|
+
return a result, not a live stream; the weakest-claim wording covers single-replica deployments).
|
|
729
|
+
Use POST /v1/runs/{taskId}/cancel for a run-level stop.
|
|
730
|
+
errorCode "steering.not_running" (reused verbatim) — terminal / not accepting, or the stream
|
|
731
|
+
settled in the race window ("run just finished on this replica — no in-flight turn to interrupt").
|
|
732
|
+
errorCode "steering.duplicate_input_id" — this Idempotency-Key was already accepted with DIFFERENT
|
|
733
|
+
content or at a DIFFERENT tier (core's replay identity includes the normalized tier: the same key
|
|
734
|
+
that once went through the steer door (default tier) then hits this door (now) is refused —
|
|
735
|
+
"idempotent success without the cut" would be a dispositive lie). Reissue with a fresh key.
|
|
736
|
+
content:
|
|
737
|
+
application/json:
|
|
738
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
739
|
+
'413':
|
|
740
|
+
description: >-
|
|
741
|
+
errorCode "request.payload_too_large" — the worker's global body cap (`MAX_BODY`, 8 MiB), raised
|
|
742
|
+
by the shared JSON reader (the steer op's 413 note applies verbatim; no separate text cap here).
|
|
743
|
+
content:
|
|
744
|
+
application/json:
|
|
745
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
746
|
+
'422':
|
|
747
|
+
description: >-
|
|
748
|
+
TWO codes, text form only (the bare form skips content validation — no text reaches the model,
|
|
749
|
+
and core's userPromptSubmit gate has no jurisdiction over a halt):
|
|
750
|
+
errorCode "steering.invalid_content" — the text carried a control-plane escape (e.g.
|
|
751
|
+
</system-reminder>); validated BEFORE the row lookup, and core re-rejects on the live leg.
|
|
752
|
+
errorCode "steering.blocked_by_hook" (S-51 / core 5.62 design/373 §4.3) — the deployment's
|
|
753
|
+
userPromptSubmit hook refused this input; block / hook timeout / hook crash are ONE code,
|
|
754
|
+
FAIL-CLOSED. The input was NOT accepted (no human_input frame, the inputId is not spent) — a
|
|
755
|
+
retry with different content is legitimate. The hook's own bounded reason is passed through
|
|
756
|
+
verbatim (first-hand triage material). SDK: falls on the SteeringError base via the `steering.`
|
|
757
|
+
prefix family (`.errorCode` carries the discriminator; same registration as
|
|
758
|
+
steering.parked_input_blocked).
|
|
759
|
+
content:
|
|
760
|
+
application/json:
|
|
761
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
762
|
+
'429': { $ref: '#/components/responses/RateLimited' } # rateLimited||quotaExceeded||leaseDenied — steer 同门(mutating + injects into a model run)
|
|
763
|
+
'501':
|
|
764
|
+
description: >-
|
|
765
|
+
errorCode "capability.run_store_required" — no durable run store wired
|
|
766
|
+
(DB_BACKEND=mysql|pg|local); the whole async-run family is off. Probe `capabilities.asyncRuns`,
|
|
767
|
+
don't trial-by-501.
|
|
768
|
+
content:
|
|
769
|
+
application/json:
|
|
770
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
771
|
+
|
|
643
772
|
/v1/runs/{taskId}/subagents/{target}/steer:
|
|
644
773
|
parameters:
|
|
645
774
|
- $ref: '#/components/parameters/PrincipalHeader'
|
|
@@ -1523,14 +1652,20 @@ paths:
|
|
|
1523
1652
|
schema:
|
|
1524
1653
|
type: object
|
|
1525
1654
|
additionalProperties: false
|
|
1526
|
-
|
|
1655
|
+
# #354 spec-drift fix(2026-08-27,亲读 server 真码):两处铸造点都恒带 `bindingEnforced:true`
|
|
1656
|
+
# (server.ts 受理点 `onAccepted` 与 driveResumeIntoRunLog 终局 return)——此前 additionalProperties:false
|
|
1657
|
+
# 把真响应判成超集;并且 [3833] S-1 起 wake 腿恒声明受理语义(notify-wake.ts 调 resumeWake 第五参=true):
|
|
1658
|
+
# durable 行在场时 200 是**受理形** `{taskId, sessionId, status:"resuming", bindingEnforced}`,腿在后台
|
|
1659
|
+
# 跑到停点(终态跟 GET /v1/runs/:id / events tail);无 durable 行的部署保持同步形(status=终局枚举)。
|
|
1660
|
+
required: [sessionId, status, bindingEnforced]
|
|
1527
1661
|
properties:
|
|
1528
1662
|
taskId: { type: string, description: 'The active run id; OMITTED when no active row.' }
|
|
1529
1663
|
sessionId: { type: string }
|
|
1530
|
-
status: { type: string, description: '
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1664
|
+
status: { type: string, description: 'ACCEPTANCE form (durable row present, [3833] S-1): "resuming" — the leg keeps running server-side; follow GET /v1/runs/:id or the events tail for the outcome. SYNCHRONOUS form (no durable run row): the run''s resulting status (completed/failed/suspended/needs_review/blocked — core TaskStatus verbatim; "timeout" retired in core 5.8.0: walltime exhaustion now lands as failed + errorCode limits.max_walltime_exceeded).' }
|
|
1665
|
+
bindingEnforced: { type: boolean, description: '[2400] HITL-12 generation probe — this server enforces decision-action binding. Always true (present on BOTH 200 forms).' }
|
|
1666
|
+
errorCode: { type: string, description: 'Failure code when the resumed leg failed (e.g. `cancelled`). Synchronous form only — never on the acceptance form.' }
|
|
1667
|
+
errorMessage: { type: string, description: 'Human-readable failure detail (server-side secret-redacted). Synchronous form only.' }
|
|
1668
|
+
retriable: { type: boolean, description: 'true = the park is STILL pending (re-parked); refetch and retry. Synchronous form only.' }
|
|
1534
1669
|
'400': { $ref: '#/components/responses/BadRequest' }
|
|
1535
1670
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
1536
1671
|
'404': { description: 'No parked checkpoint for this session (nothing to wake), or session not visible to the caller.' }
|
|
@@ -1549,14 +1684,21 @@ paths:
|
|
|
1549
1684
|
get:
|
|
1550
1685
|
tags: [workspace]
|
|
1551
1686
|
operationId: workspaceList
|
|
1552
|
-
x-status:
|
|
1553
|
-
summary:
|
|
1554
|
-
description: >
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1687
|
+
x-status: retired # S-15/design/381 (server 7.52.0): whole-tree snapshot epoch retired — every ≥7.52 deployment answers 501 capability.workspace_retired.
|
|
1688
|
+
summary: 'RETIRED (server 7.52.0, S-15) — snapshot listing; ≥7.52 answers 501 capability.workspace_retired. Live on 1.299–7.51 only.'
|
|
1689
|
+
description: >
|
|
1690
|
+
🔴 RETIRED as of `@sema-agent/server` 7.52.0 (S-15/design/381): the E19 whole-tree snapshot store this
|
|
1691
|
+
face projected was replaced by per-edited-file rewind HISTORY, which holds only files the agent edited
|
|
1692
|
+
— structurally there is no "whole workspace" manifest to browse anymore. The route SHAPE is kept
|
|
1693
|
+
server-side as a tombstone: every ≥7.52 deployment answers 501 `capability.workspace_retired` (a named
|
|
1694
|
+
refusal, not a 404 that reads like a typo), and `capabilities.workspace` is always false (same
|
|
1695
|
+
predicate). There is NO 1:1 replacement path — a future "rewind coverage" browse would be a NEW face
|
|
1696
|
+
with tracked-set semantics, not this name wired back. Related live faces: rewind restore rides
|
|
1697
|
+
`TaskRequest.restoreFiles` (probe `capabilities.restoreFiles`); per-file history blobs ride
|
|
1698
|
+
`GET /v1/sessions/{sessionId}/sync/history/blobs/{hash}`.
|
|
1699
|
+
Historical shape (servers 1.299–7.51): read-only projection over the E19 snapshot store. Rows carry
|
|
1700
|
+
`{key, files, bytes?}`; `latest` = the newest snapshot key. Owner-scoped (non-owner → 404, no
|
|
1701
|
+
oracle); 501 without the snapshot-store read faces (probe `capabilities.workspace`).
|
|
1560
1702
|
parameters:
|
|
1561
1703
|
- in: query
|
|
1562
1704
|
name: limit
|
|
@@ -1591,12 +1733,14 @@ paths:
|
|
|
1591
1733
|
get:
|
|
1592
1734
|
tags: [workspace]
|
|
1593
1735
|
operationId: workspaceTree
|
|
1594
|
-
x-status:
|
|
1595
|
-
summary:
|
|
1596
|
-
description: >
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
`
|
|
1736
|
+
x-status: retired # S-15 (server 7.52.0): retired with the family — see workspaceList's retirement note.
|
|
1737
|
+
summary: 'RETIRED (server 7.52.0, S-15) — snapshot file tree; ≥7.52 answers 501 capability.workspace_retired.'
|
|
1738
|
+
description: >
|
|
1739
|
+
🔴 RETIRED as of server 7.52.0 — same retirement (and same replacement pointers) as `workspaceList`
|
|
1740
|
+
above: every ≥7.52 deployment answers 501 `capability.workspace_retired`; `capabilities.workspace` is
|
|
1741
|
+
always false. Historical shape (1.299–7.51): entries carry `{path, hash, size?}` — `hash` is the
|
|
1742
|
+
sha256 content-address (cross-snapshot change highlighting, [1894]②); `size` only when the SQL size
|
|
1743
|
+
index is available. `limit` 1..2000 default 500, `offset` cursor; `nextOffset` present = more pages.
|
|
1600
1744
|
parameters:
|
|
1601
1745
|
- in: query
|
|
1602
1746
|
name: limit
|
|
@@ -1633,16 +1777,19 @@ paths:
|
|
|
1633
1777
|
get:
|
|
1634
1778
|
tags: [workspace]
|
|
1635
1779
|
operationId: workspaceFile
|
|
1636
|
-
x-status:
|
|
1637
|
-
summary:
|
|
1638
|
-
description: >
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1780
|
+
x-status: retired # S-15 (server 7.52.0): retired with the family — ≥7.52 the tombstone rejects BEFORE any size check, so the 413 envelope below is unreachable there.
|
|
1781
|
+
summary: 'RETIRED (server 7.52.0, S-15) — single-file read; ≥7.52 answers 501 capability.workspace_retired.'
|
|
1782
|
+
description: >
|
|
1783
|
+
🔴 RETIRED as of server 7.52.0 — same retirement (and replacement pointers) as `workspaceList` above.
|
|
1784
|
+
On ≥7.52 the tombstone 501 fires BEFORE any size check, so the 413 `workspace_file_too_large`
|
|
1785
|
+
envelope below is UNREACHABLE there (the SDK keeps its typed WorkspaceFileTooLargeError for
|
|
1786
|
+
1.299–7.51 servers still inside the support range). Historical shape (1.299–7.51): raw bytes with a
|
|
1787
|
+
CONSERVATIVE content-type table (html/svg etc. deliberately serve as octet-stream — agent output is
|
|
1788
|
+
untrusted; `x-content-type-options: nosniff` always set) + the `x-sema-content-binary` discriminator
|
|
1789
|
+
header (first-8KiB NUL sniff, [1894]③). Capped at `capabilities.workspace.maxFileBytes` (default
|
|
1790
|
+
8MiB, WORKSPACE_FILE_MAX_BYTES) — over-cap ⇒ 413 STRUCTURED envelope
|
|
1791
|
+
`{errorCode: "workspace_file_too_large", sizeBytes, limit}` ([1894]①; archive was the escape hatch).
|
|
1792
|
+
With the SQL size index the 413 fired BEFORE fetching the bytes.
|
|
1646
1793
|
parameters:
|
|
1647
1794
|
- in: query
|
|
1648
1795
|
name: path
|
|
@@ -1686,15 +1833,16 @@ paths:
|
|
|
1686
1833
|
get:
|
|
1687
1834
|
tags: [workspace]
|
|
1688
1835
|
operationId: workspaceArchive
|
|
1689
|
-
x-status:
|
|
1690
|
-
summary:
|
|
1691
|
-
description: >
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1836
|
+
x-status: retired # S-15 (server 7.52.0): retired with the family — see workspaceList's retirement note.
|
|
1837
|
+
summary: 'RETIRED (server 7.52.0, S-15) — whole-snapshot tar; ≥7.52 answers 501 capability.workspace_retired.'
|
|
1838
|
+
description: >
|
|
1839
|
+
🔴 RETIRED as of server 7.52.0 — same retirement (and replacement pointers) as `workspaceList` above:
|
|
1840
|
+
every ≥7.52 deployment answers 501 `capability.workspace_retired`. Historical shape (1.299–7.51):
|
|
1841
|
+
streamed ustar (`content-disposition: attachment`), entries in stable path order, blob-by-blob with
|
|
1842
|
+
backpressure — server memory stays O(1 file). Pre-flight checks BEFORE the headers went out: paths
|
|
1843
|
+
not representable in ustar ⇒ 422 naming the first offenders; missing blobs (raced a reap / partial
|
|
1844
|
+
import) ⇒ 502 (retryable). A blob vanishing MID-stream destroyed the connection (honest truncation)
|
|
1845
|
+
rather than faking a complete archive.
|
|
1698
1846
|
responses:
|
|
1699
1847
|
'200':
|
|
1700
1848
|
description: The tar stream.
|
|
@@ -1916,12 +2064,18 @@ paths:
|
|
|
1916
2064
|
get:
|
|
1917
2065
|
tags: [sessions]
|
|
1918
2066
|
operationId: sessionSyncGetBlob
|
|
1919
|
-
x-status:
|
|
1920
|
-
summary:
|
|
1921
|
-
description: >
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
2067
|
+
x-status: retired # S-15 (server 7.52.0): the E19-era snapshot-scoped PULL arm was REPLACED by GET …/sync/history/blobs/{hash} — ≥7.52 this path 404s (not_found.route).
|
|
2068
|
+
summary: 'RETIRED (server 7.52.0, S-15) — snapshot-scoped blob PULL; use GET …/sync/history/blobs/{hash}. Live on ≤7.51 only.'
|
|
2069
|
+
description: >
|
|
2070
|
+
🔴 RETIRED as of `@sema-agent/server` 7.52.0 (S-15/design/381): a blob is scoped by the session's
|
|
2071
|
+
HISTORY GRAPH now, not by a per-snapshot manifest key, so this arm was replaced by
|
|
2072
|
+
`GET /v1/sessions/{sessionId}/sync/history/blobs/{hash}` (sessionSyncGetHistoryBlob — same
|
|
2073
|
+
octet-stream body, same no-oracle 404 posture, scope check = "referenced by this session's
|
|
2074
|
+
exportHistory envelope"). On ≥7.52 this path no longer matches the sync route family and answers 404
|
|
2075
|
+
`not_found.route`. SDK: `sessions.sync.getHistoryBlob` (the legacy `getBlob` verb stays, deprecated,
|
|
2076
|
+
for ≤7.51 servers inside the support range).
|
|
2077
|
+
Historical shape (≤7.51): one content-addressed blob's RAW bytes; the cloud served a hash ONLY if
|
|
2078
|
+
THIS session's `:key` manifest referenced it (foreign/unreferenced → 404 `blob not found`). 400 on a
|
|
1925
2079
|
non-64-hex hash or an undecodable key/hash. Owner-scoped (404).
|
|
1926
2080
|
responses:
|
|
1927
2081
|
'200':
|
|
@@ -1934,6 +2088,43 @@ paths:
|
|
|
1934
2088
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
1935
2089
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
1936
2090
|
|
|
2091
|
+
/v1/sessions/{sessionId}/sync/history/blobs/{hash}:
|
|
2092
|
+
parameters:
|
|
2093
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
2094
|
+
- in: path
|
|
2095
|
+
name: sessionId
|
|
2096
|
+
required: true
|
|
2097
|
+
schema: { type: string }
|
|
2098
|
+
- in: path
|
|
2099
|
+
name: hash
|
|
2100
|
+
required: true
|
|
2101
|
+
schema: { type: string, pattern: '^[0-9a-f]{64}$' }
|
|
2102
|
+
description: The content-address (SHA-256, 64 lowercase-hex).
|
|
2103
|
+
get:
|
|
2104
|
+
tags: [sessions]
|
|
2105
|
+
operationId: sessionSyncGetHistoryBlob
|
|
2106
|
+
x-status: live # S-15/design/381 (server 7.52.0): the history-graph-scoped successor of the retired snapshots/{key}/blobs PULL arm.
|
|
2107
|
+
summary: PULL one content-addressed blob's raw bytes (history-graph-scoped; server ≥7.52).
|
|
2108
|
+
description: >
|
|
2109
|
+
2c session-sync PULL, S-15 epoch — one content-addressed blob's RAW bytes
|
|
2110
|
+
(`application/octet-stream`). 🔴 SCOPE BOUNDARY (the security boundary — a content-addressed blob is
|
|
2111
|
+
shared across sessions): the cloud serves a hash ONLY if THIS session's HISTORY GRAPH references it
|
|
2112
|
+
(some tracked file version's `blobHash` in its exportHistory envelope). A foreign / unreferenced hash
|
|
2113
|
+
→ 404 `not_found.blob`, never a blind read; a server whose file-history store has no blob faces
|
|
2114
|
+
(e.g. the local file backend) answers the SAME 404 shape (no oracle — "nothing servable" and "not
|
|
2115
|
+
referenced" are deliberately indistinguishable). 400 `request.id_invalid` on a non-64-hex hash.
|
|
2116
|
+
Owner-scoped (404). Replaces the retired `GET …/sync/snapshots/{key}/blobs/{hash}`.
|
|
2117
|
+
responses:
|
|
2118
|
+
'200':
|
|
2119
|
+
description: The blob bytes.
|
|
2120
|
+
content:
|
|
2121
|
+
application/octet-stream:
|
|
2122
|
+
schema: { type: string, format: binary }
|
|
2123
|
+
'400': { $ref: '#/components/responses/BadRequest' }
|
|
2124
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
2125
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
2126
|
+
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
2127
|
+
|
|
1937
2128
|
/v1/sessions/{sessionId}/sync/blobs/{hash}:
|
|
1938
2129
|
parameters:
|
|
1939
2130
|
- $ref: '#/components/parameters/PrincipalHeader'
|
|
@@ -1955,7 +2146,12 @@ paths:
|
|
|
1955
2146
|
body). The cloud verifies `sha256(body) === :hash` (the content-address integrity guard) BEFORE storing — a
|
|
1956
2147
|
mismatch → 400 `blob hash mismatch`. own-or-FRESH gate. Per-blob cap 64 MiB → 413 mid-stream beyond it. A
|
|
1957
2148
|
backend write failure (e.g. a MinIO non-2xx) → 502 `blob store write failed`. 204 on success (no body). The
|
|
1958
|
-
PUSH path is the BARE `blobs/:hash` (the PULL is
|
|
2149
|
+
PUSH path is the BARE `blobs/:hash` (the PULL is `history/blobs/:hash` since server 7.52,
|
|
2150
|
+
`snapshots/:key/blobs/:hash` before). 🔴 server ≥7.52 (S-15 换店): the blob faces ride the
|
|
2151
|
+
file-HISTORY store — a deployment whose store has no blob faces (the local file backend) answers 501
|
|
2152
|
+
`capability.snapshot_store_required` ("blob upload requires a durable file-history store") while the
|
|
2153
|
+
rest of the sync surface keeps working; on ≤7.51 the same code meant a missing snapshot store. Same
|
|
2154
|
+
code, same operator disposition (wire a backend with blob faces).
|
|
1959
2155
|
requestBody:
|
|
1960
2156
|
required: true
|
|
1961
2157
|
content:
|
|
@@ -2028,6 +2224,14 @@ paths:
|
|
|
2028
2224
|
2c session-sync PUSH Phase A — a SMALL metadata body (`entryIds, snapshots, policy, anchors, resolution?`); the
|
|
2029
2225
|
entries stream in Phase B. The cloud classifies (§7), pre-checks blob presence (a missing referenced hash →
|
|
2030
2226
|
422 `missing_blob` — PUT it first), guards the import lease + any active run (409), then mints a stagingId.
|
|
2227
|
+
⚠️ S-15 EPOCH DEBT (registered 2026-08-30, codex adversarial review; migration is a SEPARATE batch):
|
|
2228
|
+
server ≥7.52's Phase A actually consumes `{entryIds, fileHistory?, policy, anchors, resolution?,
|
|
2229
|
+
logDigest?}` (routes/session-sync.ts) — the `snapshots` key below is SILENTLY IGNORED there (an
|
|
2230
|
+
unknown body key), so on ≥7.52 a push lands entries/policy/anchors and the session itself but NO
|
|
2231
|
+
file snapshots/history; the snapshot-epoch shape below is what ≤7.51 servers consume. The full
|
|
2232
|
+
migration (SessionManifest/SessionBundle/push-pull orchestration + `fileHistory: FileHistoryExport |
|
|
2233
|
+
null` + epoch negotiation) is tracked as its own debt — this op's schema deliberately stays the
|
|
2234
|
+
≤7.51 truth until that batch lands rather than half-changing.
|
|
2031
2235
|
`identical` → `{ relation:"identical", basis, payloadVerified }` with NO stagingId (skip Phase B);
|
|
2032
2236
|
`basis` is `"entry-ids+digest"` when the caller supplied a comparable digest and it matched, else
|
|
2033
2237
|
`"entry-ids"` (id-set equality only) — see the `ImportStaged` schema, which has carried these two
|
|
@@ -2282,6 +2486,23 @@ paths:
|
|
|
2282
2486
|
already terminal (someone resolved it / abandonment-TTL GC / SLA resolve-deny). Client action:
|
|
2283
2487
|
refetch the inbox; this pending is gone. SDK → ApprovalStaleError. Note the bare CAS 409 has NO
|
|
2284
2488
|
`errorCode`, which is exactly how the three are told apart.
|
|
2489
|
+
S-02 (server ≥7.52, A-075.94③): the stale body may carry the ADDITIVE pointer key
|
|
2490
|
+
`currentPending` ($ref ApprovalStaleCurrentPending — the SESSION'S current pending's D-1
|
|
2491
|
+
coordinates `{toolName, boundCallId, boundInputHash?}`, same names/shapes as the §7b queue row) so
|
|
2492
|
+
a shell relocates and re-decides in ONE hop instead of refetching the whole queue. NEVER carries a
|
|
2493
|
+
checkpointToken. Absent when there is no pending / the gate is not a tool approval / the row read
|
|
2494
|
+
failed (loud fail-open ledger arm) / an older server. Owner domain = the route's owner gate (no
|
|
2495
|
+
new disclosure surface). SDK: `ApprovalStaleError.currentPending`, whole-or-absent guard.
|
|
2496
|
+
REACHABILITY (codex 复审 R1-[medium] 采纳成文): the stale arm fires only for callers that ECHO a
|
|
2497
|
+
`checkpointToken`; this SDK's ApprovalDecision deliberately has no such field (removed in 1.0.0 —
|
|
2498
|
+
resume credentials never ride the compliant decide), so decides issued through this SDK never
|
|
2499
|
+
trigger it. The key is documented for the wire contract's sake: legacy shells and non-SDK callers
|
|
2500
|
+
that still echo tokens DO reach it, and the SDK's read side stays typed for whatever arrives.
|
|
2501
|
+
ALSO an S-02 behavior NARROWING on this arm: an echoed `checkpointToken` that does NOT belong to
|
|
2502
|
+
this session, when the session has NO pending, now folds into the byte-identical 404
|
|
2503
|
+
`not_found.approval` (it used to 409 — a single-bit cross-tenant "who is parked on an approval"
|
|
2504
|
+
oracle); a legitimately-owned old token (a shell retrying its own already-decided row) keeps the
|
|
2505
|
+
attributable 409 + `terminal:"resolved"` verbatim.
|
|
2285
2506
|
content:
|
|
2286
2507
|
application/json:
|
|
2287
2508
|
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
@@ -5024,6 +5245,27 @@ components:
|
|
|
5024
5245
|
CC Rewind "code"-only (core 1.166.0 TaskSpec.rewindFilesTo). Restore the working tree to this user-message
|
|
5025
5246
|
entryId's snapshot WITHOUT forking the conversation (no setLeafId). Use instead of resumeAt+rewindFiles when
|
|
5026
5247
|
the user wants only files reverted. Target = a user-message SessionTreeEntry.id.
|
|
5248
|
+
restoreFiles:
|
|
5249
|
+
type: boolean
|
|
5250
|
+
description: >-
|
|
5251
|
+
S-15 epoch request key (design/381; server ≥7.52 validates boolean and passes through to core —
|
|
5252
|
+
codex review R1-[medium]: `capabilities.restoreFiles` told clients to send this key while neither
|
|
5253
|
+
this schema nor the SDK type could express it). With `resumeAt`: restore the TRACKED SET (files
|
|
5254
|
+
this session edited) to that boundary on fork — S-15 narrowed restore whole-tree ⇒ tracked-set and
|
|
5255
|
+
moved the request key off `rewindFiles` (the old `rewindFiles`+`resumeAt` spelling gets core's
|
|
5256
|
+
typed migration rejection on ≥7.52). Probe `capabilities.restoreFiles` first: bit absent = older
|
|
5257
|
+
server, send the old spelling; never send both spellings.
|
|
5258
|
+
acceptPartialRestore:
|
|
5259
|
+
type: boolean
|
|
5260
|
+
description: >-
|
|
5261
|
+
S-15 companion key (design/381 DV-15; server ≥7.52 validates boolean and passes through —
|
|
5262
|
+
semantics owned by core's restore contract, dist typings verbatim): partial-restore consent.
|
|
5263
|
+
Absent/false = when the convergence ends with ≥1 per-file refusal/failure the run fails loud
|
|
5264
|
+
with the terminal `rewind.restore_failed` carrying the per-file ledger; true = the run keeps
|
|
5265
|
+
going and the same ledger is disclosed as a `restore_partial` note on `TaskResult.rewindNotes`.
|
|
5266
|
+
🔴 Applies to BOTH restore legs (codex R2 correction): `resumeAt`+`restoreFiles` AND the
|
|
5267
|
+
code-only `rewindFilesTo` — a code-only caller omitting it gets the loud terminal too. Recovery
|
|
5268
|
+
either way: re-run the same restore (per-file idempotent, converges).
|
|
5027
5269
|
permissionMode:
|
|
5028
5270
|
type: string
|
|
5029
5271
|
enum: [default, plan, acceptEdits, bypassPermissions, auto]
|
|
@@ -5370,6 +5612,14 @@ components:
|
|
|
5370
5612
|
description: >
|
|
5371
5613
|
The gate kind that produced `checkpointToken` when the run suspended. Deliberately UNTYPED here —
|
|
5372
5614
|
the shape is core's and still evolving; treat it as opaque and branch on `status`/`errorCode` instead.
|
|
5615
|
+
toolCallId:
|
|
5616
|
+
type: string
|
|
5617
|
+
description: >
|
|
5618
|
+
[4913] (server 7.41+) — the PENDING tool call's id when this result is a durable park
|
|
5619
|
+
(`status` suspended/needs_review with a `tool_approval` pending action); same key and meaning as the
|
|
5620
|
+
`tool_approval` frame's `toolCallId` (server >= 1.307). ABSENT (key omitted, never null) on a
|
|
5621
|
+
tool-less park (resource_limit / plan_review / task_done), on every terminal result, and when the
|
|
5622
|
+
server-side checkpoint read failed — consumers test presence.
|
|
5373
5623
|
degraded:
|
|
5374
5624
|
type: object
|
|
5375
5625
|
description: >
|
|
@@ -6084,9 +6334,29 @@ components:
|
|
|
6084
6334
|
the store but no membership authority deliberately reports false (and does not mount the routes).
|
|
6085
6335
|
workflows: { type: boolean, description: "ENGINE-CAN: this worker's engine can orchestrate S8 self-orchestration workflows (server: `workflowsCapable ?? Boolean(workflowRunStore)`). The MOUNTING of the durable /v1/workflows read routes is the separate `workflowsList` bit below; the two coincide in today's wiring but are declared as orthogonal axes." }
|
|
6086
6336
|
workflowsList: { type: boolean, description: "`GET /v1/workflows` list face (separate from the per-run reads)." }
|
|
6337
|
+
workflowModels:
|
|
6338
|
+
type: array
|
|
6339
|
+
items: { type: string }
|
|
6340
|
+
description: >-
|
|
6341
|
+
[5673]③/[5697] (server ≥7.52): the model VOCABULARY for workflow-script `agent({modelName})` —
|
|
6342
|
+
teaching face = pre-dispatch validation face, single-sourced (`workflowModelAllowlistFor`: an
|
|
6343
|
+
explicit SELF_ORCHESTRATION_MODELS always wins / single-user = every catalog key / multi-tenant
|
|
6344
|
+
fail-closed = []) ∩ the live per-prepare expanded catalog — every listed word passes BOTH gates,
|
|
6345
|
+
and a configured-but-absent word is filtered (config mistakes surface as core rejections and ops
|
|
6346
|
+
diagnostics, never as vocabulary). Empty array = honest "nothing usable here" (e.g. multi-tenant
|
|
6347
|
+
with no explicit allowlist); ABSENT = an older server (degrade to not teaching, not validating).
|
|
6087
6348
|
resumeAt: { type: boolean, description: "E18 resume-at-message anchors are resolvable." }
|
|
6088
6349
|
rewindFiles: { type: boolean, description: "E19 working-tree snapshot/restore is wired." }
|
|
6089
6350
|
rewindFilesTo: { type: boolean, description: "Restore to a SPECIFIC entry id (not just the latest snapshot)." }
|
|
6351
|
+
restoreFiles:
|
|
6352
|
+
type: boolean
|
|
6353
|
+
description: >-
|
|
6354
|
+
S-15 epoch discriminator (server ≥7.52, `Boolean(fileHistoryStore)`): this server understands the
|
|
6355
|
+
`restoreFiles` REQUEST KEY / tracked-set restore semantics (design/381 — restore narrowed
|
|
6356
|
+
whole-tree ⇒ tracked set; the retired `rewindFiles`+`resumeAt` spelling gets core's typed
|
|
6357
|
+
migration rejection there). New clients negotiate on THIS bit and send `restoreFiles` (+
|
|
6358
|
+
`resumeAt`); absent ⇒ an older server, send the old spelling. `rewindFiles` keeps its name (same
|
|
6359
|
+
affordance — "rewind can move files"); this bit is what tells the two epochs apart.
|
|
6090
6360
|
manualCompact: { type: boolean, description: "`POST /v1/runs/:id/compact` (needs a run store)." }
|
|
6091
6361
|
sessions: { type: boolean, description: "Session read face (`GET /v1/sessions/:id`)." }
|
|
6092
6362
|
sessionList: { type: boolean, description: "`GET /v1/sessions` list/picker face." }
|
|
@@ -6118,10 +6388,15 @@ components:
|
|
|
6118
6388
|
steerPriority: { type: string }
|
|
6119
6389
|
workspace:
|
|
6120
6390
|
description: >
|
|
6121
|
-
#3 (server
|
|
6391
|
+
#3 (server 1.299–7.51): workspace-browse capability family — an OBJECT when the E19 snapshot-store
|
|
6122
6392
|
read faces + a durable session store are wired ({browse, archive, maxFileBytes}); `false`/absent
|
|
6123
6393
|
otherwise (same predicate as the routes' 501). `maxFileBytes` = the single-file read cap (over-cap
|
|
6124
6394
|
⇒ 413 workspace_file_too_large envelope; the archive endpoint is the escape hatch).
|
|
6395
|
+
🔴 RETIRED (server ≥7.52, S-15/design/381): the whole-tree snapshot epoch this face projected was
|
|
6396
|
+
replaced by per-edited-file rewind history, so this key is now ALWAYS false and the four
|
|
6397
|
+
/v1/sessions/{sessionId}/workspace* routes answer 501 `capability.workspace_retired` (route shape
|
|
6398
|
+
kept as a tombstone — a named refusal, not a 404 that reads like a typo). Consumption posture
|
|
6399
|
+
unchanged: probe this bit, false ⇒ don't render the panel.
|
|
6125
6400
|
oneOf:
|
|
6126
6401
|
- type: boolean
|
|
6127
6402
|
enum: [false]
|
|
@@ -6253,8 +6528,14 @@ components:
|
|
|
6253
6528
|
sessionSync:
|
|
6254
6529
|
type: boolean
|
|
6255
6530
|
description: >
|
|
6256
|
-
2c session-sync (P1d) — the `/v1/sessions/{sessionId}/sync/*` peer routes resolve (
|
|
6257
|
-
entry-export
|
|
6531
|
+
2c session-sync (P1d) — the `/v1/sessions/{sessionId}/sync/*` peer routes resolve (server ≥7.52
|
|
6532
|
+
predicate: durable backend + entry-export/import-staging seams + a file-HISTORY store wired —
|
|
6533
|
+
S-15 换店; ≤7.51 the last conjunct was the file-snapshot store). Gate the whole sync surface off
|
|
6534
|
+
this. ⚠️ The two BLOB faces (PUT …/sync/blobs/{hash}, GET …/sync/history/blobs/{hash})
|
|
6535
|
+
additionally need the store's blob faces (`syncBlobFaces` — the SQL/cloud backends; the local
|
|
6536
|
+
file backend has none): without them the PUT answers 501 `capability.snapshot_store_required`
|
|
6537
|
+
and the history PULL answers the no-oracle 404 `not_found.blob`, while the REST of the sync
|
|
6538
|
+
surface keeps working.
|
|
6258
6539
|
|
|
6259
6540
|
SkillSpec:
|
|
6260
6541
|
type: object
|
|
@@ -6691,6 +6972,21 @@ components:
|
|
|
6691
6972
|
replayed: { type: boolean, description: 'set when this agent''s result was REPLAYED from a resume journal rather than freshly run.' }
|
|
6692
6973
|
prompt: { type: string, description: 'what the worker was ASKED (core-redacted + bounded). UNTRUSTED display text.' }
|
|
6693
6974
|
output: { type: string, description: 'the worker''s final OUTPUT (core-redacted + bounded). UNTRUSTED display text.' }
|
|
6975
|
+
pendingApproval:
|
|
6976
|
+
type: object
|
|
6977
|
+
description: >-
|
|
6978
|
+
S-43(案A)E 段(server ≥7.52,core 5.65 供给;[5454] 键形冻结)— the suspended approvals this leg
|
|
6979
|
+
is CURRENTLY parked on. ABSENT = none pending. Three-state on purpose: absence ≠ empty array — an
|
|
6980
|
+
empty array would make "this engine does not supply the seat" and "truly nothing pending" the same
|
|
6981
|
+
shape. Server-side zod narrow-read projection: a drifted/half-written record omits the WHOLE key
|
|
6982
|
+
rather than projecting "a pendingApproval with nothing decidable in it".
|
|
6983
|
+
required: [askIds, oldestCreatedAtMs]
|
|
6984
|
+
additionalProperties: false
|
|
6985
|
+
properties:
|
|
6986
|
+
askIds: { type: array, items: { type: string }, minItems: 1 }
|
|
6987
|
+
oldestCreatedAtMs: { type: number }
|
|
6988
|
+
approvalWaitedMs: { type: number, description: 'S-43(案A)same batch — CUMULATIVE ms this leg has waited on approvals (#485 timeout-typing consumer; server single-sourced, core does not self-estimate). Absent on engines predating the seat.' }
|
|
6989
|
+
taskRunId: { type: string, description: '#499 (server ≥7.52, core 5.65) — the child ENGINE run identity (= the child TaskResult.runId; LAST-WINS across retries reusing the session). Named taskRunId NOT runId — in the workflow family `runId` already means the workflow run itself (core workflow-types 同名异形防撞段). Absent = stub leg / never reached prepare (additive, key not minted).' }
|
|
6694
6990
|
|
|
6695
6991
|
WorkflowPhaseProgress:
|
|
6696
6992
|
# 新(census 批2 四段):此前 `WorkflowRun.phases` 是 `items: {}`(零形状)。真形 = server
|
|
@@ -7167,12 +7463,18 @@ components:
|
|
|
7167
7463
|
description: >
|
|
7168
7464
|
FIRST frame — `{type, version, scoped, sessionScoped, bgNotifyFailClosed}` (all five UNCONDITIONAL on
|
|
7169
7465
|
server >= 1.247). `scoped:true` ⇒ owner-scoped view; `false` ⇒ fleet-wide (operator/trace token).
|
|
7466
|
+
`session` (server 7.31+, [4119] P-31 Q2): additive echo of THE session id in effect on THIS connection
|
|
7467
|
+
— present iff the connection carried `?session=`. This is the connection-level zero-content
|
|
7468
|
+
self-attestation bit client-core's P-31 residual family asks for ([5427]): a keyed host feeds a stream
|
|
7469
|
+
into a ledger, compares this echo against the ledger's session anchor, and can trust
|
|
7470
|
+
`sessionScoped`/`bgNotifyFailClosed` without content-based inference.
|
|
7170
7471
|
required: [type, version, scoped, sessionScoped, bgNotifyFailClosed]
|
|
7171
7472
|
properties:
|
|
7172
7473
|
type: { const: meta }
|
|
7173
7474
|
version: { type: integer }
|
|
7174
7475
|
scoped: { type: boolean }
|
|
7175
7476
|
sessionScoped: { type: boolean, description: 'true ⇒ this connection carried `?session=` (rows and notifications are filtered by the host session).' }
|
|
7477
|
+
session: { type: string, description: 'server 7.31+ ([4119] P-31 Q2): the session id in effect on this connection — the verbatim `?session=` value. ABSENT when the connection is not session-bound (never minted empty). Compare against your ledger''s session anchor to close the misrouted-stream trust gap.' }
|
|
7176
7478
|
bgNotifyFailClosed: { type: boolean, description: 'GENERATION marker — true ⇒ this server''s bg_notification injection path fails CLOSED for scoped subscribers, so a new-generation shell may drop its frame-level own/foreign checks entirely. ABSENT (older server) ⇒ keep them.' }
|
|
7177
7479
|
FleetFrame_snapshot:
|
|
7178
7480
|
type: object
|
|
@@ -7351,12 +7653,55 @@ components:
|
|
|
7351
7653
|
description: Envelope for pending HITL checkpoints (key `pending`, NOT a bare array — the pinned wire contract).
|
|
7352
7654
|
# 封闭(census 轴一,2026-07-30):三个铸造点(routes/approvals-assistant.ts)都只发
|
|
7353
7655
|
# `{ pending }`。行本身(`PendingCheckpoint`)保持开集(TS 侧索引签名)。
|
|
7656
|
+
# #315(server ≥7.43):第二顶层键 `livePending` = streamApproval 窗内的流内 ask(此前对轮询消费端
|
|
7657
|
+
# 结构性不可见)。ADDITIVE / tolerate-absent:键缺席=「协调器未装配(本部署无 live 审批面)」或
|
|
7658
|
+
# 旧 server;空数组=「有面、此刻无待批」——两义可判别,消费端测 presence。**分数组不混编**:
|
|
7659
|
+
# live 行的决议口=POST /v1/tool-approvals/{approvalId}/respond,durable 行=decide 口;数组名即路由判据。
|
|
7354
7660
|
additionalProperties: false
|
|
7355
7661
|
required: [pending]
|
|
7356
7662
|
properties:
|
|
7357
7663
|
pending:
|
|
7358
7664
|
type: array
|
|
7359
7665
|
items: { $ref: '#/components/schemas/PendingCheckpoint' }
|
|
7666
|
+
livePending:
|
|
7667
|
+
type: array
|
|
7668
|
+
items: { $ref: '#/components/schemas/LivePendingRow' }
|
|
7669
|
+
|
|
7670
|
+
LivePendingRow:
|
|
7671
|
+
type: object
|
|
7672
|
+
description: >-
|
|
7673
|
+
A live (in-stream) pending ask inside its streamApproval window (#315, server ≥7.43). Decide it via
|
|
7674
|
+
POST /v1/tool-approvals/{approvalId}/respond — NOT the /decide route (that is the durable rows' door).
|
|
7675
|
+
Deliberately narrow: tool input/args are NOT listed (no second redaction surface — details ride the
|
|
7676
|
+
SSE tool_approval frame). Optional flags are only-if-true (absent is NEVER encoded as false).
|
|
7677
|
+
additionalProperties: false
|
|
7678
|
+
required: [approvalId, toolName, ts, expiresAtMs]
|
|
7679
|
+
properties:
|
|
7680
|
+
approvalId: { type: string, description: 'The SAME id the respond endpoint takes (wire uuidv7).' }
|
|
7681
|
+
toolName: { type: string }
|
|
7682
|
+
ts: { type: number, description: 'Registration time (ms epoch).' }
|
|
7683
|
+
expiresAtMs: { type: number, description: 'Absolute window deadline — the SAME number minted once for the tool_approval frame (never recomputed).' }
|
|
7684
|
+
sessionId: { type: string }
|
|
7685
|
+
requiresRealApproval: { type: boolean, enum: [true] }
|
|
7686
|
+
governanceForced: { type: boolean, enum: [true] }
|
|
7687
|
+
fromSubagent: { type: boolean, enum: [true], description: 'The ask originates from a delegated subagent.' }
|
|
7688
|
+
originTaskId: { type: string, description: 'S-52 C2 (server ≥7.52, A-075.87): the delegated child taskId (= the child''s core runSourceTaskId, uuid) — correlates this live ask to the child run row. Minted by the SAME conditional spread as `fromSubagent`: the two keys are always both present or both absent (三键恒等亲证在案).' }
|
|
7689
|
+
|
|
7690
|
+
ApprovalStaleCurrentPending:
|
|
7691
|
+
type: object
|
|
7692
|
+
description: >-
|
|
7693
|
+
S-02 (server ≥7.52) — the ADDITIVE pointer key `currentPending` on the decide 409 `approval_stale`
|
|
7694
|
+
body: the session's CURRENT pending's three D-1 coordinates (same names/shapes as the §7b queue row),
|
|
7695
|
+
so a shell relocates and re-decides in ONE hop instead of refetching the whole queue. 🔴 NEVER includes
|
|
7696
|
+
a checkpointToken (resume credentials do not leave the server). Whole-or-absent: the server mints it
|
|
7697
|
+
only from a pending, owner-checked tool-approval row (no pending / non-tool gate / row read failure —
|
|
7698
|
+
a loud fail-open ledger arm — all omit the key).
|
|
7699
|
+
required: [toolName, boundCallId]
|
|
7700
|
+
additionalProperties: false
|
|
7701
|
+
properties:
|
|
7702
|
+
toolName: { type: string, description: 'The current pending''s gated tool (same source/value as the pending row''s toolName).' }
|
|
7703
|
+
boundCallId: { type: string, description: 'The current pending''s pendingAction.toolCallId — the D-1 anchor to echo back on the relocated decide.' }
|
|
7704
|
+
boundInputHash: { type: string, description: 'Server-minted; present only when the row has one. Echo verbatim, never recompute.' }
|
|
7360
7705
|
|
|
7361
7706
|
ExemptionList:
|
|
7362
7707
|
type: object
|
|
@@ -7374,14 +7719,25 @@ components:
|
|
|
7374
7719
|
description: >
|
|
7375
7720
|
The 200 body of `POST /v1/approvals/{sessionId}/decide` (census 批2 第二段, 2026-07-30 — this op's 200 was
|
|
7376
7721
|
a bare `{type: object, additionalProperties: true}`, i.e. no field set at all for a generated consumer).
|
|
7377
|
-
|
|
7378
|
-
|
|
7722
|
+
THREE real shapes share this envelope (#316, server >= 7.37 — [4660] Inkglow: the task-level leg used to
|
|
7723
|
+
drive the ENTIRE resume synchronously before responding, so its 200 took a full model round-trip with no
|
|
7724
|
+
upper bound; it now answers at the ACCEPTANCE point instead):
|
|
7725
|
+
· the TASK-LEVEL ACCEPTANCE shape (server >= 7.37, the DEFAULT on durable deployments;
|
|
7726
|
+
`http/server.ts` driveResumeIntoRunLog acceptance point) — `{taskId, sessionId, status:"resuming",
|
|
7727
|
+
bindingEnforced:true}`. Emitted AFTER every rejection-capable step has settled (lease 429, markResuming
|
|
7728
|
+
CAS 409, core pre-CAS guards + atomic CAS — those still reject synchronously); only the model leg runs
|
|
7729
|
+
on after the response. Follow the run via poll/SSE (`GET /v1/runs/:id` / `/events`) — `errorCode` /
|
|
7730
|
+
`errorMessage` / `retriable` NEVER ride this shape, failures land on the run row and its stream.
|
|
7731
|
+
· the RESUMED-TASK TERMINAL shape (pre-7.37 servers on every decide; >= 7.37 ONLY on deployments with no
|
|
7732
|
+
durable run row to follow — there the leg honestly stays synchronous) — `{taskId?, sessionId, status,
|
|
7379
7733
|
errorCode?, errorMessage?, retriable?}`, plus `rememberApplied` when the request carried
|
|
7380
7734
|
`remember:"session"` AND the worker has an exemption store;
|
|
7381
7735
|
· the PARKED-BACKGROUND-AGENT shape (`parked-decide.ts`, server >=1.267) — `{taskId, status:"resuming",
|
|
7382
7736
|
decision}`, an ACCEPTED (not completed) receipt; the revive drives asynchronously.
|
|
7383
|
-
`status` is the only key
|
|
7384
|
-
|
|
7737
|
+
`status` is the only key ALL shapes guarantee. ⚠️ `status === "resuming"` alone no longer discriminates
|
|
7738
|
+
the parked shape: branch on `decision` (parked receipt only) vs `sessionId`/`bindingEnforced` (task-level
|
|
7739
|
+
acceptance only). `taskId` rides `getActiveTaskId` on the terminal leg (omitted when there is no active
|
|
7740
|
+
row) and `sessionId` is absent from the parked shape.
|
|
7385
7741
|
required: [status]
|
|
7386
7742
|
additionalProperties: false
|
|
7387
7743
|
properties:
|
|
@@ -7397,8 +7753,9 @@ components:
|
|
|
7397
7753
|
status:
|
|
7398
7754
|
type: string
|
|
7399
7755
|
description: >
|
|
7400
|
-
OPEN string — do NOT narrow.
|
|
7401
|
-
|
|
7756
|
+
OPEN string — do NOT narrow. Task-level acceptance (server >= 7.37): the literal `resuming` (= accepted,
|
|
7757
|
+
resume in flight — follow the run stream). Terminal leg: the run's resulting status (`completed`/`failed`/
|
|
7758
|
+
`suspended`/`needs_review`). Parked leg: the literal `resuming` (= accepted, revive in flight).
|
|
7402
7759
|
decision:
|
|
7403
7760
|
type: string
|
|
7404
7761
|
enum: [approve, deny]
|
|
@@ -8257,6 +8614,7 @@ components:
|
|
|
8257
8614
|
- $ref: '#/components/schemas/Event_status'
|
|
8258
8615
|
- $ref: '#/components/schemas/Event_task_progress'
|
|
8259
8616
|
- $ref: '#/components/schemas/Event_workspace_changed'
|
|
8617
|
+
- $ref: '#/components/schemas/Event_engine_notice'
|
|
8260
8618
|
- $ref: '#/components/schemas/Event_suspended'
|
|
8261
8619
|
# 🔴 以下 6 臂是 2026-07-25 补的:`events.ts` 的 `AgentEvent` 联合有 21 个 `type` 字面量,本文件此前只声明
|
|
8262
8620
|
# 15 个 —— 照本文件生成客户端的消费方会把 6 种合法帧当成非法值(事件面是最吃重的 wire 面,这个缺口比
|
|
@@ -8313,6 +8671,7 @@ components:
|
|
|
8313
8671
|
status: '#/components/schemas/Event_status'
|
|
8314
8672
|
task_progress: '#/components/schemas/Event_task_progress'
|
|
8315
8673
|
workspace_changed: '#/components/schemas/Event_workspace_changed'
|
|
8674
|
+
engine_notice: '#/components/schemas/Event_engine_notice'
|
|
8316
8675
|
suspended: '#/components/schemas/Event_suspended'
|
|
8317
8676
|
file_link: '#/components/schemas/Event_file_link'
|
|
8318
8677
|
prompt_assembled: '#/components/schemas/Event_prompt_assembled'
|
|
@@ -8619,9 +8978,18 @@ components:
|
|
|
8619
8978
|
required: [type, phase]
|
|
8620
8979
|
properties:
|
|
8621
8980
|
type: { const: status }
|
|
8622
|
-
|
|
8981
|
+
# 🔴 A-057.27:6 员(此前只列 4,漏 recovered/gave_up 两个**终局**值 —— 一段重试序列恒恰一终局帧,
|
|
8982
|
+
# 两者对渲染方是同一句话「把重试行撤下来」)。闭集声明、开集读。
|
|
8983
|
+
phase: { type: string, description: "rate_limited | retrying | reconnecting | circuit_open | recovered | gave_up (closed in core, read as open set)" }
|
|
8623
8984
|
detail: { type: string, description: "neutral human hint (NO provider/HTTP detail)" }
|
|
8624
8985
|
retryInSec: { type: number }
|
|
8986
|
+
# 🔴 A-057.26:以下四键 server 真在发(builder brainStatusEventData,三腿共享),而本 schema 是
|
|
8987
|
+
# `additionalProperties: false` ⇒ 不声明 = 一个**合法**的真帧会被严格校验器判违约(假红),正是本仓
|
|
8988
|
+
# 元门(封闭 schema 必须镜像分支键)要防的那一形。缺席不铸键,故一律 optional。
|
|
8989
|
+
attempt: { type: number, description: "1-based index of the attempt that just FAILED (this wait precedes attempt+1)" }
|
|
8990
|
+
maxRetries: { type: number, description: "retry budget of the lane in force (refused-connect short lane = 2)" }
|
|
8991
|
+
retryInMs: { type: number, description: "same number as retryInSec at ms precision (countdown rendering)" }
|
|
8992
|
+
errClass: { type: string, description: "connect_refused | transport | rate_limit | server | http | output_cap — retry-wait frames ONLY; never inferred from phase" }
|
|
8625
8993
|
eventId: { type: string }
|
|
8626
8994
|
parentToolCallId: { type: string }
|
|
8627
8995
|
sourceTaskId: { type: string }
|
|
@@ -8846,6 +9214,36 @@ components:
|
|
|
8846
9214
|
parentToolCallId: { type: string }
|
|
8847
9215
|
sourceTaskId: { type: string }
|
|
8848
9216
|
bgAgentId: { type: string }
|
|
9217
|
+
Event_engine_notice:
|
|
9218
|
+
type: object
|
|
9219
|
+
description: >
|
|
9220
|
+
server >= 7.36 (#310, board [4630]) — an engine STRUCTURED NOTICE judged to be for THIS SESSION'S end user,
|
|
9221
|
+
routed to the session's event projection by `sessionId`. Present on BOTH legs (live SSE and the durable
|
|
9222
|
+
ledger replayed by `GET /v1/runs/:id/events`) — reconnect replay shows it again, so consume idempotently
|
|
9223
|
+
(same discipline as `workspace_changed`).
|
|
9224
|
+
RENDER BY `code` + `detail`; `message` is a FALLBACK ONLY (core states that `memory.session_polluted`'s
|
|
9225
|
+
message varies with the memoryProvenance mode — matching on message text WILL break). `code` is an OPEN
|
|
9226
|
+
set (the server whitelist grows with core's code register): render a known code specially, fall back to
|
|
9227
|
+
`message` for an unknown one — never drop the frame.
|
|
9228
|
+
Starter whitelist (server 7.36): `memory.session_polluted` `{reason, sessionId?}` ·
|
|
9229
|
+
`memory.harvest_quarantined` `{count, moved, escalated, reason?}` — `moved` and `escalated` MUST NOT be
|
|
9230
|
+
subtracted from each other (an in-place tombstone counts as both) · `memory.delegation_static_mark_waived`
|
|
9231
|
+
`{reason, subagentType?, sessionId?}`.
|
|
9232
|
+
A notice whose `detail.sessionId` is absent is HONESTLY NOT DELIVERED (the server never guesses a session),
|
|
9233
|
+
so the frame's absence does NOT mean "did not happen" — the operator-facing structured log always has the
|
|
9234
|
+
full family. `detail` is already redacted + size-bounded by the server; still treat it as external text.
|
|
9235
|
+
additionalProperties: false
|
|
9236
|
+
required: [type, code, message, detail, sessionId, ts]
|
|
9237
|
+
properties:
|
|
9238
|
+
type: { const: engine_notice }
|
|
9239
|
+
code: { type: string, description: 'Stable dot-namespaced machine code. OPEN set — never drop an unknown one.' }
|
|
9240
|
+
message: { type: string, description: 'Core-minted human line. FALLBACK DISPLAY ONLY, never a match key.' }
|
|
9241
|
+
detail:
|
|
9242
|
+
type: object
|
|
9243
|
+
additionalProperties: true
|
|
9244
|
+
description: 'Machine-readable facts (per code). Redacted + bounded server-side; open set of keys.'
|
|
9245
|
+
sessionId: { type: string, description: 'Owning session — this frame only ever appears on that session stream.' }
|
|
9246
|
+
ts: { type: integer, format: int64, description: 'SERVER observation time (ms epoch), NOT the engine mint time.' }
|
|
8849
9247
|
Event_workspace_changed:
|
|
8850
9248
|
type: object
|
|
8851
9249
|
description: >
|
|
@@ -9508,6 +9906,12 @@ components:
|
|
|
9508
9906
|
fromSubagent: { type: boolean, enum: [true], description: 'Present ⇔ the ask comes from a DELEGATED child — the explicit discriminator (a worker cannot forge it).' }
|
|
9509
9907
|
sourceTaskId: { type: string, description: 'The child''s core session id — row-join value + fallback discriminator.' }
|
|
9510
9908
|
sourceAgentName: { type: string, description: 'The child agent''s display name, redacted. UNTRUSTED.' }
|
|
9909
|
+
# A-057.④(#314):三键 server 早已随卡投出(与 tool_approval 帧同一窄读函数 ⇒ 帧与 card 同值),
|
|
9910
|
+
# 但本 schema 只在帧上公示过 —— 按 spec 生成的卡消费方拿不到。定义与帧上同构段逐字同源,
|
|
9911
|
+
# 长 description 不复制(单一真源在 ToolApprovalFrame 的同名键)。
|
|
9912
|
+
inputHasBidi: { type: boolean, enum: [true], description: 'server >= 7.45.0 (E-14): the TO-BE-EXECUTED input (redacted args, computed BEFORE the byte cap) contains Unicode bidi override characters — what the eye reads may not be the byte order that runs. DISCLOSURE bit, bytes unchanged; rendering (escape/highlight) is the shell''s. Present (true) or ABSENT; absence != "verified clean" (not detected / serialization failed / older server).' }
|
|
9913
|
+
probeCause: { type: object, additionalProperties: true, description: 'server >= 7.17.0: structured reversibility-probe tightening cause — same shape and semantics as ToolApprovalFrame.probeCause ({code, roots:{shown,total}, further?:{shown,total}}); frame and card carry the SAME value (one narrow-read function server-side). See that key for the full contract.' }
|
|
9914
|
+
ruleEvidence: { type: object, additionalProperties: true, description: 'server >= 7.23.0: rule-provenance evidence — same shape and semantics as ToolApprovalFrame.ruleEvidence (each member a value or a NAMED absence word; display/reconciliation metadata, never adjudication input). Frame and card carry the SAME value. See that key for the full contract.' }
|
|
9511
9915
|
delegation: { $ref: '#/components/schemas/ApprovalCardDelegation' }
|
|
9512
9916
|
|
|
9513
9917
|
RuleSuggestion:
|
|
@@ -9973,6 +10377,23 @@ components:
|
|
|
9973
10377
|
activeTaskStatus:
|
|
9974
10378
|
type: string
|
|
9975
10379
|
description: '`running` | `suspended` | `needs_review` — best-effort (absent when the store lookup degraded).'
|
|
10380
|
+
msSinceLastActivity:
|
|
10381
|
+
type: number
|
|
10382
|
+
description: >
|
|
10383
|
+
(#245 S1, server >= 7.18 — 既存违例回填 2026-08-20: server has been emitting this on the done frame
|
|
10384
|
+
since the key landed on the 409 body; the CLOSED schema here just never declared it, so every real
|
|
10385
|
+
frame carrying it "violated" spec. Same key, same semantics as the 409 body.) Milliseconds since the
|
|
10386
|
+
occupying run's last recorded activity — the ONLY liveness-evidence bit on this frame (same-replica
|
|
10387
|
+
best-effort). Absent = no evidence, NOT "fresh"; never narrow absence to 0.
|
|
10388
|
+
stale:
|
|
10389
|
+
const: true
|
|
10390
|
+
description: >
|
|
10391
|
+
(#316(c), server >= 7.37; [4664]② hole (c)) The occupying run row looks DEAD by the same staleness
|
|
10392
|
+
criterion the poll face (`GET /v1/runs/:id`) uses to fold a terminal status — heartbeat silence past
|
|
10393
|
+
the reap window. This frame does NOT fold the status (`activeTaskStatus` stays verbatim, the reaper
|
|
10394
|
+
owns the transition); the bit lets a shell reconcile "conflict says running / poll says failed"
|
|
10395
|
+
inside the self-heal window. ADDITIVE, present ONLY as `true`; absent = no staleness evidence (parked
|
|
10396
|
+
rows are out of reap scope and never carry it — absence is NOT "alive").
|
|
9976
10397
|
pendingGate:
|
|
9977
10398
|
allOf: [{ $ref: '#/components/schemas/PendingGateMaterial' }]
|
|
9978
10399
|
description: >
|
|
@@ -10889,6 +11310,40 @@ components:
|
|
|
10889
11310
|
priority: { $ref: '#/components/schemas/SteerPriority' }
|
|
10890
11311
|
note: { type: string, description: 'Human-readable note on the two parked legs; absent on `applied`.' }
|
|
10891
11312
|
|
|
11313
|
+
InterruptReceipt:
|
|
11314
|
+
description: >
|
|
11315
|
+
The POST /v1/runs/{taskId}/interrupt 202 receipt — TWO mutually exclusive shapes (oneOf, discriminated
|
|
11316
|
+
by which arm's keys are present, NOT by status code; both are 202 — codex 复审 R1-[medium]:单对象全
|
|
11317
|
+
可选形会让 `{taskId,status}` 与混臂形都合法,生成式消费方靠不住判别键). TEXT form (cut + steer):
|
|
11318
|
+
`delivery:"queued-immediate"` + `messageId` (≡ the engine inputId — correlate with the run stream's
|
|
11319
|
+
`task.turn_interrupted` `detail.inputId`; acceptance = enqueued, the cut itself is best-effort).
|
|
11320
|
+
BARE (halt) form: `turnCut` and deliberately NO delivery/messageId (no input frame exists to correlate
|
|
11321
|
+
— A-075.78 成文;halt's acceptance is NOT a queue insert, so the receipt can honestly answer whether
|
|
11322
|
+
THIS call cut a live turn). `status` = the run row's word as the server read it at acceptance. Honest
|
|
11323
|
+
window: a halt racing the run's natural last moments reports the natural completion, still carrying
|
|
11324
|
+
the haltedByUser seat.
|
|
11325
|
+
# 封闭两臂:两个铸造点都是逐字面量拼出的对象(server routes/runs.ts interrupt 段),不是引擎透传形。
|
|
11326
|
+
oneOf:
|
|
11327
|
+
- type: object
|
|
11328
|
+
description: TEXT form (cut + steer) — the run continues.
|
|
11329
|
+
required: [taskId, status, delivery, messageId]
|
|
11330
|
+
additionalProperties: false
|
|
11331
|
+
properties:
|
|
11332
|
+
taskId: { type: string }
|
|
11333
|
+
status: { type: string, description: 'The run status the server saw at acceptance time.' }
|
|
11334
|
+
delivery: { type: string, enum: [queued-immediate] }
|
|
11335
|
+
messageId: { type: string, description: '≡ the engine inputId (with an Idempotency-Key: `idem-<sha256[:32]>`; without one: a fresh uuidv7, minted and threaded down PER REQUEST — unlike steer''s absent arm, which lets core mint).' }
|
|
11336
|
+
note: { type: string, description: 'Contract note (逐句照 core 契约,不多许诺一个字).' }
|
|
11337
|
+
- type: object
|
|
11338
|
+
description: BARE (halt) form (cut + stop) — the run closes at this boundary.
|
|
11339
|
+
required: [taskId, status, turnCut]
|
|
11340
|
+
additionalProperties: false
|
|
11341
|
+
properties:
|
|
11342
|
+
taskId: { type: string }
|
|
11343
|
+
status: { type: string, description: 'The run status the server saw at acceptance time.' }
|
|
11344
|
+
turnCut: { type: boolean, description: 'true ⇔ THIS call cut a live turn; repeat calls answer false once the stop is latched (halt is naturally idempotent).' }
|
|
11345
|
+
note: { type: string, description: 'Contract note (逐句照 core 契约,不多许诺一个字).' }
|
|
11346
|
+
|
|
10892
11347
|
SubagentSteerReceipt:
|
|
10893
11348
|
type: object
|
|
10894
11349
|
description: >
|
|
@@ -11619,7 +12074,7 @@ components:
|
|
|
11619
12074
|
required: [rule, scope]
|
|
11620
12075
|
properties:
|
|
11621
12076
|
rule: { type: string, minLength: 1, maxLength: 1024, description: 'The canonical rule text, VERBATIM as `RuleListResult.rules[].rule` gave it.' }
|
|
11622
|
-
scope: { type: string, minLength: 1, maxLength:
|
|
12077
|
+
scope: { type: string, minLength: 1, maxLength: 4104, description: 'The scope discriminant string — `global` or `project:<root>` with a NON-EMPTY root (an empty root would match every cwd). VERBATIM as the listing gave it. Cap = "project:".length + server MAX_CWD_CHARS (8+4096) — the 1088 previously written here was the same stale independent copy the server itself fixed (rules.ts MAX_RULE_SCOPE_CHARS 顶注), found by the A-335 anchor sweep.' }
|
|
11623
12078
|
principal: { type: string, minLength: 1, maxLength: 190, description: 'OPERATOR-ONLY cross-tenant revoke. Absent (or your own principal) = revoke your own rule.' }
|
|
11624
12079
|
|
|
11625
12080
|
RuleRevokeResult:
|