@sema-agent/sdk 1.1.0 → 2.1.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/openapi.yaml CHANGED
@@ -3857,6 +3857,13 @@ components:
3857
3857
 
3858
3858
  TaskResult:
3859
3859
  type: object
3860
+ # 🔴 **刻意不封闭**(census 轴一裁定,2026-07-30)。此前是「没写」= JSON Schema 默认开放,分不清
3861
+ # 「决定了要开」还是「忘了」;现在显式写成 true,让这条决定可读、可复审。理由:server 对引擎
3862
+ # `TaskResult` 是**整体透传**(runs.ts `withModelUsage` 只做 `{...result, stats:{...}}` 的加法),
3863
+ # 而 SDK 的 `TaskResult`/`TaskStats` 两侧都带 `[key: string]: unknown` 开集(types.ts:119)——把
3864
+ # 这里改成 false 会让 spec 与本仓自己的类型面互相打脸,并且把「引擎加字段」判成 wire 违约(它不是)。
3865
+ # 收紧这一面的正解不是 additionalProperties,是把引擎真发的键**逐个登记**(字段级 drift 门的活)。
3866
+ additionalProperties: true
3860
3867
  description: Synchronous task result (`POST /v1/tasks`).
3861
3868
  required: [taskId, sessionId, status, stats]
3862
3869
  properties:
@@ -3921,6 +3928,10 @@ components:
3921
3928
 
3922
3929
  RunReceipt:
3923
3930
  type: object
3931
+ # 封闭(census 轴一,2026-07-30):producer 铸造点 routes/runs.ts:180 与 idem 重放臂各自逐字写死
3932
+ # `{taskId, sessionId, status}` —— 三键全在场、无第四键 ⇒ required 已完整,additionalProperties 收紧到
3933
+ # false 后「server 多发一个 spec 没登记的键」当场红(此前 ①档只校已声明键的类型,多键完全不可见)。
3934
+ additionalProperties: false
3924
3935
  description: Async run receipt (`POST /v1/runs` → 202). The capability token is NEVER on the wire.
3925
3936
  required: [taskId, sessionId, status]
3926
3937
  properties:
@@ -3934,6 +3945,10 @@ components:
3934
3945
  Run state (`GET /v1/runs/:id`) — live shape per the pinned wire contract Drift 2: `result` is the NESTED TaskResult
3935
3946
  object (output text in result.result, stats in result.stats; NO top-level stats), and the error text
3936
3947
  field is `error` (not errorMessage; errorCode is floated up from result.errorCode).
3948
+ # 封闭(census 轴一,2026-07-30):铸造点 routes/runs.ts:337-351 是一个逐键写死的字面量,恰好 10 键
3949
+ # (taskId/sessionId/status/result/supervisorCost/suggestions/errorCode/error/jobId/source),与本
3950
+ # properties 一一对上;只有前三键无条件在场(其余走 `?? undefined` ⇒ JSON 丢键)⇒ required 维持 3 键。
3951
+ additionalProperties: false
3937
3952
  required: [taskId, sessionId, status]
3938
3953
  properties:
3939
3954
  taskId: { type: string }
@@ -3963,10 +3978,14 @@ components:
3963
3978
  description: >
3964
3979
  Per-axis cost rollup for a supervised run. `llm` is `null` on a run whose LLM spend was not attributed
3965
3980
  (e.g. no brain call). All amounts are integer micro-USD (no float drift).
3981
+ # 封闭(census 轴一,2026-07-30):composeSupervisorCost(observability/cost-taxonomy.ts)返回的是
3982
+ # 三段闭合结构,TS 侧 `SupervisorCostBreakdown` 也没有索引签名 ⇒ 三层一并收紧。
3983
+ additionalProperties: false
3966
3984
  required: [llm, infra, totalMicroUsd]
3967
3985
  properties:
3968
3986
  llm:
3969
3987
  type: ['object', 'null']
3988
+ additionalProperties: false
3970
3989
  required: [llmRootMicroUsd, nestedSubagentMicroUsd, memoryConsolidationMicroUsd, compactionMicroUsd]
3971
3990
  properties:
3972
3991
  llmRootMicroUsd: { type: integer }
@@ -3976,6 +3995,7 @@ components:
3976
3995
  infra:
3977
3996
  type: object
3978
3997
  description: Service-owned infra rates (the engine prices only LLM tokens); all default 0 when unpriced.
3998
+ additionalProperties: false
3979
3999
  required: [toolCallMicroUsd, sandboxWalltimeMicroUsd, egressMicroUsd, totalMicroUsd]
3980
4000
  properties:
3981
4001
  toolCallMicroUsd: { type: integer }
@@ -3989,6 +4009,9 @@ components:
3989
4009
  description: >
3990
4010
  Synchronous ack for `POST /v1/runs/:id/cancel`. `status` is "cancelling" (accepted) or a terminal status
3991
4011
  (idempotent no-op). NOTE: "cancelling" is NOT a RunStatus enum member — it only appears here.
4012
+ # 封闭(census 轴一,2026-07-30):cancel 面的 7 个 sendJson 铸造点(routes/runs.ts:417/436/480/483/
4013
+ # 495/505/512)键集的并集恰好 = {taskId, status, note?, errorCode?};taskId/status 无条件在场。
4014
+ additionalProperties: false
3992
4015
  required: [taskId, status]
3993
4016
  properties:
3994
4017
  taskId: { type: string }
@@ -4151,7 +4174,12 @@ components:
4151
4174
  description: >
4152
4175
  Turn trace envelope (`GET /v1/tasks/:id/turns`). Key is `turns` (resource-named). `retainedFrom` marks
4153
4176
  the lowest retained seq. Turn ITEM shape is draft (co-design with artifacts, Q7).
4154
- required: [turns]
4177
+ # 封闭 + required 补全(census 轴一,2026-07-30):铸造点 routes/trace-usage.ts:233
4178
+ # `{ turns, ...(nextCursor?{nextCursor}:{}) , retainedFrom }` —— `retainedFrom` 是**无条件**键
4179
+ # (`RunStore.retainedFrom(): Promise<number>`,四个实现都返回数字),此前只 required 了 `turns`,
4180
+ # 于是「server 少发 retainedFrom」这种断页判据丢失对本门不可见。
4181
+ additionalProperties: false
4182
+ required: [turns, retainedFrom]
4155
4183
  properties:
4156
4184
  turns:
4157
4185
  type: array
@@ -4162,6 +4190,9 @@ components:
4162
4190
  TaskList:
4163
4191
  type: object
4164
4192
  description: Paginated run summaries (`GET /v1/tasks`). Envelope key is `tasks` (resource-named, NOT `items`).
4193
+ # 封闭(census 轴一,2026-07-30):信封铸造点 routes/trace-usage.ts:201 只有 tasks + 条件 nextCursor。
4194
+ # 行本身(`TaskSummary`)保持开集 —— 它的 TS 侧有索引签名(引擎透传),那份「开」是真的。
4195
+ additionalProperties: false
4165
4196
  required: [tasks]
4166
4197
  properties:
4167
4198
  tasks:
@@ -4220,6 +4251,9 @@ components:
4220
4251
 
4221
4252
  ArtifactList:
4222
4253
  type: object
4254
+ # 封闭(census 轴一,2026-07-30):铸造点 routes/trace-usage.ts:214 只有 artifacts 一键(无分页)。
4255
+ # 行本身(`Artifact`)保持开集(TS 侧索引签名)。
4256
+ additionalProperties: false
4223
4257
  required: [artifacts]
4224
4258
  properties:
4225
4259
  artifacts:
@@ -4282,6 +4316,10 @@ components:
4282
4316
  description: >
4283
4317
  Worker capability map (`GET /v1/capabilities`). Open set — ignore unknown keys.
4284
4318
  Booleans share deps with the route gates (can't say yes and then 501).
4319
+ # 🔴 **刻意不封闭**(census 轴一裁定,2026-07-30):能力面按定义就是「新部署先长出一位、老客户端忽略它」
4320
+ # 的探测面(TS 侧同样带索引签名),封闭它等于把「加一位能力」判成 wire 违约。`required` 也停在
4321
+ # `version` 一键 —— 其余每一位都由部署侧的 deps 决定在不在场,没有第二个恒在键可钉。
4322
+ # 本 schema 的真正收紧手段是别的两条:字段级 drift 门(键必须登记)+ capabilities-http 的整体 toEqual 门。
4285
4323
  required: [version]
4286
4324
  additionalProperties: true
4287
4325
  properties:
@@ -4992,6 +5030,9 @@ components:
4992
5030
  PendingList:
4993
5031
  type: object
4994
5032
  description: Envelope for pending HITL checkpoints (key `pending`, NOT a bare array — the pinned wire contract).
5033
+ # 封闭(census 轴一,2026-07-30):三个铸造点(routes/approvals-assistant.ts:89/620/624)都只发
5034
+ # `{ pending }`。行本身(`PendingCheckpoint`)保持开集(TS 侧索引签名)。
5035
+ additionalProperties: false
4995
5036
  required: [pending]
4996
5037
  properties:
4997
5038
  pending:
@@ -5123,27 +5164,33 @@ components:
5123
5164
 
5124
5165
  InboxRow:
5125
5166
  description: >
5126
- ASSISTANT-WIRE-CONTRACT §2 — an inbox row = a CheckpointSummary + an `objective` (enriched from the
5127
- task ctx; `null` if unavailable).
5128
- allOf:
5129
- - $ref: '#/components/schemas/CheckpointSummary'
5130
- - type: object
5131
- properties:
5132
- objective: { type: ['string', 'null'], description: 'task objective, null if unavailable.' }
5133
- toolName:
5134
- type: ['string', 'null']
5135
- description: 'The gated tool, when the pause came from a tool call (null for non-tool gates).'
5136
- toolCallId: { type: string, description: 'The gated tool-call id.' }
5137
- boundCallId: { type: string, description: 'D-1 decision binding — echo it back on /decide.' }
5138
- boundInputHash:
5139
- type: string
5140
- description: >
5141
- D-1 decision binding — hash of the bound tool input. Echo on /decide so an approval cannot land on
5142
- a DIFFERENT call that was swapped in after the operator looked.
5143
- input:
5144
- description: >
5145
- REDACTED tool input for display. Untyped by design (per-tool shape) — and note the raw `toolInput`
5146
- is deliberately NOT on this row: the handler always strips it (along with `token`) before returning.
5167
+ ASSISTANT-WIRE-CONTRACT §2 — an inbox row (server >=3.4.0 EXPLICIT-projection form): the §1 mirror keys
5168
+ of a CheckpointSummary (token stripped; no rich per-tool fields) + two nullable enrichments.
5169
+ NARROWING RECORD (2026-07-30, board [2027]->[2029]): rows used to also carry
5170
+ toolName/toolCallId/preview/principal/sourceTaskId/contentKind (handler spread the whole projection) and
5171
+ this spec over-declared decide bindings that never appeared on inbox wire at all. Four client repos were
5172
+ structurally zero-consumers, so server 3.4.0 narrowed the wire and this schema follows. For the tool face
5173
+ and decide bindings use /v1/approvals (PendingCheckpoint); for task attribution use /v1/assistant/tasks.
5174
+ type: object
5175
+ required: [sessionId, scope, objective, input]
5176
+ additionalProperties: false
5177
+ properties:
5178
+ sessionId: { type: string, description: 'The suspended task/session id.' }
5179
+ scope: { type: string, description: 'Multi-tenant owner scope.' }
5180
+ createdAt: { type: number, description: 'Ask creation time (epoch ms).' }
5181
+ gateKind:
5182
+ type: string
5183
+ description: 'Same open set as CheckpointSummary.gateKind.'
5184
+ enum: [human, irreversible_ask, resource_limit, needs_review, plan_review, task_done]
5185
+ x-open-enum: true
5186
+ severity: { type: integer, minimum: 1, maximum: 5, description: 'Sort key; only human/irreversible_ask carry it.' }
5187
+ spentMicroUsd: { type: number, description: 'Accumulated spend on the suspend chain (micro-USD).' }
5188
+ deadline: { type: number, description: 'Awaiting-human SLA deadline (epoch ms).' }
5189
+ objective: { type: ['string', 'null'], description: 'task objective, null if unavailable.' }
5190
+ input:
5191
+ description: >
5192
+ REDACTED tool-args preview for display (null on gates without one, e.g. plan_review). Untyped by
5193
+ design (per-tool shape). The raw `toolInput` is deliberately NOT on this row.
5147
5194
 
5148
5195
  InboxList:
5149
5196
  type: object
@@ -5292,6 +5339,10 @@ components:
5292
5339
  description: >
5293
5340
  Session audit (`GET /v1/sessions/:id`) — true shape. `messages` = current model context
5294
5341
  (older history folded into a summary message); can be large.
5342
+ # 🔴 **暂不封闭**(census 轴一裁定,2026-07-30):本体是 core `StoredSession.buildContext()` 的投影
5343
+ # (server `audit.ts` auditFromStorage),再叠 server 自己的两条 additive 后处理(windowMessages /
5344
+ # truncateMessageBlobs)与路由层追加的 owner —— 键集由「core 投影 × 查询参数」共同决定,不是一个
5345
+ # 写死的字面量。封闭它要先把这三层的键集逐一钉死,属于下一段的活,不在本批。
5295
5346
  required: [sessionId, createdAt, messages]
5296
5347
  additionalProperties: true
5297
5348
  properties:
@@ -5685,6 +5736,11 @@ components:
5685
5736
  - $ref: '#/components/schemas/Event_steering_injected'
5686
5737
  - $ref: '#/components/schemas/Event_done'
5687
5738
  - $ref: '#/components/schemas/Event_failed'
5739
+ # 🔴 census 轴一(2026-07-30)补的 4 臂:server 今天就真发,spec/SDK 两侧此前同缺(详见各 schema 处的注)。
5740
+ - $ref: '#/components/schemas/Event_context_usage'
5741
+ - $ref: '#/components/schemas/Event_config_assembled'
5742
+ - $ref: '#/components/schemas/Event_needs_review'
5743
+ - $ref: '#/components/schemas/Event_message_committed'
5688
5744
  discriminator:
5689
5745
  propertyName: type
5690
5746
  mapping:
@@ -5710,15 +5766,26 @@ components:
5710
5766
  steering_injected: '#/components/schemas/Event_steering_injected'
5711
5767
  done: '#/components/schemas/Event_done'
5712
5768
  failed: '#/components/schemas/Event_failed'
5769
+ context_usage: '#/components/schemas/Event_context_usage'
5770
+ config_assembled: '#/components/schemas/Event_config_assembled'
5771
+ needs_review: '#/components/schemas/Event_needs_review'
5772
+ message_committed: '#/components/schemas/Event_message_committed'
5713
5773
 
5714
5774
  # design/99 §E2 (SHIPPED, core TaskEventIdentity) — additive per-event identity on every CONTENT event (NOT on
5715
5775
  # the lifecycle arms turn_end/compacted/suspended/done/failed). eventId = uuidv7 dedup/resume handle (the durable
5716
5776
  # `text` re-stamps the turn's first text_delta eventId, runs.ts:250); parentToolCallId = sub-agent attribution.
5717
5777
  EventIdentity:
5718
5778
  type: object
5779
+ # 🔴 census 轴一(2026-07-30)亲读铸造点补的两键:LIVE 腿的白名单化 catch-all
5780
+ # (server `routes/tasks.ts:640-648`,text_delta / turn_end / message_committed / context_usage 四臂)
5781
+ # 传的是**四个** identity 键,而 durable 腿的 `identityFields`(trace/project.ts:101)只传前两个
5782
+ # ——「两腿两策」是那段代码自己写下的口径。此前 spec 只声明两键 ⇒ 在封闭这些臂时会把真键判成违约。
5783
+ # 声明面取两腿并集(逐臂再收窄不值当),缺席即缺席。
5719
5784
  properties:
5720
5785
  eventId: { type: string }
5721
5786
  parentToolCallId: { type: string }
5787
+ sourceTaskId: { type: string, description: 'LIVE 腿四臂专属(durable 腿不发):core 的来源任务归因键。' }
5788
+ bgAgentId: { type: string, description: 'LIVE 腿四臂专属(durable 腿不发):core 1.370 后台子代 agent 标识。' }
5722
5789
  Event_meta:
5723
5790
  type: object
5724
5791
  description: >-
@@ -5727,6 +5794,8 @@ components:
5727
5794
  condition: durable-ledger leg only (no ledger => neither header nor frame). sessionId rides along
5728
5795
  (needed to bind subsequent turns when the server minted a fresh session). Always precedes any content
5729
5796
  frame. NOT an engine event — server-minted, no EventIdentity.
5797
+ # 封闭:铸造点 routes/tasks.ts:122 是逐字写死的三键字面量(sessionId 条件在场)。
5798
+ additionalProperties: false
5730
5799
  required: [type, taskId]
5731
5800
  properties:
5732
5801
  type: { const: meta }
@@ -5736,43 +5805,77 @@ components:
5736
5805
  type: object
5737
5806
  description: durable stream only — turn-aggregated thinking.
5738
5807
  allOf: [{ $ref: '#/components/schemas/EventIdentity' }]
5808
+ # 🔴 封闭 + identity 本地镜像(census 轴一,2026-07-30)。为什么要镜像:JSON Schema 里
5809
+ # `additionalProperties` 只认**同一个 subschema** 的 `properties`,`allOf` 分支($ref EventIdentity)
5810
+ # 声明的键不在它的作用域内 —— 只写 `additionalProperties: false` 会把合法的 eventId 判成违约
5811
+ # (draft-07 的 ajv 也不支持能跨 allOf 结算的 `unevaluatedProperties`,写了会被静默忽略 = 假绿)。
5812
+ # 镜像取两腿并集(超集,宁可漏抓不可假红);`$ref` 保留(spec.test.ts:92 的 §E2 门要它)。
5813
+ # 镜像与 EventIdentity 的一致性由 spec.test.ts 的「封闭臂必须镜像全部 identity 键」门看守。
5814
+ additionalProperties: false
5739
5815
  required: [type, text]
5740
5816
  properties:
5741
5817
  type: { const: reasoning }
5742
5818
  text: { type: string }
5819
+ eventId: { type: string }
5820
+ parentToolCallId: { type: string }
5821
+ sourceTaskId: { type: string }
5822
+ bgAgentId: { type: string }
5743
5823
  Event_text:
5744
5824
  type: object
5745
5825
  description: durable stream only — turn-aggregated answer text (carries the E18 resume-handle eventId).
5746
5826
  allOf: [{ $ref: '#/components/schemas/EventIdentity' }]
5827
+ additionalProperties: false # 封闭 + identity 本地镜像:见 Event_reasoning 处的长注
5747
5828
  required: [type, text]
5748
5829
  properties:
5749
5830
  type: { const: text }
5750
5831
  text: { type: string }
5832
+ eventId: { type: string }
5833
+ parentToolCallId: { type: string }
5834
+ sourceTaskId: { type: string }
5835
+ bgAgentId: { type: string }
5751
5836
  Event_reasoning_delta:
5752
5837
  type: object
5753
5838
  description: live stream only — per-token thinking fragment (`delta` is the string fragment).
5754
5839
  allOf: [{ $ref: '#/components/schemas/EventIdentity' }]
5840
+ additionalProperties: false # 封闭 + identity 本地镜像:见 Event_reasoning 处的长注
5755
5841
  required: [type, delta]
5756
5842
  properties:
5757
5843
  type: { const: reasoning_delta }
5758
5844
  delta: { type: string }
5845
+ eventId: { type: string }
5846
+ parentToolCallId: { type: string }
5847
+ sourceTaskId: { type: string }
5848
+ bgAgentId: { type: string }
5759
5849
  Event_text_delta:
5760
5850
  type: object
5761
5851
  description: live stream only — per-token answer fragment.
5762
5852
  allOf: [{ $ref: '#/components/schemas/EventIdentity' }]
5853
+ additionalProperties: false # 封闭 + identity 本地镜像:见 Event_reasoning 处的长注
5763
5854
  required: [type, delta]
5764
5855
  properties:
5765
5856
  type: { const: text_delta }
5766
5857
  delta: { type: string }
5858
+ eventId: { type: string }
5859
+ parentToolCallId: { type: string }
5860
+ sourceTaskId: { type: string }
5861
+ bgAgentId: { type: string }
5767
5862
  Event_tool_start:
5768
5863
  type: object
5769
5864
  allOf: [{ $ref: '#/components/schemas/EventIdentity' }]
5865
+ additionalProperties: false # 封闭 + identity 本地镜像:见 Event_reasoning 处的长注
5770
5866
  required: [type, toolCallId, toolName]
5771
5867
  properties:
5772
5868
  type: { const: tool_start }
5773
5869
  toolCallId: { type: string }
5774
5870
  toolName: { type: string }
5871
+ # 🔴 census 轴一(2026-07-30)回填:server 白名单 builder `toolStartEventData`(trace/project.ts:112)
5872
+ # 真发 `label`(core 展示名,与 tool_end 同待遇),spec 此前漏声明 —— 封闭前必须先补,否则真帧变假红。
5873
+ label: { type: string, description: 'core 展示名(缺席 ⇒ 用 toolName 渲染)。' }
5775
5874
  args: {}
5875
+ eventId: { type: string }
5876
+ parentToolCallId: { type: string }
5877
+ sourceTaskId: { type: string }
5878
+ bgAgentId: { type: string }
5776
5879
  Event_tool_end:
5777
5880
  type: object
5778
5881
  description: >
@@ -5781,35 +5884,96 @@ components:
5781
5884
  (`truncated: true`). Absent ⇒ the tool produced no body. 🔴 UNTRUSTED RAW + observability-only: the service
5782
5885
  has redactDeep'd + size-bounded it; a consumer MAY further bound, MUST never re-feed it to a model.
5783
5886
  allOf: [{ $ref: '#/components/schemas/EventIdentity' }]
5887
+ additionalProperties: false # 封闭 + identity 本地镜像:见 Event_reasoning 处的长注
5784
5888
  required: [type, toolCallId, toolName, isError]
5785
5889
  properties:
5786
5890
  type: { const: tool_end }
5787
5891
  toolCallId: { type: string }
5788
5892
  toolName: { type: string }
5893
+ # 🔴 census 轴一(2026-07-30)回填:`toolEndEventData`(trace/project.ts:125)真发 `label` 与
5894
+ # `structured`,spec 此前两者都漏 —— SDK 的 events.ts 早已声明,漂移只在 spec 这一侧。
5895
+ label: { type: string, description: 'core 展示名(同 tool_start.label)。' }
5896
+ structured: {} # core 1.203 CC 卡片明细;与 output 同一 UNTRUSTED RAW 待遇(已 redactDeep),形状开放
5789
5897
  isError: { type: boolean }
5790
5898
  output: {} # NON-UNIFORM: string | (TextContent|ImageContent)[] | (truncated) string. Absent ⇒ no body.
5791
5899
  truncated: { type: boolean }
5792
5900
  totalChars: { type: integer, description: 'core >=1.442 (RB-210): honest ORIGINAL size when truncated — sum of each block''s true size (text=chars, image/document=base64 bytes), no JSON-wrapping overhead. Absent on older cores / untruncated results.' } # true ⇒ output was size-bounded to a truncated string.
5901
+ eventId: { type: string }
5902
+ parentToolCallId: { type: string }
5903
+ sourceTaskId: { type: string }
5904
+ bgAgentId: { type: string }
5793
5905
  Event_turn_end:
5794
5906
  type: object
5907
+ # 🔴 census 轴一(2026-07-30):`turn_end` 走 LIVE 腿的白名单 catch-all(routes/tasks.ts:646),该分支
5908
+ # 对四个 identity 键做条件 spread —— 即「core 若真在 turn_end 上盖了 identity,它会上 wire」。
5909
+ # 处置:**不** allOf EventIdentity(契约面继续不承诺生命周期臂带 identity,spec.test.ts §E2 那条钉
5910
+ # 保持有效),但把四键作为本地可选属性声明出来,好让下面的封闭对那条真实代码路径不产生假红。
5911
+ additionalProperties: false
5795
5912
  required: [type]
5796
5913
  properties:
5797
5914
  type: { const: turn_end }
5798
5915
  usage:
5799
5916
  type: object
5800
5917
  description: Full live shape — the portal cost bar's data source.
5918
+ # 本子对象是 core 的 usage 结构原样透传(builder 不重建),留开集。
5919
+ additionalProperties: true
5801
5920
  properties:
5802
5921
  inputTokens: { type: integer }
5803
5922
  outputTokens: { type: integer }
5804
5923
  cacheReadTokens: { type: integer }
5805
5924
  cacheWriteTokens: { type: integer }
5806
5925
  costMicroUsd: { type: integer }
5926
+ # 🔴 census 轴一(2026-07-30)回填:`turnEndEventData`(trace/project.ts:449)是 2026-07-25 才补的
5927
+ # builder,它带上来的这两键 spec 从未声明过 —— 而它们恰恰是**诚实缺席**语义的载体:
5928
+ # `usageMissing` 缺了,「没有 usage」会被读成「用量是 0」而不是「未知」。
5929
+ usageMissing: { const: true, description: '本轮用量不可信/缺失的诚实标记。缺席 ⇒ usage 可信。' }
5930
+ stopReason: { type: string, description: '这一轮为什么停(max_tokens / stop_sequence / …),core 原词。' }
5931
+ eventId: { type: string }
5932
+ parentToolCallId: { type: string }
5933
+ sourceTaskId: { type: string }
5934
+ bgAgentId: { type: string }
5807
5935
  Event_compacted:
5808
5936
  type: object
5937
+ # 🔴 **刻意不封闭**(SDK events.ts 的 compacted 臂带 `[k: string]: unknown`),但 census 轴一
5938
+ # (2026-07-30)把 `compactedEventData`(trace/project.ts:340-407)真发的键**全部登记**了一遍 ——
5939
+ # 此前 spec 只有 tokensBefore 一键,连 fixture 天天在喂的 `trigger` 都不在声明面上。
5940
+ additionalProperties: true
5809
5941
  required: [type]
5810
5942
  properties:
5811
5943
  type: { const: compacted }
5812
5944
  tokensBefore: { type: integer }
5945
+ trigger: { type: string, description: 'auto(逼近窗口上限)| manual(/compact)。缺席按 auto 渲染。' }
5946
+ preserved_segment:
5947
+ type: object
5948
+ required: [firstKeptEntryId]
5949
+ properties:
5950
+ firstKeptEntryId: { type: string }
5951
+ attachedFiles:
5952
+ type: array
5953
+ description: '压缩时随行附回的工作文件清单(path 已 redact;畸形元素由 builder 丢弃,全丢则整键缺席)。'
5954
+ items:
5955
+ type: object
5956
+ required: [path, chars, truncated]
5957
+ properties:
5958
+ path: { type: string }
5959
+ chars: { type: integer }
5960
+ truncated: { type: boolean }
5961
+ modelFallback: { const: true, description: 'core 1.286:压缩摘要器回退到更大窗模型。' }
5962
+ fallbackReason: { type: string, description: "回退原因(今天只有 'window')。" }
5963
+ clampedRatio: { type: number, description: 'core 1.286:keep 比例被夹紧后的值。' }
5964
+ clampReason: { type: string, description: 'budget | walltime | tolerance。' }
5965
+ tokensAfter: { type: integer }
5966
+ triggerTokensBefore: { type: integer }
5967
+ durationMs: { type: integer }
5968
+ phaseDurations:
5969
+ type: object
5970
+ description: 'core 1.289 CompactionPhaseDurations —— builder 逐字段重建的闭合数值结构。'
5971
+ additionalProperties: false
5972
+ properties:
5973
+ prepareMs: { type: integer }
5974
+ summaryMs: { type: integer }
5975
+ persistMs: { type: integer }
5976
+ ptlRetries: { type: integer }
5813
5977
  Event_suggestions:
5814
5978
  type: object
5815
5979
  description: >
@@ -5817,6 +5981,8 @@ components:
5817
5981
  post-completion "what to ask next" prompt suggestions. Appended to the DURABLE run-events tail AFTER a
5818
5982
  `done` with `status:"completed"` (core's fire-and-forget LLM pass). 🔴 UNTRUSTED model text, service-
5819
5983
  redacted, UI-ONLY — render as clickable next-prompt chips; NEVER re-feed to a model. Empty/absent ⇒ none.
5984
+ # 封闭:铸造点 runs.ts `append("suggestions", { suggestions })` 只有这一键。
5985
+ additionalProperties: false
5820
5986
  required: [type, suggestions]
5821
5987
  properties:
5822
5988
  type: { const: suggestions }
@@ -5835,27 +6001,37 @@ components:
5835
6001
  `brain_status` alias arm was REMOVED from this union in SDK 1.0.0 (supported floor = server >=1.317, at
5836
6002
  which point the literal is never emitted on either leg).
5837
6003
  allOf: [{ $ref: '#/components/schemas/EventIdentity' }]
6004
+ additionalProperties: false # 封闭 + identity 本地镜像:见 Event_reasoning 处的长注(builder=brainStatusEventData)
5838
6005
  required: [type, phase]
5839
6006
  properties:
5840
6007
  type: { const: status }
5841
6008
  phase: { type: string, description: "rate_limited | retrying | reconnecting | circuit_open (open set)" }
5842
6009
  detail: { type: string, description: "neutral human hint (NO provider/HTTP detail)" }
5843
6010
  retryInSec: { type: number }
6011
+ eventId: { type: string }
6012
+ parentToolCallId: { type: string }
6013
+ sourceTaskId: { type: string }
6014
+ bgAgentId: { type: string }
5844
6015
  Event_task_progress:
5845
6016
  type: object
5846
6017
  description: >
5847
6018
  design/99 MF-10 (core 1.147+, `task_progress` arm) — a SUBAGENT PROGRESS TICK: a delegated sub-run's usage
5848
6019
  ACCRUING at each turn boundary WHILE it runs (tokens/tool-uses climbing), so a parent UI animates the Task
5849
6020
  card rollup. Emitted ONLY for a subagent (a task carrying a `parentToolCallId`, via EventIdentity); a
5850
- top-level run's own usage is the consumer's own (turn_end). `status` is the literal "running" (the terminal
5851
- usage is the `done` result). LIVE/EPHEMERAL — NOT persisted, NOT replayed on resume.
5852
- 🔴 WIRE NOTE: core mints this arm, but as of service 1bf7a5c NO service projection forwards it onto a CLIENT
5853
- wire (/v1/tasks/stream or /v1/runs/:id/events) — the service consumes it INTERNALLY (fleetRunPublisher) and
5854
- re-expresses it as a fleet CHILD row on /v1/fleet/stream. So today MF-10 is observable via the fleet stream
5855
- subagent child rows; this arm is the core-arm MIRROR and surfaces on the task/run streams ONLY IF a future
5856
- service projection passes it through. Additive / tolerate-absent.
6021
+ top-level run's own usage is the consumer's own (turn_end).
6022
+ 🔴 WIRE NOTE(2026-07-30 更正 — 旧注「as of service 1bf7a5c NO service projection forwards it onto a
6023
+ CLIENT wire」已是 doc-rot,SDK events.ts 早已改口):server **确实**在三条腿上转发本臂
6024
+ (`taskProgressEventData`,bg runs.ts append / resume append / 同步 live SSE),此外才是 fleet 子行。
5857
6025
  allOf: [{ $ref: '#/components/schemas/EventIdentity' }]
5858
- required: [type, taskId, usage, status]
6026
+ # 🔴 census 轴一(2026-07-30)三处纠正,依据=亲读 `taskProgressEventData`(trace/project.ts:146-166):
6027
+ # ① required 过严 —— builder 里 `usage`/`status` 都是**条件 spread**,只有 `taskId` 无条件在场;
6028
+ # spec 却把它们列进 required ⇒ 一个没带 usage 的真 tick 会被判违约(方向反了的假红)。
6029
+ # ② `status` 写成 `const: running` 太窄 —— core [1414]#3 的 settle 终态 tick("completed"/"failed")
6030
+ # server ≥1.258 原样转发,SDK 的臂也是开集字符串。
6031
+ # ③ 漏 5 个 builder 真发的键(name / parentTaskId / currentAction / workflowRunId / workflowAgentLabel),
6032
+ # 全都在 SDK events.ts 上声明着 —— 漂移只在 spec 这一侧。
6033
+ additionalProperties: false # 封闭 + identity 本地镜像:见 Event_reasoning 处的长注
6034
+ required: [type, taskId]
5859
6035
  properties:
5860
6036
  type: { const: task_progress }
5861
6037
  taskId: { type: string, description: 'The subagent task id (the delegated sub-run).' }
@@ -5867,7 +6043,16 @@ components:
5867
6043
  totalTokens: { type: integer }
5868
6044
  toolUses: { type: integer }
5869
6045
  durationMs: { type: integer }
5870
- status: { const: running, description: 'Always "running" mid-run (the terminal usage is the done result).' }
6046
+ status: { type: string, description: 'running 为主;core [1414]#3 的 settle 终态 tick 亦可为 completed/failed(开集,按未知值兜底渲染)。' }
6047
+ name: { type: string, description: '子代的人类展示名(UNTRUSTED,已 redactSecrets)。缺席 ⇒ 无名,别回退到 objective。' }
6048
+ parentTaskId: { type: string, description: '嵌套子代的上级 taskId;缺席 ⇒ 一级子代。' }
6049
+ currentAction: { type: string, description: '子代最近一次 tool_start 的一行人话("Bash npm test");UNTRUSTED,已 redactSecrets。' }
6050
+ workflowRunId: { type: string, description: 'core 1.401:后台 workflow 子代的不透明 run id(下游据此跳过与 fleet 行的重复渲染)。' }
6051
+ workflowAgentLabel: { type: string, description: '脚本作者可控的展示字符串;UNTRUSTED,已 redactSecrets。' }
6052
+ eventId: { type: string }
6053
+ parentToolCallId: { type: string }
6054
+ sourceTaskId: { type: string }
6055
+ bgAgentId: { type: string }
5871
6056
  Event_workspace_changed:
5872
6057
  type: object
5873
6058
  description: >
@@ -5875,10 +6060,15 @@ components:
5875
6060
  new RAW logical cwd so a shell UI updates its prompt/path. Only fires with a real shell mounted; the initial
5876
6061
  cwd is NOT announced (only subsequent changes). EPHEMERAL — not persisted; resets across suspend/resume.
5877
6062
  allOf: [{ $ref: '#/components/schemas/EventIdentity' }]
6063
+ additionalProperties: false # 封闭 + identity 本地镜像:见 Event_reasoning 处的长注
5878
6064
  required: [type, cwd]
5879
6065
  properties:
5880
6066
  type: { const: workspace_changed }
5881
6067
  cwd: { type: string }
6068
+ eventId: { type: string }
6069
+ parentToolCallId: { type: string }
6070
+ sourceTaskId: { type: string }
6071
+ bgAgentId: { type: string }
5882
6072
  CheckpointGate:
5883
6073
  # 从 `Event_suspended.gate` 的内联 oneOf 提取为命名 schema(2026-07-25 回填批)。两个理由:
5884
6074
  # ① 内联形生成不出可复用具名类型;② 反射式 drift 门只按「命名 schema ↔ 同名 exported interface」配对,
@@ -5936,6 +6126,9 @@ components:
5936
6126
  Event_suspended:
5937
6127
  type: object
5938
6128
  description: HITL — the run paused on an F4 gate or AskUserQuestion. Resolve via approvals.decide, then resume.
6129
+ # 封闭(census 轴一,2026-07-30)。两个铸造点都是写死的字面量:server.ts:2147 `{gate}`、
6130
+ # server.ts:2161 `{gate:null, reopened}` —— 后者的 `reopened` 此前 spec 完全没有,是本次亲读补上的。
6131
+ additionalProperties: false
5939
6132
  required: [type]
5940
6133
  properties:
5941
6134
  type: { const: suspended }
@@ -5944,6 +6137,11 @@ components:
5944
6137
  oneOf:
5945
6138
  - $ref: '#/components/schemas/CheckpointGate'
5946
6139
  - type: 'null'
6140
+ reopened:
6141
+ type: ['string', 'null']
6142
+ description: >
6143
+ server.ts:2161 —— 一条 resume 腿把 run 重新挂回 suspended 时,带上「为什么重开」的 errorCode
6144
+ (无则显式 null)。只出现在这条重开臂上;普通挂起帧不带此键。
5947
6145
  ModelUsageDelta:
5948
6146
  # 新增(2026-07-25 回填批)。SDK 的 facade 审计把这个形状命名了一次(此前是在两个使用点各写一遍的匿名内联形:
5949
6147
  # `AgentEvent` 的 `model_usage` 臂 与 `done.result.stats.modelUsage` 的值类型,同源同形)。
@@ -6031,6 +6229,7 @@ components:
6031
6229
  rendering affordances). 🔴 `url` EXPIRES after `ttlSec` seconds — render a fetch-soon affordance, never
6032
6230
  persist the URL as durable.
6033
6231
  allOf: [{ $ref: '#/components/schemas/EventIdentity' }]
6232
+ additionalProperties: false # 封闭 + identity 本地镜像:见 Event_reasoning 处的长注(server 校验入参,闭集上 wire)
6034
6233
  required: [type, url, filename, size, ttlSec, status]
6035
6234
  properties:
6036
6235
  type: { const: file_link }
@@ -6041,6 +6240,10 @@ components:
6041
6240
  status: { type: string, enum: [normal, proactive], description: '`normal` = a turn deliverable; `proactive` = a mid-run push.' }
6042
6241
  caption: { type: string }
6043
6242
  display: { type: string, enum: [render, attach], description: 'absent = the consumer''s default.' }
6243
+ eventId: { type: string }
6244
+ parentToolCallId: { type: string }
6245
+ sourceTaskId: { type: string }
6246
+ bgAgentId: { type: string }
6044
6247
 
6045
6248
  Event_prompt_assembled:
6046
6249
  type: object
@@ -6059,6 +6262,9 @@ components:
6059
6262
  description: >
6060
6263
  Per-model usage delta (live-verified on canary). `usage` is keyed BY MODEL NAME — same source as
6061
6264
  `done.result.stats.modelUsage` (lets a multi-model / role-routed run attribute spend per model).
6265
+ # 封闭:铸造点 trace/project.ts:27 `append("model_usage", { usage: delta })` 只有这一键
6266
+ #(map 的**值**仍是开集 ModelUsageDelta —— 那层的「开」是真的,不动)。
6267
+ additionalProperties: false
6062
6268
  required: [type, usage]
6063
6269
  properties:
6064
6270
  type: { const: model_usage }
@@ -6275,17 +6481,106 @@ components:
6275
6481
  Event_done:
6276
6482
  type: object
6277
6483
  description: Carries the full TaskResult (output string at .result, stats at .stats) — service Drift 4.
6484
+ additionalProperties: false # 封闭:帧本身只有 {type,result};`result` 内部保持开集(见 TaskResult 处的裁定注)
6278
6485
  required: [type, result]
6279
6486
  properties:
6280
6487
  type: { const: done }
6281
6488
  result: { $ref: '#/components/schemas/TaskResult' }
6282
6489
  Event_failed:
6283
6490
  type: object
6491
+ # 封闭 + 回填 `activeTaskId`(census 轴一,2026-07-30):[1833] G10 —— session 已有活跃 run 的冲突
6492
+ # 失败形会带上占用者 taskId(SDK events.ts 早已声明,spec 侧漏)。
6493
+ additionalProperties: false
6284
6494
  required: [type]
6285
6495
  properties:
6286
6496
  type: { const: failed }
6287
6497
  errorCode: { type: string }
6288
6498
  errorMessage: { type: string }
6499
+ activeTaskId: { type: string, description: '冲突失败形:当前占着该 session 的 run —— 消费方可直接引导 cancel/attach。' }
6500
+
6501
+ # ══════════════════════════════════════════════════════════════════════════════════════════════
6502
+ # 🔴 census 轴一(2026-07-30)补的**四个未登记臂**。它们不是「将来会有」——**今天就在 wire 上**:
6503
+ # · `context_usage` LIVE(routes/tasks.ts:653)+ DURABLE(trace/ledger-sink.ts:144 / server.ts:2115)
6504
+ # · `config_assembled` DURABLE(trace/project.ts:43)
6505
+ # · `needs_review` DURABLE(http/server.ts:2148)
6506
+ # · `message_committed` LIVE(routes/tasks.ts:650)
6507
+ # durable 腿的读边界(routes/runs.ts:99 `formatEvent`)对**每一条**落库事件原样下发 `{type, ...data}`
6508
+ # (唯一归一是 brain_status→status),所以「本仓 append 了什么」= 「wire 上会出现什么」。而本文件的
6509
+ # `AgentEvent` 是闭集 oneOf ⇒ 一个真实的 context_usage 帧对着 spec 校验**当场零臂命中**。
6510
+ # ⚠️ 元教训:`spec-union-arm-gate` 比的是 spec ↔ SDK 两侧的臂集,这四个臂**两侧同缺**,所以那道门
6511
+ # 对它们结构性失明 —— 抓到它们靠的是拿 server 的 `append("…")` / live SSE 铸造点做第三方对账。
6512
+ # ══════════════════════════════════════════════════════════════════════════════════════════════
6513
+ Event_context_usage:
6514
+ type: object
6515
+ description: >
6516
+ core 1.414 first-class arm, whitelisted by `contextUsageEventData` (server trace/project.ts:431).
6517
+ 🔴 Two engine-stated conventions, pass them through, never re-derive: (a) `usedTokens > compactAtTokens`
6518
+ IS the predicate the engine itself feeds `shouldCompact`; (b) `windowTokens` is the AUTOCOMPACT window,
6519
+ NOT the model's context size (rendering it as "model context" gives the user a wrong denominator).
6520
+ ⚠️ Reported at EVERY compaction boundary (not only when compaction happens) — many per task; take the
6521
+ last one as current. It is NOT folded into `turn_end.usage` (the boundary check runs after turn_end).
6522
+ All three scalars go through a finite-number guard; non-finite ⇒ key absent (never `null`).
6523
+ additionalProperties: false
6524
+ required: [type]
6525
+ properties:
6526
+ type: { const: context_usage }
6527
+ usedTokens: { type: integer }
6528
+ windowTokens: { type: integer, description: 'autocompact 窗,不是模型上下文大小。' }
6529
+ compactAtTokens: { type: integer }
6530
+ # LIVE 腿(routes/tasks.ts:653)在这条臂上挂 identity;durable 腿不挂。同 turn_end 的处置:
6531
+ # 本地可选声明,不 allOf(不把 identity 承诺进契约面)。
6532
+ eventId: { type: string }
6533
+ parentToolCallId: { type: string }
6534
+ sourceTaskId: { type: string }
6535
+ bgAgentId: { type: string }
6536
+ Event_config_assembled:
6537
+ type: object
6538
+ description: >
6539
+ DURABLE only ([1301]③, server trace/project.ts:43) — the per-turn companion record of `prompt_assembled`:
6540
+ which config catalog version produced this turn's effective fields, and why anything was overridden.
6541
+ Bounded frame: the builder DROPS the whole record over 32 KiB rather than shipping a truncated half-shape.
6542
+ `fields` / `overrideReasons` are engine/registry-shaped passthrough — treat as opaque display data.
6543
+ additionalProperties: false
6544
+ required: [type]
6545
+ properties:
6546
+ type: { const: config_assembled }
6547
+ catalogVersion: {}
6548
+ fields: {}
6549
+ overrideReasons: {}
6550
+ ts: {}
6551
+ Event_needs_review:
6552
+ type: object
6553
+ description: >
6554
+ DURABLE only (design/80 D-B, server http/server.ts:2148) — a RESUMED plan_review leg got re-gated into
6555
+ another review pause. Same payload shape as `suspended`: the gate verbatim, or an explicit `null`.
6556
+ NOTE `needs_review` is BOTH a `RunStatus` terminal and a `CheckpointGate.kind`; this arm is the third
6557
+ use of the name — the stream frame announcing that pause.
6558
+ additionalProperties: false
6559
+ required: [type, gate]
6560
+ properties:
6561
+ type: { const: needs_review }
6562
+ gate:
6563
+ description: 'the review gate, or an explicit null (the key is always present — `?? null` at the mint point).'
6564
+ oneOf:
6565
+ - $ref: '#/components/schemas/CheckpointGate'
6566
+ - type: 'null'
6567
+ Event_message_committed:
6568
+ type: object
6569
+ description: >
6570
+ LIVE only (server routes/tasks.ts:650, whitelisted in the same catch-all as text_delta/turn_end/
6571
+ context_usage) — a session-history entry was committed. `entryId` is the durable entry handle the
6572
+ R8 rewind anchor is keyed on ("rewind to the prompt" targets a `role:"user"` entry).
6573
+ additionalProperties: false
6574
+ required: [type, entryId, role]
6575
+ properties:
6576
+ type: { const: message_committed }
6577
+ entryId: { type: string }
6578
+ role: { type: string, description: 'user | assistant | … (open on read).' }
6579
+ toolCallId: { type: string }
6580
+ eventId: { type: string }
6581
+ parentToolCallId: { type: string }
6582
+ sourceTaskId: { type: string }
6583
+ bgAgentId: { type: string }
6289
6584
 
6290
6585
  # ── resources/*.ts schema backfill, batch 2 (2026-07-28) — the 28-item WIRE_PENDING ratchet zeroed.
6291
6586
  # Each schema below is cross-checked against the server source (file:line cited in its description