@sema-agent/sdk 16.1.0 → 16.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +62 -1
  2. package/dist/errors.d.ts +47 -11
  3. package/dist/errors.d.ts.map +1 -1
  4. package/dist/errors.js +26 -4
  5. package/dist/errors.js.map +1 -1
  6. package/dist/index.d.ts +3 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/resources/adoption.d.ts +2 -1
  10. package/dist/resources/adoption.d.ts.map +1 -1
  11. package/dist/resources/adoption.js +2 -1
  12. package/dist/resources/adoption.js.map +1 -1
  13. package/dist/resources/approvals.d.ts +10 -3
  14. package/dist/resources/approvals.d.ts.map +1 -1
  15. package/dist/resources/approvals.js +11 -3
  16. package/dist/resources/approvals.js.map +1 -1
  17. package/dist/resources/assistant.d.ts +4 -2
  18. package/dist/resources/assistant.d.ts.map +1 -1
  19. package/dist/resources/assistant.js.map +1 -1
  20. package/dist/resources/leader.d.ts +5 -4
  21. package/dist/resources/leader.d.ts.map +1 -1
  22. package/dist/resources/leader.js +5 -4
  23. package/dist/resources/leader.js.map +1 -1
  24. package/dist/resources/memory.d.ts +6 -2
  25. package/dist/resources/memory.d.ts.map +1 -1
  26. package/dist/resources/memory.js +3 -1
  27. package/dist/resources/memory.js.map +1 -1
  28. package/dist/resources/ops.d.ts +3 -0
  29. package/dist/resources/ops.d.ts.map +1 -1
  30. package/dist/resources/ops.js +3 -0
  31. package/dist/resources/ops.js.map +1 -1
  32. package/dist/resources/rules.d.ts +4 -3
  33. package/dist/resources/rules.d.ts.map +1 -1
  34. package/dist/resources/rules.js +3 -3
  35. package/dist/resources/rules.js.map +1 -1
  36. package/dist/resources/runs.d.ts +20 -6
  37. package/dist/resources/runs.d.ts.map +1 -1
  38. package/dist/resources/runs.js +3 -2
  39. package/dist/resources/runs.js.map +1 -1
  40. package/dist/resources/session-sync.d.ts +11 -7
  41. package/dist/resources/session-sync.d.ts.map +1 -1
  42. package/dist/resources/session-sync.js +9 -6
  43. package/dist/resources/session-sync.js.map +1 -1
  44. package/dist/resources/sessions.d.ts +9 -3
  45. package/dist/resources/sessions.d.ts.map +1 -1
  46. package/dist/resources/sessions.js +9 -3
  47. package/dist/resources/sessions.js.map +1 -1
  48. package/dist/resources/tasks.d.ts +7 -1
  49. package/dist/resources/tasks.d.ts.map +1 -1
  50. package/dist/resources/tasks.js +7 -1
  51. package/dist/resources/tasks.js.map +1 -1
  52. package/dist/resources/tool-approvals.d.ts +17 -3
  53. package/dist/resources/tool-approvals.d.ts.map +1 -1
  54. package/dist/resources/tool-approvals.js +4 -0
  55. package/dist/resources/tool-approvals.js.map +1 -1
  56. package/dist/resources/workflows.d.ts +5 -2
  57. package/dist/resources/workflows.d.ts.map +1 -1
  58. package/dist/resources/workflows.js +5 -2
  59. package/dist/resources/workflows.js.map +1 -1
  60. package/dist/settings.d.ts +7 -3
  61. package/dist/settings.d.ts.map +1 -1
  62. package/dist/sync.d.ts +19 -2
  63. package/dist/sync.d.ts.map +1 -1
  64. package/dist/sync.js +88 -2
  65. package/dist/sync.js.map +1 -1
  66. package/dist/types.d.ts +160 -19
  67. package/dist/types.d.ts.map +1 -1
  68. package/dist/types.js.map +1 -1
  69. package/openapi.yaml +318 -63
  70. package/package.json +1 -1
package/openapi.yaml CHANGED
@@ -416,7 +416,8 @@ paths:
416
416
  🔴 PREPARE-TIME REFUSALS ARE TRIAGED ON WORK DONE (server >= 7.104.0 / S-271, one rule for the whole table): a
417
417
  prepare-time refusal code that HAS a status in the server's per-code triage table (`resume_at.*` — `resume_at.not_found` is 404, the rest 422 —, governance codes,
418
418
  `rewind.store_unconfigured` / `rewind.capture_disabled` / `rewind.child_scope_unsupported` / `rewind_snapshot.unresolvable`, the
419
- registered `config.*` codes incl. the three roster-bound ones, `corrupt_session`; codes outside the table, e.g. `rewind.invalid_spec`, stay
419
+ registered `config.*` codes incl. the three roster-bound ones and — server >= 7.116.0 — `config.invalid_checkpoint_store` (400) and
420
+ `config.user_invoked_skill_unknown` (400: top-level `userInvokedSkills` named a skill this run does not carry), `corrupt_session`; codes outside the table, e.g. `rewind.invalid_spec`, stay
420
421
  200 with a failed body) answers its 4xx / 5xx ERROR BODY only when the whole submission did ZERO WORK (cumulative
421
422
  `stats.turns === 0` AND `stats.tokens === 0` — the common case: nothing billed, not idempotency-cached). When a
422
423
  `verify` / `cascade` run's earlier tier already ran turns and only the last tier is refused, the SAME code arrives
@@ -960,7 +961,8 @@ paths:
960
961
  un-started ones settle as interrupted pairs, in-flight tools are NOT collateral. TWO shapes,
961
962
  discriminated by `text` presence.
962
963
  WITH text = cut + steer: the in-flight turn is cut and the text goes to the FRONT of the queue — the
963
- `"now"` tier is the VERB's semantics, not a body field (the steer op's `priority` stays advisory) —
964
+ `"now"` tier is the VERB's semantics, not a body field (the steer op's own `priority` is a separate
965
+ input: it takes effect only on a server whose `Capabilities.fleet.steerPriority` reads `"engine"`) —
964
966
  and the run CONTINUES. 202 `{taskId, status, delivery:"queued-immediate", messageId, note}`;
965
967
  `messageId` ≡ the engine inputId, and a real cut is proven ONLY by a `task.turn_interrupted` event on
966
968
  the run stream whose `detail.inputId` matches it (core: no false interrupt claims — every real cut
@@ -2419,9 +2421,10 @@ paths:
2419
2421
  written. DETERMINISTIC: a retry cannot change it (the SDK does not auto-retry this code; it arrives as a bare `APIError`
2420
2422
  with `errorCode: "corrupt"`); the repair is the operator's — fix the file or delete it (deleting = an explicit "no
2421
2423
  rules"). For a damaged rules row `error` names the session, the damage grade and both exits, with NO on-disk path.
2422
- ⚠️ The same code also comes from the write path's high-water-mark sidecar (`.hwm.json`) being unreadable, and that one
2423
- sentence STILL names the sidecar's on-disk path (server 7.114.0 discloses it as a known gap: core #1307 / #1309, core
2424
- 7.41.0). Also `internal.error` (unclassified).
2424
+ ⚠️ The same code also comes from the write path's high-water-mark sidecar (`.hwm.json`) being unreadable: the sentence names the
2425
+ object, the damage and both exits (fix the mark file, or delete it — deleting gives up the number it was holding). server >= 7.116.0
2426
+ does NOT name the on-disk path any more (the location goes to the operator log `file_store_corrupt_read` only); server <= 7.115.x
2427
+ still did. Also `internal.error` (unclassified).
2425
2428
  content:
2426
2429
  application/json:
2427
2430
  schema: { $ref: '#/components/schemas/ErrorResponse' }
@@ -3039,14 +3042,13 @@ paths:
3039
3042
  2c session-sync PUSH Phase A — a SMALL metadata body (`entryIds, snapshots, policy, anchors, resolution?`); the
3040
3043
  entries stream in Phase B. The cloud classifies (§7), pre-checks blob presence (a missing referenced hash →
3041
3044
  422 `missing_blob` — PUT it first), guards the import lease + any active run (409), then mints a stagingId.
3042
- ⚠️ S-15 EPOCH DEBT (registered 2026-08-30, codex adversarial review; migration is a SEPARATE batch):
3043
- server ≥7.52's Phase A actually consumes `{entryIds, fileHistory?, policy, anchors, resolution?,
3044
- logDigest?}` (routes/session-sync.ts) — the `snapshots` key below is SILENTLY IGNORED there (an
3045
- unknown body key), so on ≥7.52 a push lands entries/policy/anchors and the session itself but NO
3046
- file snapshots/history; the snapshot-epoch shape below is what ≤7.51 servers consume. The full
3047
- migration (SessionManifest/SessionBundle/push-pull orchestration + `fileHistory: FileHistoryExport |
3048
- null` + epoch negotiation) is tracked as its own debt — this op's schema deliberately stays the
3049
- ≤7.51 truth until that batch lands rather than half-changing.
3045
+ ⚠️ S-15 EPOCH: server ≥7.52's Phase A consumes `{entryIds, fileHistory?, policy, anchors, resolution?,
3046
+ logDigest?}` (routes/session-sync.ts) — the `snapshots` key is SILENTLY IGNORED there (an unknown body key); the
3047
+ file history rides `fileHistory` (a `FileHistoryExport` envelope, `null` / absent = none) and every blob it references must have been
3048
+ `PUT …/sync/blobs/{hash}` first. ≤7.51 servers consume `snapshots` and do not know `fileHistory`. SDK 16.2.0 carries
3049
+ `fileHistory` in `pushSessionBundle` / `importSessionBundle` (and fills `snapshots: []` on the pull side when the manifest omits it); a hand-rolled
3050
+ push must send `fileHistory` itself, and only to a peer whose `capabilities.sessionSyncFileHistory` is not false (server >= 7.116.0 — else the
3051
+ commit 422s `missing_blob`).
3050
3052
  `identical` → `{ relation:"identical", basis, payloadVerified }` with NO stagingId (skip Phase B);
3051
3053
  `basis` is `"entry-ids+digest"` when the caller supplied a comparable digest and it matched, else
3052
3054
  `"entry-ids"` (id-set equality only) — see the `ImportStaged` schema, which has carried these two
@@ -3063,6 +3065,11 @@ paths:
3063
3065
  properties:
3064
3066
  entryIds: { type: array, items: { type: string } }
3065
3067
  snapshots: { type: array, items: { $ref: '#/components/schemas/SyncSnapshot' } }
3068
+ fileHistory:
3069
+ oneOf:
3070
+ - { $ref: '#/components/schemas/FileHistoryExport' }
3071
+ - { type: 'null' }
3072
+ description: 'server >= 7.52: the file-history envelope (replaces `snapshots`); `null` / absent = none. Every blob it references must already be PUT.'
3066
3073
  policy: { type: array, items: { $ref: '#/components/schemas/SessionRulesRecord' } }
3067
3074
  anchors: { type: array, items: { $ref: '#/components/schemas/SyncAnchor' } }
3068
3075
  resolution: { type: string, enum: [overwrite-dst], description: Override a fork/stale loss-of-history conflict. }
@@ -4639,10 +4646,11 @@ paths:
4639
4646
  `LEADER_ENABLED` (default OFF) + `REMOTE_EXEC=e2b`. Mode is a SERVER deploy flag, not a client choice:
4640
4647
  `LEADER_FANOUT_ENABLED=true` (default) lets the router fan out to N isolated workers (= the value-HOLD
4641
4648
  correctness bet); `=false` pins a single worker. Either way it runs the E2B+git pipeline. Door B's brain
4642
- is the strong single agent via `/v1/tasks`+`/v1/runs`+`scenario`. When disabled (`LEADER_ENABLED` off)
4643
- the route is simply NOT REGISTERED → the generic **404** `not_found.route`, not 501.
4644
- (Corrected 2026-07-31: "When disabled → 501" was never true — no code path emits 501 here; a consumer
4645
- branching on 501 to detect "leader unavailable" never fires.)
4649
+ is the strong single agent via `/v1/tasks`+`/v1/runs`+`scenario`. When there is no leader face on the deployment
4650
+ (`LEADER_ENABLED` off) the answer depends on the server: server >= 7.116.0 mounts the route unconditionally and answers
4651
+ **501 `capability.leader_endpoint_required`** (new code; under multi-tenant the identity gate's 401 still comes first);
4652
+ <= 7.115.x did not register the route at all → the generic **404** `not_found.route`. Detect "leader unavailable" by
4653
+ `errorCode` (or the `capabilities.leader` bit, which reads the same fact as the gate), not by the status alone.
4646
4654
 
4647
4655
 
4648
4656
  🔴 IDEMPOTENCY (server >= 7.24.0): this POST now consumes the `Idempotency-Key` header — the header is
@@ -4783,7 +4791,8 @@ paths:
4783
4791
  description: >-
4784
4792
  (declared 2026-07-31) errorCode "auth.operator_only" — EXPLICIT operator gate (not the bare
4785
4793
  isOperator, whose empty-OPERATOR_PRINCIPALS "true-for-all" would make this index-mutating door
4786
- world-writable).
4794
+ world-writable). With an EMPTY `OPERATOR_PRINCIPALS` the answer is instead 501
4795
+ `capability.operator_principals_required` (server >= 7.116.0; any identity, ahead of authorization).
4787
4796
  content:
4788
4797
  application/json:
4789
4798
  schema: { $ref: '#/components/schemas/ErrorResponse' }
@@ -4837,7 +4846,7 @@ paths:
4837
4846
  '400': { $ref: '#/components/responses/BadRequest' }
4838
4847
  '401': { $ref: '#/components/responses/Unauthorized' }
4839
4848
  '403':
4840
- description: (declared 2026-07-31) errorCode "auth.operator_only" — EXPLICIT operator gate (§P2.4a).
4849
+ description: (declared 2026-07-31) errorCode "auth.operator_only" — EXPLICIT operator gate (§P2.4a). With an EMPTY `OPERATOR_PRINCIPALS` the answer is instead 501 `capability.operator_principals_required` (server >= 7.116.0; any identity, ahead of authorization — branch on `errorCode`, not the status).
4841
4850
  content:
4842
4851
  application/json:
4843
4852
  schema: { $ref: '#/components/schemas/ErrorResponse' }
@@ -4891,7 +4900,7 @@ paths:
4891
4900
  responses:
4892
4901
  '200': { description: '{ bake }.', content: { application/json: { schema: { type: object, required: [bake], additionalProperties: false, properties: { bake: { $ref: '#/components/schemas/BakeRecordView' } } } } } }
4893
4902
  '403':
4894
- description: (declared 2026-07-31) errorCode "auth.operator_only" — EXPLICIT operator gate.
4903
+ description: (declared 2026-07-31) errorCode "auth.operator_only" — EXPLICIT operator gate. With an EMPTY `OPERATOR_PRINCIPALS` the answer is instead 501 `capability.operator_principals_required` (server >= 7.116.0; any identity, ahead of authorization — branch on `errorCode`, not the status).
4895
4904
  content:
4896
4905
  application/json:
4897
4906
  schema: { $ref: '#/components/schemas/ErrorResponse' }
@@ -4909,7 +4918,7 @@ paths:
4909
4918
  responses:
4910
4919
  '202': { description: '{ bakeId, cancelRequested, note }.', content: { application/json: { schema: { $ref: '#/components/schemas/BakeCancelAck' } } } }
4911
4920
  '403':
4912
- description: (declared 2026-07-31) errorCode "auth.operator_only" — EXPLICIT operator gate.
4921
+ description: (declared 2026-07-31) errorCode "auth.operator_only" — EXPLICIT operator gate. With an EMPTY `OPERATOR_PRINCIPALS` the answer is instead 501 `capability.operator_principals_required` (server >= 7.116.0; any identity, ahead of authorization — branch on `errorCode`, not the status).
4913
4922
  content:
4914
4923
  application/json:
4915
4924
  schema: { $ref: '#/components/schemas/ErrorResponse' }
@@ -4971,7 +4980,7 @@ paths:
4971
4980
  responses:
4972
4981
  '200': { description: 'SSE stream.', content: { text/event-stream: { schema: { type: string } } } }
4973
4982
  '403':
4974
- description: (declared 2026-07-31) errorCode "auth.operator_only" — EXPLICIT operator gate (this summary previously did not mention the restriction at all).
4983
+ description: (declared 2026-07-31) errorCode "auth.operator_only" — EXPLICIT operator gate (this summary previously did not mention the restriction at all). With an EMPTY `OPERATOR_PRINCIPALS` the answer is instead 501 `capability.operator_principals_required` (server >= 7.116.0; any identity, ahead of authorization — branch on `errorCode`, not the status).
4975
4984
  content:
4976
4985
  application/json:
4977
4986
  schema: { $ref: '#/components/schemas/ErrorResponse' }
@@ -5183,7 +5192,7 @@ paths:
5183
5192
  schema: { $ref: '#/components/schemas/EntryProvenanceAccount' }
5184
5193
  '400': { description: 'errorCode "request.id_invalid" — the entry id could not be decoded.' }
5185
5194
  '401': { $ref: '#/components/responses/Unauthorized' }
5186
- '403': { description: 'errorCode "auth.operator_only" — provenance and erasure are operator-only deployment actions.' }
5195
+ '403': { description: 'errorCode "auth.operator_only" — provenance and erasure are operator-only deployment actions. With an EMPTY `OPERATOR_PRINCIPALS` the answer is instead 501 `capability.operator_principals_required` (server >= 7.116.0; any identity, ahead of authorization — branch on `errorCode`, not the status).' }
5187
5196
  '500':
5188
5197
  description: >
5189
5198
  errorCode "internal.memory_control_plane_corrupt" — core's FAIL-CLOSED refusal: the control plane
@@ -5253,7 +5262,7 @@ paths:
5253
5262
  application/json:
5254
5263
  schema: { $ref: '#/components/schemas/ErrorResponse' }
5255
5264
  '401': { $ref: '#/components/responses/Unauthorized' }
5256
- '403': { description: 'errorCode "auth.operator_only" — provenance and erasure are operator-only deployment actions.' }
5265
+ '403': { description: 'errorCode "auth.operator_only" — provenance and erasure are operator-only deployment actions. With an EMPTY `OPERATOR_PRINCIPALS` the answer is instead 501 `capability.operator_principals_required` (server >= 7.116.0; any identity, ahead of authorization — branch on `errorCode`, not the status).' }
5257
5266
  '409':
5258
5267
  description: >
5259
5268
  Three core-minted codes, passed through verbatim; branch on `errorCode` (the status cannot tell them
@@ -5332,7 +5341,7 @@ paths:
5332
5341
  schema: { $ref: '#/components/schemas/MemoryOriginExternalResult' }
5333
5342
  '400': { description: 'errorCode "request.query_invalid" — `scopes` is absent or normalizes to an empty list. The refusal text also names where scope keys come from and repeats that an empty answer is not a clean store.' }
5334
5343
  '401': { $ref: '#/components/responses/Unauthorized' }
5335
- '403': { description: 'errorCode "auth.operator_only" — origin marking and clearance are operator-only deployment actions.' }
5344
+ '403': { description: 'errorCode "auth.operator_only" — origin marking and clearance are operator-only deployment actions. With an EMPTY `OPERATOR_PRINCIPALS` the answer is instead 501 `capability.operator_principals_required` (server >= 7.116.0; any identity, ahead of authorization — branch on `errorCode`, not the status).' }
5336
5345
  '500':
5337
5346
  description: 'errorCode "internal.memory_control_plane_corrupt" (meaning (1) only on this read face) or "internal.error" (unclassified, text not echoed).'
5338
5347
  content:
@@ -5382,7 +5391,7 @@ paths:
5382
5391
  application/json:
5383
5392
  schema: { $ref: '#/components/schemas/MemoryOriginClearancesResult' }
5384
5393
  '401': { $ref: '#/components/responses/Unauthorized' }
5385
- '403': { description: 'errorCode "auth.operator_only" — origin marking and clearance are operator-only deployment actions.' }
5394
+ '403': { description: 'errorCode "auth.operator_only" — origin marking and clearance are operator-only deployment actions. With an EMPTY `OPERATOR_PRINCIPALS` the answer is instead 501 `capability.operator_principals_required` (server >= 7.116.0; any identity, ahead of authorization — branch on `errorCode`, not the status).' }
5386
5395
  '500':
5387
5396
  description: 'errorCode "internal.memory_control_plane_corrupt" (the ledger cannot be read soundly) or "internal.error".'
5388
5397
  content:
@@ -5446,7 +5455,7 @@ paths:
5446
5455
  application/json:
5447
5456
  schema: { $ref: '#/components/schemas/ErrorResponse' }
5448
5457
  '401': { $ref: '#/components/responses/Unauthorized' }
5449
- '403': { description: 'errorCode "auth.operator_only" — origin marking and clearance are operator-only deployment actions.' }
5458
+ '403': { description: 'errorCode "auth.operator_only" — origin marking and clearance are operator-only deployment actions. With an EMPTY `OPERATOR_PRINCIPALS` the answer is instead 501 `capability.operator_principals_required` (server >= 7.116.0; any identity, ahead of authorization — branch on `errorCode`, not the status).' }
5450
5459
  '409':
5451
5460
  description: >
5452
5461
  Six core-minted codes, passed through verbatim; BRANCH ON `errorCode` — the six operator actions all
@@ -5569,7 +5578,7 @@ paths:
5569
5578
  application/json:
5570
5579
  schema: { $ref: '#/components/schemas/WiringDiagnostics' }
5571
5580
  '401': { $ref: '#/components/responses/Unauthorized' }
5572
- '403': { description: 'Multi-tenant worker: wiring diagnostics are operator-only (`auth.operator_only`).' }
5581
+ '403': { description: 'Multi-tenant worker: wiring diagnostics are operator-only (`auth.operator_only`). With an EMPTY `OPERATOR_PRINCIPALS` the answer is instead 501 `capability.operator_principals_required` (server >= 7.116.0; any identity, ahead of authorization — branch on `errorCode`, not the status).' }
5573
5582
  '404': { description: 'This worker serves no wiring diagnostics (older server, or a process not assembled by the composition root).' }
5574
5583
 
5575
5584
  /v1/sendfile-links:
@@ -5899,7 +5908,9 @@ paths:
5899
5908
  description: >
5900
5909
  OPERATOR-ONLY cross-tenant read. Absent (or equal to the caller's own principal) = read your own
5901
5910
  rules. Anything else requires explicit OPERATOR_PRINCIPALS membership ⇒ otherwise 403
5902
- `auth.operator_only`. Rule text contains command shapes, so third-party reads are an information
5911
+ `auth.operator_only` (server >= 7.116.0: with an EMPTY list the answer is 501
5912
+ `capability.operator_principals_required` instead — any identity, ahead of authorization; your own rules never
5913
+ go through the operator gate). Rule text contains command shapes, so third-party reads are an information
5903
5914
  disclosure and are deliberately not opened to non-operators.
5904
5915
  - name: scope
5905
5916
  in: query
@@ -6644,7 +6655,14 @@ components:
6644
6655
  application/json:
6645
6656
  schema: { $ref: '#/components/schemas/QuotaError' }
6646
6657
  NotImplemented:
6647
- description: 501 — capability not available (e.g. no TiDB run store; leader gated off; `wake` unsupported).
6658
+ description: >
6659
+ 501 — "this deployment cannot answer this" (e.g. no TiDB run store; `wake` unsupported). server >= 7.116.0: HTTP has exactly ONE status for
6660
+ "unavailable on this deployment" — 501 — and the CAUSE is the code: `capability.*` = change the deployment shape / wire the facility
6661
+ (incl. the new `capability.leader_endpoint_required` for `/v1/leader*` and `capability.shared_memory_required` for `/v1/shared-memory/*`),
6662
+ `feature.*` = turn a knob on (incl. the new `feature.memory_consolidation_disabled` and `feature.config_catalog_disabled`), and
6663
+ `capability.operator_principals_required` = configure `OPERATOR_PRINCIPALS` (every operator-only endpoint answers it, for any identity,
6664
+ when the list is empty; a non-empty list the caller is not on is still 403 `auth.operator_only`). Branch on `errorCode`, not the status
6665
+ (these families answered 404 / 403 on <= 7.115.x). The one registered exception is `a2aServe`'s two routes (404, anti-fingerprint).
6648
6666
  content:
6649
6667
  application/json:
6650
6668
  schema: { $ref: '#/components/schemas/ErrorResponse' }
@@ -6824,7 +6842,18 @@ components:
6824
6842
  name: { type: string }
6825
6843
  description: { type: string }
6826
6844
  content: { type: string }
6827
- manifest: { type: object, additionalProperties: true }
6845
+ manifest:
6846
+ type: object
6847
+ additionalProperties: true
6848
+ deprecated: true
6849
+ description: >
6850
+ RETIRED — server >= 7.116.0 rejects the WHOLE skill when this key is present on a delegated agent's skill (unknown key:
6851
+ `agent.skills must be …`; a top-level skill answers 400 `request.field_invalid` "skill.manifest was retired in core 7.41.0 — …").
6852
+ core 7.41.0 retired the skill manifest (hard break, no alias). Rewrite `manifest.allowTools` to the skill's
6853
+ `allowedTools` (an allow-rule source, not a fence); `manifest.allowPaths` has no replacement (use the session rules'
6854
+ `allowDirs`). Older servers (<= 7.115.x) still accept it; the key stays declared here until the next major.
6855
+ allowedTools: { type: array, items: { type: string }, description: 'server >= 7.116.0 — the skill''s allow-rule source (see `SkillSpec.allowedTools`); <= 7.115.x rejects the key (unknown key).' }
6856
+ disallowedTools: { type: array, items: { type: string }, description: 'server >= 7.116.0 — the skill''s deny-rule source (see `SkillSpec.disallowedTools`); <= 7.115.x rejects the key (unknown key).' }
6828
6857
  files:
6829
6858
  type: array
6830
6859
  items:
@@ -6933,6 +6962,18 @@ components:
6933
6962
  LISTING RENDERER, not to this gate: turn it down with `settings.skillListingBudgetFraction` /
6934
6963
  `settings.skillListingMaxDescChars`.
6935
6964
  items: { $ref: '#/components/schemas/SkillSpec' }
6965
+ userInvokedSkills:
6966
+ type: array
6967
+ items: { type: string, minLength: 1 }
6968
+ description: >
6969
+ server >= 7.116.0 (core 7.41.0 #1303). Skills the HUMAN called by name (the shell parses the typed `/name`; by `SkillSpec.name`).
6970
+ Their `allowedTools` / `disallowedTools` take effect from the start of the run and NO `Skill` card is raised (the human naming
6971
+ it is the statement); it governs this one run only (a steer does not end it; the next submit does not carry it). Every name must be
6972
+ a skill the run carries (scenario factory ∪ this request's `skills`), else the engine refuses to start: 400
6973
+ `config.user_invoked_skill_unknown` (sync leg `POST /v1/tasks`; the async / live-stream legs answer 2xx and the same code lands on the
6974
+ terminal run row; `disableSkills: true` makes any name this code). A bad value shape (bare string / empty-string member / non-string /
6975
+ object / null) is a 400 `request.field_invalid`. Does not deliver the skill body. PROBE `capabilities.userInvokedSkills` (constant true);
6976
+ absent = an older server that answers 400 `request.body_shape` listing the key in `unknownKeys` — do not send it.
6936
6977
  reasoningEffort:
6937
6978
  type: string
6938
6979
  enum: [off, minimal, low, medium, high, xhigh, max]
@@ -7300,8 +7341,11 @@ components:
7300
7341
  for that). For "only these tools" use the real knobs: the session rule `toolAllow`
7301
7342
  (`PUT /v1/sessions/{sessionId}/policy`), per-request `excludeTools` / `excludeAllTools`, or the deployment's scenario
7302
7343
  tool surface. Entry forms: a tool-name rule (exact name, `mcp__<server>__*`, `a2a__<peer>__*`; `Bash(*)` is accepted
7303
- and compiled as a whole-tool rule); a retired name, a short name with no protocol prefix, a missing / two-segment
7304
- MCP name (`mcp__figma` -> write `mcp__figma__*`), a backslash, more than 512 characters, or a CONTENT form
7344
+ and compiled as a whole-tool rule; server >= 7.116.0 also accepts the two-segment covering form `mcp__<server>` / `a2a__<peer>`,
7345
+ read as the whole server — same meaning as `mcp__<server>__*`); a retired name, a short name with no protocol prefix, a missing-segment
7346
+ name (`mcp__` / `mcp__<server>__`), a protocol name whose server segment the engine cannot have minted (e.g. `mcp__github.com__*` —
7347
+ write `mcp__github_com__*`; server >= 7.116.0 refuses it with a 422 instead of accepting a rule that can never match), a backslash,
7348
+ more than 512 characters, or a CONTENT form
7305
7349
  (`Bash(npm:*)`, `Read(./x)`, `WebFetch(domain:...)`) is a whole-request 422 (permanent content-form allows live in the
7306
7350
  persistent rule store: `/v1/rules/cc-import`, or answer yes on a card). The same applies to a PreToolUse hook: an
7307
7351
  EXPLICIT `permissionDecision: "allow"` states it (and binds only the arguments the hook saw); a hook that only adds
@@ -8218,6 +8262,16 @@ components:
8218
8262
  is unchanged at 7.64.0 — only its source moved (projected from `result.terminal`, or read straight
8219
8263
  off a legacy flat blob). ⚠️ K-24 / S-173 (server >= 7.104.0): on a `needs_review` PARK row it is core's canonical
8220
8264
  `review.pending` (absent before) — never read "errorCode present" as "failed"; read `status` first.
8265
+ 🔴 server >= 7.116.0 (S-1099) adds three values for the run store's own terminal verdicts, which used to live only in the `error`
8266
+ sentence: `run.stalled` (heartbeat-lost reap — and the read-time fold that precedes the reap; sentence `run stalled (instance lost?)`),
8267
+ `run.orphaned` (local-lane restart / session-occupancy takeover reclaim; sentence `run orphaned by engine restart`) and
8268
+ `approval.expired` (the expiry reap of a durably parked approval; the column already carried it, the read face did not project it;
8269
+ sentence `approval expired before decision`). None of the three means the request was wrong — resending the same request is
8270
+ pointless, only a new submission makes a new run. READ `status` FIRST (`failed`), then branch on the code. Open set (a string; always keep a
8271
+ default arm). ABSENCE: rows written by <= 7.115.x carry only the sentence; a one-time startup migration backfills the code
8272
+ (log `one_time_migrations_applied` `holderDeathCodeRowsBackfilled`) and a new replica reads such a row as the code during the upgrade window,
8273
+ but the migration has a window (30 days after the release, then the write-back and the read half are deleted) — so
8274
+ "no `errorCode`" never proves "not a holder death": rows migrated late or after the window stay sentence-only.
8221
8275
  error: { type: string }
8222
8276
  jobId:
8223
8277
  type: string
@@ -9029,7 +9083,8 @@ components:
9029
9083
  server >=7.12.0 — the `GET /v1/outcomes` mechanical-signal read aggregate (design/73 §7.2) is
9030
9084
  available. Predicate mirrors the route's refusal arms exactly: a QUERYABLE ledger is wired (a
9031
9085
  file-only sink writes JSONL and has no query face ⇒ 501), AND on a multi-tenant deployment the
9032
- operator list is non-empty (single-user has no such gate — the only user IS the operator).
9086
+ operator list is non-empty (single-user has no such gate — the only user IS the operator); with an empty
9087
+ list the multi-tenant route answers 501 `capability.operator_principals_required` (server >= 7.116.0; 403 before).
9033
9088
  Added because this face previously had a 501 but NO capability bit, leaving consumers to
9034
9089
  trial-by-501 — the exact pattern this capability surface exists to remove.
9035
9090
  images:
@@ -9053,7 +9108,17 @@ components:
9053
9108
  principal→org membership fold present), so true ⟺ the face actually works — a deployment with
9054
9109
  the store but no membership authority deliberately reports false (and does not mount the routes).
9055
9110
  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." }
9056
- workflowsList: { type: boolean, description: "`GET /v1/workflows` list face (separate from the per-run reads)." }
9111
+ workflowsList: { type: boolean, description: "`GET /v1/workflows` list / detail / stream faces (the workflow run store is wired; separate from the per-run reads). server >= 7.116.0: the journal face `GET /v1/workflows/{id}/journal` is advertised by the new bit `workflowsJournal`, not by this one (value unchanged)." }
9112
+ workflowsJournal:
9113
+ type: boolean
9114
+ description: >
9115
+ server >= 7.116.0 (predicate class "facility": the workflow run store AND the journal store). Advertises
9116
+ `GET /v1/workflows/{id}/journal`. On a deployment with the run store but no journal store (`DB_BACKEND=memory`,
9117
+ multi-tenant bare boot, SQL coordinates not fully configured) with self-orchestration on, `workflowsList` is true
9118
+ and this is false; the journal face then answers 501 `capability.workflow_store_required` (any id, ahead of the
9119
+ per-row owner check; `capability.self_orchestration_required` when the run store is absent). A journal entry point reads THIS bit, not
9120
+ `workflowsList`. ABSENT = an older server (<= 7.115.x): fall back to `workflowsList` (the old behaviour — the
9121
+ bit could be true while the journal face always answers 501).
9057
9122
  workflowModels:
9058
9123
  type: array
9059
9124
  items: { type: string }
@@ -9096,8 +9161,8 @@ components:
9096
9161
  (DESIGN-278 §5 S2 — deployment config self-description) is mounted on this DEPLOYMENT. TWO-term
9097
9162
  conjunction, written against the route's own refusal arms: ① the `CONFIG_CATALOG_ENABLED` knob is
9098
9163
  on (default on; a compliance deployment that turns it off makes the path a 404 — the valve-family
9099
- precedent) AND ② the operator principal list is NON-EMPTY (with an empty list the route 403s every
9100
- identity). 🔴 DEPLOYMENT-LEVEL AVAILABILITY, NOT THE CALLER'S AUTHORIZATION: the caller's own
9164
+ precedent; server >= 7.116.0 answers 501 `feature.config_catalog_disabled` there, 404 before) AND ② the operator principal list is NON-EMPTY (with an empty list the route refuses every
9165
+ identity — 501 `capability.operator_principals_required` on server >= 7.116.0, 403 before). 🔴 DEPLOYMENT-LEVEL AVAILABILITY, NOT THE CALLER'S AUTHORIZATION: the caller's own
9101
9166
  identity is NOT in the predicate — the route additionally requires the CURRENT principal to be an
9102
9167
  explicit operator (`routes/config-catalog.ts` `explicitOperatorOk`, else 403 `auth.operator_only`),
9103
9168
  so a non-operator reads `true` and still gets 403. Show/invoke the catalog only for an identity you
@@ -9117,8 +9182,15 @@ components:
9117
9182
  `false`/absent otherwise (same predicate as the route's 501). `resume: "snapshot"` is a semantic
9118
9183
  advertisement: the bus is latest-state (not an event log) and every connection opens with a full
9119
9184
  snapshot frame, so reconnect = re-snapshot (deliberately no last-event-id cursor); transient
9120
- bg_notification frames re-deliver via the durable completion inbox. `steerPriority: "advisory"` =
9121
- steer body.priority is accepted + enum-validated but a no-op until the core queue seam (E15).
9185
+ bg_notification frames re-deliver via the durable completion inbox. `steerPriority` (a string, open
9186
+ set) says whether the steer body's `priority` takes effect on THIS server: `steerPriority: "advisory"`
9187
+ (server <=7.115.x) = accepted + enum-validated + echoed but a no-op (nothing is handed to the engine);
9188
+ `steerPriority: "engine"` (server >=7.116.0) = `priority` (`now` / `next` / `later`) is handed to the
9189
+ engine as the injection tier, and the engine owns what each tier means — `now` cuts the in-flight turn
9190
+ IMMEDIATELY (the running tool receives an abort, an approval card pending on this replica is revoked; one `engine_notice`
9191
+ `task.turn_interrupted` frame is the evidence), `later` is delivered after the turn the run would have stopped on, no `priority` =
9192
+ `next`. Read this bit before offering a `priority` choice; an absent
9193
+ or unknown value means "do not assume it takes effect", never `"engine"`.
9122
9194
  oneOf:
9123
9195
  - type: boolean
9124
9196
  enum: [false]
@@ -9373,9 +9445,25 @@ components:
9373
9445
  S-15 换店; ≤7.51 the last conjunct was the file-snapshot store). Gate the whole sync surface off
9374
9446
  this. ⚠️ The two BLOB faces (PUT …/sync/blobs/{hash}, GET …/sync/history/blobs/{hash})
9375
9447
  additionally need the store's blob faces (`syncBlobFaces` — the SQL/cloud backends; the local
9376
- file backend has none): without them the PUT answers 501 `capability.snapshot_store_required`
9377
- and the history PULL answers the no-oracle 404 `not_found.blob`, while the REST of the sync
9378
- surface keeps working.
9448
+ file backend has none): without them BOTH answer 501 `capability.snapshot_store_required`
9449
+ (server >= 7.116.0; the history PULL answered a no-oracle 404 `not_found.blob` there on <= 7.115.x),
9450
+ while the REST of the sync surface keeps working. Since server >= 7.116.0 the two blob faces are
9451
+ advertised by their own bit `sessionSyncFileHistory`; this bit speaks for the conversation half
9452
+ (its predicate additionally requires a session-policy store that can purge, `deleteBySession` —
9453
+ a deployment whose embedder-supplied store lacks it answers the whole family 501
9454
+ `capability.policy_purge_required` and this bit is false).
9455
+ sessionSyncFileHistory:
9456
+ type: boolean
9457
+ description: >
9458
+ server >= 7.116.0 (predicate class "facility": `sessionSync` AND the file-history store has blob transfer faces
9459
+ — SQL backends only; `DB_BACKEND=local` is always false). Advertises exactly the two file-history blob faces
9460
+ `PUT /v1/sessions/{sessionId}/sync/blobs/{hash}` and `GET /v1/sessions/{sessionId}/sync/history/blobs/{hash}`.
9461
+ READ IT ON BOTH ENDS of a sync: if either is false, sync the conversation only — do not push `fileHistory`, do
9462
+ not PUT blobs (a `fileHistory` pushed to a peer without the blob faces 422s `missing_blob` after commit). When
9463
+ false the two routes answer 501 `capability.snapshot_store_required` (any session id, ahead of the owner check).
9464
+ A blob that the session history graph does not reference / whose bytes are gone stays a 404 `not_found.blob`.
9465
+ ABSENT = an older server (<= 7.115.x): the bit cannot be read there (a `DB_BACKEND=local` deployment with
9466
+ `sessionSync: true` answered PUT blob 501 on every call), so try as before.
9379
9467
 
9380
9468
  # ─── S-139(sdk 8.3.0):对 server 7.60.0 的整体重对账补齐的 19 位 ───────────────────────
9381
9469
  # 行号 = server 7.60.0 树的 `src/http/routes/capabilities.ts`。语义与谓词的全文在
@@ -9468,7 +9556,8 @@ components:
9468
9556
  description: >
9469
9557
  Governance carry-out bundle pair `POST /v1/memory/{export,import}` (operator lane) —
9470
9558
  capabilities.ts:261, two conjuncts: both engine seams wired AND `operatorPrincipals` NON-EMPTY
9471
- (an empty roster makes those routes 403 for every identity). True does not promise the backend
9559
+ (an empty roster makes those routes refuse every identity — 501 `capability.operator_principals_required` on
9560
+ server >= 7.116.0, 403 before). True does not promise the backend
9472
9561
  has the bundle composite face (core then refuses loudly with `memory.export_incomplete`).
9473
9562
  memoryCompliance:
9474
9563
  type: boolean
@@ -9570,10 +9659,13 @@ components:
9570
9659
  a2aServe:
9571
9660
  type: boolean
9572
9661
  description: >
9573
- Server-as-peer knob (capabilities.ts:564 = `config.a2aServe !== undefined`): true ⟺
9574
- `GET /.well-known/agent-card.json` and `POST /v1/a2a` exist; false ⟺ both 404. Scope is ROUTE
9575
- EXISTENCE, not "every method runs" — `message/send` / `tasks/get` additionally need a durable run
9576
- store (without it they answer a named JSON-RPC `-32004`).
9662
+ Server-as-peer: true ⟺ `GET /.well-known/agent-card.json` and `POST /v1/a2a` exist; false ⟺ both 404
9663
+ (`not_found.route`, anti-fingerprint — the one registered exception to "unavailable = 501"). server >= 7.116.0
9664
+ predicate (class "facility + config"): the `A2A_SERVE_ENABLED` knob AND a durable run store; <= 7.115.x looked at the knob
9665
+ only (`config.a2aServe !== undefined`). 🔴 value change: with the knob on but NO durable run store
9666
+ (`DB_BACKEND=memory`, multi-tenant bare boot) this bit is now false and the card and the RPC both answer 404 (7.115.x:
9667
+ true, card 200, every `message/send` / `tasks/get` a JSON-RPC `-32004`); the startup log carries a warn
9668
+ `a2a_serve_not_served`. The old "the server deliberately does not fold the run store into this bit" note is retired.
9577
9669
  agentRoster:
9578
9670
  type: boolean
9579
9671
  description: >
@@ -9768,6 +9860,23 @@ components:
9768
9860
  answers the request key with a 400 `request.body_shape` naming it under `unknownKeys` — do not send the key
9769
9861
  there and tell the user the factory skills stay installed. True only says "this server knows the key", not
9770
9862
  whether the deployment has any factory skills.
9863
+ userInvokedSkills:
9864
+ type: boolean
9865
+ enum: [true]
9866
+ description: >
9867
+ server >= 7.116.0 (constant true; no predicate, no tenant gate) — the ADVANCE READING for the request key
9868
+ `TaskRequest.userInvokedSkills` (skills the human named; effective from the start of the run, no `Skill` card);
9869
+ the bit and the key are the SAME word. ABSENT = an older server (<= 7.115.x), which answers the key with a 400
9870
+ `request.body_shape` naming it under `unknownKeys` — do not send it there.
9871
+ skillToolLists:
9872
+ type: boolean
9873
+ enum: [true]
9874
+ description: >
9875
+ server >= 7.116.0 (constant true) — the skill tool lists `allowedTools` / `disallowedTools` TAKE EFFECT (allow-rule /
9876
+ deny-rule source; see `SkillSpec.allowedTools`). ABSENT = an older server (<= 7.115.x): on a top-level skill the two
9877
+ lists are accepted but the engine does not read them — they are silently inert and there is NO 400 to probe, so do not
9878
+ expect them to stop a tool (fence with deny rules instead); on a delegated agent's skill they are an unknown key that
9879
+ rejects the whole skill.
9771
9880
  WriteProtectionCapability:
9772
9881
  type: object
9773
9882
  additionalProperties: false
@@ -9902,11 +10011,42 @@ components:
9902
10011
  engine refuses the skill outright at load time, so the HTTP face answers first where the caller can read it).
9903
10012
  The bound is 1 048 576 **UTF-16 code units** (`MAX_SKILL_CONTENT_CHARS`); see the STRING-LENGTH UNIT
9904
10013
  convention in `info.description` for why this `maxLength` states that number verbatim.
10014
+ 🔴 server >= 7.116.0 (core 7.41.0): a skill carries two TOOL LISTS, `allowedTools` / `disallowedTools` — the
10015
+ run-scoped ALLOW-RULE and DENY-RULE source of the skill once it is activated. NOT a fence (<= 7.115.x the engine read
10016
+ the skill's tool list as "only these"): unlisted tools are judged as usual, listed ones are not asked about for the rest
10017
+ of the run — which makes it WIDER than `settings.permissions.allow` (that one stays a statement and does not
10018
+ release the engine's own write gate / shell ordinary asks on built-in tools). Probe `capabilities.skillToolLists`
10019
+ (absent = an old server that accepts the keys on a top-level skill and silently ignores them). The skill's `manifest`
10020
+ key is RETIRED: any value on a top-level skill is a 400 `request.field_invalid` ("skill.manifest was retired in core
10021
+ 7.41.0 — …"; rewrite `manifest.allowTools` to `allowedTools`; `allowPaths` has no replacement), and on a delegated
10022
+ agent's skill (`agents[].skills[]`) it is an unknown key that rejects the whole skill.
9905
10023
  required: [name, description, content]
9906
10024
  properties:
9907
10025
  name: { type: string, minLength: 1 }
9908
10026
  description: { type: string }
9909
10027
  content: { type: string, minLength: 1, maxLength: 1048576 }
10028
+ allowedTools:
10029
+ type: array
10030
+ items: { type: string }
10031
+ description: >
10032
+ server >= 7.116.0. The skill's run-scoped ALLOW-RULE source (permission-rule spellings: a tool name,
10033
+ `mcp__<server>` / `mcp__<server>__*`; `*`, write paths and domain forms grant nothing). Once the skill is activated
10034
+ (named in top-level `userInvokedSkills` — no `Skill` card — or activated by the model and the card approved) the
10035
+ listed tools are not asked about for the rest of the run: it clears the engine's ordinary and "nobody stated it" asks,
10036
+ so it is WIDER than `settings.permissions.allow`; deny / ask rules, `APPROVAL_REQUIRE`, `APPROVAL_NEVER_AUTO`, the shell
10037
+ floor, write protection and plan-mode asks are not cleared. NOT a fence: unlisted tools are judged as usual. A non-string-array
10038
+ is a 400 `request.field_invalid`; a protocol name whose server segment cannot be minted / is missing / is a wildcard
10039
+ (`mcp__*`) is a 400 too (top-level `skills[]` and `agents[].skills[]` judge identically). Probe `capabilities.skillToolLists`.
10040
+ disallowedTools:
10041
+ type: array
10042
+ items: { type: string }
10043
+ description: >
10044
+ server >= 7.116.0. The skill's run-scoped DENY-RULE source (same grammar / gates / probe as `allowedTools`): once the skill
10045
+ is activated these tools are refused for the run (no execution, no card; `tool_end.gate.disposition` is
10046
+ `{ kind: "denied", deniedBy: "policy" }`). `*` and domain forms (`WebFetch(domain:…)`) read as "none" on this side too —
10047
+ accepted with 2xx and only an operator-audience notice `skills.allowed_tool_not_granted`; write-path forms and the rest compile
10048
+ into deny rows (as the server contract states it for the engine's `compileSkillGrant`; the SDK did not re-run the engine on the deny side's write-path forms —
10049
+ the engine's own notice text lists "a write-path or domain entry" among entries that "grant (or refuse) nothing").
9910
10050
 
9911
10051
  McpServerSpec:
9912
10052
  type: object
@@ -11841,6 +11981,11 @@ components:
11841
11981
  expiresAtMs: { type: number, description: 'Absolute window deadline — the SAME number minted once for the tool_approval frame (never recomputed).' }
11842
11982
  sessionId: { type: string }
11843
11983
  requiresRealApproval: { type: boolean, enum: [true] }
11984
+ rememberRefused:
11985
+ type: boolean
11986
+ enum: [true]
11987
+ description: >-
11988
+ server >= 7.116.0, ADDITIVE, present ONLY when true (never `false`): the "remember for this session" answer (live `allow_session` / durable `/decide` `remember:"session"`) will NOT be remembered on this card — the decision is accepted and applies once, the receipt's `rememberApplied` is `false` (existing key, not a new one), later cards of the same kind still ask. A shell reads this to decide whether to render the "allow for the session" choice instead of inferring it from `requiresRealApproval` / `mandated` / `governanceForced`. One bit for four reasons: the operator-governance gate / a human must approve each time / a mandated card / the answer would also grant more than this one call (new in 7.116.0: approving an engine `Skill` card activates the skill and frees its listed tools for the run, which the card cannot show, so it is not remembered). ABSENT = remember works on this card, OR an older server (<= 7.115.x never carries the key). Same bit on the live frame, the `approval_request` card (= `card_json`, incl. the reconnect replay), the `livePending` row, and the `/v1/approvals` `pending[]` row (+ their `/stream` frames); NOT on inbox rows.
11844
11989
  governanceForced: { type: boolean, enum: [true] }
11845
11990
  mandated: { type: boolean, enum: [true], description: 'server >= 7.101.0 (T4①): the mandated bit, same layer and shape as the two flags above (present only when true); the SAME value as `frame.mandated` (read once when the entry is minted).' }
11846
11991
  mandate:
@@ -12003,6 +12148,9 @@ components:
12003
12148
  Present ⇔ the request carried `remember:"session"` and the worker has an exemption store. `true` = the
12004
12149
  per-session per-tool exemption landed; `false` = the grant failed (logged) or the gate carried no
12005
12150
  rememberable toolName — the decision itself still stands.
12151
+ 🔴 server >= 7.116.0: ALWAYS `false` on a card that carries `rememberRefused: true` (an engine `Skill` card etc. — the
12152
+ answer would also grant more than this one call): the decision is accepted and applies once, the exemption store gets
12153
+ zero writes (existing key, not a new one). Ordinary cards keep `true` = landed.
12006
12154
 
12007
12155
  ApprovalExemption:
12008
12156
  type: object
@@ -12146,6 +12294,11 @@ components:
12146
12294
  🔴 DISPLAY / TRIAGE ONLY, and ABSENT MEANS THERE REALLY IS NO SUPPLY (rule lane unarmed, the
12147
12295
  command yields no rule, no rule could silence this ask, or an old row) — never "the projection
12148
12296
  dropped it", which is precisely the bug this key exists to close. Never `null`, never `[]`.
12297
+ rememberRefused:
12298
+ type: boolean
12299
+ enum: [true]
12300
+ description: >-
12301
+ server >= 7.116.0, ADDITIVE, present ONLY when true (never `false`): the "remember for this session" answer (live `allow_session` / durable `/decide` `remember:"session"`) will NOT be remembered on this card — the decision is accepted and applies once, the receipt's `rememberApplied` is `false` (existing key, not a new one), later cards of the same kind still ask. A shell reads this to decide whether to render the "allow for the session" choice instead of inferring it from `requiresRealApproval` / `mandated` / `governanceForced`. One bit for four reasons: the operator-governance gate / a human must approve each time / a mandated card / the answer would also grant more than this one call (new in 7.116.0: approving an engine `Skill` card activates the skill and frees its listed tools for the run, which the card cannot show, so it is not remembered). ABSENT = remember works on this card, OR an older server (<= 7.115.x never carries the key). Same bit on the live frame, the `approval_request` card (= `card_json`, incl. the reconnect replay), the `livePending` row, and the `/v1/approvals` `pending[]` row (+ their `/stream` frames); NOT on inbox rows.
12149
12302
  governanceForced:
12150
12303
  type: boolean
12151
12304
  enum: [true]
@@ -13098,31 +13251,97 @@ components:
13098
13251
  type: object
13099
13252
  description: >
13100
13253
  2c session-sync `GET …/sync/manifest` payload (wrapped server-side as `{ manifest }`). The THIN cross-backend
13101
- snapshot: entry IDS (oldest-first, NOT payloads) + per-snapshot relPath→blobHash + policy + anchors + leaf.
13102
- # 封闭(census 批2 第三段,2026-07-30):铸造点 `session-sync.ts`(exportSessionManifest 尾部
13103
- # 字面量)恰是这 7 键,全部无条件(leafId 缺席位=null 不省略)。
13254
+ snapshot: entry IDS (oldest-first, NOT payloads) + the file-history envelope + policy + anchors + leaf.
13255
+ 🔴 server >= 7.52 (S-15, design/381; re-read byte for byte against 7.116.0): the key `snapshots` (per-snapshot
13256
+ relPath→blobHash, the retired E19 whole-tree snapshot epoch) is NO LONGER SENT — the server's `exportSessionManifest` mints
13257
+ `{ sessionId, entryIds, entryCount, leafId, fileHistory, policy, anchors }` and `fileHistory` (core's versioned
13258
+ `FileHistoryExport` envelope; `null` = no history / the source store cannot export) replaces it. <= 7.51 servers still send
13259
+ `snapshots` and no `fileHistory`. The blob BYTES never ride the manifest: content-addressed, via the history blob faces
13260
+ (`GET …/sync/history/blobs/{hash}` / `PUT …/sync/blobs/{hash}`; probe `capabilities.sessionSyncFileHistory` on BOTH ends).
13261
+ The SDK's `manifest()` fills `snapshots: []` when the wire omits it.
13262
+ # 封闭(census 批2 第三段,2026-07-30;16.2.0 按 7.116.0 发布字节重核):铸造点 `session-sync.ts`(exportSessionManifest 尾部
13263
+ # 字面量)恰是 `{ sessionId, entryIds, entryCount, leafId, fileHistory, policy, anchors }` 7 键(≥7.52),无 `snapshots`;
13264
+ # ≤7.51 老 server 发 `snapshots`、无 `fileHistory` ⇒ 两键都不进 required(支持底线 3.0.0)。
13104
13265
  additionalProperties: false
13105
- required: [sessionId, entryIds, entryCount, leafId, snapshots, policy, anchors]
13266
+ required: [sessionId, entryIds, entryCount, leafId, policy, anchors]
13106
13267
  properties:
13107
13268
  sessionId: { type: string }
13108
13269
  entryIds: { type: array, items: { type: string }, description: Oldest-first, verbatim cross-backend. }
13109
13270
  entryCount: { type: integer, description: '= entryIds.length (the `/sync/entries` trailer count).' }
13110
13271
  leafId: { type: [string, 'null'] }
13111
- snapshots: { type: array, items: { $ref: '#/components/schemas/SyncSnapshot' } }
13272
+ snapshots: { type: array, items: { $ref: '#/components/schemas/SyncSnapshot' }, description: 'server <= 7.51 ONLY (the retired E19 snapshot epoch); server >= 7.52 does not send it — see `fileHistory`.' }
13273
+ fileHistory:
13274
+ oneOf:
13275
+ - { $ref: '#/components/schemas/FileHistoryExport' }
13276
+ - { type: 'null' }
13277
+ description: 'server >= 7.52: the per-edited-file rewind history envelope; `null` = no history / the source store cannot export; key ABSENT = a <= 7.51 server (that generation sends `snapshots`).'
13112
13278
  policy: { type: array, items: { $ref: '#/components/schemas/SessionRulesRecord' } }
13113
13279
  anchors: { type: array, items: { $ref: '#/components/schemas/SyncAnchor' } }
13114
13280
 
13281
+ FileHistoryExport:
13282
+ type: object
13283
+ description: >
13284
+ Mirror of core's `FileHistoryExport` (`formatVersion: "file-history/1"`) — the session-sync envelope for the per-edited-file rewind history graph
13285
+ (server >= 7.52; replaces the retired E19 `SyncSnapshot`). Blob bytes are NOT inside: they are content-addressed and travel through the history blob faces.
13286
+ The SDK reads only `tracked[].versions[].blobHash` (which blobs to move) and passes everything else through verbatim — the server's import door judges
13287
+ `formatVersion` (refuse-not-coerce) and the closed `keyKind` / `keyFamily` sets, so they are plain strings here.
13288
+ required: [formatVersion, tracked, boundaries]
13289
+ properties:
13290
+ formatVersion: { type: string, description: 'today only `file-history/1`; the server refuses any other value.' }
13291
+ tracked:
13292
+ type: array
13293
+ items:
13294
+ type: object
13295
+ required: [relOrAbsPath, keyKind, keyFamily, baseAvailable, versions]
13296
+ properties:
13297
+ relOrAbsPath: { type: string }
13298
+ keyKind: { type: string }
13299
+ keyFamily: { type: string }
13300
+ baseAvailable: { type: boolean }
13301
+ versions:
13302
+ type: array
13303
+ items:
13304
+ type: object
13305
+ required: [version, blobHash, backupTime]
13306
+ properties:
13307
+ version: { type: integer }
13308
+ blobHash: { type: [string, 'null'], description: '`null` = the "did not exist" marker (no blob to move).' }
13309
+ mode: { type: integer }
13310
+ parentIdent: { type: string }
13311
+ backupTime: { type: string }
13312
+ boundaries:
13313
+ type: array
13314
+ items:
13315
+ type: object
13316
+ required: [entryId, files]
13317
+ properties:
13318
+ entryId: { type: string }
13319
+ files:
13320
+ type: array
13321
+ items:
13322
+ type: object
13323
+ required: [relOrAbsPath, version]
13324
+ properties:
13325
+ relOrAbsPath: { type: string }
13326
+ version: { type: integer }
13327
+
13115
13328
  SessionBundle:
13116
13329
  type: object
13117
13330
  description: >
13118
13331
  The portable state of ONE session, ready to replay into another backend. Blobs are NOT inlined (each
13119
13332
  snapshot carries only its `manifest`; the bytes stream through the content-addressed blob routes). The SDK
13120
- assembles this from a manifest + the streamed entries for a PUSH.
13333
+ assembles this from a manifest + the streamed entries for a PUSH. server >= 7.52: the file history rides `fileHistory`
13334
+ (the `FileHistoryExport` envelope) instead of `snapshots`.
13121
13335
  required: [sessionId, entries, snapshots, policy, anchors]
13122
13336
  properties:
13123
13337
  sessionId: { type: string }
13124
13338
  entries: { type: array, items: { $ref: '#/components/schemas/SyncEntry' } }
13125
13339
  snapshots: { type: array, items: { $ref: '#/components/schemas/SyncSnapshot' } }
13340
+ fileHistory:
13341
+ oneOf:
13342
+ - { $ref: '#/components/schemas/FileHistoryExport' }
13343
+ - { type: 'null' }
13344
+ description: 'server >= 7.52 file-history envelope (same as `SessionManifest.fileHistory`); key absent = the source is a <= 7.51 server.'
13126
13345
  policy: { type: array, items: { $ref: '#/components/schemas/SessionRulesRecord' } }
13127
13346
  anchors: { type: array, items: { $ref: '#/components/schemas/SyncAnchor' } }
13128
13347
 
@@ -13985,10 +14204,17 @@ components:
13985
14204
  unattended leg run under `UNATTENDED_PERMISSION_MODE=dontAsk`).
13986
14205
  AskClass:
13987
14206
  type: string
13988
- enum: [content_question, hook, ancestor, unverifiable_delete, transcript_write, org_rule, governance_unavailable, rule_unreadable, ask_rule, denial_limit_fallback, operator_forced, shell_floor, write_protection_unresolved, probe, write_protection, tool_marks_mcp, tool_marks_a2a, policy_rra, artifact_shared_read, mode, unstated, ordinary]
13989
- description: >
13990
- core `ASK_CLASSES` (twenty-two words as of core 7.40.0 — 7.34.0 minted twenty; `shell_floor` joined in 7.39.0 #1260 and rides
13991
- server >= 7.113.0; `unstated` joined in 7.40.0 and rides server >= 7.115.0) — which CLASS an ask falls in (the columns of the mode × class arbitration table).
14207
+ enum: [content_question, hook, ancestor, unverifiable_delete, transcript_write, org_rule, governance_unavailable, rule_unreadable, ask_rule, denial_limit_fallback, requires_user_interaction, operator_forced, shell_floor, write_protection_unresolved, probe, write_protection, tool_marks_mcp, tool_marks_a2a, policy_rra, artifact_shared_read, mode, unstated, ordinary]
14208
+ description: >
14209
+ core `ASK_CLASSES` (twenty-two words as of core 7.40.0, twenty-three as of core 7.41.0 — 7.34.0 minted twenty; `shell_floor` joined in 7.39.0 #1260 and rides
14210
+ server >= 7.113.0; `unstated` joined in 7.40.0 and rides server >= 7.115.0; `requires_user_interaction` joined in 7.41.0 and rides
14211
+ server >= 7.116.0) — which CLASS an ask falls in (the columns of the mode × class arbitration table).
14212
+ `requires_user_interaction` = the MCP server declared, in the tool's advertisement, `_meta["anthropic/requiresUserInteraction"] === true`
14213
+ (only the boolean `true`): EVERY call of that tool asks the human, in EVERY mode including `bypassPermissions` (it is not one of the ten
14214
+ RELEASABLE classes), and only the human's answer clears it — `settings.permissions.allow`, operator `APPROVAL_GRANT`, a skill's tool list +
14215
+ `userInvokedSkills` and a hook's explicit allow all fail to; with no approval seat the call is refused (the server sees nothing). The frame
14216
+ also carries `requiresRealApproval: true` / `mandated: true` (`origin: "safety_tighten"`). A client that renders cards by exhaustive `askClass`
14217
+ adds this one word.
13992
14218
  `unstated` = NOBODY STATED IT: in an ask-class mode (`default` / `acceptEdits` / `auto` / `plan`) a call to an MCP server tool or A2A skill
13993
14219
  (a seat that states no permission of its own) reached the end of the fold with no layer — rule / hook / approver / classifier / mode
13994
14220
  release — having said "this time is fine"; the engine raises a card with `askClass: "unstated"` (sentence `The agent requested permission to use <tool>,
@@ -15589,6 +15815,11 @@ components:
15589
15815
  args:
15590
15816
  description: '"tool_approval" only: the call''s args, secret-redacted. UNTRUSTED for display. Absent (with argsOmitted) when over the byte cap or unserializable.'
15591
15817
  argsOmitted: { type: boolean, enum: [true] }
15818
+ rememberRefused:
15819
+ type: boolean
15820
+ enum: [true]
15821
+ description: >-
15822
+ server >= 7.116.0, ADDITIVE, present ONLY when true (never `false`): the "remember for this session" answer (live `allow_session` / durable `/decide` `remember:"session"`) will NOT be remembered on this card — the decision is accepted and applies once, the receipt's `rememberApplied` is `false` (existing key, not a new one), later cards of the same kind still ask. A shell reads this to decide whether to render the "allow for the session" choice instead of inferring it from `requiresRealApproval` / `mandated` / `governanceForced`. One bit for four reasons: the operator-governance gate / a human must approve each time / a mandated card / the answer would also grant more than this one call (new in 7.116.0: approving an engine `Skill` card activates the skill and frees its listed tools for the run, which the card cannot show, so it is not remembered). ABSENT = remember works on this card, OR an older server (<= 7.115.x never carries the key). Same bit on the live frame, the `approval_request` card (= `card_json`, incl. the reconnect replay), the `livePending` row, and the `/v1/approvals` `pending[]` row (+ their `/stream` frames); NOT on inbox rows.
15592
15823
  governanceForced:
15593
15824
  type: boolean
15594
15825
  enum: [true]
@@ -16053,6 +16284,11 @@ components:
16053
16284
  argsOmitted: { type: boolean, enum: [true], description: 'Present (true) only when args exceeded the byte cap. Never false.' }
16054
16285
  toolCallId: { type: string, description: 'The engine''s tool-call id (the `call_…` on the assistant message''s tool_use block) — same domain as ToolApprovalFrame.toolCallId.' }
16055
16286
  risk: { $ref: '#/components/schemas/ApprovalRiskAxes' }
16287
+ rememberRefused:
16288
+ type: boolean
16289
+ enum: [true]
16290
+ description: >-
16291
+ server >= 7.116.0, ADDITIVE, present ONLY when true (never `false`): the "remember for this session" answer (live `allow_session` / durable `/decide` `remember:"session"`) will NOT be remembered on this card — the decision is accepted and applies once, the receipt's `rememberApplied` is `false` (existing key, not a new one), later cards of the same kind still ask. A shell reads this to decide whether to render the "allow for the session" choice instead of inferring it from `requiresRealApproval` / `mandated` / `governanceForced`. One bit for four reasons: the operator-governance gate / a human must approve each time / a mandated card / the answer would also grant more than this one call (new in 7.116.0: approving an engine `Skill` card activates the skill and frees its listed tools for the run, which the card cannot show, so it is not remembered). ABSENT = remember works on this card, OR an older server (<= 7.115.x never carries the key). Same bit on the live frame, the `approval_request` card (= `card_json`, incl. the reconnect replay), the `livePending` row, and the `/v1/approvals` `pending[]` row (+ their `/stream` frames); NOT on inbox rows.
16056
16292
  governanceForced:
16057
16293
  type: boolean
16058
16294
  enum: [true]
@@ -16707,16 +16943,27 @@ components:
16707
16943
  sessionId: { type: string, description: 'gone.ask_parked ONLY — gate reconciliation coordinate 1; drive the existing durable-gate flow with it.' }
16708
16944
  gateBoundCallId: { type: string, description: 'gone.ask_parked ONLY — gate reconciliation coordinate 2.' }
16709
16945
  gateBoundInputHash: { type: string, description: 'gone.ask_parked ONLY — gate reconciliation coordinate 3.' }
16710
- state: { type: string, enum: [DENIED, VOID], description: 'gone.ask ONLY — the terminal state.' }
16946
+ state: { type: string, enum: [DENIED, VOID], description: 'gone.ask ONLY — the terminal state. server >= 7.116.0 also mints `VOID` for "the process that held the ask is gone" (local lane restart; see `deniedReason`).' }
16711
16947
  deniedReason:
16712
16948
  type: string
16713
16949
  description: >
16714
16950
  gone.ask ONLY — attribution, GROUPED BY `state`. `DENIED` carries the single-member deny
16715
16951
  vocabulary `routing_failure_fail_closed`; `VOID` carries the separate (and growing) void
16716
- vocabulary, e.g. `adhoc_leg_no_durable_domain`. The two vocabularies never mix.
16952
+ vocabulary, seven words today: `run_cancelled`, `batch_aborted`, `batch_bound_elsewhere`,
16953
+ `adhoc_leg_no_durable_domain`, `orphan_ttl_exceeded` and — new in server 7.116.0 (local lane) —
16954
+ `crashed_before_park` (the process that held the ask crashed / was killed) and `shutdown_before_park`
16955
+ (an orderly shutdown could not settle the card inside its grace window; a fault shape, hard to build black-box).
16956
+ The last two are the SAME word as `GET /v1/approvals` `crashConverged[].cause`; the other six were already on the wire
16957
+ (7.3.0 / 7.55.0 / 7.108.0) and are only registered in the server's self-minted word table by 7.116.0.
16958
+ ABSENT = the row carries no attribution. The two vocabularies never mix; the VOID one is open (read an unknown word as "the ask ended without
16959
+ a human decision, reason unknown").
16717
16960
  description: >
16718
16961
  410 body. THE TWO LEGS ARE DISJOINT: `gone.ask_parked` carries exactly the three gate coordinates,
16719
- `gone.ask` carries exactly `{state, deniedReason?}`. Routing off field presence across the legs is how a
16962
+ `gone.ask` carries exactly `{state, deniedReason?}` (no `consumerState`).
16963
+ 🔴 server >= 7.116.0 (local lane, `DB_BACKEND=local`): after a crash / kill and a restart on the same data root, answering the PREVIOUS
16964
+ life's still-open card is now `410 gone.ask` with `state: "VOID"` and `deniedReason` `crashed_before_park` / `shutdown_before_park` —
16965
+ <= 7.115.x answered `200` + `consumerState: "absent"` (the run had long been reclaimed; nothing executed). An ask that was already decided
16966
+ (decided, then the process died mid-execution) is unchanged: `409 conflict.ask_decided`. SQL backends are unchanged in 7.116.0. Routing off field presence across the legs is how a
16720
16967
  consumer ends up driving the wrong durable gate.
16721
16968
  🔴 The checkpoint token is NEVER here: the resume credential is not externally disclosed. Use
16722
16969
  `sessionId`.
@@ -18495,9 +18742,15 @@ components:
18495
18742
  description: >-
18496
18743
  [2400] TR-4 — per-message queue priority on POST /v1/runs/{taskId}/steer. ACCEPTED + ENUM-VALIDATED
18497
18744
  FAIL-LOUD (`routes/runs.ts`; anything else is 400 `request.field_invalid`) and echoed back on the
18498
- receipt, but ADVISORY today: core's steer is a single-slot last-writer-wins with no per-message priority
18499
- or addressable drop, so a faithful priority queue (and DELETE /v1/runs/{taskId}/queue/{messageId}) waits
18500
- on a core seam.
18745
+ receipt. WHETHER IT TAKES EFFECT is the server's call — read `Capabilities.fleet.steerPriority` first:
18746
+ `"engine"` (server >=7.116.0) = the tier is handed to the engine on every delivery leg (`now` cuts the in-flight turn
18747
+ IMMEDIATELY — the running tool receives an abort, an approval card pending on this replica is revoked, one `engine_notice`
18748
+ `task.turn_interrupted` frame is the evidence; `next` ≡ no `priority`; `later` is delivered after the turn the run would have stopped on; parked
18749
+ legs store the tier and deliver on the resume's / wake's first turn; replaying the same `Idempotency-Key` + text with a different effective
18750
+ tier is 409 `steering.duplicate_input_id`); `"advisory"` (server <=7.115.x) = accepted and echoed but NOT applied —
18751
+ core's steer was a single-slot last-writer-wins with no per-message priority or addressable drop, so a
18752
+ faithful priority queue (and DELETE /v1/runs/{taskId}/queue/{messageId}) waited on a core seam. An absent
18753
+ or unknown capability value means "do not assume it is applied".
18501
18754
 
18502
18755
  SteerReceipt:
18503
18756
  type: object
@@ -18774,6 +19027,8 @@ components:
18774
19027
  server 1.239 ([1392]②): on allow_session, an honest echo of whether the grant was REALLY
18775
19028
  recorded (true = landed in the bridge store; false = no sessionId, could not record — do not
18776
19029
  render "session-wide allowed"). Omitted on non-allow_session decisions and on older servers.
19030
+ 🔴 server >= 7.116.0: ALWAYS `false` on a card that carries `rememberRefused: true` (an engine `Skill` card etc.) — the decision
19031
+ applies once and nothing is remembered (existing key, not a new one).
18777
19032
  updatedInputForwarded:
18778
19033
  type: boolean
18779
19034
  description: >