@sema-agent/sdk 24.0.0 โ†’ 25.0.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 (62) hide show
  1. package/README.md +140 -3
  2. package/dist/client.d.ts +6 -1
  3. package/dist/client.d.ts.map +1 -1
  4. package/dist/client.js +8 -1
  5. package/dist/client.js.map +1 -1
  6. package/dist/device/index.d.ts +76 -12
  7. package/dist/device/index.d.ts.map +1 -1
  8. package/dist/device/index.js +90 -33
  9. package/dist/device/index.js.map +1 -1
  10. package/dist/errors.d.ts +108 -8
  11. package/dist/errors.d.ts.map +1 -1
  12. package/dist/errors.js +34 -3
  13. package/dist/errors.js.map +1 -1
  14. package/dist/events.d.ts +40 -11
  15. package/dist/events.d.ts.map +1 -1
  16. package/dist/events.js +3 -2
  17. package/dist/events.js.map +1 -1
  18. package/dist/health.d.ts +25 -0
  19. package/dist/health.d.ts.map +1 -1
  20. package/dist/health.js.map +1 -1
  21. package/dist/index.d.ts +28 -10
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +16 -3
  24. package/dist/index.js.map +1 -1
  25. package/dist/model-route-env/index.d.ts +129 -0
  26. package/dist/model-route-env/index.d.ts.map +1 -0
  27. package/dist/model-route-env/index.js +131 -0
  28. package/dist/model-route-env/index.js.map +1 -0
  29. package/dist/resources/adoption.d.ts +8 -2
  30. package/dist/resources/adoption.d.ts.map +1 -1
  31. package/dist/resources/adoption.js.map +1 -1
  32. package/dist/resources/approvals.d.ts +22 -2
  33. package/dist/resources/approvals.d.ts.map +1 -1
  34. package/dist/resources/approvals.js +12 -2
  35. package/dist/resources/approvals.js.map +1 -1
  36. package/dist/resources/fleet.d.ts +65 -6
  37. package/dist/resources/fleet.d.ts.map +1 -1
  38. package/dist/resources/fleet.js.map +1 -1
  39. package/dist/resources/ops.d.ts +113 -0
  40. package/dist/resources/ops.d.ts.map +1 -1
  41. package/dist/resources/ops.js +10 -0
  42. package/dist/resources/ops.js.map +1 -1
  43. package/dist/resources/principal-inbox.d.ts +62 -0
  44. package/dist/resources/principal-inbox.d.ts.map +1 -0
  45. package/dist/resources/principal-inbox.js +51 -0
  46. package/dist/resources/principal-inbox.js.map +1 -0
  47. package/dist/resources/tool-approvals.d.ts +191 -16
  48. package/dist/resources/tool-approvals.d.ts.map +1 -1
  49. package/dist/resources/tool-approvals.js +183 -36
  50. package/dist/resources/tool-approvals.js.map +1 -1
  51. package/dist/settings.d.ts +70 -1
  52. package/dist/settings.d.ts.map +1 -1
  53. package/dist/sse.d.ts +5 -1
  54. package/dist/sse.d.ts.map +1 -1
  55. package/dist/sse.js +6 -2
  56. package/dist/sse.js.map +1 -1
  57. package/dist/types.d.ts +168 -15
  58. package/dist/types.d.ts.map +1 -1
  59. package/dist/types.js +5 -3
  60. package/dist/types.js.map +1 -1
  61. package/openapi.yaml +781 -44
  62. package/package.json +6 -2
package/openapi.yaml CHANGED
@@ -3424,7 +3424,7 @@ paths:
3424
3424
  `synced` (and `synced.count` still counts ONLY the durable queue).
3425
3425
  ๐Ÿ”ด THE TWO WORDS DELIBERATELY DO NOT REUSE `pending`/`resolved`: those are keyed by
3426
3426
  `(sessionId, toolCallId)` and decided at `POST /v1/approvals/{sessionId}/decide`, while a live row is
3427
- keyed by `approvalId` and decided at `POST /v1/tool-approvals/{approvalId}/respond`. Sharing one word
3427
+ keyed by `approvalId` and decided at `POST /v1/tool-approvals/{id}/respond`. Sharing one word
3428
3428
  would eventually send a live card to the decide door โ€” a 404 with no wire evidence of the mistake.
3429
3429
  โš ๏ธ Both live words come from an IN-PROCESS window projection, so their cross-replica reach is exactly
3430
3430
  that of `GET /v1/approvals`'s `livePending` โ€” narrower than the durable pair above. The deployment
@@ -4393,6 +4393,38 @@ paths:
4393
4393
  '404': { description: no pending question for this id (non-owner, answered, expired, or wrong replica) โ€” no existence oracle. }
4394
4394
  '501': { description: AskUserQuestion is not enabled on this worker (ASK_QUESTION_ENABLED). }
4395
4395
 
4396
+ /v1/tool-approvals/{id}:
4397
+ parameters:
4398
+ - $ref: '#/components/parameters/PrincipalHeader'
4399
+ - { name: id, in: path, required: true, schema: { type: string }, description: 'The ask''s `approvalId` (server >= 7.133.0 always equal to its `askId`; an opaque string).' }
4400
+ get:
4401
+ tags: [questions]
4402
+ operationId: toolApprovalRead
4403
+ x-status: live # server >= 7.133.0; SDK toolApprovals.get consumes it
4404
+ summary: Read ONE live-card ask โ€” where is it now (pending, decided, parked or void)? Pure read.
4405
+ description: >
4406
+ server >= 7.133.0 โ€” the by-ask read door. Same root and same gate as POST /v1/tool-approvals/{id}/respond (one `gatedPrincipal`; an owner-gate miss answers the SAME 404 as an unknown
4407
+ id โ€” no existence oracle), and the state is read the way the answer door reads it: a live registration on THIS replica waiting for the ask => `pending` (the body is the `livePending[]` row projection +
4408
+ `state`); otherwise the row is read by id: `DECIDED` => `decided`, `PARKING` / `PARKED` => `parked`, `DENIED` / `VOID` => `void`. A row still `STREAM_PENDING` with no local registration lives on
4409
+ another replica => 404 `tool_approval.not_pending` + `cause: "unknown_or_other_replica"` (the same answer as a late respond); a deployment with no ask ledger answers the same 404 WITHOUT `cause`.
4410
+ Pure read: zero writes, no settlement, the local registration is untouched. Use it to ask "is this card still alive / was it decided by anyone" instead of inferring it from a respond 404.
4411
+ An older server (<= 7.132.x) has no such door: routing-level 404 `not_found.route` โ€” tell it apart from this door's 404 by `errorCode`, never by status.
4412
+ responses:
4413
+ '200':
4414
+ description: The ask's current state.
4415
+ content:
4416
+ application/json:
4417
+ schema: { $ref: '#/components/schemas/ToolApprovalReadBody' }
4418
+ '401': { $ref: '#/components/responses/Unauthorized' }
4419
+ '404':
4420
+ description: >
4421
+ `tool_approval.not_pending` โ€” unknown / not yours (byte-identical) / still pending on another replica (`cause: unknown_or_other_replica`). Past the owner gate the body may carry the additive `cause`
4422
+ (see ToolApprovalNotPendingCause); no ask ledger => no `cause`.
4423
+ content:
4424
+ application/json:
4425
+ schema: { $ref: '#/components/schemas/ToolApprovalNotPendingBody' }
4426
+ '501': { description: 'Live tool-approval HITL not enabled (TOOL_APPROVAL_ENABLED) โ€” `feature.tool_approval_disabled`.' }
4427
+
4396
4428
  /v1/tool-approvals/{id}/respond:
4397
4429
  parameters:
4398
4430
  - $ref: '#/components/parameters/PrincipalHeader'
@@ -5799,6 +5831,70 @@ paths:
5799
5831
  '403': { description: 'Multi-tenant worker: outcomes view is operator-only.' }
5800
5832
  '501': { $ref: '#/components/responses/NotImplemented' }
5801
5833
 
5834
+ /v1/config/catalog:
5835
+ parameters:
5836
+ - $ref: '#/components/parameters/PrincipalHeader'
5837
+ get:
5838
+ tags: [admin]
5839
+ operationId: configCatalog
5840
+ x-status: live # operator lane (capabilities.configCatalog); SDK configCatalog.get consumes it
5841
+ summary: Deployment configuration catalog โ€” every env knob's declaration and this process's effective reading (operator-only).
5842
+ description: >
5843
+ The deployment's configuration catalog (operator lane; probe `capabilities.configCatalog` first). One row per env knob: its declared semantics, which lanes can write it, which lane is in force, the
5844
+ process's effective DISPLAY value and the centre seat (`center`) when the knob is centre-publishable; plus the `retired` tombstone list (env names this process refuses to start on). `Cache-Control: no-store`.
5845
+ Gate: `OPERATOR_PRINCIPALS` โ€” outside the list 403 `auth.operator_only`; empty list on a multi-tenant deployment 501 `capability.operator_principals_required`; `CONFIG_CATALOG_ENABLED=false` 501
5846
+ `feature.config_catalog_disabled`. A server older than 7.53 has no such route (404).
5847
+ `effectiveValue` is a DISPLAY value, never the raw env: `secret` / `opaque` rows report only `{ present, length? }`, a `url` row goes through the display mint (userinfo landed, query values become
5848
+ tokens). Two VERDICT bits on a row are true-only (never `false`; ABSENT = this cell has no such matter): `effectiveValueInvalid` (the effective value falls outside the row's own echo set; `effectiveValue` is then
5849
+ `null`) and, server >= 7.133.0, `effectiveValueWithheld` (a `url` row whose display mint WITHHELD a part โ€” a credential-shaped piece in a query / path segment โ€” so the shown value is not the whole value).
5850
+ responses:
5851
+ '200':
5852
+ description: The catalog.
5853
+ content:
5854
+ application/json:
5855
+ schema: { $ref: '#/components/schemas/ConfigCatalog' }
5856
+ '401': { $ref: '#/components/responses/Unauthorized' }
5857
+ '403': { description: 'The caller is not in OPERATOR_PRINCIPALS (`auth.operator_only`).' }
5858
+ '501': { $ref: '#/components/responses/NotImplemented' }
5859
+
5860
+ /v1/principal-inbox/messages:
5861
+ parameters:
5862
+ - $ref: '#/components/parameters/PrincipalHeader'
5863
+ post:
5864
+ tags: [admin]
5865
+ operationId: principalInboxSend
5866
+ x-status: live # server >= 7.133.0; SDK principalInbox.send consumes it
5867
+ summary: Deliver one letter to another principal's inbox in the same organization (cross-principal delivery; default OFF).
5868
+ description: >
5869
+ server >= 7.133.0 โ€” one letter is appended to the recipient principal's `principal.inbox` ONLY by the organization delivery gate: membership judgement -> mint -> inbox bound -> quota -> append, all inside one gate that answers
5870
+ with a receipt or one of four refusal codes. The SENDER is always your verified identity (`from` is not a body key: sending `from` or any unknown key => 400 `request.body_shape` + `unknownKeys`). DEFAULT OFF: without env
5871
+ `PRINCIPAL_INBOX_ASSISTANT` the route answers 501 `capability.unavailable` (no delivery gate is mounted) โ€” probe `capabilities.principalInboxDelivery` (true <=> this door works). THIS VERSION SHIPS THE DELIVERY HALF ONLY:
5872
+ a delivered letter sits in the recipient's box (at most 50 unconfirmed envelopes) and no read verb exists yet. Judging order: the sender's ATTEMPT bound (every attempt is recorded, succeeded or not, BEFORE membership) ->
5873
+ membership (including the mailbox-key judge) -> mint (a sender that cannot be spelled into the envelope's sender slot answers the same `not_member`, no quota consumed) -> inbox bound -> quota deduction -> append. A refused
5874
+ delivery appends nothing. Not auto-retried by the SDK (every attempt consumes the sender's attempt quota).
5875
+ The four refusal codes: `org_delivery.not_member` 404 (ONE sentence, byte-identical body for: different organization / no such person / directory could not answer / `to` is not a principal shape / the recipient cannot be a
5876
+ mailbox key / the sender cannot be spelled into the envelope โ€” the gate is never an existence probe); `org_delivery.rate_limited` 429 (one sentence for three bounds: 60 attempts per sender per hour โ€” a typo'd recipient costs an
5877
+ attempt โ€”, 10 letters per (sender, recipient) pair per hour, 30 letters per recipient per hour; fixed clock-hour windows, up to double across a boundary; hard-coded, no knob); `org_delivery.inbox_full` 409 (the recipient's box already holds
5878
+ 50 unconfirmed envelopes โ€” judged BEFORE quota so a full box burns none); `org_delivery.recipient_tombstoned` 410 (the recipient's mailbox is in its pre-delete state).
5879
+ requestBody:
5880
+ required: true
5881
+ content:
5882
+ application/json:
5883
+ schema: { $ref: '#/components/schemas/PrincipalInboxMessage' }
5884
+ responses:
5885
+ '200':
5886
+ description: The letter was appended; `receiptId` is the gate's delivery receipt id.
5887
+ content:
5888
+ application/json:
5889
+ schema: { $ref: '#/components/schemas/PrincipalInboxReceipt' }
5890
+ '400': { description: 'Malformed body: `request.field_invalid` (not an object / empty `to` / empty or over-long `content` / non-boolean `urgent`) or `request.body_shape` (unknown key incl. `from`; body carries `unknownKeys`).' }
5891
+ '401': { $ref: '#/components/responses/Unauthorized' }
5892
+ '404': { description: '`org_delivery.not_member` โ€” one byte-identical body for six causes (see the description); the gate is not an existence probe.' }
5893
+ '409': { description: '`org_delivery.inbox_full` โ€” the recipient''s box holds 50 unconfirmed envelopes.' }
5894
+ '410': { description: '`org_delivery.recipient_tombstoned` โ€” the recipient''s mailbox is in its pre-delete state.' }
5895
+ '429': { description: '`org_delivery.rate_limited` โ€” one sentence for the sender-attempt bound, the pair bound and the recipient aggregate bound.' }
5896
+ '501': { description: 'No delivery gate mounted (`PRINCIPAL_INBOX_ASSISTANT` off) โ€” `capability.unavailable`.' }
5897
+
5802
5898
  /v1/diagnostics/wiring:
5803
5899
  parameters:
5804
5900
  - $ref: '#/components/parameters/PrincipalHeader'
@@ -7064,6 +7160,7 @@ components:
7064
7160
  promptSummary: { type: string, description: 'a human-readable prompt DIGEST โ€” NOT the full prompt.' }
7065
7161
  enabled: { type: boolean }
7066
7162
  available: { type: boolean, description: 'whether this deployment can actually RUN the scenario right now โ€” same predicate as the 501 rejection arm on submit; false โŸบ submitting is guaranteed to 501. Independent axis from `enabled` (declared vs runnable).' }
7163
+ attachmentSupplies: { type: array, items: { type: string, enum: [path, content_text, content_image] }, description: 'server >= 7.133.0, always present there: the attachment SUPPLY words this scenario actually carries โ€” a subset of `capabilities.userAttachments.supplies` (`path` only on hands scenarios, where a workspace lives somewhere; a no-hands scenario drops `path`, and a `path` entry is refused by the engine with `config.user_attachment_invalid`). ABSENT = server <= 7.132.x: fall back to `userAttachments.supplies` plus the existing 400 handling.' }
7067
7164
  unavailableReason: { type: string, description: 'machine-readable reason, present ONLY when available:false (absent = no reason). Closed set, currently the single member `git_client_unconfigured`; branch on the key, never match the English prose. Additive-open: treat an unknown word as "unavailable, reason unknown".' }
7068
7165
 
7069
7166
  RunStatus:
@@ -7704,11 +7801,20 @@ components:
7704
7801
  `permissions.defaultMode`'s specific fold is documented on `TaskRequest.permissionMode`.
7705
7802
  ๐Ÿ”ด ON THIS SUBMIT FACE THE KEY SET IS CLOSED (server >= 7.57.0 for the sub-tree): accepted top-level keys
7706
7803
  are `env` / `hooks` / `model` / `outputStyle` / `permissions` / `skillListingBudgetFraction` /
7707
- `skillListingMaxDescChars` / `ultracode` / `webSearch`, and accepted `permissions.*` keys are
7708
- `allow` / `ask` / `defaultMode` / `deny` / `disableAutoMode`; anything else is 400 `request.body_shape`
7804
+ `skillListingMaxDescChars` / `ultracode` / `webSearch` (server >= 7.133.0 adds the three TIGHTEN-ONLY seats `complianceAdditionalDenies` / `readDenyPatterns` / `writeProtectedExtra`), and accepted `permissions.*` keys are
7805
+ `allow` / `ask` / `defaultMode` / `deny` / `disableAutoMode` (server >= 7.133.0 adds `neverAuto`); anything else is 400 `request.body_shape`
7709
7806
  with the names in the refusal's `unknownKeys` / `unsupportedKeys` arrays (REFUSED, not dropped โ€” a dropped
7710
7807
  key reads as "in effect" to the caller). The object stays OPEN here because the same `SemaSettings`
7711
7808
  schema also serves the local `settings.json` wiring, where deferred CC keys are legal.
7809
+ ๐Ÿ”ด TIGHTEN-ONLY SEATS (server >= 7.133.0; probe `capabilities.taskSettings.tightenOnlySeats`, an absent bit = an older server that answers 400 `request.body_shape` for these four keys): `readDenyPatterns` (entries: a bare
7810
+ pattern string, or `{ pattern, caseSensitive? }`), `writeProtectedExtra` (a bare name โ€” no `/` reads as `segment`, a `/` reads as `segment-run` โ€” or `{ name, kind: basename | segment | segment-run }`),
7811
+ `complianceAdditionalDenies` (`mcp_servers` | `workflows` | `web_fetch` | `org_memory_mount`) and `permissions.neverAuto` (tool names that ALWAYS ask a human). Each seat = the deployment table UNION the request list, ONLY EVER GROWING
7812
+ (the request face has no loosening arm); an empty array is the same as not sent. Bad value => 400 `request.field_invalid` with `field` + `reason` (`over_cap` + `count` + `cap` / `bad_entry` + the first bad `index`).
7813
+ Caps: 1000 entries per seat, EXCEPT `readDenyPatterns`: at most 32 entries, at most 2 `*` per path segment and 16 `*` in the whole table (every star counts, nothing is folded). A `neverAuto` name that can never match any tool => 422.
7814
+ A resume replay of a stored body whose seat went bad => 409 `resume_blocked_by_policy`. A compliance deny is a REFUSAL, not a narrowing: a request denying `web_fetch` is refused whole where the default tool surface still mounts `WebFetch` (the single-user default does).
7815
+ ๐Ÿ”ด `settings.hooks[]` (server >= 7.133.0; probe `capabilities.taskSettings.pluginHooks` / `managedHookGroups` โ€” an absent or false bit = the keys are STRIPPED SILENTLY): group key `source` (closed: `managedPolicy` / `user` / `project` / `local` / `flag` /
7816
+ `managedPlugin:<name>` / `plugin:<name>`; absent = unmanaged; managed groups run in an independent lane after the unmanaged rewrites), command-entry keys `plugin { name, root, dataDir?, options? }` and exec-form `args`; a malformed one =>
7817
+ 400 `request.field_invalid` with `detail: { reason: "wire_key_invalid", key, path }`; the plugin context has one 8192-character budget.
7712
7818
  ๐Ÿ”ด `skillListingBudgetFraction` (number in (0,1]) / `skillListingMaxDescChars` (positive integer),
7713
7819
  server >= 7.78.1: the SKILL-LISTING RENDER BUDGET, passed to the engine with ZERO processing
7714
7820
  (no clamping, no conversion, no folding) โ€” ABSENT means the key is not written at all (the server does
@@ -8374,6 +8480,10 @@ components:
8374
8480
  cause (`terminal`), not the eight parallel plane keys โ€” see `TerminalCause`.
8375
8481
  required: [taskId, sessionId, terminal, stats]
8376
8482
  properties:
8483
+ warnings:
8484
+ type: array
8485
+ items: { $ref: '#/components/schemas/EntryProblem' }
8486
+ description: 'server >= 7.133.0, top level of the sync `POST /v1/tasks` 200 body (beside `notice`), present only when non-empty: the accept-time NOTICES for allow-side selector-list entries the engine reads as nothing or that are not in the roster. ABSENT = no notice (older servers too).'
8377
8487
  taskId: { type: string }
8378
8488
  sessionId: { type: string }
8379
8489
  wiringManifest:
@@ -8676,6 +8786,10 @@ components:
8676
8786
  taskId: { type: string }
8677
8787
  sessionId: { type: string }
8678
8788
  status: { $ref: '#/components/schemas/RunStatus' }
8789
+ warnings:
8790
+ type: array
8791
+ items: { $ref: '#/components/schemas/EntryProblem' }
8792
+ description: 'server >= 7.133.0, present only when non-empty: the accept-time NOTICES for allow-side selector-list entries the engine reads as nothing or that are not in the roster (acceptance is unchanged โ€” the engine takes them, only the operator hears afterwards; this makes "this name will not take effect" visible at the call). Same list as the stream `meta` frame''s and the sync 200 body''s. ABSENT = no notice (older servers too).'
8679
8793
 
8680
8794
  SealedKeyCapability:
8681
8795
  type: object
@@ -9803,6 +9917,11 @@ components:
9803
9917
  store backend deliberately wires no image index (the pool is cloud/fleet-only) โ‡’ false there.
9804
9918
  SCOPE: P1 only โ€” `/v1/images/bakes*` (the P2 bake control plane) is a separate dependency and an
9805
9919
  operator/runner-internal face, NOT promised by this bit.
9920
+ principalInboxDelivery:
9921
+ type: boolean
9922
+ description: >-
9923
+ server >= 7.133.0 โ€” the cross-principal delivery door `POST /v1/principal-inbox/messages` is wired (delivery gate present: env `PRINCIPAL_INBOX_ASSISTANT` on and the gate's dependencies in place).
9924
+ True iff that route works; absent gate => the route answers 501 `capability.unavailable`. ABSENT key = server <= 7.132.x (no such route; routing-level 404). Probe this bit; do not trial-by-501.
9806
9925
  sharedMemory:
9807
9926
  type: boolean
9808
9927
  description: >
@@ -9973,6 +10092,12 @@ components:
9973
10092
  lane) or 422 (hosted).
9974
10093
  mcp: { type: boolean, description: "MCP servers can be attached." }
9975
10094
  mcpInjection: { type: boolean, description: "Per-request MCP injection is accepted." }
10095
+ mcpAssertionHeaderSent:
10096
+ type: boolean
10097
+ description: >-
10098
+ server >= 7.133.0 โ€” the SEND seat self-report (a boolean, today ALWAYS `false`): whether `mcpServers[].transport.assertionHeader` (accepted since server 7.128.0, mutually exclusive with `principalHeader`)
10099
+ is actually SENT to the MCP peer. `false` = the declaration is carried but NOT sent (the same switch drives the key stripping, the operator info line and the probe-door refusal; the disclosure sentences leave when it turns true).
10100
+ ABSENT = an older server, which does not send either โ€” treat it as `false`; never rely on the header for peer authentication.
9976
10101
  mcpProbe:
9977
10102
  type: boolean
9978
10103
  description: >
@@ -10012,6 +10137,17 @@ components:
10012
10137
  โš ๏ธ ABSENT = an older server (<7.87.1) โ‡’ cannot tell whether the push face carries the live family;
10013
10138
  do NOT read "no `live_pending` frame arrived yet" as "this deployment never pushes live" โ€” the two
10014
10139
  are indistinguishable on an older server. Fall back to `GET /v1/approvals`'s `livePending` pull face.
10140
+ approvalFrameFamilies:
10141
+ type: array
10142
+ maxItems: 1
10143
+ items: { type: string, enum: [tool_approval, approval_request] }
10144
+ description: >-
10145
+ server >= 7.133.0, ALWAYS present (the projection of the same judgement as `streamApproval`) โ€” which FRAME FAMILY this worker's approval opening frames belong to:
10146
+ a closed-word array with AT MOST ONE member. `["approval_request"]` = the card protocol is on: one ask travels only `approval_request` -> `approval_complete` / `approval_revoke`
10147
+ (the legacy `tool_approval` / `tool_approval_complete` pair is NOT sent โ€” BREAKING vs <= 7.132.x, which sent both families for one ask and relied on `approvalId` de-duplication;
10148
+ remedy on the worker: `STREAM_APPROVAL_ENABLED=false`). `["tool_approval"]` = the card protocol is not on (`STREAM_APPROVAL_ENABLED=false`, no store backend, or no checkpoint store) but the live card leg is.
10149
+ `[]` = per-call approvals are off altogether (`TOOL_APPROVAL_ENABLED=false`): no card is presented. Dispatch frame families off THIS array, never off `streamApproval`.
10150
+ KEY ABSENT (not `[]`) = server <= 7.132.x โ€” there a `streamApproval: true` leg sends BOTH families for one ask.
10015
10151
  streamApproval:
10016
10152
  type: boolean
10017
10153
  description: >
@@ -10066,6 +10202,13 @@ components:
10066
10202
  a server <= 7.118.x returns the archive as written; a 7.119.x server already redacts but does not report the bit. A true
10067
10203
  bit does not mean the read face exists (that is `sessions`; without a session audit face that door answers 501 and this
10068
10204
  bit is still true), nor that the archive holds no credential (the redaction is read-side only).
10205
+ approvalReadRootGrant:
10206
+ type: boolean
10207
+ description: >-
10208
+ server >= 7.133.0, ALWAYS present, a FACT that varies by deployment โ€” whether this deployment accepts the reply-body key `readRootGrant` ("Yes, and add directory": the card's
10209
+ `readRootCandidate.dir` is registered as a read root together with this one allow). True iff the single-user host-lane posture (the same predicate as `projectContext` / the cwd face); false elsewhere.
10210
+ A client shows the option only when the card carries `readRootCandidate` AND this bit is true. On a non-accepting posture a reply carrying `readRootGrant` is refused
10211
+ `400 request.body_shape` (the ask stays unsettled โ€” answer again without the key). ABSENT = server <= 7.132.x = not accepted; do not send the key.
10069
10212
  approvalReach:
10070
10213
  type: boolean
10071
10214
  description: >-
@@ -10178,6 +10321,9 @@ components:
10178
10321
  outputStyle: { type: boolean }
10179
10322
  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.' }
10180
10323
  hooks: { type: boolean, description: 'Single-user deployments only (`requirePrincipal !== true`).' }
10324
+ pluginHooks: { type: boolean, description: 'server >= 7.133.0 โ€” the request `settings.hooks[]` PLUGIN-CONTEXT keys (command entry `plugin { name, root, dataDir?, options? }` / exec-form `args`) are accepted; same predicate as `hooks` (single-user true, multi-tenant false). ABSENT or false = the keys are stripped silently โ€” send only on sight of the bit.' }
10325
+ managedHookGroups: { type: boolean, description: 'server >= 7.133.0 โ€” the request `settings.hooks[]` GROUP `source` key (managed lanes `managedPolicy` / `managedPlugin:<name>`) is accepted; same predicate as `hooks`. ABSENT or false = stripped silently (the managed-lane semantics do not apply, it is not a refusal).' }
10326
+ tightenOnlySeats: { type: boolean, description: 'server >= 7.133.0, constant true โ€” the four tighten-only request seats `settings.readDenyPatterns` / `settings.writeProtectedExtra` / `settings.complianceAdditionalDenies` / `settings.permissions.neverAuto` are accepted (each seat = deployment table UNION request list, only ever growing; an empty array is the same as not sent). ABSENT = server <= 7.132.x, which answers 400 `request.body_shape` for those keys โ€” send only on sight of the bit.' }
10181
10327
  settings:
10182
10328
  type: object
10183
10329
  description: >
@@ -10206,6 +10352,7 @@ components:
10206
10352
  properties:
10207
10353
  allowGrant: { type: boolean, description: 'Constant `true` on server >= 7.115.0: `settings.permissions.allow` is read as allow RULES, not as a list. The whole `settings` key being absent = an older server.' }
10208
10354
  allowLayer: { type: boolean, description: 'Constant `true` on server >= 7.117.0 (core 7.42.0): the whole `settings.permissions.allow` list goes to the engine allow layer (listed calls are not asked; content forms accepted; entries read as nothing get an `engine_notice` `permission.allowed_tool_not_granted`). Absent = a server without it (7.115.0โ€“7.116.x: allow is a statement; older: a list).' }
10355
+ callerTrust: { type: string, description: 'server >= 7.133.0 โ€” THIS caller''s trust tier (per caller / per credential, NOT a version bit). Request rule tables that take effect without a person answering (`settings.permissions` buckets; a named-invoked skill''s `allowedTools` / `disallowedTools`) are admitted by it: `allow` loosens the no-ask surface and needs `global`; `deny` / `ask` only tighten and are admitted at any tier. Today `global` (single-user turnkey; multi-tenant with the deployment service credential; multi-tenant sign-in token whose scope role >= the governance write role) or `project` (everything else). Read OPEN: an unknown word is treated as `project` (fail-closed). A request below the floor is refused whole with 400 `request.field_invalid` (`detail.problems[].reason: below_trust_floor`, `detail.callerTrust`). ABSENT = server <= 7.132.x: no per-tier admission.' }
10209
10356
  memoryWrite:
10210
10357
  type: boolean
10211
10358
  description: >
@@ -10268,7 +10415,7 @@ components:
10268
10415
  additionalProperties: true
10269
10416
  properties:
10270
10417
  enabled: { type: boolean, enum: [true] }
10271
- protocolVersion: { type: integer }
10418
+ protocolVersion: { type: integer, description: 'The protocol REVISION this server speaks (the `challenge` frame carries the same value; accepted window [1, 2] on server >= 7.133.0). Server >= 7.133.0 reads 2 (<= 7.132.x read 1). An executor declares the SMALLER of its own highest revision and this value, and takes its outcome-code set from the declared revision (revision 2 added `liveness_no_output`).' }
10272
10419
  maxInflightPerDevice: { type: integer }
10273
10420
  wsPath: { type: string }
10274
10421
  management: { type: boolean, description: 'server >= 7.88.0. `/v1/devices/*` admin verbs present. PRESENT-IFF โ€” absent means a pre-7.88.0 peer (unknown), not false.' }
@@ -10762,9 +10909,9 @@ components:
10762
10909
  server >= 7.65.0 (S-167) โ€” the READ containment rung of this deployment AND WHO SET IT (operator face
10763
10910
  `GET /v1/diagnostics/wiring` -> `readFace`). `face` is the same boot artefact as the tenant-facing
10764
10911
  `capabilities.readFace` (`null` = nothing pinned by this deployment, the engine default is in force).
10765
- `source` is a CLOSED four-word set on the server (exhaustive switch โ€” adding a word is a compile error
10912
+ `source` is a CLOSED five-word set on the server (exhaustive switch โ€” adding a word is a compile error
10766
10913
  there): `env` = this machine's READ_FACE env var (wins over everything) ยท `center` = the config-center
10767
- readFace domain (restart-to-apply) ยท `posture` = derived from the single-user turnkey posture
10914
+ readFace domain (restart-to-apply) ยท `lane` = (server >= 7.133.0) the isolated execution lane's default โ€” with `READ_FACE` absent, e2b / k8s / local-docker mint `open` ยท `posture` = derived from the single-user turnkey posture
10768
10915
  (REQUIRE_PRINCIPAL unset โ‡’ `open`; server >= 7.65.0 behaviour change) ยท `engine-default` = not pinned.
10769
10916
  `note` is a human-facing pointer for operators. Minimal disclosure: the deny table is never on this face.
10770
10917
  required: [face, source, note]
@@ -10774,7 +10921,7 @@ components:
10774
10921
  enum: [open, roots, null]
10775
10922
  source:
10776
10923
  type: string
10777
- enum: [env, center, posture, engine-default]
10924
+ enum: [env, center, lane, posture, engine-default]
10778
10925
  note:
10779
10926
  type: string
10780
10927
 
@@ -10791,9 +10938,9 @@ components:
10791
10938
  `DURABLE_APPROVAL` ON and a 24h approval window when neither is set, so a default FLIP has to be visible
10792
10939
  on an operator surface or nobody sees it.
10793
10940
 
10794
- `source` is the CLOSED four-word set (exhaustive switch on the server โ€” adding a word is a compile error
10941
+ `source` is the CLOSED five-word set (exhaustive switch on the server โ€” adding a word is a compile error
10795
10942
  there), in PRECEDENCE order: `env` = pinned by this machine's env var (deployment sovereignty; beats any
10796
- published value) ยท `center` = applied from the config-center (restart-to-apply) ยท `posture` = derived from
10943
+ published value) ยท `center` = applied from the config-center (restart-to-apply) ยท `lane` = (server >= 7.133.0) derived from the isolated execution lane (e2b / k8s / local-docker mint a lane default, e.g. `READ_FACE` -> `open`) ยท `posture` = derived from
10797
10944
  the deployment shape (single-user turnkey) ยท `engine-default` = nothing pinned, the built-in default is in
10798
10945
  force. Only `env`/`center` count as "an operator asked for this"; a posture-derived `true` does not.
10799
10946
  `note` is PROSE for a human (per-knob, and on the `posture` arm it is that knob's own fact sentence) โ€”
@@ -10808,7 +10955,7 @@ components:
10808
10955
  description: 'The knob''s effective value. Typed by the referencing site (boolean / integer / โ€ฆ).'
10809
10956
  source:
10810
10957
  type: string
10811
- enum: [env, center, posture, engine-default]
10958
+ enum: [env, center, lane, posture, engine-default]
10812
10959
  note: { type: string }
10813
10960
 
10814
10961
  SkillSpec:
@@ -11630,6 +11777,24 @@ components:
11630
11777
  `"other"`). This is the "where did those 180 404s actually land" cell. Empty object = zero failures;
11631
11778
  key absent = an older server.
11632
11779
 
11780
+ ModelPlane:
11781
+ type: object
11782
+ additionalProperties: false
11783
+ description: >
11784
+ `GET /v1/models.modelPlane` (server >= 7.133.0; always present; conditional keys are absent, never `null` / empty string) โ€” the single read for "which model table is this engine serving".
11785
+ Two axes. SERVED TABLE: `servedTable` = `env` (the env table minted from MODEL_ID / MODEL_CHEAP_ID โ€” no catalog is in charge: none configured / source unreachable with no last-known-good / the candidate was refused /
11786
+ no enabled entry) | `catalog` (a catalog's model plane landed and replaced the whole env table). CANDIDATE OUTCOME: `catalog` = `refused` (the latest candidate was refused whole โ€” by the candidate judge or the model-plane
11787
+ group; with no previous generation the env table serves, with one the catalog table still serves โ€” who serves is `servedTable`'s answer, NOT merged into this field) | `adopted` (no whole refusal and the catalog table
11788
+ serves; NOT "the latest candidate landed" โ€” that question belongs to the /health generation ledger) | `absent` (no whole refusal and the env table serves). START-UP ROUTE VERDICT: `refusalCode` / `gap` (both ABSENT when the
11789
+ judge passes โ€” the normal state of a running process): `refusalCode` = `config.model_route_endpoint_missing` | `config.gateway_fallback_without_primary`; `gap` = `endpoint_missing` (set MODEL_GATEWAY_BASEURL or the entry's
11790
+ own `baseUrl`) | `lane_credential_missing` (an anthropic-lane choice with neither ANTHROPIC_API_KEY nor ANTHROPIC_AUTH_TOKEN fell into the endpoint-less openai leg). No sentence is carried. Words are server-owned โ€” read OPEN
11791
+ (an unknown word is "unknown", never "passed").
11792
+ required: [servedTable, catalog]
11793
+ properties:
11794
+ servedTable: { type: string, description: '`env` | `catalog` (open on read).' }
11795
+ catalog: { type: string, description: '`absent` | `adopted` | `refused` (open on read).' }
11796
+ refusalCode: { type: string, description: '`config.model_route_endpoint_missing` | `config.gateway_fallback_without_primary` (open on read); ABSENT = the judge passed.' }
11797
+ gap: { type: string, description: '`endpoint_missing` | `lane_credential_missing` (open on read); ABSENT = the judge passed.' }
11633
11798
  ModelsResponse:
11634
11799
  type: object
11635
11800
  description: >
@@ -11677,6 +11842,11 @@ components:
11677
11842
  Upgrade window (as it is): an on-disk LKG written by server <= 7.122.x only recorded the dropped entries that had a name AND were
11678
11843
  enabled; while serving from it with the center still unreachable this key lists only those rows (possibly `[]`) and the first pull after the
11679
11844
  center is reachable fills it in. An unknown `reasonCode` word reads as "dropped, reason unknown".
11845
+ modelPlane:
11846
+ allOf: [{ $ref: '#/components/schemas/ModelPlane' }]
11847
+ description: >-
11848
+ server >= 7.133.0, ALWAYS present there (appended last): which model table this engine SERVES, whether the catalog was adopted, and what the start-up route judge says โ€” read this instead of predicting
11849
+ (the judge's inputs are the serving process's facts; mirroring two of its judgements drifts). ABSENT = an earlier server (<= 7.132.x). Field reading: ModelPlane.
11680
11850
  candidateRejected:
11681
11851
  allOf: [{ $ref: '#/components/schemas/CandidateRejected' }]
11682
11852
  description: >
@@ -12898,14 +13068,14 @@ components:
12898
13068
  ts: { type: integer }
12899
13069
  FleetFrame_hook_notice:
12900
13070
  description: >
12901
- Hook observability frame, TWO kinds (both pure observe โ€” never block, resume, or inject anything;
13071
+ Hook observability frame, FOUR kinds (server >= 7.133.0 added the third and fourth; all pure observe โ€” never block, resume, or inject anything;
12902
13072
  visibility fail-CLOSED like bg_notification: the owner keys ownerScope/ownerSessionId gate delivery
12903
13073
  and are STRIPPED before send; wire is FLAT โ€” `send("hook_notice", { type, ...wire, ts })`, the
12904
13074
  server-internal `{notice}` nesting never reaches it). codex R1-F4: modeled as the discriminated union
12905
13075
  it really is โ€” the common `{type, ts}` frame base composed with each kind's payload arm โ€” so a
12906
13076
  generated client gets the SAME per-kind narrowing as the TS type (a second-family frame missing
12907
- hookName/entryType is invalid, not silently accepted). `kind` is CLOSED two-member on the server but
12908
- OPEN ON READ: degrade an unknown future kind to a generic observability row.
13077
+ hookName/entryType is invalid, not silently accepted). `kind` is CLOSED four-member on the server but
13078
+ OPEN ON READ: degrade an unknown future kind to a generic observability row. DISPATCH on `kind`; never infer a call's outcome from any hook_notice frame (read the tool / approval frames).
12909
13079
  oneOf:
12910
13080
  - allOf:
12911
13081
  - $ref: '#/components/schemas/FleetHookNoticeFrameBase'
@@ -12913,10 +13083,16 @@ components:
12913
13083
  - allOf:
12914
13084
  - $ref: '#/components/schemas/FleetHookNoticeFrameBase'
12915
13085
  - $ref: '#/components/schemas/FleetHookNonBlockingFailure'
13086
+ - allOf:
13087
+ - $ref: '#/components/schemas/FleetHookNoticeFrameBase'
13088
+ - $ref: '#/components/schemas/FleetHookManagedUnrun'
13089
+ - allOf:
13090
+ - $ref: '#/components/schemas/FleetHookNoticeFrameBase'
13091
+ - $ref: '#/components/schemas/FleetHookVerdictUntrusted'
12916
13092
 
12917
13093
  FleetHookNoticeFrameBase:
12918
13094
  type: object
12919
- description: 'The hook_notice frame envelope shared by both kinds: the SSE frame discriminant and the server timestamp.'
13095
+ description: 'The hook_notice frame envelope shared by all four kinds: the SSE frame discriminant and the server timestamp.'
12920
13096
  required: [type, ts]
12921
13097
  properties:
12922
13098
  type: { const: hook_notice }
@@ -12965,7 +13141,7 @@ components:
12965
13141
  # #315(server โ‰ฅ7.43):็ฌฌไบŒ้กถๅฑ‚้”ฎ `livePending` = streamApproval ็ช—ๅ†…็š„ๆตๅ†… ask(ๆญคๅ‰ๅฏน่ฝฎ่ฏขๆถˆ่ดน็ซฏ
12966
13142
  # ็ป“ๆž„ๆ€งไธๅฏ่ง)ใ€‚ADDITIVE / tolerate-absent:้”ฎ็ผบๅธญ=ใ€Œๅ่ฐƒๅ™จๆœช่ฃ…้…(ๆœฌ้ƒจ็ฝฒๆ—  live ๅฎกๆ‰น้ข)ใ€ๆˆ–
12967
13143
  # ๆ—ง server;็ฉบๆ•ฐ็ป„=ใ€Œๆœ‰้ขใ€ๆญคๅˆปๆ— ๅพ…ๆ‰นใ€โ€”โ€”ไธคไน‰ๅฏๅˆคๅˆซ,ๆถˆ่ดน็ซฏๆต‹ presenceใ€‚**ๅˆ†ๆ•ฐ็ป„ไธๆทท็ผ–**:
12968
- # live ่กŒ็š„ๅ†ณ่ฎฎๅฃ=POST /v1/tool-approvals/{approvalId}/respond,durable ่กŒ=decide ๅฃ;ๆ•ฐ็ป„ๅๅณ่ทฏ็”ฑๅˆคๆฎใ€‚
13144
+ # live ่กŒ็š„ๅ†ณ่ฎฎๅฃ=POST /v1/tool-approvals/{id}/respond,durable ่กŒ=decide ๅฃ;ๆ•ฐ็ป„ๅๅณ่ทฏ็”ฑๅˆคๆฎใ€‚
12969
13145
  # 22.0.0:็ฌฌไธ‰ใ€ๅ››้กถๅฑ‚้”ฎๆญคๅ‰ๆผๅฃฐๆ˜Ž โ€”โ€” ๅฐ้—ญ schema ไธŠไธ€ไธช็œŸ้”ฎ = ๅˆๆณ•ไฟกๅฐ่ขซไธฅๆ ผๆ ก้ชŒๅ™จๆ‹’ๆ”ถใ€‚
12970
13146
  additionalProperties: false
12971
13147
  required: [pending]
@@ -13020,7 +13196,7 @@ components:
13020
13196
  type: object
13021
13197
  description: >-
13022
13198
  A live (in-stream) pending ask inside its streamApproval window (#315, server โ‰ฅ7.43). Decide it via
13023
- POST /v1/tool-approvals/{approvalId}/respond โ€” NOT the /decide route (that is the durable rows' door).
13199
+ POST /v1/tool-approvals/{id}/respond โ€” NOT the /decide route (that is the durable rows' door).
13024
13200
  Optional flags are only-if-true (absent is NEVER encoded as false).
13025
13201
  ๐Ÿ”ด THE "tool input/args are NOT listed" NARROWING IS GONE (server >= 7.87.0 / S-454 P1). The old reason
13026
13202
  was "details ride the stream frame" โ€” but a SUSPENDED ask never has a stream frame (at mint time it had
@@ -13038,7 +13214,15 @@ components:
13038
13214
  additionalProperties: true
13039
13215
  required: [approvalId, toolName, ts, expiresAtMs]
13040
13216
  properties:
13041
- approvalId: { type: string, description: 'The SAME id the respond endpoint takes (wire uuidv7).' }
13217
+ approvalId: { type: string, description: 'The SAME id the respond endpoint takes. server >= 7.133.0: for an ask that has a ledger row it is ALWAYS the row''s key `askId` (a 64-character lowercase hexadecimal string โ€” an OPAQUE string, never a uuid); on <= 7.132.x a wire uuidv7.' }
13218
+ consumerState:
13219
+ type: string
13220
+ enum: [absent]
13221
+ description: >-
13222
+ server >= 7.133.0, TRUE-ONLY (present as `absent`): no engine leg on THIS replica is waiting for this ask. Present <=> the row was ADOPTED from the ask ledger at start-up (minted by a previous
13223
+ process / another replica, the row still undecided โ€” a "zombie card"): it lists and can be answered (the row becomes decided; the answer ack carries the same bit), but that decision resumes
13224
+ nothing running on this replica โ€” read the run surface to learn whether anything continued. The `live_pending` stream frame and the `pending` arm of GET /v1/tool-approvals/{id} carry it too. After a restart an
13225
+ ask still undecided on the ledger RETURNS to `livePending[]` (it used to vanish and answer 404). Absent = this replica has an engine leg waiting, or an older server.
13042
13226
  toolName: { type: string }
13043
13227
  ts: { type: number, description: 'Registration time (ms epoch).' }
13044
13228
  expiresAtMs: { type: number, description: 'Absolute window deadline โ€” the SAME number minted once for the tool_approval frame (never recomputed).' }
@@ -13063,9 +13247,13 @@ components:
13063
13247
  allOf: [{ $ref: '#/components/schemas/ToolApprovalCardWord' }]
13064
13248
  description: 'server >= 7.126.0 (core 7.49.0 #1414): which card this ask renders on โ€” the engine-minted card word, the SAME value as `frame.approvalCard` (the row reads the frame''s own seat, no second read), same layer and shape as the operator queue row''s `approvalCard`. It REPLACES `contentKind` (server <= 7.125.x derived it from the tool name; deleted at 7.126.0, no alias). ABSENT = the engine minted none. Display / triage only; it NEVER takes part in decision routing.'
13065
13249
  frame:
13066
- allOf: [{ $ref: '#/components/schemas/ToolApprovalFrame' }]
13250
+ oneOf:
13251
+ - $ref: '#/components/schemas/ToolApprovalFrame'
13252
+ - $ref: '#/components/schemas/ApprovalRequestFrame'
13067
13253
  description: >-
13068
- server >= 7.87.0 (S-454 P1; cli L-397) โ€” THE CARD FRAME THIS ASK WOULD HAVE EMITTED. It is the SAME
13254
+ server >= 7.87.0 (S-454 P1; cli L-397) โ€” THE CARD FRAME THIS ASK WOULD HAVE EMITTED. TWO SHAPES, by leg (sdk 25.0.0 declares the union; discriminant
13255
+ `frame.type`): on a leg that speaks the card protocol (`capabilities.approvalFrameFamilies` contains `approval_request`, server >= 7.133.0) it is the
13256
+ `approval_request` frame (card facts under `frame.card`, the ten off-card live facts flat at the top); on a legacy-family leg it is the `tool_approval` frame. It is the SAME
13069
13257
  OBJECT as the server's `PendingApproval.frame` (the one construction site in `askBroadcast`, already
13070
13258
  redacted / byte-capped / window-keys filled), shared by the suspended and the reachable path โ€” so
13071
13259
  this is NOT a second card projection computed for the list, and there is no second redaction surface.
@@ -13285,6 +13473,7 @@ components:
13285
13473
  required: [sessionId, scope, createdAt]
13286
13474
  additionalProperties: true
13287
13475
  properties:
13476
+ checkpointId: { type: string, minLength: 1, maxLength: 128, description: 'server >= 7.133.0: the NON-SECRET stable identity of the pause this row parks on โ€” the join key where the token cannot go; the SAME value as `PausedCause.checkpointId`. Carried on three durable read surfaces (GET /v1/approvals `pending[]` rows, the /v1/approvals/stream `pending` frame, GET /v1/assistant/inbox rows). A row whose value is unreadable or untrusted (non-string / empty / over 128 / contains the token / an old row / an older server) is STILL LISTED with the key ABSENT โ€” read absence as "this row has no joinable identity", never as a required key.' }
13288
13477
  sessionId: { type: string, description: 'The decide handle: POST /v1/approvals/:sessionId/decide.' }
13289
13478
  scope: { type: string, description: 'Principal; "_" = submitted without one.' }
13290
13479
  taskId: { type: ['string', 'null'], description: 'Suspended run holding the session claim (service-pinned) โ€” join key to trace tool-call blocks + context link.' }
@@ -13496,6 +13685,7 @@ components:
13496
13685
  additionalProperties: false
13497
13686
  properties:
13498
13687
  sessionId: { type: string, description: 'The suspended task/session id.' }
13688
+ checkpointId: { type: string, minLength: 1, maxLength: 128, description: 'server >= 7.133.0: the NON-SECRET stable identity of the pause this row parks on โ€” the join key where the token cannot go; the SAME value as `PausedCause.checkpointId`. Carried on three durable read surfaces (GET /v1/approvals `pending[]` rows, the /v1/approvals/stream `pending` frame, GET /v1/assistant/inbox rows). A row whose value is unreadable or untrusted (non-string / empty / over 128 / contains the token / an old row / an older server) is STILL LISTED with the key ABSENT โ€” read absence as "this row has no joinable identity", never as a required key.' }
13499
13689
  scope: { type: string, description: 'Multi-tenant owner scope.' }
13500
13690
  createdAt: { type: number, description: 'Ask creation time (epoch ms).' }
13501
13691
  gateKind:
@@ -13819,6 +14009,17 @@ components:
13819
14009
  โš ๏ธ The key name is final: `permissionModeAfter` (the build brief once said `nextPermissionMode`,
13820
14010
  which never shipped โ€” zero aliases).
13821
14011
 
14012
+ SessionAgent:
14013
+ type: object
14014
+ additionalProperties: false
14015
+ description: >-
14016
+ The placement-tuple projection of a delegated subagent's transcript (`SessionRecord.agent`, server >= 7.133.0). Each member is independently optional (only what was stored is projected); `{}` is a legal value
14017
+ (the tuple held only a `kind`). `id` = the tuple's `handle` (the durable agent row's `aโ€ฆ` key โ€” the SAME string as the fleet row's `id` and `GET /v1/agents/roster`'s `handle`); `parentSessionId` = the host
14018
+ session that spawned it; `rootSessionId` = the delegation tree's root host session (a nested grandchild anchors to the root).
14019
+ properties:
14020
+ id: { type: string }
14021
+ parentSessionId: { type: string }
14022
+ rootSessionId: { type: string }
13822
14023
  SessionRecord:
13823
14024
  type: object
13824
14025
  description: >
@@ -13851,6 +14052,14 @@ components:
13851
14052
  $ref: '#/components/schemas/SessionWindow'
13852
14053
  promptEpoch:
13853
14054
  $ref: '#/components/schemas/PromptEpoch'
14055
+ agent:
14056
+ allOf: [{ $ref: '#/components/schemas/SessionAgent' }]
14057
+ description: >-
14058
+ server >= 7.133.0, top-level additive key โ€” WHICH delegated subagent's transcript this session is (read by terminal side-chain projections that join a fleet frame's `transcriptId`). Only the stored placement
14059
+ tuple is projected (minted by the engine when a BACKGROUND subagent with a durable agent row creates the session on a store that declares placements; a synchronously delegated subagent's transcript is not in
14060
+ the host store and has no tuple); the three members are each optional. ๐Ÿ”ด READ IT AS "WAS IT STORED", NEVER AS "IS IT A SUBAGENT": key PRESENT = a delegated subagent's transcript (even `{}` when the tuple held only a
14061
+ kind); key ABSENT has three causes โ€” an older server (<= 7.132.x) / an ordinary session / a deployment whose store keeps no placement tuple (the local backend: the copy parked from the private short-TTL store is an
14062
+ ordinary session row). Absence is NOT "not a subagent": fall back to the identity the fleet row (`id`) / `GET /v1/agents/roster` (`handle`) already carry.
13854
14063
  userInputUndrained:
13855
14064
  type: array
13856
14065
  description: >-
@@ -14885,7 +15094,7 @@ components:
14885
15094
  center 404 / 429 / 3xx included). On 400 `request.field_invalid` for a `settings.permissions` bucket (server >= 7.106.0 /
14886
15095
  S-904): `over_cap` (more than `cap` entries; beside `count` + `cap`) / `bad_entry` (a non-string or empty item; beside
14887
15096
  `index`); server >= 7.132.0 widens that group to every tool-name list on the body, the agent definitions and the two host-path lists, and adds
14888
- `too_long` (a text over its character cap; beside `count` + `cap`) โ€” closed three words `over_cap` / `too_long` / `bad_entry` (SDK `RequestFieldRefusalExtra`).
15097
+ `too_long` (a text over its character cap; beside `count` + `cap`) โ€” closed words `over_cap` / `too_long` / `bad_entry` (SDK `RequestFieldRefusalExtra`); server >= 7.133.0 adds `too_large` (a payload over its BYTE cap, only on 413 `request.payload_too_large`: `{ field: "body", reason: "too_large", cap }` โ€” `count` ABSENT when the read stopped at the cap and cannot measure; a request body over 8 MiB is now always that 413) and the `request.invalid_json` reasons `not_json` | `not_object` (400, every JSON-body door; formerly `request.rejected`/`request.body_shape` by door).
14889
15098
  On 409 refusals of a resume (server >= 7.132.0): the engine's `CheckpointErrorReason` arm (`detail.reason` passed through; see `blockedBy`). On 400 `rules.inert_entry` (server >= 7.129.0): the engine's `ALLOW_ENTRY_INERT_REASONS` (`tool_name` / `wildcard_all` / `namespace_wildcard` / `content_unsupported` / `grammar` / `near_miss` / `earlier_grammar`). Out-of-vocabulary word โ‡’ treat as unknown. (Other codes carry their own `reason` words.)
14890
15099
  retryAfterSec:
14891
15100
  type: number
@@ -15006,6 +15215,14 @@ components:
15006
15215
  properties:
15007
15216
  reason: { type: string, enum: [unnamed_refusal] }
15008
15217
  path: { $ref: '#/components/schemas/SettingsHooksConfigPath' }
15218
+ - type: object
15219
+ description: 'one of the three wire keys this service reads itself is malformed (server >= 7.133.0): the group `source` / a command entry''s `plugin` / `args`; `key` names it, `path` is its coordinate. The whole request is refused (they used to be stripped silently as unknown keys).'
15220
+ required: [reason, key, path]
15221
+ additionalProperties: false
15222
+ properties:
15223
+ reason: { type: string, enum: [wire_key_invalid] }
15224
+ key: { type: string }
15225
+ path: { $ref: '#/components/schemas/SettingsHooksConfigPath' }
15009
15226
  - type: object
15010
15227
  description: 'server 7.115.0โ€“7.125.x: the dead-alias matcher refusal before `reason` existed.'
15011
15228
  required: [event, alias, canonical]
@@ -15027,13 +15244,41 @@ components:
15027
15244
  properties:
15028
15245
  scenario: { type: string }
15029
15246
  source: { type: string, enum: [ruling, deployment] }
15247
+ - type: object
15248
+ description: >-
15249
+ server >= 7.133.0 โ€” entry problems on a tool-name list / settings-rule refusal (400 `request.field_invalid`, 422): `problems` (at most 1000, each EntryProblem) + `total` (the true count; `total > problems.length` => cut).
15250
+ With `callerTrust` it is the below-trust-floor refusal: the caller's admission tier (`global` | `project`, read open; the same words as `capabilities.settings.permissions.callerTrust`).
15251
+ required: [problems, total]
15252
+ additionalProperties: false
15253
+ properties:
15254
+ problems: { type: array, items: { $ref: '#/components/schemas/EntryProblem' } }
15255
+ total: { type: integer }
15256
+ callerTrust: { type: string }
15257
+ - type: object
15258
+ description: >-
15259
+ server >= 7.133.0 โ€” 400 `request.field_conflict`: the request body contradicts itself. `kind` = which constraint (a CLOSED fourteen-word set, read open: cascade_verify | image_profile | resume_anchor |
15260
+ system_prompt_fold | file_history_restore | skills_disabled | existing_session | scratchpad_root | ask_answer | policy_person_ask | plan_edit | plan_mode_after | bound_call_door | remember_decision);
15261
+ `fields` = the named body coordinates, subject first (at least one; a dynamic index reads `agents[1].skills`); `count` / `cap` only on the fold-cap kind (the folded total and its cap).
15262
+ required: [kind, fields]
15263
+ additionalProperties: false
15264
+ properties:
15265
+ kind: { type: string }
15266
+ fields: { type: array, items: { type: string } }
15267
+ count: { type: integer }
15268
+ cap: { type: integer }
15269
+ - type: object
15270
+ description: '409 `conflict.session_placed` (server >= 7.133.0): the session sync commit targeted a session that holds a delegated agent''s transcript. `placement_kind` is the stored placement kind (today only `subagent`; read open).'
15271
+ required: [placement_kind]
15272
+ additionalProperties: false
15273
+ properties:
15274
+ placement_kind: { type: string }
15030
15275
  - type: string
15031
15276
  count:
15032
15277
  type: integer
15033
- description: '`reason: over_cap` / `too_long` only โ€” how much was sent (entries / characters; S-904 since server >= 7.106.0, every capped face since >= 7.132.0). SDK: `err.extra.count`.'
15278
+ description: '`reason: over_cap` / `too_long` / `too_large` โ€” how much was sent (entries / characters / bytes; every capped face since >= 7.132.0). ABSENT on the unmeasured form (a body read that stopped at the cap). SDK: `err.extra.count`.'
15034
15279
  cap:
15035
15280
  type: integer
15036
- description: '`reason: over_cap` / `too_long` only โ€” the cap (1000 for a permissions bucket; per face otherwise). SDK: `err.extra.cap`.'
15281
+ description: '`reason: over_cap` / `too_long` / `too_large` โ€” the cap (1000 for a permissions bucket; per face otherwise; bytes on a too_large 413). SDK: `err.extra.cap`.'
15037
15282
  index:
15038
15283
  type: integer
15039
15284
  description: '`reason: bad_entry` only โ€” the index of the first bad item; ABSENT when the list itself is not an array. SDK: `err.extra.index`.'
@@ -15169,6 +15414,11 @@ components:
15169
15414
  - $ref: '#/components/schemas/Event_tool_approval_complete'
15170
15415
  - $ref: '#/components/schemas/Event_approval_request'
15171
15416
  - $ref: '#/components/schemas/Event_approval_revoke'
15417
+ # server >= 7.133.0 (sdk 25.0.0): the card family's single-card CLOSE frame (`approval_complete`; one frame family per leg โ€” it replaces the legacy
15418
+ # `tool_approval_complete` on a leg where `capabilities.approvalFrameFamilies` = ["approval_request"]) and the LIVE-ONLY `session_titled` frame (a session
15419
+ # title was written; never ledgered, no eventId).
15420
+ - $ref: '#/components/schemas/Event_approval_complete'
15421
+ - $ref: '#/components/schemas/Event_session_titled'
15172
15422
  # sdk 9.1.0: two LIVE-leg arms the service has been emitting while neither this union nor the
15173
15423
  # SDK's declared them โ€” `text_end` since server 7.66.0 (segment boundary; retires the idle-flush
15174
15424
  # heuristic) and `tool_roster_delta` since server 7.70.0 (run-time roster change). A generated client
@@ -15230,6 +15480,8 @@ components:
15230
15480
  tool_approval_complete: '#/components/schemas/Event_tool_approval_complete'
15231
15481
  approval_request: '#/components/schemas/Event_approval_request'
15232
15482
  approval_revoke: '#/components/schemas/Event_approval_revoke'
15483
+ approval_complete: '#/components/schemas/Event_approval_complete'
15484
+ session_titled: '#/components/schemas/Event_session_titled'
15233
15485
  text_end: '#/components/schemas/Event_text_end'
15234
15486
  tool_roster_delta: '#/components/schemas/Event_tool_roster_delta'
15235
15487
  reasoning_end: '#/components/schemas/Event_reasoning_end'
@@ -15259,13 +15511,18 @@ components:
15259
15511
  condition: durable-ledger leg only (no ledger => neither header nor frame). sessionId rides along
15260
15512
  (needed to bind subsequent turns when the server minted a fresh session). Always precedes any content
15261
15513
  frame. NOT an engine event โ€” server-minted, no EventIdentity.
15262
- # ๅฐ้—ญ:้“ธ้€ ็‚น routes/tasks.ts ๆ˜ฏ้€ๅญ—ๅ†™ๆญป็š„ไธ‰้”ฎๅญ—้ข้‡(sessionId ๆกไปถๅœจๅœบ)ใ€‚
15514
+ # ๅฐ้—ญ:้“ธ้€ ็‚น routes/tasks.ts ๆ˜ฏ้€ๅญ—ๅ†™ๆญป็š„ไธ‰้”ฎๅญ—้ข้‡(sessionId ๆกไปถๅœจๅœบ);server >= 7.133.0 ่ตท็ฌฌๅ››ไธชๆกไปถ้”ฎ `warnings`(ๆœ‰ๆ‰ๅ‘)ใ€‚
15263
15515
  additionalProperties: false
15264
15516
  required: [type, taskId]
15265
15517
  properties:
15266
15518
  type: { const: meta }
15267
15519
  taskId: { type: string }
15268
15520
  sessionId: { type: string }
15521
+ warnings:
15522
+ type: array
15523
+ items: { $ref: '#/components/schemas/EntryProblem' }
15524
+ description: 'server >= 7.133.0, present only when non-empty: the accept-time NOTICES for entries of the allow-side selector lists (root / agent `allowTools`) the engine reads as nothing or that are not in the roster. Same list as the 202 receipt''s and the sync 200 body''s `warnings`. ABSENT = no notice (older servers too).'
15525
+
15269
15526
  Event_reasoning:
15270
15527
  type: object
15271
15528
  description: durable stream only โ€” turn-aggregated thinking.
@@ -15857,6 +16114,29 @@ components:
15857
16114
  WHO ASKED. Same-present/same-absent as `settlement` (I1). CLOSED here (archive read, `GateRecordAskOrigin`) โ€” see the
15858
16115
  note above.
15859
16116
 
16117
+ McpProbeFaceRow:
16118
+ # server 7.133.0: the face''s row = the wiring-manifest row projection + TWO face-only optional keys. The manifest row is CLOSED (so an `allOf` over it would reject the two extra keys), hence a
16119
+ # standalone closed schema whose per-key shapes are `$ref`s into WiringManifestMcpEntry's own properties (one source per shape); the key SET is pinned by test (manifest row keys + the two).
16120
+ type: object
16121
+ description: >
16122
+ One row of the no-run MCP status face (GET /v1/capabilities/mcp and POST /v1/capabilities/mcp/probe): the SAME projection a prepared leg pushes onto `wiring_manifest.mcp[]`, PLUS two optional keys only this
16123
+ face carries (server >= 7.133.0). `toolNames` = the tool names this server will mount (the same probe reading `servers[i].tools`, engine namespace form `mcp__<server>__<tool>`, after the schema gate and the per-server
16124
+ allow-list; the server neither composes nor renames), at most 200 per server, the reading-order prefix. ABSENT = an older server or no `tools` in that server's reading โ€” NEVER invent `[]` (it would read as "this server
16125
+ has no tools"); a present `[]` is real. `toolNamesTruncated` = TRUE-ONLY: the list was cut (more than 200; exactly 200 is NOT cut and the key stays absent, never `false`) โ€” when true `toolCount` is the true count, not
16126
+ `toolNames.length`. A misaligned reading (not an array / count differs from the row count) answers 500 `internal.error` on both doors instead of shipping a list that could be paired wrongly.
16127
+ additionalProperties: false
16128
+ required: [name, status]
16129
+ properties:
16130
+ name: { $ref: '#/components/schemas/WiringManifestMcpEntry/properties/name' }
16131
+ status: { $ref: '#/components/schemas/WiringManifestMcpEntry/properties/status' }
16132
+ source: { $ref: '#/components/schemas/WiringManifestMcpEntry/properties/source' }
16133
+ toolCount: { $ref: '#/components/schemas/WiringManifestMcpEntry/properties/toolCount' }
16134
+ errorCode: { $ref: '#/components/schemas/WiringManifestMcpEntry/properties/errorCode' }
16135
+ delivered: { $ref: '#/components/schemas/WiringManifestMcpEntry/properties/delivered' }
16136
+ httpStatus: { $ref: '#/components/schemas/WiringManifestMcpEntry/properties/httpStatus' }
16137
+ liveness: { $ref: '#/components/schemas/WiringManifestMcpEntry/properties/liveness' }
16138
+ toolNames: { type: array, items: { type: string }, maxItems: 200 }
16139
+ toolNamesTruncated: { type: boolean, enum: [true] }
15860
16140
  McpProbeFace:
15861
16141
  type: object
15862
16142
  description: >
@@ -15882,8 +16162,8 @@ components:
15882
16162
  description: 'How long this reading may be reused (seconds; 30s per identity + declaration set server-side). A hit inside the window does NOT count against the rate limit.'
15883
16163
  servers:
15884
16164
  type: array
15885
- items: { $ref: '#/components/schemas/WiringManifestMcpEntry' }
15886
- description: 'One row per declaration, SAME ROW SHAPE as the wiring manifest, index-aligned (see this schema''s description).'
16165
+ items: { $ref: '#/components/schemas/McpProbeFaceRow' }
16166
+ description: 'One row per declaration, the wiring-manifest row shape plus (server >= 7.133.0) this face''s two optional keys, index-aligned (see this schema''s description).'
15887
16167
 
15888
16168
  McpLivenessState:
15889
16169
  type: string
@@ -16654,6 +16934,12 @@ components:
16654
16934
  (`capabilities.rewindFiles`); only a run the REQUEST word turned off cannot restore itself.
16655
16935
  OPEN on read: the server does not enumerate the word (a present non-string is dropped), a third word passes
16656
16936
  through โ€” treat it as unknown. ABSENT = an older engine / external derivation, never "capturing".
16937
+ permissionMode:
16938
+ allOf: [{ $ref: '#/components/schemas/WiringManifestPermissionMode' }]
16939
+ description: >-
16940
+ server >= 7.133.0 (core 7.53.0; TENANT and OPERATOR faces alike) โ€” this leg's PERMISSION-MODE receipt `{ requested?, effective, foldedBy? }`; the shell renders its mode indicator from `effective`
16941
+ (`foldedBy` present = the requested mode was folded), never from the word it sent. ABSENT = an older engine / server, not an assertion of the default mode. Effective half only (this frame);
16942
+ the same section rides the non-streaming `wiringManifest` receipt and the ledger row. Section shape: WiringManifestPermissionMode.
16657
16943
  instructions:
16658
16944
  allOf: [{ $ref: '#/components/schemas/WiringManifestInstructions' }]
16659
16945
  description: >
@@ -17533,7 +17819,7 @@ components:
17533
17819
  payload carried `type` (the read boundary emits `{type, ...data}`) so runtime discrimination worked,
17534
17820
  but a generated client had nothing to switch on and dropped approval cards. That gap is closed.
17535
17821
  The shell renders `tool_approval` as the three-choice card and dismisses it on `tool_approval_complete`.
17536
- Respond via POST /v1/tool-approvals/{approvalId}/respond.
17822
+ Respond via POST /v1/tool-approvals/{id}/respond.
17537
17823
  required: [type, approvalId]
17538
17824
  additionalProperties: true
17539
17825
  properties:
@@ -17929,8 +18215,8 @@ components:
17929
18215
 
17930
18216
  # โ”€โ”€ design/172 ๆตๅ†…ๅฎกๆ‰นๅ่ฎฎ(#151)โ€”โ€” ๅทฒ้š server 7.3.0 ไธŠ็บฟ;ๅผ€ๅ…ณ STREAM_APPROVAL_ENABLED ็š„้ป˜่ฎคๅ€ผ
17931
18217
  # ๅˆ†ไธคๆฎต:server โ‰ค7.4.0 ้ป˜่ฎค OFF,โ‰ฅ7.5.0 ้ป˜่ฎค ON(BREAKING,่ง server CHANGELOG)ใ€‚โ”€โ”€
17932
- # ไธŽไธŠ้ข็š„ ToolApprovalFrame ๆ˜ฏ**ๅนถ่กŒๅŠ ๅธง,ไธๆ›ฟๆข**:ๅผ€ๅ…ณๆ‰“ๅผ€ๆ—ถๅŒไธ€ๅช ask ๅ‡บไธคๅธง(ๅ…ˆ tool_approval,
17933
- # ๅŽ approval_request),ไธคๅธงๅŒๅธฆ `approvalId` โ‡’ ๆถˆ่ดน็ซฏๆŒ‰ๅฎƒๅŽป้‡ใ€ไผ˜ๅ…ˆๆธฒๆŸ“ๆ–ฐๅธงใ€‚
18218
+ # ไธŽไธŠ้ข็š„ ToolApprovalFrame ๆ˜ฏไธคไธชๅธงๆ—,**ไธ€ๆก่…ฟๅช่ตฐไธ€ไธช**(server >= 7.133.0,capabilities.approvalFrameFamilies):ๅ‘ˆๅกๅ่ฎฎไธŠๅœบ็š„่…ฟไธŠไธ€ๅช ask ๅชๅ‡บๆœฌๆ—็š„ๅธงใ€
18219
+ # ๆ—งๆ—ไธ€ๅธง้ƒฝไธๅ‘,ๆถˆ่ดน็ซฏไธๅ†ๆŒ‰ `approvalId` ๅŽป้‡;โ‰ค7.132.x ็š„ๅ‘ˆๅก่…ฟไธคๆ—ๅนถๅ‘(ๅ…ˆ tool_approvalใ€ๅŽ approval_request,ๅŒๅธฆ `approvalId`)โ‡’ ๆŒ‰ๅฎƒๅŽป้‡ใ€ไผ˜ๅ…ˆๆธฒๆŸ“ๆ–ฐๅธงใ€‚
17934
18220
  classifierUnavailable:
17935
18221
  $ref: '#/components/schemas/ClassifierUnavailable'
17936
18222
  description: >-
@@ -17972,6 +18258,14 @@ components:
17972
18258
  (a malformed shape, or a `dir` over 1024 UTF-16 code units โ‡’ the key is ABSENT โ€” never truncated,
17973
18259
  because a truncated directory is a DIFFERENT directory and adding it would not clear this ask;
17974
18260
  see the STRING-LENGTH UNIT convention in `info.description` for this `maxLength`'s counting unit).
18261
+ covers:
18262
+ allOf: [{ $ref: '#/components/schemas/ReadRootCover' }]
18263
+ description: >-
18264
+ Optional (engine core 7.22.0 `ReadRootGrantCandidate.covers`; the server carries it through key by key). ABSENT โ‡’ `dir` is a DIRECTORY
18265
+ (adding it to the read roots releases what lies under it). `exact` โ‡’ `dir` is the path the command NAMED, offered verbatim
18266
+ (a file or one concrete path, not a directory containing it): the consumer ADDS the string itself โ€” expanding it into a
18267
+ pattern grants nothing โ€” and must not render the offer as "add directory". The domain is the single word `exact`
18268
+ (a word outside it makes the server drop the whole candidate). The `approval_request` frame reads this very definition.
17975
18269
  clearsThisAsk:
17976
18270
  type: boolean
17977
18271
  enum: [true]
@@ -18052,8 +18346,23 @@ components:
18052
18346
  approvalId:
18053
18347
  type: string
18054
18348
  description: >
18055
- Bridge to the legacy frame / dedupe key โ€” same value as the parallel tool_approval frame. During the
18056
- transition window POST /v1/tool-approvals/{approvalId}/respond still works.
18349
+ The id of POST /v1/tool-approvals/{id}/respond and GET /v1/tool-approvals/{id}. server >= 7.133.0: ALWAYS EQUAL TO `askId` (the row's key is the ask's one wire
18350
+ identity; a 64-character lowercase hexadecimal string โ€” treat it as an OPAQUE string, never parse it as a uuid); the matching close frame `approval_complete` carries it too.
18351
+ On server <= 7.132.x it is the legacy bridge / dedupe key (a uuid, the same value as the parallel tool_approval frame).
18352
+ # โ”€โ”€ server >= 7.133.0: the TEN off-card LIVE facts on the opening frame's top level (one frame family per leg) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
18353
+ # Same keys, same shapes, same values as on the legacy `tool_approval` frame (each is a `$ref` into ToolApprovalFrame's own property โ€” one
18354
+ # source per shape). Only the frame minted LIVE carries them (and what the durable ledger recorded of that frame): a disconnect-replay frame
18355
+ # (rebuilt from `card_json`) never does. Absence means "this frame does not carry it", not "there is none". See APPROVAL_REQUEST_LIVE_FACT_KEYS (sdk) / the server's twin.
18356
+ mandated: { $ref: '#/components/schemas/ToolApprovalFrame/properties/mandated' }
18357
+ mandate: { $ref: '#/components/schemas/ToolApprovalFrame/properties/mandate' }
18358
+ origin: { $ref: '#/components/schemas/ToolApprovalFrame/properties/origin' }
18359
+ boundGrantDigest: { $ref: '#/components/schemas/ToolApprovalFrame/properties/boundGrantDigest' }
18360
+ boundGrants: { $ref: '#/components/schemas/ToolApprovalFrame/properties/boundGrants' }
18361
+ classifierNoRuling: { $ref: '#/components/schemas/ToolApprovalFrame/properties/classifierNoRuling' }
18362
+ boundGrantAbsence: { $ref: '#/components/schemas/ToolApprovalFrame/properties/boundGrantAbsence' }
18363
+ approvalCard: { $ref: '#/components/schemas/ToolApprovalFrame/properties/approvalCard' }
18364
+ readRootCandidate: { $ref: '#/components/schemas/ToolApprovalFrame/properties/readRootCandidate' }
18365
+ probeReason: { $ref: '#/components/schemas/ToolApprovalFrame/properties/probeReason' }
18057
18366
 
18058
18367
  ApprovalCard:
18059
18368
  type: object
@@ -18316,6 +18625,13 @@ components:
18316
18625
  and a word outside the set all read as ABSENT โ€” a card without this word must not be treated as "approving it grants nothing else"; whether to render "allow for this session" is `rememberRefused`.
18317
18626
  Display metadata, never an adjudication input. CLOSED: the server narrows with the engine's members, anything else reads as absent.
18318
18627
 
18628
+ ReadRootCover:
18629
+ type: string
18630
+ enum: [exact]
18631
+ description: >
18632
+ The closed vocabulary of `readRootCandidate.covers` (engine core 7.22.0 `ReadRootGrantCandidate.covers`; the server's narrow read `readReadRootCandidate` drops the whole
18633
+ candidate for a word outside it). One word today: `exact` โ€” `dir` is the path the command NAMED, not a directory containing it. The SDK exports the same table as
18634
+ `READ_ROOT_COVERS` and its `isApprovalRequestFrameV1` judges `covers` by membership in it; a source-level anchor reads the word off the server's minting site and the engine's type.
18319
18635
  PersistedRuleMandate:
18320
18636
  type: string
18321
18637
  enum: [operator_always, tool_marks, probe_mandate, probe_unanswered, write_protection, target_unresolved]
@@ -18729,6 +19045,40 @@ components:
18729
19045
  depth: { type: integer, description: 'Delegation depth (a direct child of the host = 1).' }
18730
19046
  agentName: { type: string, description: 'Child agent name. UNTRUSTED โ€” spawning-model free text (server-redacted and length-bounded).' }
18731
19047
 
19048
+ ApprovalCompleteFrame:
19049
+ type: object
19050
+ description: >
19051
+ The CLOSE frame of the card family (`approval_complete`; server >= 7.133.0; SSE, `type` IS the event name; the durable leg appends the same frame, so it replays through
19052
+ GET /v1/runs/:id/events as the `Event_approval_complete` arm). ONE FRAME FAMILY PER LEG: on a leg where the card protocol is on (`capabilities.approvalFrameFamilies`
19053
+ = `["approval_request"]`) one ask travels only `approval_request` -> `approval_complete` / `approval_revoke`; the legacy `tool_approval` / `tool_approval_complete` pair is NOT sent
19054
+ (BREAKING vs <= 7.132.x, which sent both families for one ask; remedy on the worker: `STREAM_APPROVAL_ENABLED=false`).
19055
+ Same meaning as the legacy `tool_approval_complete` (one settle, one verdict: the `outcome` three words, the non-human `cause`, the park-route `parked`); dismiss the card by the opening frame's
19056
+ `askId`. Division of labour with ApprovalRevokeFrame: the revoke frame is BATCH-level (siblings voided by a winner), this one is the SINGLE card's end (answered / window expired / park route / cancelled);
19057
+ one ask can receive both โ€” clearing by `askId` is enough. The gate is "this connection's opening frame was actually delivered". The frame is a notification, the row is the source of truth: a lost
19058
+ frame is compensated by the open-stream preamble (see ApprovalRequestFrame). THIS SCHEMA DESCRIBES v1 ONLY (read it as ApprovalFrameEnvelope first; the SDK's `isApprovalCompleteFrameV1` narrows).
19059
+ required: [type, schemaVersion, askId, taskId, approvalId, outcome, serverNowMs]
19060
+ additionalProperties: true
19061
+ properties:
19062
+ type: { type: string, enum: [approval_complete] }
19063
+ schemaVersion: { type: integer, enum: [1], description: 'This schema describes v1 only (see ApprovalFrameEnvelope for the unknown-version read).' }
19064
+ askId: { type: string, description: 'The durable identity โ€” dismiss the card by it (the same key as the opening frame''s `askId`).' }
19065
+ taskId: { type: string, description: 'wire run id (same as the opening frame).' }
19066
+ approvalId: { type: string, description: 'Always equal to `askId` (same as the opening frame).' }
19067
+ outcome:
19068
+ type: string
19069
+ enum: [allowed, denied, expired]
19070
+ description: 'How the ask settled โ€” the same three words as the legacy frame: `allowed` / `denied` = a decision; `expired` = a non-human ending (the reason is in `cause`).'
19071
+ cause:
19072
+ allOf: [{ $ref: '#/components/schemas/ApprovalSettleCause' }]
19073
+ description: >-
19074
+ The non-human ending''s cause (present only on `outcome: "expired"`; the fourteen words of the approval seat''s ending-cause set, the same word as on the audit settle row). A human answer carries none.
19075
+ ABSENCE IS NOT EVIDENCE.
19076
+ parked:
19077
+ type: boolean
19078
+ enum: [true]
19079
+ description: 'TRUE-ONLY: this ask itself ended on the park route (row moved to PARKING / PARKED; a late answer is reachable). Absent = no evidence โ€” NEVER read as a real denial.'
19080
+ serverNowMs: { type: integer, description: 'Server clock at mint time.' }
19081
+
18732
19082
  ApprovalRevokeFrame:
18733
19083
  type: object
18734
19084
  description: >
@@ -19028,7 +19378,7 @@ components:
19028
19378
  description: >
19029
19379
  server >= 7.126.0 (sdk 21.1.0 `APPROVAL_DECIDE_GATE_KINDS`, verbatim and in order; single owner = server
19030
19380
  `APPROVAL_GATE_KINDS`) โ€” the parked gate kinds the DURABLE decision door `POST /v1/approvals/{sessionId}/decide`
19031
- accepts (the live `POST /v1/tool-approvals/{approvalId}/respond` answers the same card family). A parked row of
19381
+ accepts (the live `POST /v1/tool-approvals/{id}/respond` answers the same card family). A parked row of
19032
19382
  kind `plan_review` goes to the plan-review door instead (`PlanReviewGateKind`); `resource_limit` / `needs_review` /
19033
19383
  `task_done` have no human decision door (resume / terminal). Clients route a pending row by reading this set,
19034
19384
  never by a hand-written copy of it.
@@ -19043,7 +19393,7 @@ components:
19043
19393
  enum: [person, policy]
19044
19394
  description: >
19045
19395
  server >= 7.118.0 (`APPROVAL_DECIDED_BY`, verbatim and in order) โ€” WHO made an approval decision, one key on all three decision
19046
- doors (live `POST /v1/tool-approvals/{approvalId}/respond`, durable `POST /v1/tasks/{taskId}/asks/{askId}/decision`, durable
19396
+ doors (live `POST /v1/tool-approvals/{id}/respond`, durable `POST /v1/tasks/{taskId}/asks/{askId}/decision`, durable
19047
19397
  `POST /v1/approvals/{sessionId}/decide`). `person` = a person decided through the approval channel (ABSENT means this);
19048
19398
  `policy` = this deployment's own policy decided (a rule / posture answering on someone's behalf, a headless lane) โ€” BOTH
19049
19399
  directions: the engine records `tool_end.gate.settlement.kind` = `policy_allowed` / `policy_refused` (`who.party: "policy"`).
@@ -19135,6 +19485,20 @@ components:
19135
19485
  `updatedInputForwarded` this field IS persisted, so an idempotent replay DOES promise byte-identity.
19136
19486
  The 409 `conflict.ask_decided` body echoes the same field โ€” and there it is always the FIRST
19137
19487
  decision's note, not the one the rejected request carried.
19488
+ noteRecorded:
19489
+ type: boolean
19490
+ description: >
19491
+ server >= 7.133.0, ADDITIVE: did the `note` THIS request carried land as the row's text? Present ONLY when the request carried a `note`. `true` <=> the row's `decisionNote` is
19492
+ verbatim the text this request submitted (text that happens to be identical counts even if someone else wrote it first); `false` = not confirmed (the row holds another reason /
19493
+ the store could not be read back). Same shape and meaning as `noteRecorded` on the live respond ack and on its replayed arm (one judgement for the three doors). Carried by the
19494
+ first-decision and idempotent-replay forms; the PARKED redemption leg does not carry it (read it as unknown). ABSENT is not `false`.
19495
+ consumerState:
19496
+ type: string
19497
+ enum: [absent]
19498
+ description: >
19499
+ TRUE-ONLY disclosure (present as `absent`): no engine leg on THIS replica is waiting for this ask โ€” the decision is on the row (an idempotent replay will read it, the audit surface keeps it)
19500
+ but nothing running here resumes because of it (the holder of the suspended promise may be another replica, or this replica restarted and its local window is empty). Read the RUN surface to
19501
+ learn whether anything continued; NEVER take a 200 without this key as proof the run continued (older servers never send it). Not a 409 / 410: the decision DID take effect.
19138
19502
 
19139
19503
  QuestionFrame:
19140
19504
  type: object
@@ -19669,7 +20033,7 @@ components:
19669
20033
  Event_tool_approval:
19670
20034
  description: >
19671
20035
  DURABLE-leg replay of the legacy tool-approval OPEN frame. Same payload as ToolApprovalFrame with the
19672
- discriminant narrowed to `tool_approval`. Settle it via POST /v1/tool-approvals/{approvalId}/respond.
20036
+ discriminant narrowed to `tool_approval`. Settle it via POST /v1/tool-approvals/{id}/respond.
19673
20037
  Anchor the card on `toolCallId` (NOT `approvalId`); render `governanceForced` as a non-bypassable
19674
20038
  "forced by governance" badge. UNTRUSTED for display: `message`/`args` are redacted but model-authored.
19675
20039
  allOf:
@@ -19692,9 +20056,11 @@ components:
19692
20056
  type: { type: string, enum: [tool_approval_complete] }
19693
20057
  Event_approval_request:
19694
20058
  description: >
19695
- The design/172 in-stream approval card as an AgentEvent arm. PARALLEL to `tool_approval`, not a
19696
- replacement: with the protocol on, one ask emits BOTH frames (legacy first) carrying the SAME
19697
- `approvalId` โ€” dedupe on it and prefer this arm. The decision key is `askId`; settle via
20059
+ The design/172 in-stream approval card as an AgentEvent arm. ONE FRAME FAMILY PER LEG (server >= 7.133.0,
20060
+ `capabilities.approvalFrameFamilies`): on a leg where the card protocol is on, one ask emits only this family
20061
+ (`approval_request` -> `approval_complete` / `approval_revoke`) and none of the legacy `tool_approval` family,
20062
+ so there is nothing to deduplicate. On server <= 7.132.x the two families ran side by side (legacy first) carrying
20063
+ the SAME `approvalId` โ€” there, dedupe on it and prefer this arm. The decision key is `askId`; settle via
19698
20064
  POST /v1/tasks/{taskId}/asks/{askId}/decision.
19699
20065
 
19700
20066
  ๐Ÿ”ด THIS ARM IS THE OPEN ENVELOPE, NOT `ApprovalRequestFrame`, AND THAT IS DELIBERATE. The v1 frame
@@ -19741,6 +20107,37 @@ components:
19741
20107
  properties:
19742
20108
  type: { type: string, enum: [approval_revoke] }
19743
20109
 
20110
+ Event_approval_complete:
20111
+ description: >
20112
+ The card family's single-card CLOSE frame as an AgentEvent arm (server >= 7.133.0). One frame family per leg: with `capabilities.approvalFrameFamilies` = `["approval_request"]` this frame
20113
+ REPLACES the legacy `tool_approval_complete` (one ask no longer travels two families). Live SSE and the durable ledger replay carry the same shape (same settle, same verdict: `outcome`,
20114
+ non-human `cause`, `parked`).
20115
+ ๐Ÿ”ด THIS ARM IS THE OPEN ENVELOPE, NOT `ApprovalCompleteFrame` โ€” same ruling as Event_approval_request / Event_approval_revoke: narrow with `schemaVersion == 1` (SDK:
20116
+ `isApprovalCompleteFrameV1`) before touching `outcome` / `cause` / `parked`; a frame that does not narrow still dismisses the card by its `askId` (when it has one) โ€” never guess the outcome.
20117
+ ๐Ÿ”ด Consumption discipline: (a) dismiss by the opening frame's `askId`; (b) a ledger-replayed frame is for TIMELINE RENDERING ONLY โ€” the card-set baseline stays the open-stream preamble;
20118
+ (c) NEVER derive the run's fate from this frame (read the run row / checkpoint face); (d) it does not conflict with ApprovalRevokeFrame โ€” one ask can receive both.
20119
+ allOf:
20120
+ - $ref: '#/components/schemas/ApprovalFrameEnvelope'
20121
+ - type: object
20122
+ required: [type]
20123
+ properties:
20124
+ type: { type: string, enum: [approval_complete] }
20125
+ Event_session_titled:
20126
+ description: >
20127
+ server >= 7.133.0 โ€” the session title was WRITTEN (contract ยง4a-ttl): the title generator wrote the title into the session row and minted this frame on THE RUN'S LIVE STREAM afterwards.
20128
+ ๐Ÿ”ด LIVE-ONLY, NEVER STORED: the session row (`GET /v1/sessions` list row `title`) is the single source of the title; this frame only says "the row just changed". It is not ledgered
20129
+ (GET /v1/runs/:id/events replay never carries it), not part of any replay surface, and has NO `eventId` (no SSE `id:` line, no part in the `Last-Event-ID` cursor). Only a leg holding a live stream
20130
+ receives it (today `POST /v1/tasks/stream`); the sync-JSON leg and the durable background leg structurally cannot. NOT sent when the session already has a title, on a user rename / pin
20131
+ (`PATCH /v1/sessions/{id}` does not go through the generator), with `SESSION_AUTO_TITLE=false`, after `done`, or once the connection is gone. THE FRAME MAY NOT ARRIVE โ€” the title is whatever
20132
+ the session row says: refresh on the frame, read the row on cold start / another window / reconnect. `title` has passed the generator's redaction and cleaning and is the SAME value as the row's
20133
+ (still UNTRUSTED for display โ€” model-authored text).
20134
+ type: object
20135
+ required: [type, sessionId, title]
20136
+ additionalProperties: false
20137
+ properties:
20138
+ type: { type: string, enum: [session_titled] }
20139
+ sessionId: { type: string, description: 'The session whose row was just written.' }
20140
+ title: { type: string, description: 'The title that landed in the session row (redacted and cleaned; bounded by the server''s TITLE_MAX_CHARS).' }
19744
20141
  # โ”€โ”€ resources/*.ts schema backfill, batch 2 (2026-07-28) โ€” the 28-item WIRE_PENDING ratchet zeroed.
19745
20142
  # Each schema below is cross-checked against the server source (file:line cited in its description
19746
20143
  # or in the SDK resource JSDoc it mirrors). Wire objects are OPEN sets (additionalProperties: true).
@@ -19765,6 +20162,10 @@ components:
19765
20162
  sessionId: { type: string, description: '`resolved` frames: which pending settled.' }
19766
20163
  toolCallId: { type: string, description: '`resolved` frames: the settled pending''s bound tool-call id (absent when the pending carried none).' }
19767
20164
  count: { type: integer, description: '`synced` frames: how many pendings the connect-time snapshot carried.' }
20165
+ checkpointId: { type: string, description: '`pending` frames only (server >= 7.133.0): the spread PendingCheckpoint row''s `checkpointId` (key absent when the row has no trusted value โ€” read as "no joinable identity").' }
20166
+ consumerState: { type: string, enum: [absent], description: '`live_pending` frames only (server >= 7.133.0): the spread LivePendingRow''s true-only `consumerState` (no engine leg on this replica is waiting for the ask).' }
20167
+ errorCode: { type: string, description: '`error` frames: the machine code (`STREAM_MAX_DURATION` / `draining` / server >= 7.133.0 `approvals.read_unavailable` โ€” the stream STAYS open, do NOT reconnect on it โ€” / `approvals.read_corrupt` โ€” a corrupt row was read, the server closes the stream after the frame).' }
20168
+ message: { type: string, description: '`error` frames: the human sentence (redacted); never match on it.' }
19768
20169
  hasBidiControls:
19769
20170
  type: boolean
19770
20171
  enum: [true]
@@ -19885,20 +20286,24 @@ components:
19885
20286
 
19886
20287
  FleetHookNotice:
19887
20288
  description: >
19888
- Hook observability payload, TWO kinds (service fleet-bus.ts `HookNotice` minus the wire-stripped
20289
+ Hook observability payload, FOUR kinds (server >= 7.133.0 added `hook_managed_unrun` and `hook_verdict_untrusted`; service fleet-bus.ts `HookNotice` minus the wire-stripped
19889
20290
  ownerScope/ownerSessionId โ€” the payload view of FleetFrame_hook_notice; a discriminated union on
19890
20291
  `kind`, mirroring the SDK type verbatim). Both kinds are pure observe โ€” never block, resume, or
19891
20292
  inject. Visibility is fail-CLOSED: a ?session= subscriber only receives notices provably owned by
19892
- that session. `kind` is a CLOSED two-member set on the server (exhaustive-switch discipline) but
20293
+ that session. `kind` is a CLOSED four-member set on the server (exhaustive-switch discipline) but
19893
20294
  OPEN ON READ: degrade an unknown future kind to a generic observability row, never crash.
19894
20295
  oneOf:
19895
20296
  - $ref: '#/components/schemas/FleetHookDecisionUnavailable'
19896
20297
  - $ref: '#/components/schemas/FleetHookNonBlockingFailure'
20298
+ - $ref: '#/components/schemas/FleetHookManagedUnrun'
20299
+ - $ref: '#/components/schemas/FleetHookVerdictUntrusted'
19897
20300
  discriminator:
19898
20301
  propertyName: kind
19899
20302
  mapping:
19900
20303
  hook_decision_unavailable: '#/components/schemas/FleetHookDecisionUnavailable'
19901
20304
  hook_non_blocking_failure: '#/components/schemas/FleetHookNonBlockingFailure'
20305
+ hook_managed_unrun: '#/components/schemas/FleetHookManagedUnrun'
20306
+ hook_verdict_untrusted: '#/components/schemas/FleetHookVerdictUntrusted'
19902
20307
 
19903
20308
  FleetHookDecisionUnavailable:
19904
20309
  type: object
@@ -19933,7 +20338,7 @@ components:
19933
20338
  reason:
19934
20339
  # ๐Ÿ”ด NO `enum` โ€” same open-on-read ruling as kind 1 (SDK type is `... | (string & {})`).
19935
20340
  type: string
19936
- description: 'Machine-branchable failure shape. CLOSED server-side, OPEN ON READ โ€” KNOWN WORDS TODAY: exit_nonzero | timeout | spawn_failed | bad_json.'
20341
+ description: 'Machine-branchable failure shape. CLOSED server-side, OPEN ON READ โ€” KNOWN WORDS TODAY: exit_nonzero | timeout | spawn_failed | bad_json | matcher_over_budget (server >= 7.133.0: the group matcher / the entry `if` name part did not finish evaluating within its 1000 ms budget on this name โ€” NOT a broken hook script but an unevaluable selection condition; the server treats it as selected, the entry still runs; ONE frame per group, `if` one per entry, no exitCode / stderr).'
19937
20342
  hookName: { type: string, description: 'Hook identity (statusMessage, else command/URL/prompt; redacted + bounded, http entries stripped of query/fragment). Always sent.' }
19938
20343
  entryType: { type: string, description: 'command | http | prompt | agent โ€” without it, hookName''s nature is a guess. Closed server-side, open on read. Always sent.' }
19939
20344
  exitCode: { type: integer, description: 'Absent for timeout/spawn_failed (no exit code exists โ€” the server never mints a legit-looking 0).' }
@@ -19941,6 +20346,57 @@ components:
19941
20346
  stderr: { type: string, description: 'Redacted + truncated stderr digest (512-char cap incl. marker). Empty stderr => absent.' }
19942
20347
  detail: { type: string, description: 'Redacted supplement (e.g. where an exit-2 had no venue). Additive / tolerate-absent.' }
19943
20348
 
20349
+ FleetHookManagedUnrun:
20350
+ type: object
20351
+ description: >
20352
+ Kind 3 (server >= 7.133.0): a MANAGED hook did not complete โ€” the group carries a `source`; managed groups (`managedPolicy` / `managedPlugin:<name>`) are the organization policy's guard and run in an independent lane
20353
+ after the unmanaged rewrites, on the final input. A synchronous managed entry that gave neither a legal verdict (exit 0 with no verdict / a closed-set verdict) nor a legal veto (exit 2 / `decision:"block"` /
20354
+ `continue:false`) is "unrun": on `PreToolUse` the conclusion folds to `ask` when nothing vetoed (a person in the loop; unmanaged allows are not trusted; any phase's veto is still the last vote), on other events the
20355
+ conclusion is unchanged โ€” but this frame is ALWAYS sent. One frame per unrun managed entry. The frame does NOT assert how the call ended (read the tool / approval frames).
20356
+ required: [kind, event, reason, source, hookName, entryType]
20357
+ additionalProperties: true
20358
+ properties:
20359
+ kind: { type: string, enum: [hook_managed_unrun] }
20360
+ event: { type: string, description: 'Which hook event. On PreToolUse this entry sets the managed lane: with no veto the call folds to `ask`.' }
20361
+ reason:
20362
+ # NO `enum` โ€” same open-on-read ruling as kinds 1 and 2.
20363
+ type: string
20364
+ description: >-
20365
+ Why it did not complete. CLOSED on the server (thirteen words), OPEN ON READ โ€” KNOWN WORDS TODAY: matcher_invalid | llm_carrier_unavailable | llm_cap | powershell_unsupported | if_unevaluable | budget_exhausted |
20366
+ spawn_failed | timeout | exit_nonzero | bad_json | decision_out_of_set | evidence_unavailable | rewrite_unsettled. `input_truncated` / `matcher_over_budget` are NOT in this kind (they belong to kind 4).
20367
+ source: { type: string, description: 'The group''s source word (`managedPolicy` or `managedPlugin:<name>`; the name is trace only, never a criterion).' }
20368
+ hookName: { type: string, description: 'Hook identity (same rule as kind 2); redacted + bounded.' }
20369
+ entryType: { type: string, description: 'command | http | prompt | agent โ€” closed server-side, open on read.' }
20370
+ toolName: { type: string, description: 'Present on tool events.' }
20371
+ detail: { type: string, description: 'Redacted supplement.' }
20372
+
20373
+ FleetHookVerdictUntrusted:
20374
+ type: object
20375
+ description: >
20376
+ Kind 4 (server >= 7.133.0): a hook's VERDICT IS UNTRUSTED โ€” one rule: an entry that did not get the complete payload, or was pulled in BY SELECTION by an over-budget matcher, has its VETO count (deny / exit 2 /
20377
+ `decision:"block"` / `continue:false` / ask) and its ALLOW and `updatedInput` NOT count. The rule holds on both lanes, so there is one frame; reason, lane and identity are machine fields. Emission condition per lane:
20378
+ managed = the entry ran with an untrusted cause (sent whatever it judged; `PreToolUse` then folds to `ask` when nothing vetoed); unmanaged = it gave an allow or a real rewrite (an identity echo does not count) that was
20379
+ dropped. Today only on `PreToolUse` (the event with an allow word). THE FRAME DOES NOT ASSERT HOW THE CALL ENDED: a managed entry that itself vetoes ends in deny with the frame sent; another trusted allow in the group still
20380
+ lets the call through โ€” read the call's own result. NEVER infer a call outcome from any hook_notice frame.
20381
+ required: [kind, event, reason, lane, hookName, entryType]
20382
+ additionalProperties: true
20383
+ properties:
20384
+ kind: { type: string, enum: [hook_verdict_untrusted] }
20385
+ event: { type: string, description: 'Which hook event (today always PreToolUse).' }
20386
+ reason:
20387
+ # NO `enum` โ€” same open-on-read ruling as kinds 1โ€“3.
20388
+ type: string
20389
+ description: >-
20390
+ Why the verdict is untrusted. CLOSED on the server (two words), OPEN ON READ โ€” KNOWN WORDS TODAY: input_truncated (the entry did not get the full payload: `tool_input` over the cap was replaced by a truncation marker, or a prompt /
20391
+ agent entry''s whole payload text was cut at the same cap) | matcher_over_budget (the group matcher / entry `if` name part ran past its budget and the entry ran BY SELECTION; the same moment also sends a kind-2 frame
20392
+ `matcher_over_budget`).
20393
+ lane: { type: string, description: 'The lane the entry sits in, read off the source table: `managedPolicy` / `managedPlugin:*` => `managed`, everything else and absent => `unmanaged`. Closed (two words) server-side, open on read. Read THIS field, never re-derive from `source`.' }
20394
+ hookName: { type: string, description: 'Hook identity (same rule as kind 2); redacted + bounded.' }
20395
+ entryType: { type: string, description: 'command | http | prompt | agent โ€” closed server-side, open on read.' }
20396
+ toolName: { type: string, description: 'Present on tool events.' }
20397
+ source: { type: string, description: 'The source word the group declared (both lanes give it; ABSENT = the group declared none, so only unmanaged). Trace only, never a criterion.' }
20398
+ detail: { type: string, description: 'Redacted supplement (human-facing; machines read `reason` / `lane`).' }
20399
+
19944
20400
  BakeStatus:
19945
20401
  # ๆ–ฐ(census ๆ‰น2 ๅ››ๆฎต)โ€”โ€”server `BakeStatus`(plugins/store-contracts.ts), the COARSE lifecycle
19946
20402
  # bakeView/claimResponse/bakesCreate all key on.
@@ -20450,6 +20906,11 @@ components:
20450
20906
  K-24 (server >= 7.104.0 / S-724) โ€” does this leg capture file history (`on` / `disabled`, OPEN word). EFFECTIVE
20451
20907
  HALF ONLY โ‡’ ALWAYS ABSENT ON THIS ENDPOINT; the deployment-level reading is `capabilities.fileHistoryCapture`.
20452
20908
  Full per-key contract: see Event_wiring_manifest.fileHistory.
20909
+ permissionMode:
20910
+ allOf: [{ $ref: '#/components/schemas/WiringManifestPermissionMode' }]
20911
+ description: >-
20912
+ core 7.53.0, server >= 7.133.0 โ€” the leg's permission-mode receipt (section shape WiringManifestPermissionMode). EFFECTIVE HALF ONLY โ‡’ this (static) face NEVER carries it, and absence here carries no
20913
+ information; the reading holds only on a live `wiring_manifest` frame.
20453
20914
  instructions:
20454
20915
  allOf: [{ $ref: '#/components/schemas/WiringManifestInstructions' }]
20455
20916
  description: >
@@ -20521,6 +20982,24 @@ components:
20521
20982
  type: array
20522
20983
  description: 'The selected chain member(s), spelled as the chain spells them (relative to the cwd); `[]` = the chain was walked, nothing was selected.'
20523
20984
  items: { type: string, enum: ['CLAUDE.md', '.claude/CLAUDE.md', 'AGENTS.md', '.claude/AGENTS.md'] }
20985
+ WiringManifestPermissionMode:
20986
+ # server 7.133.0 / core 7.53.0:ๅŒไธ€ๆฎตๅฝข่ขซ live ๅธง(ไธคๅผ ่„ธ)ใ€้™ๆ€่ฏŠๆ–ญ้ขใ€`POST /v1/tasks` ็š„ `wiringManifest` ๅ›žๆ‰งไธŽ่ดฆๆœฌ่กŒๅผ•็”จ โ‡’ ๅ…ทๅๅ•ๆบ(`spec-field-drift-gate` ไนŸๆŒ‰ใ€Œๅฏผๅ‡บ็š„ interface ๅฟ…ๆœ‰ๅŒๅ schemaใ€้€ผๅ‡บๅฎƒ)ใ€‚
20987
+ type: object
20988
+ additionalProperties: false
20989
+ description: >
20990
+ The `wiring_manifest.permissionMode` section (core 7.53.0 `PermissionModeReceipt`; server >= 7.133.0 projects it on BOTH faces โ€” tenant and operator โ€” and the same section rides the `wiringManifest`
20991
+ receipt of the `POST /v1/tasks` 200 body and the `/events` ledger row; the server judges SHAPE only: `effective` a non-empty string, `requested` / `foldedBy` absent or non-empty strings, else the whole section
20992
+ is dropped). The engine gives the same three-member receipt on EVERY mode question: `requested` = the mode word the caller declared (ABSENT = declared nothing โ€” a root inherits `default`, a child inherits its
20993
+ parent); `effective` = the mode the leg actually runs under (the request clamped under a parent's ceiling, then folded to `default` where the deployment denies it or the classifier face is absent) โ€” ALWAYS present,
20994
+ the shell renders its mode indicator from it; `foldedBy` = PRESENT IFF `requested` and `effective` differ โ€” `operator_deny` (the deployment / organization ceiling denies the mode) / `face_absent` (`auto` needs
20995
+ a classifier face this leg lacks) / `inherited_ceiling` (a delegated child clamped under its parent's ceiling); read OPEN (a fourth word passes through as "folded, cause unknown"). ABSENT section = an older
20996
+ engine / server (<= 7.132.x), not an assertion of the default mode. This is ONE added key on a closed object: a consumer validating `wiring_manifest` strictly against sdk <= 24.0.0's spec fails the whole frame
20997
+ on 7.133.0+ โ€” upgrade to sdk 25.0.0 or relax unknown keys on this section.
20998
+ required: [effective]
20999
+ properties:
21000
+ requested: { type: string, description: 'The mode word the caller declared (a PermissionMode word today; read open). ABSENT = declared nothing.' }
21001
+ effective: { type: string, description: 'The mode the leg actually runs under (a PermissionMode word today; read open). Always present.' }
21002
+ foldedBy: { type: string, description: 'Present iff `requested` and `effective` differ: `operator_deny` / `face_absent` / `inherited_ceiling` (engine words; read OPEN โ€” an unknown word means "folded, cause unknown").' }
20524
21003
  WiringManifestModEntry:
20525
21004
  # server 7.132.0 / core 7.52.0:ๅ–ไปฃ้’ฉๅญๅธญไฝ่กŒๅฝข(ๅˆ ,้›ถๅˆซๅ)ใ€‚ๅŒไธ€ไปฝ่กŒๅฝข่ขซ live ๅธงไธŽ้™ๆ€่ฏŠๆ–ญ้ขไธคๅค„ๅผ•็”จ โ‡’ ๅ…ทๅ;ไธคๅฝขๅ„่‡ชๅ…ทๅ(โ‘คbis ๅๅ•้€ๅฝขๅฏน่ดฆ)ใ€‚
20526
21005
  description: >
@@ -21050,10 +21529,218 @@ components:
21050
21529
  remember for this session's category (fs-write tools share ONE category: approving Write covers
21051
21530
  Edit/NotebookEdit; other tools are per-tool) โ€” the CC "don't ask again this session" semantic.
21052
21531
 
21532
+ ConfigCatalog:
21533
+ type: object
21534
+ description: >
21535
+ The 200 body of GET /v1/config/catalog (server `ConfigCatalogWire`) โ€” a PROJECTION, left open on read (the producer ships independently and adds keys additively between SDK releases; the request side has no body).
21536
+ required: [catalogVersion, envLaneTiming, restartSlices, centerDomains, summary, rows, retired]
21537
+ additionalProperties: true
21538
+ properties:
21539
+ catalogVersion: { type: integer, enum: [1] }
21540
+ envLaneTiming: { type: string, description: 'The env lane''s uniform timing (`deploy`: env is immutable at runtime, so changing env = changing the deployment). Open on read.' }
21541
+ restartSlices: { type: array, items: { type: string }, description: 'The restart-slice names a centre write can land in (open on read).' }
21542
+ centerDomains: { type: array, items: { type: string }, description: 'The contract package''s centre-configuration domains.' }
21543
+ summary:
21544
+ type: object
21545
+ required: [total, envHeld, secret, centerManaged]
21546
+ additionalProperties: true
21547
+ properties:
21548
+ total: { type: integer }
21549
+ envHeld: { type: integer }
21550
+ secret: { type: integer }
21551
+ centerManaged: { type: integer }
21552
+ rows: { type: array, items: { $ref: '#/components/schemas/ConfigCatalogRow' } }
21553
+ retired: { type: array, items: { $ref: '#/components/schemas/ConfigCatalogRetiredEnv' }, description: 'Tombstoned env names this process refuses to start on (always present, may be empty). A name is never in `rows` and here at once.' }
21554
+ ConfigCatalogRow:
21555
+ type: object
21556
+ description: >
21557
+ One catalog row (server `ConfigCatalogRow`): a deployment knob's static declaration plus this process's effective reading. OPEN on read (projection). The two VERDICT bits are the true-only family:
21558
+ `effectiveValueInvalid` (the effective value fell outside the row's echo set; `effectiveValue` is `null`) and `effectiveValueWithheld` (server >= 7.133.0) โ€” see the property.
21559
+ required: [name, domain, type, semantics, writeLanes, effectiveLane, envHeld, envTiming, effectiveValue, valueClass, danger]
21560
+ additionalProperties: true
21561
+ properties:
21562
+ name: { type: string, description: 'The knob (env) name; the catalog is sorted by it.' }
21563
+ domain: { type: string, description: 'One of the nine configuration groups (store / modelPlane / approval / memory / auth / orchestration / limitsHttp / observability / integrations). Open on read.' }
21564
+ type: { type: string, description: 'Value type: boolean | number | string | enum | csv | json | path | url | secret | opaque. Open on read.' }
21565
+ semantics: { type: string, description: 'Human sentence (true to the read site).' }
21566
+ writeLanes: { type: array, items: { type: string, enum: [env, center] }, description: 'Which lanes can write this knob.' }
21567
+ effectiveLane: { type: string, description: 'The lane actually in force: env | center | default. Open on read.' }
21568
+ envHeld: { type: boolean, description: 'This machine''s env sets it explicitly.' }
21569
+ envTiming: { type: string, description: '`deploy` (changing env = changing the deployment) | `inert` (retired / zero-consumer key). Open on read.' }
21570
+ staticDefault: { type: string }
21571
+ derivedDefaultNote: { type: string }
21572
+ effectiveValue:
21573
+ description: 'This process''s effective DISPLAY value: a scalar / a word list / `null` (unset) / `{ present, length? }` (`secret` / `opaque` rows report presence and length only).'
21574
+ oneOf:
21575
+ - { type: [string, number, boolean, 'null'] }
21576
+ - { type: array, items: { type: string } }
21577
+ - { type: object, required: [present], additionalProperties: true, properties: { present: { type: boolean }, length: { type: integer } } }
21578
+ valueClass: { type: string, description: '`plain` (echoable) | `secret` | `opaque` (the value never goes on the wire). Open on read.' }
21579
+ danger: { type: array, items: { type: string }, description: 'The risk axes of a wrong setting: security | billing | data (open on read).' }
21580
+ center: { $ref: '#/components/schemas/ConfigCatalogRowCenter' }
21581
+ centerDomain: { type: string }
21582
+ centerPath: { type: string }
21583
+ scope: { type: string, description: '`bootstrap-only` = read only at start-up.' }
21584
+ enumValues: { type: array, items: { type: string }, description: 'An `enum` row''s writable word set.' }
21585
+ effectiveValueInvalid:
21586
+ type: boolean
21587
+ enum: [true]
21588
+ description: 'TRUE-ONLY verdict bit: the effective value fell OUTSIDE the row''s own echo set (or a boolean without a declared mapping) โ€” `effectiveValue` is then `null` (echoing an out-of-set value would teach a consumer to write back an illegal word). Enum rows only. ABSENT = this cell has no such matter.'
21589
+ effectiveValueWithheld:
21590
+ type: boolean
21591
+ enum: [true]
21592
+ description: >-
21593
+ server >= 7.133.0, TRUE-ONLY verdict bit on `type: url` rows (present only when true, never `false`): the display mint judged this cell (single value) or any item of it (list) WITHHELD โ€” a part the position rule
21594
+ would have shown (the whole query / a path segment) was NOT shown because it held a credential-shaped piece. The mint owns the verdict; the catalog does not judge again. So the shown value is not the whole value:
21595
+ a catalog page / `sema config show` marks the row ("part of this value is withheld"), and must not read the shown value as "the value is exactly that". ABSENT = nothing withheld (older servers never send it).
21596
+ The standard position rule (userinfo landed, query values become tokens) holds for every row and is NOT a withholding; a value that became a placeholder as a whole means the row's display rule cannot show it
21597
+ (not a hierarchical URL; the two outbound-proxy rows show only scheme://host[:port]) โ€” also not this bit.
21598
+ ConfigCatalogRowCenter:
21599
+ type: object
21600
+ description: 'A catalog row''s centre seat: whether the knob is centre-publishable, who wins, when it takes effect. Present only on rows with a centre seat.'
21601
+ required: [domain, path, precedence, timing, managedNow, effectiveNow]
21602
+ additionalProperties: true
21603
+ properties:
21604
+ domain: { type: string }
21605
+ path: { type: string, description: 'Path in the domain (`a.b` into objects; `xs[].c` into array elements).' }
21606
+ precedence: { type: string, description: '`center-wins` | `env-wins` (open on read).' }
21607
+ timing: { type: string, description: '`hot` | `restart` | `inert` (open on read; not `deploy`).' }
21608
+ restartSlice: { type: string }
21609
+ restartReason: { type: string }
21610
+ managedNow: { type: boolean, description: 'The centre''s latest effective config currently MANAGES this key (no config pipeline => always false).' }
21611
+ effectiveNow: { type: boolean, description: 'The centre write is in force right now (managed AND not held down by an env placeholder).' }
21612
+ ConfigCatalogRetiredEnv:
21613
+ type: object
21614
+ description: 'A tombstoned env name (the contract package''s RETIRED_ENVS wire projection): a name this process refuses to start on. `renamed` carries only `replacement`; `replaced` carries `replacement` + `reason` (how the value semantics changed); `removed` carries only `reason`.'
21615
+ required: [name, kind]
21616
+ additionalProperties: true
21617
+ properties:
21618
+ name: { type: string }
21619
+ kind: { type: string, description: '`renamed` | `replaced` | `removed` (open on read).' }
21620
+ replacement: { type: string }
21621
+ reason: { type: string }
21622
+ EntryProblem:
21623
+ type: object
21624
+ description: >
21625
+ One ENTRY PROBLEM (server >= 7.133.0): a refusal body's `detail.problems[]` element and an accept-time `warnings[]` element share this shape. `code` = the refusal code the problem belongs to (in `warnings[]`: the engine's
21626
+ reading code for that entry); `field` = the coordinate of the list holding the entry in the request body (`settings.permissions.allow`, `skills[i].allowedTools` โ€ฆ); `entry` = the entry string (passed through the server''s
21627
+ redaction mouth โ€” not a guarantee of your original bytes); `reason` = the machine reason โ€” mostly the ENGINE''s word verbatim (why it read the entry as nothing); the server mints two of its own, `not_in_roster` (accept-time
21628
+ warnings only: the name in an allow-side selector list is not in the roster and is no protocol namespace name โ€” the engine accepts it anyway) and `home_unresolvable` (settings-rule 422: a `~/` path rule on a deployment that cannot
21629
+ resolve the execution environment''s home); and `below_trust_floor` (the caller''s trust tier is below the member''s admission floor โ€” see ErrorResponse.detail.callerTrust). Read OPEN.
21630
+ required: [code, field, entry, reason]
21631
+ additionalProperties: false
21632
+ properties:
21633
+ code: { type: string }
21634
+ field: { type: string }
21635
+ entry: { type: string }
21636
+ reason: { type: string }
21637
+ PrincipalInboxMessage:
21638
+ type: object
21639
+ description: 'The request body of POST /v1/principal-inbox/messages: a CLOSED three-key shape (`from` is minted by the server from the verified identity and cannot be sent).'
21640
+ required: [to, content]
21641
+ additionalProperties: false
21642
+ properties:
21643
+ to: { type: string, minLength: 1, description: 'The recipient principal string. Must belong to the sender''s organization (the membership judgement reads the organization directory); a failed judgement is 404 `org_delivery.not_member`, byte-identical to "no such person".' }
21644
+ content: { type: string, minLength: 1, maxLength: 32000, description: 'The letter body (characters; over 32000 => 400 `request.field_invalid`).' }
21645
+ urgent: { type: boolean, description: 'The urgent flag on the envelope (absent = ordinary).' }
21646
+ PrincipalInboxReceipt:
21647
+ type: object
21648
+ description: 'The 200 receipt: the letter was appended. `receiptId` = the gate-minted delivery receipt id (the envelope''s `gateReceiptId` carries the same value).'
21649
+ required: [receiptId]
21650
+ additionalProperties: false
21651
+ properties:
21652
+ receiptId: { type: string }
21653
+ OrgDeliveryRefusalCode:
21654
+ type: string
21655
+ enum: [org_delivery.not_member, org_delivery.rate_limited, org_delivery.inbox_full, org_delivery.recipient_tombstoned]
21656
+ description: >
21657
+ The four cross-principal delivery refusal codes (server >= 7.133.0; the engine's closed `ORG_DELIVERY_REFUSAL_CODES`, one frozen sentence each โ€” machines read the CODE): `org_delivery.not_member` (404),
21658
+ `org_delivery.rate_limited` (429), `org_delivery.inbox_full` (409), `org_delivery.recipient_tombstoned` (410). See POST /v1/principal-inbox/messages.
21659
+ ToolApprovalReadState:
21660
+ type: string
21661
+ enum: [pending, decided, parked, void]
21662
+ description: >
21663
+ `state` of GET /v1/tool-approvals/{id} (server >= 7.133.0), a CLOSED four-word set โ€” read it exhaustively: `pending` = a live registration on this replica is waiting (answer door open);
21664
+ `decided` = the row is DECIDED; `parked` = PARKING / PARKED (a late answer redeems the parked card); `void` = DENIED / VOID (nobody decided it, the answer has nowhere to land).
21665
+ ApprovalsStreamReadErrorCode:
21666
+ type: string
21667
+ enum: [approvals.read_unavailable, approvals.read_corrupt]
21668
+ description: >
21669
+ The `errorCode` of the two READ-FAILURE `error` frames on GET /v1/approvals/stream (server >= 7.133.0). `approvals.read_unavailable` = this tick's approval read failed (the store was momentarily
21670
+ unreadable) โ€” the stream STAYS open and the next tick reads again; it is NOT a terminal frame and NOT a reconnect signal (do not reconnect on it, and do not read it as "nothing pending").
21671
+ `approvals.read_corrupt` = a corrupt row was read โ€” the server closes the stream after the frame; deal with that row before reopening. Branch on the code, never match the message.
21672
+ Older servers (<= 7.132.x) answer a read failure with nothing but heartbeats.
21673
+ ToolApprovalReadProjection:
21674
+ type: object
21675
+ description: >
21676
+ The row projection shared by the three non-`pending` arms of GET /v1/tool-approvals/{id} (server >= 7.133.0): a SUBSET of the `livePending[]` row keys โ€” one reader, one key set. `frame` = the disconnect-replay
21677
+ opening frame (an `approval_request` frame; ABSENT when the card cannot be read back โ€” never invented).
21678
+ required: [approvalId, ts, expiresAtMs]
21679
+ additionalProperties: true
21680
+ properties:
21681
+ approvalId: { type: string }
21682
+ ts: { type: number, description: 'Registration time (ms epoch).' }
21683
+ expiresAtMs: { type: number, description: 'Absolute window deadline (cast once, never recomputed).' }
21684
+ sessionId: { type: string }
21685
+ toolName: { type: string }
21686
+ frame: { $ref: '#/components/schemas/ApprovalRequestFrame' }
21687
+ ToolApprovalReadBody:
21688
+ description: >
21689
+ The 200 body of GET /v1/tool-approvals/{id} (server >= 7.133.0) โ€” a four-arm union discriminated by `state` (closed, read it exhaustively): `pending` = a live registration on this replica is waiting
21690
+ (the `livePending[]` row + `state`; answer door open); `decided` = the row is DECIDED (`decision` / `decidedAtMs` / `decisionNote?`, the same three keys as the late-replay body); `parked` = PARKING / PARKED
21691
+ (a late answer redeems the parked card; `decidePath?` points at the durable decision path); `void` = DENIED / VOID (nobody decided it, the answer has nowhere to land; `cause?` is the same cause word as the
21692
+ late-answer 404's, see ToolApprovalNotPendingCause).
21693
+ oneOf:
21694
+ - $ref: '#/components/schemas/ToolApprovalReadPending'
21695
+ - $ref: '#/components/schemas/ToolApprovalReadDecided'
21696
+ - $ref: '#/components/schemas/ToolApprovalReadParked'
21697
+ - $ref: '#/components/schemas/ToolApprovalReadVoid'
21698
+ discriminator:
21699
+ propertyName: state
21700
+ mapping:
21701
+ pending: '#/components/schemas/ToolApprovalReadPending'
21702
+ decided: '#/components/schemas/ToolApprovalReadDecided'
21703
+ parked: '#/components/schemas/ToolApprovalReadParked'
21704
+ void: '#/components/schemas/ToolApprovalReadVoid'
21705
+ ToolApprovalReadPending:
21706
+ allOf:
21707
+ - $ref: '#/components/schemas/LivePendingRow'
21708
+ - type: object
21709
+ required: [state]
21710
+ properties:
21711
+ state: { type: string, enum: [pending] }
21712
+ ToolApprovalReadDecided:
21713
+ allOf:
21714
+ - $ref: '#/components/schemas/ToolApprovalReadProjection'
21715
+ - type: object
21716
+ required: [state, decision, decidedAtMs]
21717
+ properties:
21718
+ state: { type: string, enum: [decided] }
21719
+ decision: { type: string, enum: [allow, deny] }
21720
+ decidedAtMs: { type: integer }
21721
+ decisionNote: { type: string }
21722
+ ToolApprovalReadParked:
21723
+ allOf:
21724
+ - $ref: '#/components/schemas/ToolApprovalReadProjection'
21725
+ - type: object
21726
+ required: [state]
21727
+ properties:
21728
+ state: { type: string, enum: [parked] }
21729
+ decidePath: { type: string, description: 'The durable decision path for this ask when it can be built.' }
21730
+ ToolApprovalReadVoid:
21731
+ allOf:
21732
+ - $ref: '#/components/schemas/ToolApprovalReadProjection'
21733
+ - type: object
21734
+ required: [state]
21735
+ properties:
21736
+ state: { type: string, enum: [void] }
21737
+ cause:
21738
+ allOf: [{ $ref: '#/components/schemas/AskRowEndingCause' }]
21739
+ description: 'The row''s recorded ending cause (absent for a legacy row with no cause written โ€” never invented).'
21053
21740
  ToolApprovalRespondApplied:
21054
21741
  type: object
21055
21742
  description: >
21056
- The APPLIED arm of the 200 ack of POST /v1/tool-approvals/{approvalId}/respond (server tool-approval.ts โ€” exact
21743
+ The APPLIED arm of the 200 ack of POST /v1/tool-approvals/{id}/respond (server tool-approval.ts โ€” exact
21057
21744
  shape, `decision` echoed): the decision settled a live gate. Owner-gated with a 404 and NO existence oracle: non-owner and
21058
21745
  unknown/settled/expired/wrong-replica ids are indistinguishable. See ToolApprovalRespondAck for the replayed arm.
21059
21746
  # ๅฐ้—ญ(census ๆ‰น2 ็ฌฌไธ‰ๆฎต,2026-07-30):้“ธ้€ ็‚น `tool-approval.ts` ไธ‰ๆ’ๅœจ้”ฎ + ไธคๆกไปถ้”ฎ
@@ -21069,6 +21756,13 @@ components:
21069
21756
  approvalId: { type: string }
21070
21757
  delivery: { type: string, enum: [applied] }
21071
21758
  decision: { $ref: '#/components/schemas/ToolApprovalDecision' }
21759
+ consumerState:
21760
+ type: string
21761
+ enum: [absent]
21762
+ description: >-
21763
+ server >= 7.133.0, TRUE-ONLY: no engine leg on THIS replica was waiting for the ask this answer settled โ€” the same bit, with the same meaning, as `LivePendingRow.consumerState` and the
21764
+ durable decision ack's. Present <=> the answered entry was ADOPTED from the ask ledger at start-up. The decision lands on the row as usual, but nothing running here resumes because of it โ€” read the run
21765
+ surface; do not read the 200 as "the run continued". Absent = a local engine leg was waiting, or an older server.
21072
21766
  rememberApplied:
21073
21767
  type: boolean
21074
21768
  description: >
@@ -21160,7 +21854,7 @@ components:
21160
21854
  ToolApprovalRespondReplayed:
21161
21855
  type: object
21162
21856
  description: >
21163
- The REPLAYED arm of the 200 ack of POST /v1/tool-approvals/{approvalId}/respond: the ask was already DECIDED
21857
+ The REPLAYED arm of the 200 ack of POST /v1/tool-approvals/{id}/respond: the ask was already DECIDED
21164
21858
  (by anyone) when this answer arrived, so the server replays the FIRST decision โ€” `decision` is the row's two-word
21165
21859
  value (`allow_session` was folded to `allow` when it landed), `decidedAtMs` the first decision's clock,
21166
21860
  `decisionNote` only when the row has one, `rememberApplied: false` only when THIS request asked `allow_session`.
@@ -21176,14 +21870,57 @@ components:
21176
21870
  decidedAtMs: { type: integer }
21177
21871
  decisionNote: { type: string }
21178
21872
  rememberApplied: { type: boolean, enum: [false] }
21873
+ noteRecorded: { type: boolean, description: 'server >= 7.133.0 (two doors, one shape): present ONLY when THIS request carried a `note`; `true` <=> the row''s text is verbatim what this request submitted, `false` = not confirmed (another reason is on the row / no store / read-back failed). A late answer''s note never overwrites the first decision''s reason โ€” that is `decisionNote`.' }
21874
+ rulePersisted: { type: boolean, enum: [false], description: 'server >= 7.133.0: present (as `false`) only when this late answer carried `persistRule`: the rule is redeemed on the FIRST decision only, a late answer never saves one.' }
21875
+ ruleRefusal:
21876
+ type: string
21877
+ enum: [rule_lane_unavailable]
21878
+ description: 'with `rulePersisted: false`; on this arm always `rule_lane_unavailable` (the only word the published late-answer path mints).'
21879
+ ToolApprovalRespondParkedRedeemed:
21880
+ type: object
21881
+ description: >
21882
+ The PARKED-REDEEMED arm of the 200 ack of POST /v1/tool-approvals/{id}/respond (server >= 7.133.0; server tool-approval.ts `redeemParkedAsk` /
21883
+ `replayRedeemReceipt`): the card's window had run out and the ask moved to background waiting (PARKED); this late answer โ€” on another replica or after a restart โ€”
21884
+ goes through the EXISTING durable redemption leg (the same CAS as POST /v1/approvals/{sessionId}/decide) instead of the old 404 `tool_approval.not_pending`
21885
+ (`cause: unknown_or_other_replica`). Body = the redemption leg's receipt (ApprovalDecisionResult's keys โ€” `status` / `taskId` / `sessionId` / ... as they come) plus the three keys
21886
+ this door adds: `approvalId` / `askId` (the same 64-hex value) and the discriminant `accepted`. `decision` is the two-word direction on the REPLAY form only (`allow` / `deny`,
21887
+ `allow_session` folded to `allow`); `rememberApplied: false` appears only when this request asked `allow_session` (a parked redemption cannot remember a session grant);
21888
+ `rulePersisted` / `ruleRefusal` / `persistedRule*` are the `persistRule` outcome on the redemption leg (a rule that fails to store NEVER flips the verdict).
21889
+ `idempotent: true` appears only on the REPLAY (the same already-redeemed card answered again returns the first redemption's receipt).
21890
+ CLOSED like its two sibling arms (the census keeps this op's 200 body closed): the key set is ApprovalDecisionResult's own keys (one source per shape, by `$ref`) plus the added ones.
21891
+ required: [approvalId, askId, accepted]
21892
+ additionalProperties: false
21893
+ properties:
21894
+ approvalId: { type: string }
21895
+ askId: { type: string }
21896
+ accepted: { type: string, enum: [parked_redeemed] }
21897
+ status: { $ref: '#/components/schemas/ApprovalDecisionResult/properties/status' }
21898
+ taskId: { $ref: '#/components/schemas/ApprovalDecisionResult/properties/taskId' }
21899
+ sessionId: { $ref: '#/components/schemas/ApprovalDecisionResult/properties/sessionId' }
21900
+ bindingEnforced: { $ref: '#/components/schemas/ApprovalDecisionResult/properties/bindingEnforced' }
21901
+ decision: { type: string, description: 'REPLAY form: `allow` / `deny` (`allow_session` folded to `allow`). FIRST redemption: whatever the redemption leg''s own receipt says (`approve` / `deny` today) โ€” read it open.' }
21902
+ errorCode: { $ref: '#/components/schemas/ApprovalDecisionResult/properties/errorCode' }
21903
+ errorMessage: { $ref: '#/components/schemas/ApprovalDecisionResult/properties/errorMessage' }
21904
+ retriable: { $ref: '#/components/schemas/ApprovalDecisionResult/properties/retriable' }
21905
+ idempotent: { $ref: '#/components/schemas/ApprovalDecisionResult/properties/idempotent' }
21906
+ executionOutcome: { $ref: '#/components/schemas/ApprovalDecisionResult/properties/executionOutcome' }
21907
+ rememberApplied: { type: boolean, enum: [false] }
21908
+ rulePersisted: { $ref: '#/components/schemas/ToolApprovalRespondApplied/properties/rulePersisted' }
21909
+ ruleRefusal: { $ref: '#/components/schemas/ToolApprovalRespondApplied/properties/ruleRefusal' }
21910
+ persistedRule: { $ref: '#/components/schemas/ToolApprovalRespondApplied/properties/persistedRule' }
21911
+ persistedRules: { $ref: '#/components/schemas/ToolApprovalRespondApplied/properties/persistedRules' }
21912
+ persistedRuleAnchors: { $ref: '#/components/schemas/ToolApprovalRespondApplied/properties/persistedRuleAnchors' }
21179
21913
  ToolApprovalRespondAck:
21180
21914
  description: >
21181
- The 200 ack of POST /v1/tool-approvals/{approvalId}/respond โ€” a discriminated union: the decision was APPLIED to a live
21915
+ The 200 ack of POST /v1/tool-approvals/{id}/respond โ€” a discriminated union: the decision was APPLIED to a live
21182
21916
  gate (ToolApprovalRespondApplied, `delivery: "applied"`) or the ask was already decided and the first decision is
21183
- REPLAYED (ToolApprovalRespondReplayed, `idempotent: true`). The discriminant is `idempotent`.
21917
+ REPLAYED (ToolApprovalRespondReplayed, `idempotent: true`), or (server >= 7.133.0, sdk 25.0.0) the answer REDEEMED a PARKED card through the durable redemption
21918
+ leg (ToolApprovalRespondParkedRedeemed, `accepted: "parked_redeemed"`). The discriminants are `delivery` / `idempotent` / `accepted` (a replayed redemption carries
21919
+ BOTH `accepted: "parked_redeemed"` and `idempotent: true` โ€” read `accepted` first).
21184
21920
  oneOf:
21185
21921
  - $ref: '#/components/schemas/ToolApprovalRespondApplied'
21186
21922
  - $ref: '#/components/schemas/ToolApprovalRespondReplayed'
21923
+ - $ref: '#/components/schemas/ToolApprovalRespondParkedRedeemed'
21187
21924
  UsageMetric:
21188
21925
  type: string
21189
21926
  enum: [tasks, tokensIn, tokensOut, costUsd]
@@ -22141,7 +22878,7 @@ components:
22141
22878
 
22142
22879
  AdoptionNotMigratedFace:
22143
22880
  type: string
22144
- enum: [usage-window, cost-quota, rate-limit, approval-exemption-grantor, approval-ask-decision-actor, image-bake-requested-by]
22881
+ enum: [usage-window, cost-quota, rate-limit, approval-exemption-grantor, approval-ask-decision-actor, image-bake-requested-by, permission-rule-approval-history, permission-rule-import-ticket, org-delivery-quota]
22145
22882
  description: >
22146
22883
  FROZEN machine-readable vocabulary of the faces the server deliberately does not migrate. Renaming or
22147
22884
  removing a member is a wire break; adding one is a new behaviour face. Consumers assert POSITIVELY on