@sema-agent/sdk 16.0.1 → 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 (81) hide show
  1. package/README.md +125 -1
  2. package/dist/errors.d.ts +53 -11
  3. package/dist/errors.d.ts.map +1 -1
  4. package/dist/errors.js +32 -4
  5. package/dist/errors.js.map +1 -1
  6. package/dist/events.d.ts +4 -0
  7. package/dist/events.d.ts.map +1 -1
  8. package/dist/events.js.map +1 -1
  9. package/dist/index.d.ts +3 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js.map +1 -1
  12. package/dist/registry/admin.d.ts +3 -1
  13. package/dist/registry/admin.d.ts.map +1 -1
  14. package/dist/registry/admin.js +4 -2
  15. package/dist/registry/admin.js.map +1 -1
  16. package/dist/resources/adoption.d.ts +2 -1
  17. package/dist/resources/adoption.d.ts.map +1 -1
  18. package/dist/resources/adoption.js +2 -1
  19. package/dist/resources/adoption.js.map +1 -1
  20. package/dist/resources/approvals.d.ts +10 -3
  21. package/dist/resources/approvals.d.ts.map +1 -1
  22. package/dist/resources/approvals.js +11 -3
  23. package/dist/resources/approvals.js.map +1 -1
  24. package/dist/resources/assistant.d.ts +4 -2
  25. package/dist/resources/assistant.d.ts.map +1 -1
  26. package/dist/resources/assistant.js.map +1 -1
  27. package/dist/resources/leader.d.ts +5 -4
  28. package/dist/resources/leader.d.ts.map +1 -1
  29. package/dist/resources/leader.js +5 -4
  30. package/dist/resources/leader.js.map +1 -1
  31. package/dist/resources/memory.d.ts +6 -2
  32. package/dist/resources/memory.d.ts.map +1 -1
  33. package/dist/resources/memory.js +3 -1
  34. package/dist/resources/memory.js.map +1 -1
  35. package/dist/resources/ops.d.ts +3 -0
  36. package/dist/resources/ops.d.ts.map +1 -1
  37. package/dist/resources/ops.js +3 -0
  38. package/dist/resources/ops.js.map +1 -1
  39. package/dist/resources/rules.d.ts +4 -3
  40. package/dist/resources/rules.d.ts.map +1 -1
  41. package/dist/resources/rules.js +3 -3
  42. package/dist/resources/rules.js.map +1 -1
  43. package/dist/resources/runs.d.ts +20 -6
  44. package/dist/resources/runs.d.ts.map +1 -1
  45. package/dist/resources/runs.js +3 -2
  46. package/dist/resources/runs.js.map +1 -1
  47. package/dist/resources/session-sync.d.ts +11 -7
  48. package/dist/resources/session-sync.d.ts.map +1 -1
  49. package/dist/resources/session-sync.js +9 -6
  50. package/dist/resources/session-sync.js.map +1 -1
  51. package/dist/resources/sessions.d.ts +16 -5
  52. package/dist/resources/sessions.d.ts.map +1 -1
  53. package/dist/resources/sessions.js +15 -4
  54. package/dist/resources/sessions.js.map +1 -1
  55. package/dist/resources/tasks.d.ts +7 -1
  56. package/dist/resources/tasks.d.ts.map +1 -1
  57. package/dist/resources/tasks.js +7 -1
  58. package/dist/resources/tasks.js.map +1 -1
  59. package/dist/resources/tool-approvals.d.ts +24 -3
  60. package/dist/resources/tool-approvals.d.ts.map +1 -1
  61. package/dist/resources/tool-approvals.js +6 -0
  62. package/dist/resources/tool-approvals.js.map +1 -1
  63. package/dist/resources/workflows.d.ts +5 -2
  64. package/dist/resources/workflows.d.ts.map +1 -1
  65. package/dist/resources/workflows.js +5 -2
  66. package/dist/resources/workflows.js.map +1 -1
  67. package/dist/settings.d.ts +34 -8
  68. package/dist/settings.d.ts.map +1 -1
  69. package/dist/sync.d.ts +19 -2
  70. package/dist/sync.d.ts.map +1 -1
  71. package/dist/sync.js +88 -2
  72. package/dist/sync.js.map +1 -1
  73. package/dist/transport.d.ts +2 -0
  74. package/dist/transport.d.ts.map +1 -1
  75. package/dist/transport.js +2 -0
  76. package/dist/transport.js.map +1 -1
  77. package/dist/types.d.ts +213 -30
  78. package/dist/types.d.ts.map +1 -1
  79. package/dist/types.js.map +1 -1
  80. package/openapi.yaml +497 -74
  81. package/package.json +2 -2
package/openapi.yaml CHANGED
@@ -414,9 +414,10 @@ paths:
414
414
  Synchronous run-to-completion. Auto `Idempotency-Key` (→ retry-safe). Supports `verify`/`cascade`.
415
415
  For anything that may take minutes or hit a HITL gate, use `/v1/runs` instead (this call blocks).
416
416
  🔴 PREPARE-TIME REFUSALS ARE TRIAGED ON WORK DONE (server >= 7.104.0 / S-271, one rule for the whole table): a
417
- prepare-time refusal code that HAS a status in the server's per-code triage table (`resume_at.*`, governance codes,
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
@@ -442,6 +443,16 @@ paths:
442
443
  '400': { $ref: '#/components/responses/BadRequest' }
443
444
  '401': { $ref: '#/components/responses/Unauthorized' }
444
445
  '403': { $ref: '#/components/responses/Forbidden' } # (declared 2026-08-01) 带 `sessionId` 复用他人会话:归属不匹配 ⇒ 403(自公开快照起点即有,spec 此前漏登记)。server 3.15.0 起多一个触发条件:多租部署上 session store 无归属判别能力时,复用**任何** caller 自报的 sessionId 都 403(含本 worker 自己刚铸的 —— 无 register seam 时归属从未被记下)。core 2.11.0 起默认内存店自带该能力 ⇒ 该触发条件在默认部署上不再出现。 ⚠️ (declared 2026-08-05,server >=7.0.0)`memory.admission_denied` 也走本状态 —— 本状态的 producer 全清单与分支判据写在 `Forbidden` 组件的 description 里(**那段是被解析的**,本行注释只是指路,不复述以免两处漂移)。
446
+ '404':
447
+ description: >-
448
+ Two codes. `resume.session_not_found` (server >= 7.115.0 / S-1038): the body carried `requireExistingSession: true` and its
449
+ `sessionId` does not exist in this deployment. `resume_at.not_found` (server `dist/runs.js` `resumeAtHttpStatus`): a `resumeAt`
450
+ anchor that is genuinely absent (compacted away) — the ONLY `resume_at.*` code that is a 404 (the rest are 422 below); like
451
+ every prepare-time refusal in that table it answers its error body only when the submission did ZERO WORK (turns 0, tokens 0),
452
+ otherwise 200 + a failed body carrying the code in `terminal.code`.
453
+ content:
454
+ application/json:
455
+ schema: { $ref: '#/components/schemas/ErrorResponse' }
445
456
  '409':
446
457
  description: >-
447
458
  Conflict (ConflictError). 409 — session already has an active run (`conflict.session_active_run`, `activeTaskId`,
@@ -460,7 +471,10 @@ paths:
460
471
  Semantically unprocessable. K-24 (server >= 7.104.0 / S-724): `rewind.capture_disabled` — the run turned file
461
472
  history off (`fileHistory: "disabled"`) and asks to restore in the same breath; this server's request face
462
473
  refuses that combination earlier (400 `request.field_conflict`), so only a host building a TaskSpec directly can
463
- reach this code. `rewind.child_scope_unsupported` / `resume_at.*` also land here.
474
+ reach this code. `rewind.child_scope_unsupported` and every `resume_at.*` code EXCEPT `resume_at.not_found` (that one is
475
+ a 404 above) also land here — e.g. `resume_at.before_target_not_user` / `resume_at.before_root_unsupported` (`resumeAtMode:
476
+ "before"`). Also `request.field_conflict` (server >= 7.115.0 / S-1038): `requireExistingSession: true` without a `sessionId`
477
+ (the only 422 this code carries; the request contradicts itself).
464
478
  `rewind_snapshot.unresolvable` (server 7.114.0 `dist/runs.js` `resumeAtHttpStatus`: a `resumeAt` file restore —
465
479
  `restoreFiles` — that finds no file-history boundary at / above the branch point) is in the same per-code table and lands
466
480
  here too, on the same ZERO WORK condition (turns 0, tokens 0: nothing ran, nothing billed); a submission that already ran
@@ -574,6 +588,20 @@ paths:
574
588
  application/json:
575
589
  schema: { $ref: '#/components/schemas/ErrorResponse' }
576
590
  '401': { $ref: '#/components/responses/Unauthorized' }
591
+ '404':
592
+ description: >-
593
+ `resume.session_not_found` (server >= 7.115.0 / S-1038): the body carried `requireExistingSession: true` and its `sessionId` does not
594
+ exist in this deployment (the previous status-derived coarse code `not_found.resource` is gone). Dispatch on `errorCode`.
595
+ content:
596
+ application/json:
597
+ schema: { $ref: '#/components/schemas/ErrorResponse' }
598
+ '422':
599
+ description: >-
600
+ `request.field_conflict` (server >= 7.115.0 / S-1038): the body carried `requireExistingSession: true` without a `sessionId` —
601
+ the request contradicts itself (the previous status-derived coarse code was `request.unprocessable`). The only 422 this code carries.
602
+ content:
603
+ application/json:
604
+ schema: { $ref: '#/components/schemas/ErrorResponse' }
577
605
  '429': { $ref: '#/components/responses/RateLimited' }
578
606
  '503': { $ref: '#/components/responses/Unauthorized' } # (declared 2026-07-31) this path is in server.ts's isBillableSubmitPath set — same three 503 doors as POST /v1/tasks and /v1/runs (no service token / draining / model-roster-pending)
579
607
 
@@ -603,7 +631,21 @@ paths:
603
631
  schema: { $ref: '#/components/schemas/RunReceipt' }
604
632
  '400': { $ref: '#/components/responses/BadRequest' }
605
633
  '401': { $ref: '#/components/responses/Unauthorized' }
634
+ '404':
635
+ description: >-
636
+ `resume.session_not_found` (server >= 7.115.0 / S-1038): the body carried `requireExistingSession: true` and its `sessionId` does not
637
+ exist in this deployment (the previous status-derived coarse code `not_found.resource` is gone). Dispatch on `errorCode`.
638
+ content:
639
+ application/json:
640
+ schema: { $ref: '#/components/schemas/ErrorResponse' }
606
641
  '409': { $ref: '#/components/responses/Conflict' }
642
+ '422':
643
+ description: >-
644
+ `request.field_conflict` (server >= 7.115.0 / S-1038): the body carried `requireExistingSession: true` without a `sessionId` —
645
+ the request contradicts itself (the previous status-derived coarse code was `request.unprocessable`). The only 422 this code carries.
646
+ content:
647
+ application/json:
648
+ schema: { $ref: '#/components/schemas/ErrorResponse' }
607
649
  '429': { $ref: '#/components/responses/RateLimited' }
608
650
  '501': { $ref: '#/components/responses/NotImplemented' }
609
651
  '503': { $ref: '#/components/responses/Unauthorized' }
@@ -919,7 +961,8 @@ paths:
919
961
  un-started ones settle as interrupted pairs, in-flight tools are NOT collateral. TWO shapes,
920
962
  discriminated by `text` presence.
921
963
  WITH text = cut + steer: the in-flight turn is cut and the text goes to the FRONT of the queue — the
922
- `"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"`) —
923
966
  and the run CONTINUES. 202 `{taskId, status, delivery:"queued-immediate", messageId, note}`;
924
967
  `messageId` ≡ the engine inputId, and a real cut is proven ONLY by a `task.turn_interrupted` event on
925
968
  the run stream whose `detail.inputId` matches it (core: no false interrupt claims — every real cut
@@ -2048,7 +2091,11 @@ paths:
2048
2091
  # 亲读真码:wake 的成功腿是 `driveResumeIntoRunLog` 的返回(`server.ts`,成功腿与取消臂两处铸造点),
2049
2092
  # **恒为 200**(wake 是同步驱动到终局/再挂起,不是 accepted 回执),且 `taskId` 有条件
2050
2093
  # (无活跃行即省略——ApprovalDecisionResult 同一铸造点的同一注)。旧 202+required taskId 双谎。
2051
- description: Woken — the resumed leg ran to its next settle (terminal, re-suspended, or needs_review).
2094
+ description: >-
2095
+ Woken — the resumed leg ran to its next settle (terminal, re-suspended, or needs_review). 🔴 A message-carrying wake that is CANCELLED
2096
+ (`POST /v1/runs/{taskId}/cancel`) while the deployment's `userPromptSubmit` gate is still answering lands HERE, not as a 409 (server
2097
+ >= 7.115.0 / core 7.40.1): `{ status: "failed", errorCode: "cancelled", bindingEnforced: true }` — RETURNED, branch on `errorCode`; the
2098
+ park is closed with the cancel, so a second wake is 404 `not_found.parked_checkpoint` (continue the session with a new submit).
2052
2099
  content:
2053
2100
  application/json:
2054
2101
  schema:
@@ -2070,12 +2117,17 @@ paths:
2070
2117
  retriable: { type: boolean, description: 'true = the park is STILL pending (re-parked); refetch and retry. Synchronous form only.' }
2071
2118
  '400': { $ref: '#/components/responses/BadRequest' }
2072
2119
  '401': { $ref: '#/components/responses/Unauthorized' }
2073
- '404': { description: 'No parked checkpoint for this session (nothing to wake), or session not visible to the caller.' }
2120
+ '404': { description: 'No parked checkpoint for this session (nothing to wake; `not_found.parked_checkpoint`), or session not visible to the caller. Also what a SECOND wake gets after a message-carrying wake was cancelled (server >= 7.115.0: that park was closed with the cancel — continue the session with a new submit).' }
2074
2121
  '409':
2075
2122
  description: >
2076
2123
  Pending gate (`wake.gate_pending` — decide it through its own entry) or resume context missing; also the resume-leg refusals core
2077
2124
  raises before the claim (`steering.blocked_by_hook` when the wake carried a `message` and the deployment's `userPromptSubmit`
2078
- hook crashed / timed out / refused — server >= 7.113.0 adds `retriable: true` there; `checkpoint.config_refused`). Value change on
2125
+ hook crashed / timed out / refused — server >= 7.113.0 adds `retriable: true` there; since server >= 7.115.0 that is the ONLY thing it
2126
+ means on this door — a message-carrying wake CANCELLED while the gate was still answering (`POST /v1/runs/{taskId}/cancel`) is no longer a
2127
+ 409 but a 200 `{ status: "failed", errorCode: "cancelled" }` (see 200; <= 7.114.x answered it 409 `steering.blocked_by_hook` and left the park
2128
+ pending — the wrong code's side effect); `checkpoint.unsupported_version` (server >= 7.115.0, `retriable: true` = the card is still pending,
2129
+ NOT "a verbatim retry will succeed": a task parked by <= 7.114.x whose submit body carried a non-empty `settings.permissions.allow` is not replayed
2130
+ under the new reading — resubmit it as a new task; no `taskId` in the body); `checkpoint.config_refused`). Value change on
2079
2131
  the run row the wake minted for the refused attempt (server >= 7.113.0): it now ends `failed` with a structured terminal carrying
2080
2132
  `terminal.code: "steering.blocked_by_hook"` and holds no session lock (before 7.113.0 it ended with a code-less terminal and
2081
2133
  `result: null` — a zero-execution refusal recorded as an ordinary failure); the card itself stays pending.
@@ -2369,9 +2421,10 @@ paths:
2369
2421
  written. DETERMINISTIC: a retry cannot change it (the SDK does not auto-retry this code; it arrives as a bare `APIError`
2370
2422
  with `errorCode: "corrupt"`); the repair is the operator's — fix the file or delete it (deleting = an explicit "no
2371
2423
  rules"). For a damaged rules row `error` names the session, the damage grade and both exits, with NO on-disk path.
2372
- ⚠️ The same code also comes from the write path's high-water-mark sidecar (`.hwm.json`) being unreadable, and that one
2373
- sentence STILL names the sidecar's on-disk path (server 7.114.0 discloses it as a known gap: core #1307 / #1309, core
2374
- 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).
2375
2428
  content:
2376
2429
  application/json:
2377
2430
  schema: { $ref: '#/components/schemas/ErrorResponse' }
@@ -2989,14 +3042,13 @@ paths:
2989
3042
  2c session-sync PUSH Phase A — a SMALL metadata body (`entryIds, snapshots, policy, anchors, resolution?`); the
2990
3043
  entries stream in Phase B. The cloud classifies (§7), pre-checks blob presence (a missing referenced hash →
2991
3044
  422 `missing_blob` — PUT it first), guards the import lease + any active run (409), then mints a stagingId.
2992
- ⚠️ S-15 EPOCH DEBT (registered 2026-08-30, codex adversarial review; migration is a SEPARATE batch):
2993
- server ≥7.52's Phase A actually consumes `{entryIds, fileHistory?, policy, anchors, resolution?,
2994
- logDigest?}` (routes/session-sync.ts) — the `snapshots` key below is SILENTLY IGNORED there (an
2995
- unknown body key), so on ≥7.52 a push lands entries/policy/anchors and the session itself but NO
2996
- file snapshots/history; the snapshot-epoch shape below is what ≤7.51 servers consume. The full
2997
- migration (SessionManifest/SessionBundle/push-pull orchestration + `fileHistory: FileHistoryExport |
2998
- null` + epoch negotiation) is tracked as its own debt — this op's schema deliberately stays the
2999
- ≤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`).
3000
3052
  `identical` → `{ relation:"identical", basis, payloadVerified }` with NO stagingId (skip Phase B);
3001
3053
  `basis` is `"entry-ids+digest"` when the caller supplied a comparable digest and it matched, else
3002
3054
  `"entry-ids"` (id-set equality only) — see the `ImportStaged` schema, which has carried these two
@@ -3013,6 +3065,11 @@ paths:
3013
3065
  properties:
3014
3066
  entryIds: { type: array, items: { type: string } }
3015
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.'
3016
3073
  policy: { type: array, items: { $ref: '#/components/schemas/SessionRulesRecord' } }
3017
3074
  anchors: { type: array, items: { $ref: '#/components/schemas/SyncAnchor' } }
3018
3075
  resolution: { type: string, enum: [overwrite-dst], description: Override a fork/stale loss-of-history conflict. }
@@ -4589,10 +4646,11 @@ paths:
4589
4646
  `LEADER_ENABLED` (default OFF) + `REMOTE_EXEC=e2b`. Mode is a SERVER deploy flag, not a client choice:
4590
4647
  `LEADER_FANOUT_ENABLED=true` (default) lets the router fan out to N isolated workers (= the value-HOLD
4591
4648
  correctness bet); `=false` pins a single worker. Either way it runs the E2B+git pipeline. Door B's brain
4592
- is the strong single agent via `/v1/tasks`+`/v1/runs`+`scenario`. When disabled (`LEADER_ENABLED` off)
4593
- the route is simply NOT REGISTERED → the generic **404** `not_found.route`, not 501.
4594
- (Corrected 2026-07-31: "When disabled → 501" was never true — no code path emits 501 here; a consumer
4595
- 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.
4596
4654
 
4597
4655
 
4598
4656
  🔴 IDEMPOTENCY (server >= 7.24.0): this POST now consumes the `Idempotency-Key` header — the header is
@@ -4733,7 +4791,8 @@ paths:
4733
4791
  description: >-
4734
4792
  (declared 2026-07-31) errorCode "auth.operator_only" — EXPLICIT operator gate (not the bare
4735
4793
  isOperator, whose empty-OPERATOR_PRINCIPALS "true-for-all" would make this index-mutating door
4736
- 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).
4737
4796
  content:
4738
4797
  application/json:
4739
4798
  schema: { $ref: '#/components/schemas/ErrorResponse' }
@@ -4787,7 +4846,7 @@ paths:
4787
4846
  '400': { $ref: '#/components/responses/BadRequest' }
4788
4847
  '401': { $ref: '#/components/responses/Unauthorized' }
4789
4848
  '403':
4790
- 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).
4791
4850
  content:
4792
4851
  application/json:
4793
4852
  schema: { $ref: '#/components/schemas/ErrorResponse' }
@@ -4841,7 +4900,7 @@ paths:
4841
4900
  responses:
4842
4901
  '200': { description: '{ bake }.', content: { application/json: { schema: { type: object, required: [bake], additionalProperties: false, properties: { bake: { $ref: '#/components/schemas/BakeRecordView' } } } } } }
4843
4902
  '403':
4844
- 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).
4845
4904
  content:
4846
4905
  application/json:
4847
4906
  schema: { $ref: '#/components/schemas/ErrorResponse' }
@@ -4859,7 +4918,7 @@ paths:
4859
4918
  responses:
4860
4919
  '202': { description: '{ bakeId, cancelRequested, note }.', content: { application/json: { schema: { $ref: '#/components/schemas/BakeCancelAck' } } } }
4861
4920
  '403':
4862
- 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).
4863
4922
  content:
4864
4923
  application/json:
4865
4924
  schema: { $ref: '#/components/schemas/ErrorResponse' }
@@ -4921,7 +4980,7 @@ paths:
4921
4980
  responses:
4922
4981
  '200': { description: 'SSE stream.', content: { text/event-stream: { schema: { type: string } } } }
4923
4982
  '403':
4924
- 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).
4925
4984
  content:
4926
4985
  application/json:
4927
4986
  schema: { $ref: '#/components/schemas/ErrorResponse' }
@@ -5133,7 +5192,7 @@ paths:
5133
5192
  schema: { $ref: '#/components/schemas/EntryProvenanceAccount' }
5134
5193
  '400': { description: 'errorCode "request.id_invalid" — the entry id could not be decoded.' }
5135
5194
  '401': { $ref: '#/components/responses/Unauthorized' }
5136
- '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).' }
5137
5196
  '500':
5138
5197
  description: >
5139
5198
  errorCode "internal.memory_control_plane_corrupt" — core's FAIL-CLOSED refusal: the control plane
@@ -5203,7 +5262,7 @@ paths:
5203
5262
  application/json:
5204
5263
  schema: { $ref: '#/components/schemas/ErrorResponse' }
5205
5264
  '401': { $ref: '#/components/responses/Unauthorized' }
5206
- '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).' }
5207
5266
  '409':
5208
5267
  description: >
5209
5268
  Three core-minted codes, passed through verbatim; branch on `errorCode` (the status cannot tell them
@@ -5282,7 +5341,7 @@ paths:
5282
5341
  schema: { $ref: '#/components/schemas/MemoryOriginExternalResult' }
5283
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.' }
5284
5343
  '401': { $ref: '#/components/responses/Unauthorized' }
5285
- '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).' }
5286
5345
  '500':
5287
5346
  description: 'errorCode "internal.memory_control_plane_corrupt" (meaning (1) only on this read face) or "internal.error" (unclassified, text not echoed).'
5288
5347
  content:
@@ -5332,7 +5391,7 @@ paths:
5332
5391
  application/json:
5333
5392
  schema: { $ref: '#/components/schemas/MemoryOriginClearancesResult' }
5334
5393
  '401': { $ref: '#/components/responses/Unauthorized' }
5335
- '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).' }
5336
5395
  '500':
5337
5396
  description: 'errorCode "internal.memory_control_plane_corrupt" (the ledger cannot be read soundly) or "internal.error".'
5338
5397
  content:
@@ -5396,7 +5455,7 @@ paths:
5396
5455
  application/json:
5397
5456
  schema: { $ref: '#/components/schemas/ErrorResponse' }
5398
5457
  '401': { $ref: '#/components/responses/Unauthorized' }
5399
- '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).' }
5400
5459
  '409':
5401
5460
  description: >
5402
5461
  Six core-minted codes, passed through verbatim; BRANCH ON `errorCode` — the six operator actions all
@@ -5519,7 +5578,7 @@ paths:
5519
5578
  application/json:
5520
5579
  schema: { $ref: '#/components/schemas/WiringDiagnostics' }
5521
5580
  '401': { $ref: '#/components/responses/Unauthorized' }
5522
- '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).' }
5523
5582
  '404': { description: 'This worker serves no wiring diagnostics (older server, or a process not assembled by the composition root).' }
5524
5583
 
5525
5584
  /v1/sendfile-links:
@@ -5849,7 +5908,9 @@ paths:
5849
5908
  description: >
5850
5909
  OPERATOR-ONLY cross-tenant read. Absent (or equal to the caller's own principal) = read your own
5851
5910
  rules. Anything else requires explicit OPERATOR_PRINCIPALS membership ⇒ otherwise 403
5852
- `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
5853
5914
  disclosure and are deliberately not opened to non-operators.
5854
5915
  - name: scope
5855
5916
  in: query
@@ -6594,7 +6655,14 @@ components:
6594
6655
  application/json:
6595
6656
  schema: { $ref: '#/components/schemas/QuotaError' }
6596
6657
  NotImplemented:
6597
- 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).
6598
6666
  content:
6599
6667
  application/json:
6600
6668
  schema: { $ref: '#/components/schemas/ErrorResponse' }
@@ -6774,7 +6842,18 @@ components:
6774
6842
  name: { type: string }
6775
6843
  description: { type: string }
6776
6844
  content: { type: string }
6777
- 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).' }
6778
6857
  files:
6779
6858
  type: array
6780
6859
  items:
@@ -6837,6 +6916,13 @@ components:
6837
6916
  instead of silently starting a fresh empty session ("looks warm, actually fresh"). Sending `true`
6838
6917
  with NO sessionId also fails loud (nothing to resume). Absent/`false` ⇒ the historical create-on-miss.
6839
6918
  Rides the persisted body onto resume legs.
6919
+ 🔴 The two refusals carry machine codes (server >= 7.115.0 / S-1038; before that both answered the status-derived coarse
6920
+ codes `not_found.resource` / `request.unprocessable` and the real code lived only in the sentence's trailing
6921
+ parenthesis): `sessionId` not found in this deployment ⇒ **404 `resume.session_not_found`** (premise: the session store
6922
+ can answer ownership — SQL, local and the engine's default in-memory store all can); no `sessionId` at all ⇒ **422
6923
+ `request.field_conflict`** (the request contradicts itself — `sessionId` is its precondition; NOT "session not found",
6924
+ and the only 422 this code carries). Same body on `POST /v1/tasks`, `/v1/tasks/stream` and `/v1/runs`; authorization
6925
+ happens before the SSE head, so the stream door answers the real status too. Dispatch on `errorCode`.
6840
6926
  projectId:
6841
6927
  type: string
6842
6928
  description: >
@@ -6876,6 +6962,18 @@ components:
6876
6962
  LISTING RENDERER, not to this gate: turn it down with `settings.skillListingBudgetFraction` /
6877
6963
  `settings.skillListingMaxDescChars`.
6878
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.
6879
6977
  reasoningEffort:
6880
6978
  type: string
6881
6979
  enum: [off, minimal, low, medium, high, xhigh, max]
@@ -6936,7 +7034,8 @@ components:
6936
7034
  type: string
6937
7035
  description: >
6938
7036
  CC Rewind parity (E18). Fork the conversation FROM this user-message SessionTreeEntry.id (non-destructive
6939
- branch via setLeafId). With rewindFiles=true ⇒ "both" (also restore the working tree). Alone ⇒
7037
+ branch via setLeafId). With `restoreFiles: true` ⇒ "both" (also restore the working tree — the retired spelling
7038
+ `rewindFiles` still rides the wire but its restore sense is core's typed migration rejection naming `restoreFiles`). Alone ⇒
6940
7039
  "conversation". HONORED only where the deployment wired a resume-anchor store + getLeafId (capabilities.resumeAt);
6941
7040
  unknown anchor ⇒ 4xx, capability-off ⇒ 501. Cannot combine with a durable resume.
6942
7041
  resumeAtMode:
@@ -6949,7 +7048,8 @@ components:
6949
7048
  everything after it"). core enforces the edges and the service passes its rejection codes through
6950
7049
  UNCHANGED for the shell to render: `resume_at.before_target_not_user` (plain user-message targets
6951
7050
  only), `resume_at.before_root_unsupported` (cannot rewind past the session root — start a new
6952
- session), `rewind_snapshot.unresolvable` (with rewindFiles: no snapshot at/above the branch point).
7051
+ session), `rewind_snapshot.unresolvable` (with a file restore, `restoreFiles`: the target has no file-history boundary of its
7052
+ own — a "before" restore only trusts the target's OWN boundary and never crosses into an earlier turn).
6953
7053
  Only meaningful WITH `resumeAt` — sent alone it is a 400 (fail-loud, not a silent no-op).
6954
7054
  rewindFiles:
6955
7055
  type: boolean
@@ -7024,8 +7124,8 @@ components:
7024
7124
  `acceptEdits` (in-workspace writes do not ask) or `bypassPermissions` (422 `request.host_posture_refused` on hosted
7025
7125
  deployments) for the old no-ask posture. `auto` arms iff `permissionMode === "auto"` ∧ a classifier seat is mounted ∧
7026
7126
  `caps.autoMode !== false` (one polarity); when the requested mode does not take effect the stream gets `engine_notice`
7027
- `config.permission_mode_unavailable`. `bypassPermissions` RELEASES the nine releasable ask classes and DISCLOSES each
7028
- (`tool_end.gate.disposition.released` + one `engine_notice` `permission.ask_released_by_mode` per (mode, class) per run);
7127
+ `config.permission_mode_unavailable`. `bypassPermissions` RELEASES the ten releasable ask classes (server >= 7.115.0: the tenth is `unstated`, a server-tool call nobody stated)
7128
+ and DISCLOSES each (`tool_end.gate.disposition.released` + one `engine_notice` `permission.ask_released_by_mode` per (mode, class) per run);
7029
7129
  immune classes (deployment-authored, ask / org rules, hooks, ancestors, questions …) still ask; a write whose protection
7030
7130
  cannot be judged is refused (`deniedBy: mode`). Request vocabulary: FIVE words — `dontAsk` on a fresh submit is 400
7031
7131
  `request.field_invalid` (S-902). The three sentences below — "omitting both carriers means no mode-derived gate", "`auto`
@@ -7230,6 +7330,26 @@ components:
7230
7330
  the delivery lane, so raising it only helps a model whose declared window is below 200 000 tokens.
7231
7331
  The engine's own twin code `config.skill_listing_budget_invalid` (400) is NOT reachable through this
7232
7332
  submit leg today — it belongs to callers that build a `TaskSpec` directly.
7333
+ 🔴 `permissions.allow` IS A STATEMENT, NOT A WHITELIST (server >= 7.115.0 / S-654; probe
7334
+ `capabilities.settings.permissions.allowGrant === true` — an absent key is an older server that reads the bucket as a
7335
+ list, so put nothing in it there). The bucket is compiled by the SAME rule compiler as `deny` / `ask`: a hit is a
7336
+ reasoned allow = "somebody stated it" (it answers an MCP / A2A server-tool call nobody stated; without it an
7337
+ ask-class mode raises a card, and with no approval surface the call is refused); a miss states nothing and the call is
7338
+ judged as usual — a tool off the list is NOT refused any more (<= 7.114.x refused it). It does NOT skip the question
7339
+ on built-in tools: the engine's own write gate and shell checks still ask in `default` mode, and deny / ask win
7340
+ (unlike the reference implementation, where `allow: ["Edit"]` skips the edit confirmation; use `permissionMode`
7341
+ for that). For "only these tools" use the real knobs: the session rule `toolAllow`
7342
+ (`PUT /v1/sessions/{sessionId}/policy`), per-request `excludeTools` / `excludeAllTools`, or the deployment's scenario
7343
+ tool surface. Entry forms: a tool-name rule (exact name, `mcp__<server>__*`, `a2a__<peer>__*`; `Bash(*)` is accepted
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
7349
+ (`Bash(npm:*)`, `Read(./x)`, `WebFetch(domain:...)`) is a whole-request 422 (permanent content-form allows live in the
7350
+ persistent rule store: `/v1/rules/cc-import`, or answer yes on a card). The same applies to a PreToolUse hook: an
7351
+ EXPLICIT `permissionDecision: "allow"` states it (and binds only the arguments the hook saw); a hook that only adds
7352
+ `additionalContext` / only rewrites `updatedInput` states nothing.
7233
7353
  additionalProperties: true
7234
7354
  cwd: { type: string, description: Working directory for the execution env. }
7235
7355
  deviceId:
@@ -7452,6 +7572,12 @@ components:
7452
7572
  to turn them off, or `{at: [r1, r2]}` with fractions in (0, 1], r1 <= r2 to tune thresholds
7453
7573
  (bad shapes are a 400). Budget ceilings stay TOP-LEVEL body keys
7454
7574
  (`maxCostUsd`/`maxTokens`) — the server folds them into the engine's limits internally.
7575
+ 🔴 The key set is CLOSED (the same four keys as the server's published `TaskRequestBody.limits`):
7576
+ `limits.brainCallGuardrailMs` (the engine's wall on a SINGLE brain call, default one hour, `false` / `0` off) is NOT
7577
+ one of them — server >= 7.115.0 answers it with 400 `request.field_invalid` (a millisecond number, `false` and `0`
7578
+ alike; the sentence names the key; <= 7.114.x accepted it but it took no effect and the wall stayed at one hour).
7579
+ This server has no knob for a caller to set that wall. `budgetStreamCancel` and `degrade` are the same kind of
7580
+ "not caller-settable" keys.
7455
7581
  properties:
7456
7582
  maxWalltimeMs: { type: integer }
7457
7583
  maxOutputTokens: { type: integer }
@@ -8136,6 +8262,16 @@ components:
8136
8262
  is unchanged at 7.64.0 — only its source moved (projected from `result.terminal`, or read straight
8137
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
8138
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.
8139
8275
  error: { type: string }
8140
8276
  jobId:
8141
8277
  type: string
@@ -8947,7 +9083,8 @@ components:
8947
9083
  server >=7.12.0 — the `GET /v1/outcomes` mechanical-signal read aggregate (design/73 §7.2) is
8948
9084
  available. Predicate mirrors the route's refusal arms exactly: a QUERYABLE ledger is wired (a
8949
9085
  file-only sink writes JSONL and has no query face ⇒ 501), AND on a multi-tenant deployment the
8950
- 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).
8951
9088
  Added because this face previously had a 501 but NO capability bit, leaving consumers to
8952
9089
  trial-by-501 — the exact pattern this capability surface exists to remove.
8953
9090
  images:
@@ -8971,7 +9108,17 @@ components:
8971
9108
  principal→org membership fold present), so true ⟺ the face actually works — a deployment with
8972
9109
  the store but no membership authority deliberately reports false (and does not mount the routes).
8973
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." }
8974
- 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).
8975
9122
  workflowModels:
8976
9123
  type: array
8977
9124
  items: { type: string }
@@ -9014,8 +9161,8 @@ components:
9014
9161
  (DESIGN-278 §5 S2 — deployment config self-description) is mounted on this DEPLOYMENT. TWO-term
9015
9162
  conjunction, written against the route's own refusal arms: ① the `CONFIG_CATALOG_ENABLED` knob is
9016
9163
  on (default on; a compliance deployment that turns it off makes the path a 404 — the valve-family
9017
- precedent) AND ② the operator principal list is NON-EMPTY (with an empty list the route 403s every
9018
- 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
9019
9166
  identity is NOT in the predicate — the route additionally requires the CURRENT principal to be an
9020
9167
  explicit operator (`routes/config-catalog.ts` `explicitOperatorOk`, else 403 `auth.operator_only`),
9021
9168
  so a non-operator reads `true` and still gets 403. Show/invoke the catalog only for an identity you
@@ -9035,8 +9182,15 @@ components:
9035
9182
  `false`/absent otherwise (same predicate as the route's 501). `resume: "snapshot"` is a semantic
9036
9183
  advertisement: the bus is latest-state (not an event log) and every connection opens with a full
9037
9184
  snapshot frame, so reconnect = re-snapshot (deliberately no last-event-id cursor); transient
9038
- bg_notification frames re-deliver via the durable completion inbox. `steerPriority: "advisory"` =
9039
- 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"`.
9040
9194
  oneOf:
9041
9195
  - type: boolean
9042
9196
  enum: [false]
@@ -9257,6 +9411,25 @@ components:
9257
9411
  outputStyle: { type: boolean }
9258
9412
  env: { type: boolean, description: 'TRUE on the single-user host lane (server emits `cwdHonored(config)`). It is NOT hard-coded false any more — the old always-false value predated the per-task shellEnv seam and hid a live capability.' }
9259
9413
  hooks: { type: boolean, description: 'Single-user deployments only (`requirePrincipal !== true`).' }
9414
+ settings:
9415
+ type: object
9416
+ description: >
9417
+ 🔴 server >= 7.115.0 (S-654), ALWAYS present: `{ permissions: { allowGrant: true } }` — the VERSION bit for "which reading does this
9418
+ server give `settings.permissions.allow` on a task submit". `permissions.allowGrant === true` = the `allow` bucket is compiled into
9419
+ ALLOW RULES: a hit gives "somebody stated it" (so an MCP / A2A server-tool call nobody stated no longer raises a card) — it is NOT
9420
+ "skip the question" and NOT a whitelist (a tool off the list is no longer refused; the engine's own write gate / shell checks still ask
9421
+ on built-in tools, deny / ask still win; a bad-shape or content-form name is a whole-request 422).
9422
+ 🔴 ABSENT KEY = an older server (<= 7.114.x), which read `allow` as a WHITELIST (everything off the list refused) — do not put anything
9423
+ in the allow bucket there. NOT the same bit as `taskSettings.permissions` (unchanged, still a boolean: "this server accepts the
9424
+ `settings.permissions` face"). A version bit: constant `true`, never varies with the deployment; the deployment-level "who stated what"
9425
+ (operator env `APPROVAL_GRANT`) is on the operator diagnostics face (`serverGates.approvalGrant`), not here.
9426
+ additionalProperties: true
9427
+ properties:
9428
+ permissions:
9429
+ type: object
9430
+ additionalProperties: true
9431
+ properties:
9432
+ allowGrant: { type: boolean, description: 'Constant `true` on server >= 7.115.0: `settings.permissions.allow` is read as allow RULES (a statement), not as a list. The whole `settings` key being absent = an older server.' }
9260
9433
  memoryWrite:
9261
9434
  type: boolean
9262
9435
  description: >
@@ -9272,9 +9445,25 @@ components:
9272
9445
  S-15 换店; ≤7.51 the last conjunct was the file-snapshot store). Gate the whole sync surface off
9273
9446
  this. ⚠️ The two BLOB faces (PUT …/sync/blobs/{hash}, GET …/sync/history/blobs/{hash})
9274
9447
  additionally need the store's blob faces (`syncBlobFaces` — the SQL/cloud backends; the local
9275
- file backend has none): without them the PUT answers 501 `capability.snapshot_store_required`
9276
- and the history PULL answers the no-oracle 404 `not_found.blob`, while the REST of the sync
9277
- 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.
9278
9467
 
9279
9468
  # ─── S-139(sdk 8.3.0):对 server 7.60.0 的整体重对账补齐的 19 位 ───────────────────────
9280
9469
  # 行号 = server 7.60.0 树的 `src/http/routes/capabilities.ts`。语义与谓词的全文在
@@ -9367,7 +9556,8 @@ components:
9367
9556
  description: >
9368
9557
  Governance carry-out bundle pair `POST /v1/memory/{export,import}` (operator lane) —
9369
9558
  capabilities.ts:261, two conjuncts: both engine seams wired AND `operatorPrincipals` NON-EMPTY
9370
- (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
9371
9561
  has the bundle composite face (core then refuses loudly with `memory.export_incomplete`).
9372
9562
  memoryCompliance:
9373
9563
  type: boolean
@@ -9469,10 +9659,13 @@ components:
9469
9659
  a2aServe:
9470
9660
  type: boolean
9471
9661
  description: >
9472
- Server-as-peer knob (capabilities.ts:564 = `config.a2aServe !== undefined`): true ⟺
9473
- `GET /.well-known/agent-card.json` and `POST /v1/a2a` exist; false ⟺ both 404. Scope is ROUTE
9474
- EXISTENCE, not "every method runs" — `message/send` / `tasks/get` additionally need a durable run
9475
- 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.
9476
9669
  agentRoster:
9477
9670
  type: boolean
9478
9671
  description: >
@@ -9667,6 +9860,23 @@ components:
9667
9860
  answers the request key with a 400 `request.body_shape` naming it under `unknownKeys` — do not send the key
9668
9861
  there and tell the user the factory skills stay installed. True only says "this server knows the key", not
9669
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.
9670
9880
  WriteProtectionCapability:
9671
9881
  type: object
9672
9882
  additionalProperties: false
@@ -9801,11 +10011,42 @@ components:
9801
10011
  engine refuses the skill outright at load time, so the HTTP face answers first where the caller can read it).
9802
10012
  The bound is 1 048 576 **UTF-16 code units** (`MAX_SKILL_CONTENT_CHARS`); see the STRING-LENGTH UNIT
9803
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.
9804
10023
  required: [name, description, content]
9805
10024
  properties:
9806
10025
  name: { type: string, minLength: 1 }
9807
10026
  description: { type: string }
9808
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").
9809
10050
 
9810
10051
  McpServerSpec:
9811
10052
  type: object
@@ -11740,6 +11981,11 @@ components:
11740
11981
  expiresAtMs: { type: number, description: 'Absolute window deadline — the SAME number minted once for the tool_approval frame (never recomputed).' }
11741
11982
  sessionId: { type: string }
11742
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.
11743
11989
  governanceForced: { type: boolean, enum: [true] }
11744
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).' }
11745
11991
  mandate:
@@ -11902,6 +12148,9 @@ components:
11902
12148
  Present ⇔ the request carried `remember:"session"` and the worker has an exemption store. `true` = the
11903
12149
  per-session per-tool exemption landed; `false` = the grant failed (logged) or the gate carried no
11904
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.
11905
12154
 
11906
12155
  ApprovalExemption:
11907
12156
  type: object
@@ -12045,6 +12294,11 @@ components:
12045
12294
  🔴 DISPLAY / TRIAGE ONLY, and ABSENT MEANS THERE REALLY IS NO SUPPLY (rule lane unarmed, the
12046
12295
  command yields no rule, no rule could silence this ask, or an old row) — never "the projection
12047
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.
12048
12302
  governanceForced:
12049
12303
  type: boolean
12050
12304
  enum: [true]
@@ -12997,31 +13251,97 @@ components:
12997
13251
  type: object
12998
13252
  description: >
12999
13253
  2c session-sync `GET …/sync/manifest` payload (wrapped server-side as `{ manifest }`). The THIN cross-backend
13000
- snapshot: entry IDS (oldest-first, NOT payloads) + per-snapshot relPath→blobHash + policy + anchors + leaf.
13001
- # 封闭(census 批2 第三段,2026-07-30):铸造点 `session-sync.ts`(exportSessionManifest 尾部
13002
- # 字面量)恰是这 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)。
13003
13265
  additionalProperties: false
13004
- required: [sessionId, entryIds, entryCount, leafId, snapshots, policy, anchors]
13266
+ required: [sessionId, entryIds, entryCount, leafId, policy, anchors]
13005
13267
  properties:
13006
13268
  sessionId: { type: string }
13007
13269
  entryIds: { type: array, items: { type: string }, description: Oldest-first, verbatim cross-backend. }
13008
13270
  entryCount: { type: integer, description: '= entryIds.length (the `/sync/entries` trailer count).' }
13009
13271
  leafId: { type: [string, 'null'] }
13010
- 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`).'
13011
13278
  policy: { type: array, items: { $ref: '#/components/schemas/SessionRulesRecord' } }
13012
13279
  anchors: { type: array, items: { $ref: '#/components/schemas/SyncAnchor' } }
13013
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
+
13014
13328
  SessionBundle:
13015
13329
  type: object
13016
13330
  description: >
13017
13331
  The portable state of ONE session, ready to replay into another backend. Blobs are NOT inlined (each
13018
13332
  snapshot carries only its `manifest`; the bytes stream through the content-addressed blob routes). The SDK
13019
- 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`.
13020
13335
  required: [sessionId, entries, snapshots, policy, anchors]
13021
13336
  properties:
13022
13337
  sessionId: { type: string }
13023
13338
  entries: { type: array, items: { $ref: '#/components/schemas/SyncEntry' } }
13024
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.'
13025
13345
  policy: { type: array, items: { $ref: '#/components/schemas/SessionRulesRecord' } }
13026
13346
  anchors: { type: array, items: { $ref: '#/components/schemas/SyncAnchor' } }
13027
13347
 
@@ -13270,7 +13590,27 @@ components:
13270
13590
  carry their own `field`.) Resume legs carry the same group on 409 `resume_blocked_by_policy`
13271
13591
  (`blockedBy: "request.field_invalid"`). Also on 409 `checkpoint.config_refused` (server >= 7.113.0): which part of the
13272
13592
  row's configuration was refused — `taskConfig` (change the configuration, then decide the SAME card again) or
13273
- `inheritedGate` (the row's recorded mode seed is bad — the run can only be cancelled). SDK: `err.extra.field`.
13593
+ `inheritedGate` (the row's recorded mode seed is bad — the run can only be cancelled). Also on 400 `request.field_invalid` for a
13594
+ `settings.hooks` tool-event matcher that selects an engine alias but not its canonical name (server >= 7.115.0 / S-1025):
13595
+ `field` is always the literal `settings.hooks` (see `detail`). SDK: `err.extra.field`.
13596
+ detail:
13597
+ description: >
13598
+ 🔴 TWO SHAPES under one code — branch on `field` (or the `errorCode`), never guess from the type of `detail`. On 400
13599
+ `request.field_invalid` for a `settings.hooks` tool-event matcher (server >= 7.115.0 / S-1025; additive, the sentence and the code
13600
+ are unchanged): an OBJECT `{ event, alias, canonical }` — `event` = the tool event of the refused group (the first refused one when
13601
+ several are), `alias` / `canonical` = the engine catalog's alias and canonical name (closed strings, never the caller's raw bytes),
13602
+ beside `field: "settings.hooks"`. On 400 `request.field_invalid` for the two `POST /v1/adoption` principal sentences (adoption):
13603
+ a human-readable STRING. Other codes (`rules.*` content refusals: `reason` + human `detail`; core `checkpoint.*` mirrors) carry their
13604
+ own `detail`; read it by `errorCode`. SDK: `err.extra.detail` (verbatim, unnamed).
13605
+ oneOf:
13606
+ - type: object
13607
+ required: [event, alias, canonical]
13608
+ additionalProperties: false
13609
+ properties:
13610
+ event: { type: string }
13611
+ alias: { type: string }
13612
+ canonical: { type: string }
13613
+ - type: string
13274
13614
  count:
13275
13615
  type: integer
13276
13616
  description: 'S-904 `reason: over_cap` only — how many entries the bucket had (server >= 7.106.0). SDK: `err.extra.count`.'
@@ -13864,16 +14204,29 @@ components:
13864
14204
  unattended leg run under `UNATTENDED_PERMISSION_MODE=dontAsk`).
13865
14205
  AskClass:
13866
14206
  type: string
13867
- 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, ordinary]
13868
- description: >
13869
- core `ASK_CLASSES` (twenty-one words as of core 7.39.0 — 7.34.0 minted twenty; `shell_floor` joined in 7.39.0 #1260 and rides
13870
- server >= 7.113.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.
14218
+ `unstated` = NOBODY STATED IT: in an ask-class mode (`default` / `acceptEdits` / `auto` / `plan`) a call to an MCP server tool or A2A skill
14219
+ (a seat that states no permission of its own) reached the end of the fold with no layer — rule / hook / approver / classifier / mode
14220
+ release — having said "this time is fine"; the engine raises a card with `askClass: "unstated"` (sentence `The agent requested permission to use <tool>,
14221
+ but it has not been granted yet.`), and with no approval surface the call is refused. It is clearable (one of the ten RELEASABLE
14222
+ classes: an allow rule — `settings.permissions.allow` / operator `APPROVAL_GRANT` — an explicit hook `permissionDecision: "allow"`, the auto-mode
14223
+ classifier or `bypassPermissions` clears it); under `plan` only READ-ONLY server tools raise it.
13871
14224
  `shell_floor` = the operator's `shellGate: "always"` floor on a shell-running seat (the question the `operator_always` mandate
13872
14225
  word discloses), a class of its own so a set that holds another forced question still holds it; like `operator_forced` it is
13873
14226
  bypass-IMMUNE (not releasable).
13874
- CLOSED on the gate record (screened with core `isAskClass`). `bypassPermissions` releases only the nine RELEASABLE classes
14227
+ CLOSED on the gate record (screened with core `isAskClass`). `bypassPermissions` releases only the ten RELEASABLE classes
13875
14228
  (`transcript_write` / `rule_unreadable` / `probe` / `write_protection` / `tool_marks_mcp` / `tool_marks_a2a` /
13876
- `artifact_shared_read` / `mode` / `ordinary`); every other class (deployment-authored `operator_forced`, ask / org rules, hook,
14229
+ `artifact_shared_read` / `mode` / `unstated` / `ordinary`); every other class (deployment-authored `operator_forced`, ask / org rules, hook,
13877
14230
  ancestor, content question, …) asks under every mode.
13878
14231
  ModeRelease:
13879
14232
  type: object
@@ -14514,6 +14867,15 @@ components:
14514
14867
  properties:
14515
14868
  backgroundAgentStore: { type: boolean }
14516
14869
  hostChildEventSink: { type: boolean }
14870
+ subagentTranscripts:
14871
+ type: string
14872
+ description: >
14873
+ server >= 7.115.0 (S-744; core 7.32.0 tier now on the wire) — the deployment's SUBAGENT TRANSCRIPT TIER, so a shell can render the
14874
+ "durable record" wording truthfully: `none` (no agent-row store: subagent records are not kept) / `rows` (rows and terminal snapshots
14875
+ survive a restart but the CONVERSATION does not: continuable only in this process / within the session store's TTL) / `full` (an
14876
+ ended subagent can be resumed across a restart). 🔴 OPEN SET — read it open: the three words are core's today, do not enumerate (a
14877
+ fourth word would be swallowed into "absent"); an unknown word means "unknown tier", never treat it as `full`. ABSENT = unknown
14878
+ (older server / older engine) — never read it as `none`. Tenant and operator frames carry it alike.
14517
14879
  permissionRules:
14518
14880
  type: object
14519
14881
  additionalProperties: false
@@ -14645,7 +15007,8 @@ components:
14645
15007
  deliberately never reported (pattern TEXT is deployment material, not an enum).
14646
15008
  🔴 AN EMPTY ARRAY IS NEVER EMITTED ("off" and "on to nothing" are one fact) ⇒ treat `[]` as a bad
14647
15009
  producer. The tier vocabulary is owned by core — do not narrow it on the consumer side.
14648
- ⚠️ Deployment semantics flipped in core 7.22.0: `READ_DENY_BUILTIN_TIERS` UNSET = OFF.
15010
+ ⚠️ Deployment default (server config catalog): an ABSENT `READ_DENY_BUILTIN_TIERS` ⇒ the factory pair `credentials,agent-config`
15011
+ is on (core 7.34.0 R-2 onward; it was OFF in 7.22.0–7.33.x); `none` = the explicit empty posture; an empty value refuses to boot.
14649
15012
  autoConsolidation:
14650
15013
  allOf: [{ $ref: '#/components/schemas/WiringManifestAutoConsolidation' }]
14651
15014
  description: >
@@ -15452,6 +15815,11 @@ components:
15452
15815
  args:
15453
15816
  description: '"tool_approval" only: the call''s args, secret-redacted. UNTRUSTED for display. Absent (with argsOmitted) when over the byte cap or unserializable.'
15454
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.
15455
15823
  governanceForced:
15456
15824
  type: boolean
15457
15825
  enum: [true]
@@ -15584,6 +15952,18 @@ components:
15584
15952
  properties:
15585
15953
  shown: { type: array, items: { type: string } }
15586
15954
  total: { type: integer }
15955
+ probeReason:
15956
+ type: string
15957
+ description: >
15958
+ server >= 7.115.0 (S-733), ADDITIVE, "tool_approval" only: the PROSE reason a reversibility probe gave for tightening this ask
15959
+ (core `AskRequest.probeReason`; the server's narrow read: a non-string / blank value ⇒ absent, more than 201 code points ⇒
15960
+ dropped WHOLE rather than truncated, content-family redaction — the same rule as the durable face's
15961
+ `gate.riskDescriptor.probeReason`, so the two faces carry the same bytes for the same sentence). It runs ALONGSIDE
15962
+ `probeCause` and does not replace it: engine-minted causes ride the structured seat, a deployment-written probe can only write this
15963
+ sentence. RENDER: prefer `probeCause`'s structure; fall back to this key when there is none; neither ⇒ no cause to render
15964
+ (absent != "no reason"). PRESENT ONLY WHEN IT HAS A VALUE (never an empty string) and ONLY ON THE LIVE card frame —
15965
+ the presented frame (`approval_request`) and the durable card (`card_json`) do NOT carry it (the durable twin is
15966
+ `gate.riskDescriptor.probeReason` on the parked row). Display / triage metadata, never an adjudication input; never in `message`.
15587
15967
  ruleEvidence:
15588
15968
  type: object
15589
15969
  description: >
@@ -15904,6 +16284,11 @@ components:
15904
16284
  argsOmitted: { type: boolean, enum: [true], description: 'Present (true) only when args exceeded the byte cap. Never false.' }
15905
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.' }
15906
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.
15907
16292
  governanceForced:
15908
16293
  type: boolean
15909
16294
  enum: [true]
@@ -16558,16 +16943,27 @@ components:
16558
16943
  sessionId: { type: string, description: 'gone.ask_parked ONLY — gate reconciliation coordinate 1; drive the existing durable-gate flow with it.' }
16559
16944
  gateBoundCallId: { type: string, description: 'gone.ask_parked ONLY — gate reconciliation coordinate 2.' }
16560
16945
  gateBoundInputHash: { type: string, description: 'gone.ask_parked ONLY — gate reconciliation coordinate 3.' }
16561
- 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`).' }
16562
16947
  deniedReason:
16563
16948
  type: string
16564
16949
  description: >
16565
16950
  gone.ask ONLY — attribution, GROUPED BY `state`. `DENIED` carries the single-member deny
16566
16951
  vocabulary `routing_failure_fail_closed`; `VOID` carries the separate (and growing) void
16567
- 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").
16568
16960
  description: >
16569
16961
  410 body. THE TWO LEGS ARE DISJOINT: `gone.ask_parked` carries exactly the three gate coordinates,
16570
- `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
16571
16967
  consumer ends up driving the wrong durable gate.
16572
16968
  🔴 The checkpoint token is NEVER here: the resume credential is not externally disclosed. Use
16573
16969
  `sessionId`.
@@ -17913,6 +18309,15 @@ components:
17913
18309
  properties:
17914
18310
  backgroundAgentStore: { type: boolean }
17915
18311
  hostChildEventSink: { type: boolean }
18312
+ subagentTranscripts:
18313
+ type: string
18314
+ description: >
18315
+ server >= 7.115.0 (S-744; core 7.32.0 tier now on the wire) — the deployment's SUBAGENT TRANSCRIPT TIER, so a shell can render the
18316
+ "durable record" wording truthfully: `none` (no agent-row store: subagent records are not kept) / `rows` (rows and terminal snapshots
18317
+ survive a restart but the CONVERSATION does not: continuable only in this process / within the session store's TTL) / `full` (an
18318
+ ended subagent can be resumed across a restart). 🔴 OPEN SET — read it open: the three words are core's today, do not enumerate (a
18319
+ fourth word would be swallowed into "absent"); an unknown word means "unknown tier", never treat it as `full`. ABSENT = unknown
18320
+ (older server / older engine) — never read it as `none`. Tenant and operator frames carry it alike.
17916
18321
  governance:
17917
18322
  type: object
17918
18323
  description: 'OPERATOR-AUDIENCE section (presence of the four deployment governance faces). Absent on every tenant-readable face.'
@@ -18242,6 +18647,16 @@ components:
18242
18647
  `SESSION_AUTO_TITLE` AND who set it. Its default is the SAME on every deployment shape (true), so this
18243
18648
  knob has NO posture arm — an unset one reads `source: "engine-default"` and says so in `note`.
18244
18649
  checkpointStore: { type: boolean, description: 'A checkpoint store is wired (= the park facility exists).' }
18650
+ approvalGrant:
18651
+ type: array
18652
+ items: { type: string }
18653
+ description: >
18654
+ server >= 7.115.0 — the operator's allow rules `APPROVAL_GRANT` (env; off by default) VERBATIM (tool-name rules are not secrets;
18655
+ an EMPTY list = the knob is unset, count = length). It answers "who did the DEPLOYMENT state for": a matching MCP / A2A
18656
+ server-tool call no longer raises a card for want of a statement — it releases no layer's own question (deny / ask / session rules /
18657
+ the engine's write gate all still apply). OPERATOR face only (this endpoint is operator-only); deliberately NOT in `GET /v1/capabilities`
18658
+ (that face says what a caller can use — see `Capabilities.settings`). Not in `required`: the key is absent on a server < 7.115.0
18659
+ (no such knob there), which is a different fact from `[]` (knob present, unset).
18245
18660
 
18246
18661
  WiringDiagnostics:
18247
18662
  type: object
@@ -18327,9 +18742,15 @@ components:
18327
18742
  description: >-
18328
18743
  [2400] TR-4 — per-message queue priority on POST /v1/runs/{taskId}/steer. ACCEPTED + ENUM-VALIDATED
18329
18744
  FAIL-LOUD (`routes/runs.ts`; anything else is 400 `request.field_invalid`) and echoed back on the
18330
- receipt, but ADVISORY today: core's steer is a single-slot last-writer-wins with no per-message priority
18331
- or addressable drop, so a faithful priority queue (and DELETE /v1/runs/{taskId}/queue/{messageId}) waits
18332
- 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".
18333
18754
 
18334
18755
  SteerReceipt:
18335
18756
  type: object
@@ -18606,6 +19027,8 @@ components:
18606
19027
  server 1.239 ([1392]②): on allow_session, an honest echo of whether the grant was REALLY
18607
19028
  recorded (true = landed in the bridge store; false = no sessionId, could not record — do not
18608
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).
18609
19032
  updatedInputForwarded:
18610
19033
  type: boolean
18611
19034
  description: >