@sema-agent/sdk 0.1.9 → 1.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.
Files changed (82) hide show
  1. package/README.md +17 -0
  2. package/dist/client.d.ts +1 -1
  3. package/dist/client.d.ts.map +1 -1
  4. package/dist/client.js +1 -1
  5. package/dist/client.js.map +1 -1
  6. package/dist/errors.d.ts +97 -28
  7. package/dist/errors.d.ts.map +1 -1
  8. package/dist/errors.js +249 -117
  9. package/dist/errors.js.map +1 -1
  10. package/dist/events.d.ts +11 -22
  11. package/dist/events.d.ts.map +1 -1
  12. package/dist/events.js.map +1 -1
  13. package/dist/index.d.ts +2 -2
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +10 -1
  16. package/dist/index.js.map +1 -1
  17. package/dist/normalize.d.ts +17 -4
  18. package/dist/normalize.d.ts.map +1 -1
  19. package/dist/normalize.js +16 -9
  20. package/dist/normalize.js.map +1 -1
  21. package/dist/resources/approvals.d.ts +2 -7
  22. package/dist/resources/approvals.d.ts.map +1 -1
  23. package/dist/resources/approvals.js +3 -3
  24. package/dist/resources/approvals.js.map +1 -1
  25. package/dist/resources/assistant.d.ts +1 -1
  26. package/dist/resources/assistant.js +1 -1
  27. package/dist/resources/attachments.d.ts.map +1 -1
  28. package/dist/resources/attachments.js +3 -4
  29. package/dist/resources/attachments.js.map +1 -1
  30. package/dist/resources/fleet.d.ts +4 -4
  31. package/dist/resources/fleet.d.ts.map +1 -1
  32. package/dist/resources/fleet.js +2 -3
  33. package/dist/resources/fleet.js.map +1 -1
  34. package/dist/resources/images.d.ts +4 -4
  35. package/dist/resources/images.d.ts.map +1 -1
  36. package/dist/resources/images.js +7 -6
  37. package/dist/resources/images.js.map +1 -1
  38. package/dist/resources/leader.d.ts +2 -3
  39. package/dist/resources/leader.d.ts.map +1 -1
  40. package/dist/resources/leader.js +2 -3
  41. package/dist/resources/leader.js.map +1 -1
  42. package/dist/resources/memory.d.ts +11 -53
  43. package/dist/resources/memory.d.ts.map +1 -1
  44. package/dist/resources/memory.js +2 -51
  45. package/dist/resources/memory.js.map +1 -1
  46. package/dist/resources/models.d.ts +4 -4
  47. package/dist/resources/models.js +1 -1
  48. package/dist/resources/ops.d.ts +3 -3
  49. package/dist/resources/ops.d.ts.map +1 -1
  50. package/dist/resources/ops.js +3 -1
  51. package/dist/resources/ops.js.map +1 -1
  52. package/dist/resources/runs.d.ts.map +1 -1
  53. package/dist/resources/runs.js +1 -2
  54. package/dist/resources/runs.js.map +1 -1
  55. package/dist/resources/session-sync.d.ts +1 -2
  56. package/dist/resources/session-sync.d.ts.map +1 -1
  57. package/dist/resources/session-sync.js +4 -13
  58. package/dist/resources/session-sync.js.map +1 -1
  59. package/dist/resources/sessions.d.ts +4 -4
  60. package/dist/resources/sessions.d.ts.map +1 -1
  61. package/dist/resources/sessions.js +9 -5
  62. package/dist/resources/sessions.js.map +1 -1
  63. package/dist/resources/tasks.d.ts.map +1 -1
  64. package/dist/resources/tasks.js +1 -2
  65. package/dist/resources/tasks.js.map +1 -1
  66. package/dist/resources/workflows.d.ts.map +1 -1
  67. package/dist/resources/workflows.js +1 -2
  68. package/dist/resources/workflows.js.map +1 -1
  69. package/dist/resources/workspace.d.ts.map +1 -1
  70. package/dist/resources/workspace.js +8 -9
  71. package/dist/resources/workspace.js.map +1 -1
  72. package/dist/sse.d.ts.map +1 -1
  73. package/dist/sse.js +7 -5
  74. package/dist/sse.js.map +1 -1
  75. package/dist/transport.d.ts +25 -0
  76. package/dist/transport.d.ts.map +1 -1
  77. package/dist/transport.js +37 -2
  78. package/dist/transport.js.map +1 -1
  79. package/dist/types.d.ts +108 -55
  80. package/dist/types.d.ts.map +1 -1
  81. package/openapi.yaml +184 -219
  82. package/package.json +2 -2
package/openapi.yaml CHANGED
@@ -9,7 +9,7 @@ openapi: 3.1.0
9
9
  # 回填这里——本文件开头曾自称"the SINGLE SOURCE OF TRUTH"、"drift fails in CI"两句均已不成立
10
10
  # (spec.test.ts 只覆盖 ~15 个类型的局部字段,`PendingCheckpoint`/`TaskStats`/`Capabilities`
11
11
  # 等核心类型的大面积字段级 drift 对 CI 完全不可见)。**现行权威 = `packages/sdk/src/types.ts`
12
- # (+ events.ts/errors.ts),逐类型带 `server.ts:行号` 实拆坐标,三批核查每一处改动都配红先行
12
+ # (+ events.ts/errors.ts),逐类型带 server 实拆坐标,三批核查每一处改动都配红先行
13
13
  # 测试锚定,是目前唯一真正贴合 server 当前实现的文档**。本文件保留作 M0 时代的历史参照(早期
14
14
  # verb 清单/大致响应形状仍可读),但**任何具体字段名/是否顶层/是否为 optional 的判断,一律以
15
15
  # types.ts 为准,本文件不可信**——已实证撞获至少一处两者互斥的真矛盾(`PendingCheckpoint`,
@@ -37,6 +37,14 @@ openapi: 3.1.0
37
37
  # bake 控制面(SDK 侧还用 `Record<string, unknown>`、没有具名类型 ⇒ 应先在 types.ts 具名);
38
38
  # `SessionBundle` 目前是本文件里唯一的孤儿 schema(无人 $ref)。
39
39
  #
40
+ # ── 📍 取证坐标的约定:**文件级,不是行级**(2026-07-29 F-f 批)────────────────────────────
41
+ # 本文件与 `types.ts` 里的 server 取证坐标一律写成 **`src/http/routes/<域文件>.ts`** 这样的文件级
42
+ # 指路,不再写 `server.ts:<行号>`。理由是实测的:server 已把 `src/http/server.ts` 拆成
43
+ # `src/http/routes/*.ts`(主体只剩约 2800 行),此前散在两侧的 ~140 处行号坐标**绝大多数已经指到
44
+ # 文件结尾之外** —— 而且就算今天逐个改对,下一次重排会再烂一遍。文件级坐标对重排免疫。
45
+ # **要行级取证**:去 server 仓按符号名/路由字面量 grep,或查该文件的 git 历史 —— 那是唯一不会腐
46
+ # 的行级真源。别把行号写回来。
47
+ #
40
48
  # Consumed by @sema-agent/sdk (→ both doors: the CC/Codex MCP façade and the
41
49
  # portal BFF). Producer = the service.
42
50
  #
@@ -176,42 +184,6 @@ paths:
176
184
  application/json:
177
185
  schema: { $ref: '#/components/schemas/ErrorResponse' }
178
186
 
179
- /v1/memory:
180
- parameters:
181
- - $ref: '#/components/parameters/PrincipalHeader'
182
- get:
183
- tags: [sessions]
184
- operationId: memoryGet
185
- x-status: live # service 170c384. Owner-scoped by construction.
186
- summary: Read your own <user_memory> (transparency surface).
187
- description: >
188
- Scope derives from the request principal (same rule as task execution) — a caller CANNOT address
189
- anyone else's memory. No principal header → 401. Discover via capabilities().memory.
190
- responses:
191
- '200':
192
- description: The memory record (content null = empty).
193
- content:
194
- application/json:
195
- schema: { $ref: '#/components/schemas/MemoryRecord' }
196
- '401': { $ref: '#/components/responses/Unauthorized' }
197
- delete:
198
- tags: [sessions]
199
- operationId: memoryClear
200
- x-status: live
201
- summary: Clear your WHOLE memory scope (per-entry deletion is a future additive).
202
- responses:
203
- '200':
204
- description: Cleared.
205
- content:
206
- application/json:
207
- schema:
208
- type: object
209
- required: [ok, scope]
210
- properties:
211
- ok: { type: boolean }
212
- scope: { type: string }
213
- '401': { $ref: '#/components/responses/Unauthorized' }
214
-
215
187
  /v1/tasks:
216
188
  parameters:
217
189
  - $ref: '#/components/parameters/PrincipalHeader'
@@ -543,7 +515,10 @@ paths:
543
515
  '404': { $ref: '#/components/responses/NotFound' }
544
516
  '400': { $ref: '#/components/responses/BadRequest' }
545
517
  '409':
546
- description: 'errorCode "steering.not_running" — the run/subagent is settled or unknown (also: ambiguous agentName).'
518
+ description: >
519
+ errorCode "steering.not_running" — the run/subagent is settled or unknown; or
520
+ "steering.ambiguous_target" when more than one live subagent in this run matches the given
521
+ agentName (address it by parentToolCallId instead — retrying the same target never resolves).
547
522
  content:
548
523
  application/json:
549
524
  schema: { $ref: '#/components/schemas/ErrorResponse' }
@@ -729,7 +704,14 @@ paths:
729
704
  '413':
730
705
  description: 'errorCode "steer.content_too_large" — the revival prompt is over the cap (same gate as subagent steer).'
731
706
  '409':
732
- description: 'core''s typed rejections verbatim as `errorCode`: resume.still_running / resume.retain_off / resume.evicted / resume.cap / resume.session_not_found (design/122 D2).'
707
+ description: >
708
+ core's typed rejections verbatim as `errorCode`: resume.retain_off / resume.evicted / resume.cap /
709
+ resume.session_not_found (design/122 D2), plus "steering.ambiguous_target" (>1 live subagent matches
710
+ the target) and "steering.still_running" — the child (or a prior resume) is STILL IN FLIGHT, so
711
+ revive is not yet legal: steer it instead, or wait for it to settle.
712
+ 🔴 CORRECTION (2026-07-29, read off `src/http/routes/runs.ts` in server 3.x): the in-flight code is
713
+ `steering.still_running`, NOT `resume.still_running` — this line named a code the server never sends.
714
+ The `resume.*` family is the retain/evict/cap/session set only.
733
715
  content:
734
716
  application/json:
735
717
  schema: { $ref: '#/components/schemas/ErrorResponse' }
@@ -900,7 +882,7 @@ paths:
900
882
  🔴 The 5 query params below are ADDITIVE: a request with NONE of them returns the byte-identical legacy
901
883
  payload. `?message=` SHORT-CIRCUITS to a different response schema (`SessionMessageEnvelope`) — see below.
902
884
  # 补漏(2026-07-25):此前本操作**一个 query 参数都没声明**,所以整个分页/截断/单条展开面对生成式消费方
903
- # 等于不存在。以下 5 个按 server 亲读取证(`http/server.ts:5692-5723`)。
885
+ # 等于不存在。以下 5 个按 server 亲读取证(`src/http/routes/sessions.ts`)。
904
886
  parameters:
905
887
  - name: message
906
888
  in: query
@@ -1400,7 +1382,7 @@ paths:
1400
1382
  deliberately serve as octet-stream — agent output is untrusted; `x-content-type-options: nosniff`
1401
1383
  always set) + the `x-sema-content-binary` discriminator header (first-8KiB NUL sniff, [1894]③).
1402
1384
  Capped at `capabilities.workspace.maxFileBytes` (default 8MiB, WORKSPACE_FILE_MAX_BYTES) — over-cap ⇒
1403
- 413 STRUCTURED envelope `{code: "workspace_file_too_large", sizeBytes, limit}` ([1894]①; the archive
1385
+ 413 STRUCTURED envelope `{errorCode: "workspace_file_too_large", sizeBytes, limit}` ([1894]①; the archive
1404
1386
  endpoint is the escape hatch). When the SQL size index knows the size, the 413 fires BEFORE fetching
1405
1387
  the bytes.
1406
1388
  parameters:
@@ -1419,15 +1401,15 @@ paths:
1419
1401
  '401': { $ref: '#/components/responses/Unauthorized' }
1420
1402
  '404': { $ref: '#/components/responses/NotFound' }
1421
1403
  '413':
1422
- description: 'Structured over-cap envelope: {error, code: "workspace_file_too_large", sizeBytes, limit}.'
1404
+ description: 'Structured over-cap envelope: {error, errorCode: "workspace_file_too_large", sizeBytes, limit}.'
1423
1405
  content:
1424
1406
  application/json:
1425
1407
  schema:
1426
1408
  type: object
1427
- required: [error, code, sizeBytes, limit]
1409
+ required: [error, errorCode, sizeBytes, limit]
1428
1410
  properties:
1429
1411
  error: { type: string }
1430
- code: { type: string, enum: [workspace_file_too_large] }
1412
+ errorCode: { type: string, enum: [workspace_file_too_large] }
1431
1413
  sizeBytes: { type: integer }
1432
1414
  limit: { type: integer }
1433
1415
  '501': { $ref: '#/components/responses/NotImplemented' }
@@ -1565,90 +1547,6 @@ paths:
1565
1547
  schema: { $ref: '#/components/schemas/McpStatusPanel' }
1566
1548
  '401': { $ref: '#/components/responses/Unauthorized' }
1567
1549
 
1568
- /v1/sessions/{sessionId}/memory:
1569
- parameters:
1570
- - $ref: '#/components/parameters/PrincipalHeader'
1571
- - in: path
1572
- name: sessionId
1573
- required: true
1574
- schema: { type: string }
1575
- post:
1576
- tags: [sessions]
1577
- operationId: memoryAppend
1578
- x-status: live # MF-30, service @sema-agent/server 1.3.0 (server.ts:2880).
1579
- summary: Append a memory note (MF-30 — the /memory write verb).
1580
- description: >
1581
- 🔴 Memory is PER-PRINCIPAL, not per-session — {sessionId} is only the shell's addressing context (the
1582
- /memory command runs inside some session); a note appended for a principal is visible from ALL that
1583
- principal's sessions. Note stored VERBATIM (≤8 KiB, user-authored = intentional, NOT redacted).
1584
- Empty/oversized → 400. Always available when capabilities().memoryWrite.
1585
- requestBody:
1586
- required: true
1587
- content:
1588
- application/json:
1589
- schema:
1590
- type: object
1591
- required: [content]
1592
- properties: { content: { type: string } }
1593
- responses:
1594
- '200':
1595
- description: Append ack — unified { ok, id, scope } (service; handler always returns ok).
1596
- content:
1597
- application/json:
1598
- schema: { $ref: '#/components/schemas/MemoryWriteAck' }
1599
- '400': { description: "Empty / oversized / missing content." }
1600
- '401': { $ref: '#/components/responses/Unauthorized' }
1601
-
1602
- /v1/sessions/{sessionId}/memory/{noteId}:
1603
- parameters:
1604
- - $ref: '#/components/parameters/PrincipalHeader'
1605
- - in: path
1606
- name: sessionId
1607
- required: true
1608
- schema: { type: string }
1609
- - in: path
1610
- name: noteId
1611
- required: true
1612
- schema: { type: string }
1613
- patch:
1614
- tags: [sessions]
1615
- operationId: memoryEdit
1616
- x-status: live # MF-30 (server.ts:2881).
1617
- summary: Edit one memory note by id (id unchanged).
1618
- description: >
1619
- 🔴 501 when the backend lacks id-addressable update (honest degrade, never a fake 200). Per-PRINCIPAL.
1620
- requestBody:
1621
- required: true
1622
- content:
1623
- application/json:
1624
- schema:
1625
- type: object
1626
- required: [content]
1627
- properties: { content: { type: string } }
1628
- responses:
1629
- '200':
1630
- description: Edited note ack.
1631
- content:
1632
- application/json:
1633
- schema: { $ref: '#/components/schemas/MemoryWriteAck' }
1634
- '401': { $ref: '#/components/responses/Unauthorized' }
1635
- '501': { description: "Backend lacks id-addressable update." }
1636
- delete:
1637
- tags: [sessions]
1638
- operationId: memoryForget
1639
- x-status: live # MF-30 (server.ts:2882).
1640
- summary: Forget one memory note by id.
1641
- description: >
1642
- 🔴 501 when the backend lacks id-addressable delete (honest degrade). Per-PRINCIPAL.
1643
- responses:
1644
- '200':
1645
- description: Forgotten note ack.
1646
- content:
1647
- application/json:
1648
- schema: { $ref: '#/components/schemas/MemoryWriteAck' }
1649
- '401': { $ref: '#/components/responses/Unauthorized' }
1650
- '501': { description: "Backend lacks id-addressable delete." }
1651
-
1652
1550
  # ── 2c session-sync (P1d) — the cloud as a SYNC PEER ──────────────────────────────────────────────────────────
1653
1551
  # The local shell's local backend ⇄ this cloud service. PULL a session's whole state out, or PUSH the local
1654
1552
  # peer's in. Gate the whole surface off `capabilities.sessionSync` (= durable backend + entry export + a
@@ -1997,9 +1895,8 @@ paths:
1997
1895
  description: >
1998
1896
  Resolves a `suspended` run: an F4 approve/deny, or an answer to an AskUserQuestion. The resumed run
1999
1897
  continues its durable stream (`/v1/runs/:id/events`). design/80 D-1: the decision SHOULD carry the
2000
- action binding (checkpointToken/boundCallId/boundInputHash read off the PendingCheckpoint) — the server
2001
- resolves THAT exact checkpoint, fail-closed on mismatch. (Path param stays `sessionId` until service
2002
- finalizes a checkpointToken-keyed route; the binding travels in the body regardless.)
1898
+ action binding (boundCallId + boundInputHash, read off the PendingCheckpoint) — the server resolves THAT
1899
+ exact checkpoint, fail-closed on mismatch. (Path param stays `sessionId`; the binding travels in the body.)
2003
1900
  requestBody:
2004
1901
  required: true
2005
1902
  content:
@@ -2179,7 +2076,7 @@ paths:
2179
2076
  Owner-gated (NOT D-G); non-owner → 404. SDK → GateNotResumableError on the 409.
2180
2077
  responses:
2181
2078
  '200':
2182
- # 2026-07-25:此前只声明 `{status}`。真体(`http/server.ts:7688-7696`,与 plan_review 共用
2079
+ # 2026-07-25:此前只声明 `{status}`。真体(`src/http/routes/approvals-assistant.ts`,与 plan_review 共用
2183
2080
  # `driveResumeIntoRunLog`)带 taskId/sessionId + 失败态的 errorCode/errorMessage/retriable。
2184
2081
  description: >
2185
2082
  Resumed. 🔴 A 200 does NOT mean success — `status` can be `failed`, in which case `errorCode`/
@@ -2491,7 +2388,7 @@ paths:
2491
2388
  get:
2492
2389
  tags: [models]
2493
2390
  operationId: modelsList
2494
- x-status: live # service GET /v1/models (server.ts:859-882). SDK models.list().
2391
+ x-status: live # service GET /v1/models (src/http/routes/capabilities.ts). SDK models.list().
2495
2392
  summary: Non-secret model catalog (the `@model` picker / autocomplete data source).
2496
2393
  description: >
2497
2394
  READ-ONLY catalog of `@mention`-pickable models. The user selects a model PER-TASK by typing `@<name>` in
@@ -2787,7 +2684,7 @@ paths:
2787
2684
  get:
2788
2685
  tags: [fleet]
2789
2686
  operationId: fleetStream
2790
- x-status: live # service 1bf7a5c — streamFleet (http/server.ts:4874) + fleet/fleet-bus.ts. Owner-gated; in-process bus; NOT resumable.
2687
+ x-status: live # service 1bf7a5c — streamFleet (src/http/routes/fleet.ts) + fleet/fleet-bus.ts. Owner-gated; in-process bus; NOT resumable.
2791
2688
  summary: Live multi-row fleet view SSE (NON-resumable).
2792
2689
  description: >
2793
2690
  MF-Fleet (shell-host data contract). The LIVE fleet as an SSE PUSH stream: a SNAPSHOT on connect, then a
@@ -3474,9 +3371,12 @@ components:
3474
3371
  schema: { $ref: '#/components/schemas/ErrorResponse' }
3475
3372
  RateLimited:
3476
3373
  description: >
3477
- 429 — per-principal CUMULATIVE quota exceeded (`quotaExceeded`). `Retry-After` (seconds) honored by the
3478
- SDK on retry. This is the ONLY budget/limit signal at the HTTP layer; per-task budget gates are
3479
- result-level (200 + errorCode), NOT here (pinned wire contract).
3374
+ 429 — a limit was hit. Three real `errorCode`s ride this status: `limit.cost_quota_exceeded`
3375
+ (per-principal CUMULATIVE cost quota — the QuotaError body below), `limit.rate_exceeded`
3376
+ (request-rate limit; also the status-derived fallback code), and `quota_exhausted` (the E4 fleet
3377
+ quota-LEASE budget). `Retry-After` (seconds) honored by the SDK on retry. These are the ONLY
3378
+ budget/limit signals at the HTTP layer; per-task budget gates are result-level (200 + errorCode),
3379
+ NOT here (pinned wire contract).
3480
3380
  headers:
3481
3381
  Retry-After:
3482
3382
  schema: { type: integer }
@@ -3521,7 +3421,7 @@ components:
3521
3421
 
3522
3422
  ScenarioDetail:
3523
3423
  # 新增(2026-07-25 回填批):`GET /v1/capabilities/scenarios/{name}` 此前**整条路径**都不在本文件里
3524
- # (只有 `Capabilities.scenarios` 那个名字数组)。形按 server 亲读取证(`http/server.ts:1517-1527`)。
3424
+ # (只有 `Capabilities.scenarios` 那个名字数组)。形按 server 亲读取证(`src/http/routes/capabilities.ts`)。
3525
3425
  type: object
3526
3426
  description: >
3527
3427
  One scenario's detail card (`GET /v1/capabilities/scenarios/{name}`). The LIST of names rides
@@ -3557,7 +3457,6 @@ components:
3557
3457
  promptTokens: { type: integer }
3558
3458
  cachedTokens: { type: integer }
3559
3459
  outputTokens: { type: integer }
3560
- costUsd: { type: number, deprecated: true, description: "Legacy float track — prefer integer costMicroUsd for accounting (no FP drift); canonical read = the SDK's taskCostMicroUsd() normalizer. Kept on the wire (older engines still emit it)." }
3561
3460
  cacheHitRate: { type: number }
3562
3461
  toolCalls: { type: integer }
3563
3462
  cacheWriteTokens: { type: integer, description: Short-TTL cache-write tokens. }
@@ -3568,8 +3467,15 @@ components:
3568
3467
  costMicroUsd:
3569
3468
  type: integer
3570
3469
  description: >
3571
- Integer micro-USD cost. Prefer this over the float `costUsd` for accounting (no FP drift). Both are
3572
- emitted; they describe the same spend.
3470
+ Integer micro-USD cost — the ONLY cost track on this object (core 2.0.0 removed the legacy float
3471
+ `costUsd` at the source, server 1.319.0 adopted it, and SDK 1.0.0 removed the mirroring field plus its
3472
+ ×1e6 fallback). Canonical read = the SDK's `taskCostMicroUsd()` normalizer (integer pass-through + a
3473
+ non-finite guard).
3474
+ ⚠️ SCOPE: this removal covers `TaskStats` (and its nested/checkpoint variants) ONLY. The float
3475
+ `costUsd` on the SERVER's own accounting surfaces is alive and untouched — `GET /metrics/summary`
3476
+ (`costUsd`/`costUsdByModel`) and the usage-analytics face (`UsageTotals.costUsd`, `?metric=costUsd`)
3477
+ are server-side aggregates computed as Σ costMicroUsd / 1e6. Seeing those is NOT evidence of an
3478
+ un-upgraded peer.
3573
3479
  costBreakdown:
3574
3480
  description: >
3575
3481
  Per-axis cost detail (LLM root / nested subagents / compaction / infra). Deliberately untyped — it is an
@@ -3665,6 +3571,25 @@ components:
3665
3571
  sessionId:
3666
3572
  type: string
3667
3573
  description: Continue a conversation (same sessionId across turns). Omit on first turn → service mints one.
3574
+ requireExistingSession:
3575
+ type: boolean
3576
+ description: >
3577
+ design/114 Phase3 warm-resume assertion: require `sessionId` to ALREADY exist. `true` + a genuinely
3578
+ missing session (expired / purged / never created) ⇒ the run FAILS LOUD (`resume.session_not_found`)
3579
+ instead of silently starting a fresh empty session ("looks warm, actually fresh"). Sending `true`
3580
+ with NO sessionId also fails loud (nothing to resume). Absent/`false` ⇒ the historical create-on-miss.
3581
+ Rides the persisted body onto resume legs.
3582
+ projectId:
3583
+ type: string
3584
+ description: >
3585
+ design/142-S4: which PROJECT this run belongs to (a center registry key — generic lowercase UUID).
3586
+ A SELECTOR, not an identity or a capability: the tenant segment of any scope always comes from the
3587
+ verified principal, and the client only passes this through (zero minting authority). Shape-gated in
3588
+ the server authorizer — a malformed value is a 422 with `errorCode: "invalid_project_id"`. On the
3589
+ multi-tenant DB memory face it moves the derived scope from the user shelf to the project shelf
3590
+ (`proj:<tenant>/<projectId>`) and seeds `memory.scopes` from that project's `defaultScopes`;
3591
+ single-user deployments ignore the derivation and only apply the seed. Rides the persisted body
3592
+ onto resume legs.
3668
3593
  jobId:
3669
3594
  type: string
3670
3595
  minLength: 1
@@ -3717,6 +3642,18 @@ components:
3717
3642
  branch via setLeafId). With rewindFiles=true ⇒ "both" (also restore the working tree). Alone ⇒
3718
3643
  "conversation". HONORED only where the deployment wired a resume-anchor store + getLeafId (capabilities.resumeAt);
3719
3644
  unknown anchor ⇒ 4xx, capability-off ⇒ 501. Cannot combine with a durable resume.
3645
+ resumeAtMode:
3646
+ type: string
3647
+ enum: [at, before]
3648
+ description: >
3649
+ [833] EXCLUSIVE rewind (core 1.292 TaskSpec.resumeAtMode) — qualifies `resumeAt`'s landing point.
3650
+ `at` (the default when absent — zero regression) KEEPS the target message in the branched context;
3651
+ `before` branches at the target's PARENT, EXCLUDING the target itself ("remove this prompt and
3652
+ everything after it"). core enforces the edges and the service passes its rejection codes through
3653
+ UNCHANGED for the shell to render: `resume_at.before_target_not_user` (plain user-message targets
3654
+ only), `resume_at.before_root_unsupported` (cannot rewind past the session root — start a new
3655
+ session), `rewind_snapshot.unresolvable` (with rewindFiles: no snapshot at/above the branch point).
3656
+ Only meaningful WITH `resumeAt` — sent alone it is a 400 (fail-loud, not a silent no-op).
3720
3657
  rewindFiles:
3721
3658
  type: boolean
3722
3659
  description: >
@@ -3890,7 +3827,28 @@ components:
3890
3827
  deadlineNudge: { type: boolean, enum: [false] }
3891
3828
  callCapByDeadline: { type: boolean, enum: [false] }
3892
3829
  gracefulFinalize: { type: boolean, enum: [false] }
3830
+ outputSchema:
3831
+ type: object
3832
+ additionalProperties: true
3833
+ description: >
3834
+ Structured-output constraint (CC `--json-schema`): a JSON Schema the model's FINAL answer must match.
3835
+ Threaded into core's TaskSpec.outputSchema, which injects a built-in `submit_output` tool and surfaces
3836
+ the validated object as `TaskResult.structuredOutput`. An invalid submit retries (see `outputRetries`),
3837
+ then fails the task with `output.invalid`. A plain JSON-Schema OBJECT — the server validates shape and
3838
+ size; deep validity is core's. (Paired knob: `outputRetries` was already declared without this one.)
3893
3839
  outputRetries: { type: integer, description: How many times to retry a malformed structured output. }
3840
+ resilience:
3841
+ type: object
3842
+ description: >
3843
+ design/131 (core 1.246) per-task resilience INTENT flags. `allowDegrade` / `allowFailover` are
3844
+ caller-facing (permit model degradation / provider failover for this run). `bypassBreaker` is
3845
+ OPERATOR-ONLY: for a non-operator caller resolveSpec DROPS the key — a permission downgrade, not a
3846
+ 400, so a 2xx does NOT mean the breaker was actually bypassed. All opt-in; absent ⇒ the deployment's
3847
+ default resilience posture.
3848
+ properties:
3849
+ allowDegrade: { type: boolean }
3850
+ allowFailover: { type: boolean }
3851
+ bypassBreaker: { type: boolean, description: 'Operator-only; silently dropped for non-operator callers.' }
3894
3852
  compaction:
3895
3853
  type: object
3896
3854
  description: Compaction tuning for this task.
@@ -4143,7 +4101,10 @@ components:
4143
4101
 
4144
4102
  QuotaError:
4145
4103
  type: object
4146
- description: 429 per-principal cumulative-quota body (`quotaExceeded`).
4104
+ description: >
4105
+ 429 per-principal cumulative COST-quota body (`errorCode: "limit.cost_quota_exceeded"`). The other two
4106
+ 429 codes carry different bodies: `limit.rate_exceeded` sends only `retryAfterSec`, and
4107
+ `quota_exhausted` (E4 lease) passes the center's structured deny fields through verbatim.
4147
4108
  additionalProperties: true
4148
4109
  properties:
4149
4110
  error: { type: string }
@@ -4304,10 +4265,11 @@ components:
4304
4265
  selected: { type: array, items: { type: string } }
4305
4266
  note: { type: string }
4306
4267
  reason: { type: string }
4307
- # design/80 D-1 — action binding (TOCTOU guard). Read the three off the PendingCheckpoint the human saw
4268
+ # design/80 D-1 — action binding (TOCTOU guard). Read the TWO off the PendingCheckpoint the human saw
4308
4269
  # and echo VERBATIM; server resolves THAT exact checkpoint, fail-closed on mismatch (approval_binding_mismatch).
4309
4270
  # Omit only against a pre-D-1 worker. boundInputHash is OPAQUE — never hashed/recomputed client-side.
4310
- checkpointToken: { type: string, description: 'Single-use token of the exact checkpoint the human saw.' }
4271
+ # 🔴 `checkpointToken` REMOVED (SDK 1.0.0): §4a makes it server-INTERNAL and it is NEVER surfaced on a
4272
+ # /v1/approvals row, so a compliant client has no way to obtain a correct value for it.
4311
4273
  boundCallId: { type: string, description: '= PendingCheckpoint.boundCallId (the bound pendingAction.toolCallId).' }
4312
4274
  boundInputHash: { type: string, description: 'Echo PendingCheckpoint.boundInputHash byte-for-byte; server-minted opaque, NEVER recompute.' }
4313
4275
  updatedInput:
@@ -4337,7 +4299,14 @@ components:
4337
4299
  description: false ⇒ stats.costMicroUsd=0 may mean "no MODEL_COST_* configured", not "free".
4338
4300
  memory:
4339
4301
  type: boolean
4340
- description: Memory transparency endpoints (GET/DELETE /v1/memory) available. the pinned wire contract.
4302
+ description: >
4303
+ 🔴 HARD-CODED `false` on every current server (design/138 S1, clay 2026-07-08): it advertised the
4304
+ legacy MemoryStore surface (`GET`/`DELETE /v1/memory`), which was retired together with the store
4305
+ itself. The memory ENGINE that replaced it is model-facing skills over an injected memory dir and
4306
+ has NO service HTTP verbs, so this flag is not its availability signal. The SDK removed the five
4307
+ legacy verbs in 1.0.0; the live face is `client.memory.exportScope` / `.sync`
4308
+ (`GET /v1/memory/export`, `POST /v1/memory/sync/{scope}`), which this flag does NOT gate.
4309
+ Kept on the wire so an older shell that still reads it honestly hides the dead affordances.
4341
4310
  workflows: { type: boolean, description: "S8 self-orchestration workflow routes are mounted." }
4342
4311
  workflowsList: { type: boolean, description: "`GET /v1/workflows` list face (separate from the per-run reads)." }
4343
4312
  resumeAt: { type: boolean, description: "E18 resume-at-message anchors are resolvable." }
@@ -4444,12 +4413,16 @@ components:
4444
4413
  hooks: { type: boolean }
4445
4414
  memoryWrite:
4446
4415
  type: boolean
4447
- description: MF-30 — memory WRITE (append/edit/forget) available (deps.memory.append; server.ts:828).
4416
+ description: >
4417
+ 🔴 HARD-CODED `false` on every current server, for the same reason as `memory` above: it advertised
4418
+ the MF-30 session memory WRITE verbs, retired with the legacy MemoryStore (SDK 1.0.0 removed them).
4419
+ NOT related to `TaskRequest.memoryWrite` (the per-run memory-engine write PAUSE toggle), which is
4420
+ live and ungated by this flag.
4448
4421
  sessionSync:
4449
4422
  type: boolean
4450
4423
  description: >
4451
4424
  2c session-sync (P1d) — the `/v1/sessions/{sessionId}/sync/*` peer routes resolve (durable backend +
4452
- entry-export seam + a file-snapshot store wired; server.ts:850). Gate the whole sync surface off this.
4425
+ entry-export seam + a file-snapshot store wired; src/http/routes/capabilities.ts). Gate the whole sync surface off this.
4453
4426
 
4454
4427
  SkillSpec:
4455
4428
  type: object
@@ -4495,28 +4468,10 @@ components:
4495
4468
  egress: { type: boolean }
4496
4469
  irreversibility: { type: string, enum: [always, never] }
4497
4470
 
4498
- MemoryRecord:
4499
- type: object
4500
- description: The caller's own <user_memory> (owner-scoped by construction). content null = empty.
4501
- required: [scope, content]
4502
- properties:
4503
- scope: { type: string }
4504
- content: { type: ['string', 'null'] }
4505
-
4506
- MemoryWriteAck:
4507
- type: object
4508
- description: MF-30 write ack — unified { ok, id, scope } for append/edit/forget (service; the handler
4509
- always returns ok — the old src:2880 summary comment that dropped it was a doc-bug). id = the note (unchanged on edit).
4510
- required: [ok, id, scope]
4511
- properties:
4512
- ok: { type: boolean }
4513
- id: { type: string }
4514
- scope: { type: string }
4515
-
4516
4471
  ModelInfo:
4517
4472
  type: object
4518
4473
  description: >
4519
- A non-secret `@model` catalog entry (GET /v1/models; service server.ts:860-880). `name` = the `@handle` the
4474
+ A non-secret `@model` catalog entry (GET /v1/models; service src/http/routes/capabilities.ts). `name` = the `@handle` the
4520
4475
  user types in the objective body to pick this model per-task. 🔴 NON-SECRET — name/capabilities only, never
4521
4476
  baseUrl/apiKey/headers (the service strips them). Open set (additive future fields).
4522
4477
  required: [name]
@@ -4867,7 +4822,7 @@ components:
4867
4822
  tokens: { type: integer }
4868
4823
  FleetFrame:
4869
4824
  description: >
4870
- One decoded frame off GET /v1/fleet/stream (service streamFleet emit set, http/server.ts:4877-4908). On the
4825
+ One decoded frame off GET /v1/fleet/stream (service streamFleet emit set, src/http/routes/fleet.ts). On the
4871
4826
  wire this is SSE framing (the `type` here = the SSE `event` name; the object = the decoded `data:` JSON).
4872
4827
  Emit order: `meta` (FIRST) → `snapshot` (connect-time full set) → per-row `task`/`workflow` upserts +
4873
4828
  `task_remove`/`workflow_remove` departures. The SDK PRODUCES these frames; the consumer maintains the
@@ -5070,24 +5025,38 @@ components:
5070
5025
  description: >
5071
5026
  The `GET /v1/approvals` operator queue row (true shape). NEVER includes a capability token.
5072
5027
  ⚠️ `createdAt`/`deadline` are EPOCH MILLISECONDS (numbers), not ISO strings.
5073
- 🔴 真矛盾撞获+纠正(2026-07-25,core [1640] 审计撞获 + server 复核确认 server.ts:4536→
5028
+ 🔴 真矛盾撞获+纠正(2026-07-25,core [1640] 审计撞获 + server 复核确认 src/http/routes/approvals-assistant.ts→
5074
5029
  listPending() 真实实现 `src/plugins/tidb-checkpoint-store.ts:54-79`):这份 schema 此前声称
5075
5030
  "MIRRORS core CheckpointSummary,顶层 gateKind/severity,NEVER a riskDescriptor object"——
5076
5031
  与真实实现完全相反。真实的 `PendingCheckpoint`(service 自定义类型,不是 core 的
5077
5032
  `CheckpointSummary`)**没有 token 字段**(store 层注释:"deliberately NO token")、**没有
5078
- 顶层 gateKind**、**没有顶层 severity**——risk 信息只在 `riskDescriptor` 对象里(§1 的
5033
+ 顶层 severity**——risk 信息只在 `riskDescriptor` 对象里(§1 的
5079
5034
  `CheckpointSummary`/`/v1/assistant/inbox` 才是那个有顶层 severity 标量、无 riskDescriptor
5080
5035
  的姊妹形状,是完全不同的端点,不要混)。按真实实现改写如下,与
5081
5036
  `packages/sdk/src/types.ts` 的 `PendingCheckpoint` interface(已验证与 server 实现一致)
5082
5037
  对齐。
5038
+ 🔴 二次纠正(2026-07-29,server 3.0.0 [1995]③):上一版里"**没有顶层 gateKind**"这句话已经
5039
+ 过期 —— 3.0.0 additive 加了顶层 `gateKind`,两个 checkpoint store(local 文件店 / SQL 店)的
5040
+ 富行投影都发它。"顶层 severity 仍然没有"那半句依旧成立,两件事不要一起读。
5083
5041
  required: [sessionId, scope, createdAt]
5084
5042
  additionalProperties: true
5085
5043
  properties:
5086
5044
  sessionId: { type: string, description: 'The decide handle: POST /v1/approvals/:sessionId/decide.' }
5087
5045
  scope: { type: string, description: 'Principal; "_" = submitted without one.' }
5088
5046
  taskId: { type: ['string', 'null'], description: 'Suspended run holding the session claim (service-pinned) — join key to trace tool-call blocks + context link.' }
5089
- toolName: { type: ['string', 'null'], description: Tool awaiting approval (AskUserQuestion = the question gate; riskDescriptor 无 gateKind 时的类别推断来源). }
5047
+ toolName: { type: ['string', 'null'], description: 'Tool awaiting approval (AskUserQuestion = the question gate). NULL on every non-tool gate — read `gateKind` for the category, do NOT infer it from this.' }
5090
5048
  toolCallId: { type: ['string', 'null'] }
5049
+ gateKind:
5050
+ type: string
5051
+ description: >
5052
+ Which gate this row is parked on (server >=3.0.0, ADDITIVE — both checkpoint stores project it).
5053
+ Lets a durable-recovery consumer route plan / tool / resource off the SAME queue: a plan gate takes
5054
+ the plan_review decision, a tool gate takes /decide, a resource gate takes resume (going through the
5055
+ wrong door yields `gate_not_resumable` / `gate_not_plan_review`).
5056
+ 🔴 OPEN SET — deliberately NOT an enum: observed values include `human`, `irreversible_ask`,
5057
+ `plan_review`, `needs_review`, `resource_limit`, and core keeps adding. Pinning an enum would make a
5058
+ new gate kind "not exist" for generated clients.
5059
+ ABSENT on a pre-`gate_kind` legacy row (SQL column NULL) — degrade honestly, never assume `human`.
5091
5060
  input:
5092
5061
  description: >
5093
5062
  The pending tool call's args (post-hook), REDACTED + size-bounded — for a tool gate the write
@@ -5243,7 +5212,7 @@ components:
5243
5212
  type: string
5244
5213
  # 🔴 `needs_review` 是 2026-07-25 补的:server 的行过滤器逐字是
5245
5214
  # `.filter(r => r.status === "running" || r.status === "suspended" || r.status === "needs_review")`
5246
- # (`http/server.ts:4677`,已亲读取证)——本枚举此前只有前两个,是真陈旧,照它生成的客户端会把一个
5215
+ # (`src/http/routes/approvals-assistant.ts`,已亲读取证)——本枚举此前只有前两个,是真陈旧,照它生成的客户端会把一个
5247
5216
  # 合法的一等 triage 态判成非法值。字段级 drift 门只比字段名、比不到枚举值,所以它没抓到这条。
5248
5217
  enum: [running, suspended, needs_review]
5249
5218
  description: >
@@ -5277,9 +5246,9 @@ components:
5277
5246
  AssistantTaskStatus:
5278
5247
  # 新增(2026-07-25 回填批):§4b resume / §4c plan_review / §4d preempt 三条决策端点此前在本文件里都只声明
5279
5248
  # 了 `{status}` 一个字段——与 SDK 三批核查前的旧类型同样陈旧。下面的形按 server 源码逐字取证:
5280
- # • resume / plan_review 200(共用 `driveResumeIntoRunLog`,`http/server.ts:7688-7696`):
5249
+ # • resume / plan_review 200(共用 `driveResumeIntoRunLog`,`src/http/routes/approvals-assistant.ts`):
5281
5250
  # `{ taskId, sessionId, status, errorCode?, errorMessage?, retriable? }`(后三个条件 spread ⇒ 缺则省键)
5282
- # • preempt 202(`http/server.ts:4726/4734/4737` 三个出口):`{ taskId, status, note }`
5251
+ # • preempt 202(`src/http/routes/approvals-assistant.ts` 三个出口):`{ taskId, status, note }`
5283
5252
  # ⚠️ `status` 在 202 上**并非恒为 `"preempting"`**:两个 no-op 出口分别回 `run.status` 与
5284
5253
  # `now?.status ?? "failed"` —— 所以这里不能把它写成 const。
5285
5254
  type: object
@@ -5649,17 +5618,33 @@ components:
5649
5618
  description: >
5650
5619
  Typed-error source. The SDK maps (status, errorCode) → a semantic error class so callers branch on the
5651
5620
  error, not a string. Messages are secret-scrubbed before surfacing.
5652
- 🔴 CANONICAL KEY (server >=1.302, 2026-07-28 归一): `errorCode` is ALWAYS present on typed error
5653
- bodies — the historical `code`-only track is retired (a source-level gate enforces it server-side).
5654
- `code` may still ride alongside as the historical/original-core-code slot; consumers read `errorCode`.
5621
+ 🔴 SINGLE KEY (server >=3.0.0): `errorCode` is the ONLY machine key on a typed error body. The
5622
+ historical `code` slot is GONE at the source (`http/send.ts` emits `{error, errorCode, ...extra}`;
5623
+ a source-level gate enforces it server-side) and the SDK no longer reads it — a body that still
5624
+ sends `code` yields `errorCode: undefined` and no typed subclass.
5655
5625
  additionalProperties: true
5656
5626
  properties:
5657
5627
  errorCode:
5658
5628
  type: string
5659
5629
  description: >
5660
- HTTP-error typed code. Known: `conflict` (409 session active / CAS), `quotaExceeded` (429).
5630
+ HTTP-error typed code. Real examples: `conflict` (409 session active / CAS);
5631
+ `limit.cost_quota_exceeded` (429 — the principal's CUMULATIVE cost quota; body = QuotaError);
5632
+ `limit.rate_exceeded` (429 — request-rate limit, also the status-derived fallback code);
5633
+ `quota_exhausted` (429 — the E4 fleet quota-LEASE budget, distinct from the cost quota).
5661
5634
  NOTE: budget (`budget.precall`/`budget.exceeded`) and `cancelled` are RESULT-level codes on
5662
5635
  TaskResult/RunRecord, NOT HTTP errors (pinned wire contract).
5636
+ 🔴 STABLE PREFIX FAMILIES (wire-contract Appendix A, criterion 3) — nine prefixes are a stable
5637
+ COARSE branch surface, so falling back on the prefix (unknown code → look at its prefix → only then
5638
+ at the status) is always safe: `auth.` `request.` `not_found.` `conflict.` `limit.` `capability.`
5639
+ `feature.` `internal.` `state.`. The SDK maps each family to a typed class, so a code this SDK has
5640
+ never seen still degrades INFORMATIVELY (e.g. a future `capability.xyz_required` already lands on
5641
+ CapabilityUnavailableError). Family examples not named elsewhere in this document:
5642
+ `request.payload_too_large` (413 — a field or the whole body is over the server cap; shorten and
5643
+ resend, retrying verbatim never succeeds), `state.model_roster_pending` (503 — retry shortly),
5644
+ `internal.cancel_not_terminalized` (500 — retry the cancel).
5645
+ The two 501 families are deliberately NOT interchangeable: `capability.*` means this deployment did
5646
+ not wire that surface (change the deployment / hide the entry point), `feature.*` means the surface
5647
+ exists but its switch is off (ask an admin to turn it on).
5663
5648
  error: { type: string }
5664
5649
  errorMessage: { type: string }
5665
5650
  activeTaskId:
@@ -5700,8 +5685,6 @@ components:
5700
5685
  - $ref: '#/components/schemas/Event_steering_injected'
5701
5686
  - $ref: '#/components/schemas/Event_done'
5702
5687
  - $ref: '#/components/schemas/Event_failed'
5703
- # design/158 B2: deprecated durable-leg alias — see Event_brain_status's own description.
5704
- - $ref: '#/components/schemas/Event_brain_status'
5705
5688
  discriminator:
5706
5689
  propertyName: type
5707
5690
  mapping:
@@ -5727,7 +5710,6 @@ components:
5727
5710
  steering_injected: '#/components/schemas/Event_steering_injected'
5728
5711
  done: '#/components/schemas/Event_done'
5729
5712
  failed: '#/components/schemas/Event_failed'
5730
- brain_status: '#/components/schemas/Event_brain_status'
5731
5713
 
5732
5714
  # design/99 §E2 (SHIPPED, core TaskEventIdentity) — additive per-event identity on every CONTENT event (NOT on
5733
5715
  # the lifecycle arms turn_end/compacted/suspended/done/failed). eventId = uuidv7 dedup/resume handle (the durable
@@ -5831,7 +5813,7 @@ components:
5831
5813
  Event_suggestions:
5832
5814
  type: object
5833
5815
  description: >
5834
- E12 (shell-host; service runs.ts:392 + server.ts:3633, opt-in via `TaskRequest.suggestNextPrompts`) —
5816
+ E12 (shell-host; service runs.ts:392 + src/http/routes/runs.ts, opt-in via `TaskRequest.suggestNextPrompts`) —
5835
5817
  post-completion "what to ask next" prompt suggestions. Appended to the DURABLE run-events tail AFTER a
5836
5818
  `done` with `status:"completed"` (core's fire-and-forget LLM pass). 🔴 UNTRUSTED model text, service-
5837
5819
  redacted, UI-ONLY — render as clickable next-prompt chips; NEVER re-feed to a model. Empty/absent ⇒ none.
@@ -5848,10 +5830,10 @@ components:
5848
5830
  phase on its own channel). 🔴 WIRE NOTE (design/158 B2, corrects the prior "EPHEMERAL — never
5849
5831
  persisted/replayed on resume" claim): the durable/replay leg (GET /v1/runs/:id/events) DOES persist this
5850
5832
  as its own liveness observation row. Server >=1.317 uses this SAME arm name on both legs AND normalizes
5851
- pre-upgrade rows (stored under the literal `brain_status`) to `status` at the read boundary — stored
5852
- bytes untouched, but a >=1.317 server never delivers the old literal; it only reaches consumers of a
5853
- server <1.317 (see Event_brain_status below — same shape, deprecated alias, additive-only, never emitted
5854
- on the live leg at any version).
5833
+ pre-upgrade rows (stored under the historical literal `brain_status`) to `status` at the read boundary —
5834
+ stored bytes untouched, but a >=1.317 server never delivers the old literal. 🔴 The deprecated
5835
+ `brain_status` alias arm was REMOVED from this union in SDK 1.0.0 (supported floor = server >=1.317, at
5836
+ which point the literal is never emitted on either leg).
5855
5837
  allOf: [{ $ref: '#/components/schemas/EventIdentity' }]
5856
5838
  required: [type, phase]
5857
5839
  properties:
@@ -5859,23 +5841,6 @@ components:
5859
5841
  phase: { type: string, description: "rate_limited | retrying | reconnecting | circuit_open (open set)" }
5860
5842
  detail: { type: string, description: "neutral human hint (NO provider/HTTP detail)" }
5861
5843
  retryInSec: { type: number }
5862
- Event_brain_status:
5863
- type: object
5864
- deprecated: true
5865
- description: >
5866
- design/158 B2 — DEPRECATED historical durable-leg alias for Event_status (same field shape). Server
5867
- <1.317 wrote (and on replay delivers) the liveness-observation row under the literal type `brain_status`
5868
- instead of `status`. Server >=1.317 never writes this literal AND normalizes pre-upgrade rows to `status`
5869
- at the read boundary, so this literal only ever reaches a consumer whose server is <1.317 — kept in the
5870
- spec/SDK union only so that consumer can still type-check the frame. Never emitted on the live leg
5871
- (POST /v1/tasks/stream) at any server version.
5872
- allOf: [{ $ref: '#/components/schemas/EventIdentity' }]
5873
- required: [type, phase]
5874
- properties:
5875
- type: { const: brain_status }
5876
- phase: { type: string, description: "rate_limited | retrying | reconnecting | circuit_open (open set)" }
5877
- detail: { type: string, description: "neutral human hint (NO provider/HTTP detail)" }
5878
- retryInSec: { type: number }
5879
5844
  Event_task_progress:
5880
5845
  type: object
5881
5846
  description: >
@@ -6329,7 +6294,7 @@ components:
6329
6294
  ApprovalStreamEvent:
6330
6295
  type: object
6331
6296
  description: >
6332
- One delta event off GET /v1/approvals/stream (SSE; server streamApprovals, http/server.ts:8924).
6297
+ One delta event off GET /v1/approvals/stream (SSE; server streamApprovals, src/http/routes/approvals-assistant.ts).
6333
6298
  Every data payload echoes a `type` field mirroring the SSE event name (so a client can dispatch
6334
6299
  without the `event:` line). OPEN set — a consumer that does not parse the delta can treat any
6335
6300
  non-heartbeat event as a "changed" signal and refetch the authoritative GET /v1/approvals list.
@@ -6366,7 +6331,7 @@ components:
6366
6331
  FleetMeta:
6367
6332
  type: object
6368
6333
  description: >
6369
- The connect-time `meta` frame payload of GET /v1/fleet/stream (server http/server.ts:9675 —
6334
+ The connect-time `meta` frame payload of GET /v1/fleet/stream (server src/http/routes/fleet.ts —
6370
6335
  the payload view of FleetFrame_meta, without the SSE frame envelope).
6371
6336
  required: [version, scoped]
6372
6337
  additionalProperties: true
@@ -6455,7 +6420,7 @@ components:
6455
6420
  type: object
6456
6421
  description: >
6457
6422
  The RUNNER claim response of POST /v1/images/bakes/claim and POST /v1/images/bakes/{bakeId}/claim
6458
- (server claimResponse, http/server.ts:9164 — exact key set): the VETTED argv + the per-bake ingest
6423
+ (server claimResponse, src/http/routes/images.ts — exact key set): the VETTED argv + the per-bake ingest
6459
6424
  secret (minted at claim, echoed on every ingest via the x-bake-ingest-secret header) + the
6460
6425
  profile/bands the runner needs for its profile-aware hard deadline. The runner executes `argv`
6461
6426
  VERBATIM — it never assembles its own command line. 204 (no body) = queue empty.
@@ -6497,7 +6462,7 @@ components:
6497
6462
  type: object
6498
6463
  description: >
6499
6464
  One SendUserFile distribution-link ledger row of GET /v1/sendfile-links (envelope `{links,
6500
- nextBefore?}`; server http/server.ts:5504 — exact projection). A principal caller is PINNED to its
6465
+ nextBefore?}`; server src/http/routes/sessions-list.ts — exact projection). A principal caller is PINNED to its
6501
6466
  own scope (?scope= ignored); a fleet credential may pass ?scope= (default = the single-user
6502
6467
  sentinel "_"). Page with `before` = a previous page's `nextBefore` (uuidv7; malformed → 400).
6503
6468
  required: [id, filename, size, ttlSec, track, bucket, key, createdAt]
@@ -6518,7 +6483,7 @@ components:
6518
6483
  type: object
6519
6484
  description: >
6520
6485
  The 200 receipt of POST /v1/runs/{runId}/subagents/{target}/steer and …/resume (server
6521
- http/server.ts:4143/4148 — exact literal: `status:"running"`, `delivery:"applied"`, `marker` =
6486
+ src/http/routes/runs.ts — exact literal: `status:"running"`, `delivery:"applied"`, `marker` =
6522
6487
  the engine-minted delivery marker, `note` = the CC-verbatim "Message queued for delivery…" copy).
6523
6488
  required: [taskId, target]
6524
6489
  additionalProperties: true
@@ -6534,7 +6499,7 @@ components:
6534
6499
  type: object
6535
6500
  description: >
6536
6501
  The 200 body of GET /v1/runs/{runId}/subagents/{target}/output and the generic
6537
- …/tasks/{target}/output|/stop faces (server http/server.ts:3852/4070). `content` = the engine
6502
+ …/tasks/{target}/output|/stop faces (server src/http/routes/runs.ts). `content` = the engine
6538
6503
  TaskOutput-tool text (the child's final assistant body once terminal) — UNTRUSTED model output,
6539
6504
  same posture as a run `result`. `output` = the engine registry's UnifiedTaskOutput projection
6540
6505
  passed through VERBATIM.
@@ -6571,7 +6536,7 @@ components:
6571
6536
  CompactAck:
6572
6537
  type: object
6573
6538
  description: >
6574
- The 202 ack of POST /v1/runs/{runId}/compact (K-1c manual compaction; server http/server.ts:3595 —
6539
+ The 202 ack of POST /v1/runs/{runId}/compact (K-1c manual compaction; server src/http/routes/runs.ts —
6575
6540
  exact literal: `status:"running"`, `delivery:"accepted"`). ACCEPTANCE, not an outcome: compaction
6576
6541
  runs at the next turn boundary and a `compacted{trigger:"manual"}` event rides the run stream ONLY
6577
6542
  if anything was summarized (mooted/failed → no event).
@@ -6586,7 +6551,7 @@ components:
6586
6551
  DetachAck:
6587
6552
  type: object
6588
6553
  description: >
6589
- The 202 ack of POST /v1/runs/{runId}/detach (server http/server.ts:3650 — exact literal:
6554
+ The 202 ack of POST /v1/runs/{runId}/detach (server src/http/routes/runs.ts — exact literal:
6590
6555
  `toolCallId` echoed, `delivery:"requested"`). Fire-and-forget: acceptance only — the
6591
6556
  authoritative outcome is the tool call's own `tool_end.structured {detached:true, task_id}`;
6592
6557
  a non-detachable env makes the request a fail-safe no-op.
@@ -6601,7 +6566,7 @@ components:
6601
6566
  SideQueryRequest:
6602
6567
  type: object
6603
6568
  description: >
6604
- The POST /v1/side-query body ([1469], server 1.242; http/server.ts:1853 — whitelist passthrough of
6569
+ The POST /v1/side-query body ([1469], server 1.242; src/http/routes/side-query.ts — whitelist passthrough of
6605
6570
  core SideQuerySpec). One-shot brain routing Q&A: no session side effects, no tool EXECUTION, no
6606
6571
  engine-side policy gate, no streaming (v1). `signal` is server territory — client disconnect
6607
6572
  abandons the call; sending it in the body is discarded. Rides every billable-submit gate
@@ -6633,7 +6598,7 @@ components:
6633
6598
  type: object
6634
6599
  description: >
6635
6600
  The 200 body of POST /v1/side-query (core SideQueryResult passed through verbatim, server
6636
- http/server.ts:1917). `model` = routing identity; `servedModel` = who actually served (degrading
6601
+ src/http/routes/side-query.ts). `model` = routing identity; `servedModel` = who actually served (degrading
6637
6602
  fallback attributes honestly). A post-stream error lands in `errorMessage`, NOT an HTTP error
6638
6603
  (400 is reserved for validation/model-resolution; 5xx = a server defect).
6639
6604
  required: [text, toolCalls, model, servedModel, usage, stopReason]
@@ -6737,7 +6702,7 @@ components:
6737
6702
  type: object
6738
6703
  description: >
6739
6704
  The common window envelope of the /v1/usage/{summary,series,breakdown} responses (server
6740
- http/server.ts:1284). `from`/`to` are the RESOLVED window (ISO-8601; defaults now-7d..now).
6705
+ src/http/routes/trace-usage.ts). `from`/`to` are the RESOLVED window (ISO-8601; defaults now-7d..now).
6741
6706
  required: [from, to]
6742
6707
  additionalProperties: true
6743
6708
  properties:
@@ -6749,14 +6714,14 @@ components:
6749
6714
  description: 'Present (true) ⇔ the store scan hit its row limit (USAGE_SCAN_LIMIT) — the window was truncated, never silently.'
6750
6715
 
6751
6716
  UsageSummary:
6752
- description: 'The 200 body of GET /v1/usage/summary — window envelope + totals (server http/server.ts:1286).'
6717
+ description: 'The 200 body of GET /v1/usage/summary — window envelope + totals (server src/http/routes/trace-usage.ts).'
6753
6718
  allOf:
6754
6719
  - $ref: '#/components/schemas/UsageWindowBase'
6755
6720
  - $ref: '#/components/schemas/UsageTotals'
6756
6721
 
6757
6722
  UsageSeries:
6758
6723
  description: >
6759
- The 200 body of GET /v1/usage/series (server http/server.ts:1294). UTC buckets; EMPTY BUCKETS ARE
6724
+ The 200 body of GET /v1/usage/series (server src/http/routes/trace-usage.ts). UTC buckets; EMPTY BUCKETS ARE
6760
6725
  NOT EMITTED — consumers zero-fill as needed.
6761
6726
  allOf:
6762
6727
  - $ref: '#/components/schemas/UsageWindowBase'
@@ -6776,7 +6741,7 @@ components:
6776
6741
 
6777
6742
  UsageBreakdown:
6778
6743
  description: >
6779
- The 200 body of GET /v1/usage/breakdown (server http/server.ts:1302). dimension=model expands the
6744
+ The 200 body of GET /v1/usage/breakdown (server src/http/routes/trace-usage.ts). dimension=model expands the
6780
6745
  per-model echo (one run may contribute to several model buckets; fallback rows land in
6781
6746
  "(unattributed)"); dimension=principal maps a NULL owner to "(anonymous)".
6782
6747
  allOf:
@@ -6798,7 +6763,7 @@ components:
6798
6763
  WorkflowAgentSteerReceipt:
6799
6764
  type: object
6800
6765
  description: >
6801
- The 200 receipt of POST /v1/workflows/{runId}/agents/{label}/steer (server http/server.ts:3752 —
6766
+ The 200 receipt of POST /v1/workflows/{runId}/agents/{label}/steer (server src/http/routes/workflows.ts —
6802
6767
  exact literal: `status:"running"`, `delivery:"applied"`, `marker` = the engine-minted delivery
6803
6768
  marker; NO `note`, unlike the run-subagent verb).
6804
6769
  required: [runId, label]
@@ -6814,7 +6779,7 @@ components:
6814
6779
  type: object
6815
6780
  description: >
6816
6781
  One snapshot row of GET /v1/sessions/{sessionId}/workspace (envelope `{sessionId, snapshots,
6817
- latest?, total}`; server http/server.ts:6109 — newest first, uuidv7 keys). E19 snapshot store's
6782
+ latest?, total}`; server src/http/routes/sessions.ts — newest first, uuidv7 keys). E19 snapshot store's
6818
6783
  read-only projection: each row is the freeze at a turn boundary, NOT a live workspace.
6819
6784
  required: [key, files]
6820
6785
  additionalProperties: true
@@ -6827,7 +6792,7 @@ components:
6827
6792
  type: object
6828
6793
  description: >
6829
6794
  One file entry of GET /v1/sessions/{sessionId}/workspace/{key}/tree (envelope `{sessionId, key,
6830
- entries, total, nextOffset?}`; server http/server.ts:6144 — stable path order, offset paging).
6795
+ entries, total, nextOffset?}`; server src/http/routes/sessions.ts — stable path order, offset paging).
6831
6796
  required: [path, hash]
6832
6797
  additionalProperties: true
6833
6798
  properties: