@sema-agent/sdk 26.0.0 โ†’ 26.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +48 -0
  2. package/dist/errors.d.ts +53 -3
  3. package/dist/errors.d.ts.map +1 -1
  4. package/dist/errors.js +18 -1
  5. package/dist/errors.js.map +1 -1
  6. package/dist/events.d.ts +28 -2
  7. package/dist/events.d.ts.map +1 -1
  8. package/dist/events.js.map +1 -1
  9. package/dist/index.d.ts +12 -4
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +6 -0
  12. package/dist/index.js.map +1 -1
  13. package/dist/resources/approvals.d.ts +4 -2
  14. package/dist/resources/approvals.d.ts.map +1 -1
  15. package/dist/resources/approvals.js +1 -1
  16. package/dist/resources/approvals.js.map +1 -1
  17. package/dist/resources/fleet.d.ts +4 -0
  18. package/dist/resources/fleet.d.ts.map +1 -1
  19. package/dist/resources/fleet.js.map +1 -1
  20. package/dist/resources/rules.d.ts +1 -1
  21. package/dist/resources/runs.d.ts +4 -1
  22. package/dist/resources/runs.d.ts.map +1 -1
  23. package/dist/resources/runs.js +4 -1
  24. package/dist/resources/runs.js.map +1 -1
  25. package/dist/resources/session-sync.d.ts +9 -2
  26. package/dist/resources/session-sync.d.ts.map +1 -1
  27. package/dist/resources/session-sync.js +9 -2
  28. package/dist/resources/session-sync.js.map +1 -1
  29. package/dist/resources/sessions.d.ts +5 -3
  30. package/dist/resources/sessions.d.ts.map +1 -1
  31. package/dist/resources/sessions.js +5 -3
  32. package/dist/resources/sessions.js.map +1 -1
  33. package/dist/resources/side-query.d.ts +8 -0
  34. package/dist/resources/side-query.d.ts.map +1 -1
  35. package/dist/resources/side-query.js.map +1 -1
  36. package/dist/resources/tasks.d.ts +2 -0
  37. package/dist/resources/tasks.d.ts.map +1 -1
  38. package/dist/resources/tasks.js +2 -0
  39. package/dist/resources/tasks.js.map +1 -1
  40. package/dist/resources/tool-approvals.d.ts +47 -3
  41. package/dist/resources/tool-approvals.d.ts.map +1 -1
  42. package/dist/resources/tool-approvals.js +18 -3
  43. package/dist/resources/tool-approvals.js.map +1 -1
  44. package/dist/settings.d.ts +1 -1
  45. package/dist/sync.d.ts.map +1 -1
  46. package/dist/sync.js +2 -0
  47. package/dist/sync.js.map +1 -1
  48. package/dist/transport.d.ts +5 -2
  49. package/dist/transport.d.ts.map +1 -1
  50. package/dist/transport.js +6 -2
  51. package/dist/transport.js.map +1 -1
  52. package/dist/types.d.ts +104 -19
  53. package/dist/types.d.ts.map +1 -1
  54. package/dist/types.js +3 -0
  55. package/dist/types.js.map +1 -1
  56. package/openapi.yaml +333 -31
  57. package/package.json +2 -3
package/openapi.yaml CHANGED
@@ -2428,7 +2428,7 @@ paths:
2428
2428
  summary: Write the per-session tool-permission rules (tighten-only).
2429
2429
  description: >
2430
2430
  E6 โ€” write the per-session rules. ๐Ÿ”ด TIGHTEN-ONLY for a normal principal: a loosen (remove a deny / widen
2431
- an allowlist or allowDirs) โ†’ 403 `loosen_forbidden` (operator-only). `expectedRev` = optimistic-concurrency
2431
+ an allowlist or allowDirs) โ†’ 403 `loosen_forbidden` (operator-only). ๐Ÿ”ด server >= 7.135.0 (engine 7.55.0): the tighten gate is a gate on the row's OPERATOR PART (`operatorRules`), not on the whole row โ€” a non-operator (the session's owner) can take back what NON-operator writes added, but not what an operator wrote (moving anything inside the operator part, even an entry both sides wrote, answers 403 `loosen_forbidden`; the row stays). <= 7.134.x: a non-operator write was subtract-only against the whole row. Read the CODE (the sentence changed: `a non-operator session-rule write may not loosen what an operator wrote; refused: โ€ฆ` vs `โ€ฆ may only tighten โ€ฆ`) โ€” never the sentence. `expectedRev` = optimistic-concurrency
2432
2432
  CAS; a stale rev โ†’ 409 `conflict`. Each rule field must be `string[]` and the body carries only the three buckets (`toolAllow` / `toolDeny` / `allowDirs`; any other
2433
2433
  key, including the retired `commandAllow` / `commandDeny`, โ†’ 400 `rules.invalid_shape` on server >= 7.129.0) โ†’ malformed body 400. ownerless
2434
2434
  session โ†’ 409; non-owner โ†’ 404. 501 without a session-policy store.
@@ -2456,6 +2456,9 @@ paths:
2456
2456
  # write judge stamps the current grammar). `grammar` newer than the engine's โ‡’ `400 rules.invalid_shape`.
2457
2457
  rev: { type: integer, minimum: 0 }
2458
2458
  grammar: { type: integer, minimum: 0 }
2459
+ # server >= 7.135.0 (engine 7.55.0): a stored body echoed back (GET โ†’ edit โ†’ PUT) may also carry its `operatorRules`; on THIS door the echoed part is ALWAYS ignored โ€” the route reads the value through the write reader
2460
+ # (its product carries no `rev`) and the store judges it again: over an existing row the STORED row's operator part is kept, over no row an empty stamped part is minted. Do not rely on an echo to move the operator line โ€” send the new value without `operatorRules`.
2461
+ operatorRules: { $ref: '#/components/schemas/SessionRulesOperatorPart' }
2459
2462
  expectedRev: { type: integer, minimum: 0, description: CAS โ€” reject if the stored rev differs. }
2460
2463
  responses:
2461
2464
  '200':
@@ -2492,7 +2495,8 @@ paths:
2492
2495
  schema: { $ref: '#/components/schemas/ErrorResponse' }
2493
2496
  '401': { $ref: '#/components/responses/Unauthorized' }
2494
2497
  '403':
2495
- description: 403 `loosen_forbidden` โ€” a non-operator write tried to relax the rules (tighten-only).
2498
+ description: >-
2499
+ 403 `loosen_forbidden` โ€” a non-operator write tried to relax what an OPERATOR wrote (server >= 7.135.0; <= 7.134.x: relax the rules at all, tighten-only). Branch on the code, never the sentence (it changed with 7.135.0).
2496
2500
  content:
2497
2501
  application/json:
2498
2502
  schema: { $ref: '#/components/schemas/ErrorResponse' }
@@ -2581,7 +2585,7 @@ paths:
2581
2585
  narrow it, switch with a default arm; `httpStatus` 401 is a re-authorize door); `reason` is HUMAN TEXT
2582
2586
  (server-redacted, length-bounded) and must NOT be parsed.
2583
2587
  Gates, the same family as steer / capture-optout: 501 `capability.run_store_required` (no durable run
2584
- ledger) ยท 501 `capability.session_ownership_required` (no `sessionStorage.ownerOf`) ยท 400
2588
+ ledger) ยท 501 `capability.session_ownership_required` (no `sessionStorage.headOf`; <= 7.134.x: `ownerOf`) ยท 400
2585
2589
  `request.path_malformed` (bad percent-encoding in the id) ยท 404 `not_found.session` โ€” unknown session
2586
2590
  AND someone else's session answer the SAME code and SAME message (deliberate: no existence oracle); an
2587
2591
  EXPLICIT operator may re-dial any tenant's session ยท 422 `steering.invalid_content` ยท 409
@@ -2647,7 +2651,7 @@ paths:
2647
2651
  the model's description, falling back to the launch command text (content family: redacted, cut at 500
2648
2652
  chars). At most 500 rows (core `list()`'s own cap). ๐Ÿ”ด IN-PROCESS: only rows on THIS replica.
2649
2653
  Zero model work: not billable, not refused by drain. Gates (same family as `mcp/reconnect`): 501
2650
- `capability.session_ownership_required` (no `sessionStorage.ownerOf`) ยท 404 `not_found.session` โ€” unknown
2654
+ `capability.session_ownership_required` (no `sessionStorage.headOf`; <= 7.134.x: `ownerOf`) ยท 404 `not_found.session` โ€” unknown
2651
2655
  session AND someone else's session answer the SAME code and SAME message (no existence oracle); an EXPLICIT
2652
2656
  operator may read any session ยท 400 `request.path_malformed`. Probe with `capabilities.background.listFace`;
2653
2657
  ABSENT key = an older server, where this path is 404 `not_found.route` (same status, different code).
@@ -2920,6 +2924,14 @@ paths:
2920
2924
  '401': { $ref: '#/components/responses/Unauthorized' }
2921
2925
  '404': { $ref: '#/components/responses/NotFound' }
2922
2926
  '501': { $ref: '#/components/responses/NotImplemented' }
2927
+ '503':
2928
+ description: >-
2929
+ server >= 7.135.0: `503 state.file_history_unreadable` โ€” this session's file history cannot be read WHOLE (a boundary torn / unreadable, the tracked set unreadable); nothing is exported this time. ๐Ÿ”ด It is NOT "no history":
2930
+ with nothing to export the manifest still answers `200` with `fileHistory: null`. May carry `permanence` (`transient` / `terminal`; absent = the store did not judge โ€” see `ErrorResponse.permanence`). Older servers read an
2931
+ unreadable history as "none" (manifest 200 / blob 404).
2932
+ content:
2933
+ application/json:
2934
+ schema: { $ref: '#/components/schemas/ErrorResponse' }
2923
2935
 
2924
2936
  /v1/sessions/{sessionId}/sync/entries:
2925
2937
  parameters:
@@ -2994,6 +3006,14 @@ paths:
2994
3006
  '401': { $ref: '#/components/responses/Unauthorized' }
2995
3007
  '404': { $ref: '#/components/responses/NotFound' }
2996
3008
  '501': { $ref: '#/components/responses/NotImplemented' }
3009
+ '503':
3010
+ description: >-
3011
+ server >= 7.135.0: `503 state.file_history_unreadable` โ€” the history cannot be read WHOLE, so the server cannot tell whether this hash is one the session references โ€” it does NOT answer "no such blob" (that is `404 not_found.blob`) and gives no bytes. ๐Ÿ”ด It is NOT "no history":
3012
+ with nothing to export the manifest still answers `200` with `fileHistory: null`. May carry `permanence` (`transient` / `terminal`; absent = the store did not judge โ€” see `ErrorResponse.permanence`). Older servers read an
3013
+ unreadable history as "none" (manifest 200 / blob 404).
3014
+ content:
3015
+ application/json:
3016
+ schema: { $ref: '#/components/schemas/ErrorResponse' }
2997
3017
 
2998
3018
  /v1/sessions/{sessionId}/sync/blobs/{hash}:
2999
3019
  parameters:
@@ -3091,12 +3111,15 @@ paths:
3091
3111
  description: >
3092
3112
  2c session-sync PUSH Phase A โ€” a SMALL metadata body (`entryIds, fileHistory?, policy, anchors, resolution?, logDigest?, title?, pinned?`); the
3093
3113
  entries stream in Phase B. The cloud classifies (ยง7), pre-checks blob presence (a missing referenced hash โ†’
3094
- 422 `missing_blob` โ€” PUT it first), guards the import lease + any active run (409), then mints a stagingId.
3114
+ 422 `missing_blob` โ€” PUT it first; server >= 7.135.0 answers it in this FIRST phase โ€” zero staging, zero commit, ahead of the `identical` replay arm โ€” and
3115
+ a deployment whose `capabilities.sessionSyncFileHistory` is false, e.g. `DB_BACKEND=local`, takes a `fileHistory` that references blobs as a first-phase 422 too: the conversation does not land either; <= 7.134.x answered it after the conversation
3116
+ had committed), guards the import lease + any active run (409), then mints a stagingId. ๐Ÿ”ด server >= 7.135.0: a file-history replay fault (the replay arms of `identical` re-push and of the commit) answers `422 adopt_failed`
3117
+ (+ optional `permanence`, see `ErrorResponse.permanence`; <= 7.134.x: `unsupported_format`) โ€” distinct from `missing_blob`.
3095
3118
  The file history rides `fileHistory` (a `FileHistoryExport` envelope, `null` / absent = none) and every blob it references must have been
3096
3119
  `PUT โ€ฆ/sync/blobs/{hash}` first (the `snapshots` key of servers <= 7.51 is outside the support range; a supported server ignores it and sdk 26.0.0
3097
3120
  no longer declares it). The SDK carries `fileHistory` in `pushSessionBundle` / `importSessionBundle`; a hand-rolled
3098
- push must send `fileHistory` itself, and only to a peer whose `capabilities.sessionSyncFileHistory` is not false (server >= 7.116.0 โ€” else the
3099
- commit 422s `missing_blob`).
3121
+ push must send `fileHistory` itself, and only to a peer whose `capabilities.sessionSyncFileHistory` is not false (server >= 7.116.0 โ€” else
3122
+ `422 missing_blob` in the first phase from server >= 7.135.0; after the commit on <= 7.134.x).
3100
3123
  `identical` โ†’ `{ relation:"identical", basis, payloadVerified }` with NO stagingId (skip Phase B);
3101
3124
  `basis` is `"entry-ids+digest"` when the caller supplied a comparable digest and it matched, else
3102
3125
  `"entry-ids"` (id-set equality only) โ€” see the `ImportStaged` schema, which has carried these two
@@ -3169,7 +3192,12 @@ paths:
3169
3192
  content:
3170
3193
  application/json:
3171
3194
  schema: { $ref: '#/components/schemas/SyncConflict' }
3172
- '422': { $ref: '#/components/responses/UnprocessableEntity' }
3195
+ '422':
3196
+ description: >-
3197
+ 422 โ€” `missing_blob` (first phase from server >= 7.135.0), `restore_failed`, and (server >= 7.135.0) `adopt_failed` (+ optional `permanence`) for the file-history replay; `invalid_entries` etc. as before. Branch on `errorCode`.
3198
+ content:
3199
+ application/json:
3200
+ schema: { $ref: '#/components/schemas/ErrorResponse' }
3173
3201
  '501': { $ref: '#/components/responses/NotImplemented' }
3174
3202
  '503': { $ref: '#/components/responses/Unauthorized' } # (declared 2026-08-01) E6 ๅ…„ๅผŸๅฎˆๅซ:ๆ—  service token + ๆœชๅผ€ ALLOW_UNAUTHED_WRITES ๆ—ถ,่ฟ™ๆก import ๆไบค่…ฟไธŽ `PUT โ€ฆ/policy` ๅ†™็š„ๆ˜ฏๅŒไธ€ไธช policy store โ‡’ ๅŒ้—จๅŒ็  `auth.service_token_required`(server 3.15.0 ่ตท)
3175
3203
 
@@ -3211,7 +3239,8 @@ paths:
3211
3239
  '400': { $ref: '#/components/responses/BadRequest' }
3212
3240
  '401': { $ref: '#/components/responses/Unauthorized' }
3213
3241
  '403':
3214
- description: 403 `loosen_forbidden` โ€” a post-commit policy replay tried to relax the rules (tighten-only).
3242
+ description: >-
3243
+ 403 `loosen_forbidden` โ€” a post-commit policy replay tried to relax what an operator wrote (server >= 7.135.0; <= 7.134.x: tighten-only against the whole row). A non-operator replay keeps each record's own operator part; an operator replay makes the imported row the operator's.
3215
3244
  content:
3216
3245
  application/json:
3217
3246
  schema: { $ref: '#/components/schemas/ErrorResponse' }
@@ -3259,7 +3288,14 @@ paths:
3259
3288
  server >= 7.134.0: a queue row whose stored body cannot be read (the store's refusal contract โ€” a retry fails the same way; an operator has to repair
3260
3289
  or delete that row) answers `500` + `errorCode: "approvals.read_corrupt"` (body `{ error, errorCode }`; the bad row's coordinates stay off the wire) โ€”
3261
3290
  the same judge and the same code as `GET /v1/approvals/stream`. Do NOT retry it as-is (the SDK's GET retry arm exempts the code). Every other read
3262
- failure (connection / timeout / store hiccup) is still `500 internal.error`. Older servers answered the corrupt row with `internal.error`.
3291
+ failure (connection / timeout / store hiccup) is `503 approvals.read_unavailable` from server >= 7.135.0 (below); servers <= 7.134.x answered it `500 internal.error`. Older servers answered the corrupt row with `internal.error`.
3292
+ content:
3293
+ application/json:
3294
+ schema: { $ref: '#/components/schemas/ErrorResponse' }
3295
+ '503':
3296
+ description: >-
3297
+ server >= 7.135.0: `503 approvals.read_unavailable` โ€” the approvals queue could not be read RIGHT NOW (the store hiccuped; transient โ€” the SDK retries it by status). Until a read succeeds, an answer here does NOT mean nothing is pending.
3298
+ Body `{ error, errorCode }`. Older servers answered a read that could not complete with the global fallback `500 internal.error`.
3263
3299
  content:
3264
3300
  application/json:
3265
3301
  schema: { $ref: '#/components/schemas/ErrorResponse' }
@@ -3707,6 +3743,20 @@ paths:
3707
3743
  application/json:
3708
3744
  schema: { $ref: '#/components/schemas/InboxList' }
3709
3745
  '401': { $ref: '#/components/responses/Unauthorized' }
3746
+ '500':
3747
+ description: >-
3748
+ server >= 7.135.0: `500 approvals.read_corrupt` โ€” a queue row's stored body cannot be read (the store's refusal contract: a retry fails the same way; an operator has to repair or delete that row) โ€” the SAME judge and the same code as `GET /v1/approvals`
3749
+ and `GET /v1/approvals/stream`. Do NOT retry it as-is (the SDK's retry arm exempts the code). Every other read failure is `503 approvals.read_unavailable` (below). Older servers: `500 internal.error`.
3750
+ content:
3751
+ application/json:
3752
+ schema: { $ref: '#/components/schemas/ErrorResponse' }
3753
+ '503':
3754
+ description: >-
3755
+ server >= 7.135.0: `503 approvals.read_unavailable` โ€” the approvals queue could not be read RIGHT NOW (the store hiccuped; transient โ€” the SDK retries it by status). Until a read succeeds, an answer here does NOT mean nothing is pending.
3756
+ Body `{ error, errorCode }`. Older servers answered a read that could not complete with the global fallback `500 internal.error`.
3757
+ content:
3758
+ application/json:
3759
+ schema: { $ref: '#/components/schemas/ErrorResponse' }
3710
3760
 
3711
3761
  /v1/assistant/tasks:
3712
3762
  parameters:
@@ -3728,6 +3778,20 @@ paths:
3728
3778
  application/json:
3729
3779
  schema: { $ref: '#/components/schemas/AssistantTaskList' }
3730
3780
  '401': { $ref: '#/components/responses/Unauthorized' }
3781
+ '500':
3782
+ description: >-
3783
+ server >= 7.135.0: `500 approvals.read_corrupt` โ€” a queue row's stored body cannot be read (the store's refusal contract: a retry fails the same way; an operator has to repair or delete that row) โ€” the SAME judge and the same code as `GET /v1/approvals`
3784
+ and `GET /v1/approvals/stream`. Do NOT retry it as-is (the SDK's retry arm exempts the code). Every other read failure is `503 approvals.read_unavailable` (below). Older servers: `500 internal.error`.
3785
+ content:
3786
+ application/json:
3787
+ schema: { $ref: '#/components/schemas/ErrorResponse' }
3788
+ '503':
3789
+ description: >-
3790
+ server >= 7.135.0: `503 approvals.read_unavailable` โ€” the approvals queue could not be read RIGHT NOW (the store hiccuped; transient โ€” the SDK retries it by status). Until a read succeeds, an answer here does NOT mean nothing is pending.
3791
+ Body `{ error, errorCode }`. Older servers answered a read that could not complete with the global fallback `500 internal.error`.
3792
+ content:
3793
+ application/json:
3794
+ schema: { $ref: '#/components/schemas/ErrorResponse' }
3731
3795
 
3732
3796
  /v1/assistant/tasks/{taskId}/resume:
3733
3797
  parameters:
@@ -5921,6 +5985,14 @@ paths:
5921
5985
  schema: { $ref: '#/components/schemas/WorkflowJournalPage' }
5922
5986
  '401': { $ref: '#/components/responses/Unauthorized' }
5923
5987
  '404': { $ref: '#/components/responses/NotFound' }
5988
+ '500':
5989
+ description: >-
5990
+ server >= 7.135.0 (core 7.55.0): `500 workflow.journal_incompatible` โ€” a journal row the engine's judge rejects (a parked row with no checkpoint token, a parked row whose terminal is not paused, a row that does not read as an object โ€” hand-edited / another writer /
5991
+ a newer format). The judgment covers the WHOLE journal, on every backend, on every page (a bad row on another page, or one over 64 KiB, answers the same); the body carries `ordinal` (the first bad row, ascending; `-1` = the refusal names no entry). The run's resume is refused by the engine
5992
+ with the same code. Repair that row from the retained copy โ€” NEVER by deleting it (a missing entry re-runs that step live). A retry changes nothing (the SDK's retry arm exempts the code). <= 7.134.x read such rows back as `200`.
5993
+ content:
5994
+ application/json:
5995
+ schema: { $ref: '#/components/schemas/ErrorResponse' }
5924
5996
  '501': { $ref: '#/components/responses/NotImplemented' }
5925
5997
 
5926
5998
  /v1/workflows/{workflowRunId}/agents/{label}/steer:
@@ -7006,8 +7078,32 @@ components:
7006
7078
  enabled: { type: boolean }
7007
7079
  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).' }
7008
7080
  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.' }
7081
+ skills:
7082
+ type: array
7083
+ items: { $ref: '#/components/schemas/ScenarioSkill' }
7084
+ description: >-
7085
+ server >= 7.135.0, always present there (`[]` when none): the deployment SKILLS this scenario's run mounts (the deployment skills directory UNION what the config source hands over, filtered by scenario; a request's own `skills` are not
7086
+ included) โ€” the same single filter that feeds `TaskSpec`, same group, same order. A client's `/skill-name` completion reads it; naming one goes through `userInvokedSkills`. Entries carry no body and none of a skill's tool lists. On the card
7087
+ but not in the run: `disableSkills: true` (nothing mounted) and a skill whose body exceeds the engine's load cap (naming it answers `config.user_invoked_skill_unknown`). ABSENT = an older server: the list cannot be read โ€”
7088
+ NOT "no skills" (that is `[]`). Bit: `capabilities.scenarioSkills`.
7009
7089
  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".' }
7010
7090
 
7091
+ ScenarioSkill:
7092
+ type: object
7093
+ description: One row of `ScenarioDetail.skills` (server >= 7.135.0). No body, no tool lists.
7094
+ additionalProperties: false
7095
+ required: [name, description, source]
7096
+ properties:
7097
+ name: { type: string }
7098
+ description: { type: string }
7099
+ source: { $ref: '#/components/schemas/ScenarioSkillSource' }
7100
+ ScenarioSkillSource:
7101
+ type: string
7102
+ description: >
7103
+ Where a scenario skill came from (server >= 7.135.0; the server's self-minted word table `scenarioSkillSource`, closed two words): `factory` = the deployment's skills directory (`SKILLS_DIR`);
7104
+ `center` = the config source (the center / the local `config.d` skills list, or a config-declared plugin) โ€” a skill that overrides a same-named factory one by name is also `center`.
7105
+ enum: [factory, center]
7106
+
7011
7107
  RunStatus:
7012
7108
  type: string
7013
7109
  enum: [running, completed, failed, suspended, needs_review, blocked]
@@ -7290,7 +7386,7 @@ components:
7290
7386
  CC Rewind parity (E18). Fork the conversation FROM this user-message SessionTreeEntry.id (non-destructive
7291
7387
  branch via setLeafId). With `restoreFiles: true` โ‡’ "both" (also restore the working tree โ€” the retired spelling
7292
7388
  `rewindFiles` still rides the wire but its restore sense is core's typed migration rejection naming `restoreFiles`). Alone โ‡’
7293
- "conversation". HONORED only where the deployment wired a resume-anchor store + getLeafId (capabilities.resumeAt);
7389
+ "conversation". HONORED only where the deployment wired a resume-anchor store + the session head read `headOf` (<= 7.134.x: `getLeafId`) (capabilities.resumeAt);
7294
7390
  unknown anchor โ‡’ 4xx, capability-off โ‡’ 501. Cannot combine with a durable resume.
7295
7391
  resumeAtMode:
7296
7392
  type: string
@@ -7324,7 +7420,7 @@ components:
7324
7420
  kind of handle as `resumeAt`, resolved through the SAME anchor store โ€” typically the `taskId` from that turn's
7325
7421
  202 receipt (the server keys the user-message anchor by the run's taskId). Server `src/boot/resolve-spec.ts:686-697`:
7326
7422
  `resumeAnchorStore.resolve(sessionId, handle, principal)` โ†’ entry id; unknown handle โ‡’ 404
7327
- `rewind_files_to.unknown_event`; no session โ‡’ 422 `rewind_files_to.no_session`; no anchor store / no getLeafId โ‡’
7423
+ `rewind_files_to.unknown_event`; no session โ‡’ 422 `rewind_files_to.no_session`; no anchor store / no session head read (`headOf`; <= 7.134.x: `getLeafId`) โ‡’
7328
7424
  501. โš ๏ธ The 501 is NOT the capability's whole predicate: `capabilities.rewindFilesTo` also requires the
7329
7425
  file-history store; without it the server does not 501 โ€” core refuses at prepare with
7330
7426
  `rewind.store_unconfigured` (server >= 7.104.0 / S-724: a 409 error body on the synchronous `POST /v1/tasks` when
@@ -7920,6 +8016,15 @@ components:
7920
8016
  combination); `taskWriteFaceOpen` is NOT its reading. Residual (server S-908): the binding is keyed by SESSION and written
7921
8017
  after the run is claimed (a refused request writes nothing); on deployments without a run store two concurrently accepted
7922
8018
  runs on one session are last-writer-wins. Delegated children's shells keep the default scrub.
8019
+ projectMemory:
8020
+ type: string
8021
+ enum: [disabled]
8022
+ description: >
8023
+ server >= 7.135.0 (engine 7.55.0 `TaskSpec.projectMemory`, passed through verbatim): this ONE run does not read the deployment's project context โ€” the engine does not call the deployment's project-context seat, so the project layer,
8024
+ the declared instruction files and the few things they feed are all absent (the engine's own git-status lane, the memory engine, skills / hooks / MCP are unaffected; delegated / background / fork / workflow children inherit it from
8025
+ the parent). The only word is `disabled`; the key ABSENT = call the seat. The leg's `wiring_manifest.projectMemory` reads `disabled` and the server-minted `instructions` section is absent. A deployment without the seat still accepts it
8026
+ (the manifest answers `absent`). Anything else (`off`, a boolean, `null`) โ‡’ a fresh submit answers 400 `request.field_invalid`; a resume replay judges the same and folds into 409 `resume_blocked_by_policy`
8027
+ (`blockedBy: request.field_invalid`). Read `capabilities.projectMemory` beforehand: older servers answer 400 `request.body_shape` (`unknownKeys: ["projectMemory"]`).
7923
8028
  attachments:
7924
8029
  type: object
7925
8030
  description: >
@@ -9596,8 +9701,8 @@ components:
9596
9701
  (`decision: "approve"`, `decidedBy` absent or `person`): on a deny or a `decidedBy: "policy"` decision โ‡’ 400 `request.field_conflict` with
9597
9702
  `detail.kind: "approval_feedback"` and `detail.fields` `["feedback", "decision"]` / `["feedback", "decidedBy"]` (a denial's words go in `reason`);
9598
9703
  a non-string โ‡’ 400 `request.field_invalid`; any value on a direct-door worker โ‡’ 400 `feedback_not_in_proof` (not inside the signed proof); words
9599
- carrying `<system-reminder>` โ‡’ 409 `approval_binding_mismatch` (the engine's sentence; the card stays pending โ€” ask the person to change the words,
9600
- do not auto-refetch and re-decide on the code). The same door serves parked background-agent cards and workflow child cards; the live
9704
+ carrying `<system-reminder>` โ‡’ server >= 7.135.0 (engine 7.55.0): 409 `resume_outcome_invalid` + `field: "feedback"` + `retriable: true` (the engine's sentence naming `feedback`; refused before the CAS, the card stays pending โ€” ask the person to change
9705
+ or drop the words, do not auto-refetch and re-decide on the code); server 7.134.x: 409 `approval_binding_mismatch` (no `field`). The same door serves parked background-agent cards and workflow child cards; the live
9601
9706
  `POST /v1/tool-approvals/{id}/respond` does NOT take it. Servers <= 7.133.x refuse the key (400 `request.body_shape`, `unknownKeys: ["feedback"]`).
9602
9707
 
9603
9708
  Capabilities:
@@ -9807,7 +9912,7 @@ components:
9807
9912
  type: boolean
9808
9913
  description: >-
9809
9914
  Code-only rewind (`TaskRequest.rewindFilesTo`) is wired โ€” mint `routes/capabilities.ts:839` =
9810
- `Boolean(deps.fileHistoryStore && deps.resumeAnchorStore && deps.sessionStorage?.getLeafId)`: it needs the
9915
+ `Boolean(deps.fileHistoryStore && deps.resumeAnchorStore && headReadOf(deps.sessionStorage))` (<= 7.134.x last conjunct: `deps.sessionStorage?.getLeafId`): it needs the
9811
9916
  file-history store (to restore) AND the anchor map (to resolve the target HANDLE). Corrected in sdk 11.4.0
9812
9917
  (board [8437] U3): the store is the FILE-HISTORY store (not the pre-S-15 whole-tree snapshot store).
9813
9918
  ๐Ÿ”ด K-24: on server 7.97.0โ€“7.103.x that seat was the CAPTURE face (backend AND the `FILE_HISTORY_CAPTURE` knob), so a
@@ -9889,8 +9994,9 @@ components:
9889
9994
  description: >
9890
9995
  ๐Ÿ”ด ๅฅ‘็บฆ่ฐƒๅ’Œ่ฝฆ1 Hโ‘ก (2026-08-02): server HAS been emitting this key (the `sessionEvents` bit in `src/http/routes/capabilities.ts`)
9891
9996
  while both this spec and the SDK type were missing it. Gates `GET /v1/sessions/{sessionId}/events`
9892
- (the session-level SSE head subscription). Predicate is a THREE-way AND: a durable session-watch seam
9893
- plus a session store exposing BOTH `getLeafId` and `ownerOf` โ€” the route's 501 uses the same
9997
+ (the session-level SSE head subscription). Predicate (server >= 7.135.0) is a TWO-way AND: a durable session-watch seam
9998
+ plus a session store exposing the session head read `headOf` (<= 7.134.x it was a THREE-way AND with BOTH `getLeafId` and `ownerOf`; those two
9999
+ methods left the store contract in 7.135.0, so `DB_BACKEND=memory` now answers `true`, was `false`) โ€” the route's 501 uses the same
9894
10000
  predicate, so "says yes <=> route works" holds here. False/absent => fall back to polling
9895
10001
  `GET /v1/sessions/{sessionId}/head`. NOTE (server [2373]A-5): no consumer is wired to it yet โ€” it is
9896
10002
  a prepared slot, correct to gate on, wrong to read as "widely consumed".
@@ -10222,11 +10328,29 @@ components:
10222
10328
  โ€” SQL backends only; `DB_BACKEND=local` is always false). Advertises exactly the two file-history blob faces
10223
10329
  `PUT /v1/sessions/{sessionId}/sync/blobs/{hash}` and `GET /v1/sessions/{sessionId}/sync/history/blobs/{hash}`.
10224
10330
  READ IT ON BOTH ENDS of a sync: if either is false, sync the conversation only โ€” do not push `fileHistory`, do
10225
- not PUT blobs (a `fileHistory` pushed to a peer without the blob faces 422s `missing_blob` after commit). When
10331
+ not PUT blobs (a `fileHistory` pushed to a peer without the blob faces 422s `missing_blob` in the FIRST phase โ€” zero staging, zero commit, the conversation does not land either โ€” from server >= 7.135.0; <= 7.134.x answered it in the second phase, after the conversation committed). When
10226
10332
  false the two routes answer 501 `capability.snapshot_store_required` (any session id, ahead of the owner check).
10227
10333
  A blob that the session history graph does not reference / whose bytes are gone stays a 404 `not_found.blob`.
10228
10334
  ABSENT = an older server (<= 7.115.x): the bit cannot be read there (a `DB_BACKEND=local` deployment with
10229
10335
  `sessionSync: true` answered PUT blob 501 on every call), so try as before.
10336
+ sessionRulesNamespaceForms:
10337
+ type: boolean
10338
+ description: >
10339
+ server >= 7.135.0 (predicate class "always true", read per request from the engine's protocol table): the session-rule write faces (`PUT /v1/sessions/{id}/policy`, the `policy` face of `POST /v1/sessions/{id}/sync/import`)
10340
+ accept โ€” in `toolAllow` / `toolDeny` โ€” the three COVERING FORMS of every protocol namespace the engine knows (today `mcp__` and `a2a__`): the whole peer (`mcp__srv`), a tool-segment wildcard (`mcp__srv__*`)
10341
+ and a tool-segment glob (`mcp__srv__get_*`); the engine's matcher reads each as ONE GROUP of tools. A client may stop refusing those three forms losslessly on this bit alone (never on a version guess).
10342
+ The bit says NOTHING about other wildcards: `*`, partial peer-segment wildcards (`mcp__git*`) and a trailing-separator `mcp__srv__` are still refused by the write face (`400 rules.inert_entry`); the whole-protocol
10343
+ name `mcp__*` is accepted only in `toolDeny`. Orthogonal to `sessionPolicy` (does the deployment have a session-rules write face at all): read `sessionPolicy` AND this bit. ABSENT (older server) and `false` โ‡’ read as "do not widen".
10344
+ projectMemory:
10345
+ type: boolean
10346
+ description: >
10347
+ server >= 7.135.0 (always true): the top-level request key `projectMemory: "disabled"` is accepted. The read face hangs on no facility โ€” a deployment without a project-context seat still accepts it
10348
+ (the manifest answers `"absent"`). ABSENT = an older server, whose key-closure gate answers `400 request.body_shape` (`unknownKeys` names it): send the key on sight of the bit only.
10349
+ scenarioSkills:
10350
+ type: boolean
10351
+ description: >
10352
+ server >= 7.135.0 (always true): `GET /v1/capabilities/scenarios/{name}` carries `skills[]` (`ScenarioDetail.skills`). ABSENT = an older server: the card has no list โ€” do NOT read that as "this scenario has no skills"
10353
+ (that is `skills: []`).
10230
10354
 
10231
10355
  # โ”€โ”€โ”€ S-139(sdk 8.3.0):ๅฏน server 7.60.0 ็š„ๆ•ดไฝ“้‡ๅฏน่ดฆ่กฅ้ฝ็š„ 19 ไฝ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
10232
10356
  # ่กŒๅท = server 7.60.0 ๆ ‘็š„ `src/http/routes/capabilities.ts`ใ€‚่ฏญไน‰ไธŽ่ฐ“่ฏ็š„ๅ…จๆ–‡ๅœจ
@@ -10513,7 +10637,7 @@ components:
10513
10637
  type: boolean
10514
10638
  description: >
10515
10639
  server >= 7.85.0 (S-381 / core 7.22.0 #857; mint `routes/capabilities.ts:264` =
10516
- `Boolean(deps.runStore) && Boolean(deps.sessionStorage?.ownerOf)`) โ€” presence of
10640
+ `Boolean(deps.runStore) && Boolean(deps.sessionStorage?.ownerOf)`; server >= 7.135.0 the last conjunct is `headReadOf(deps.sessionStorage) !== undefined`) โ€” presence of
10517
10641
  `POST /v1/sessions/{sessionId}/mcp/reconnect`. FACILITY DEPENDENCY, two conjuncts: a durable run
10518
10642
  ledger (the verb reaches the live stream through the run row) AND the session-ownership face (the
10519
10643
  inbound owner gate is fail-closed; without it the whole door is 501). Both 501s derive from the SAME
@@ -10634,7 +10758,7 @@ components:
10634
10758
  sessionBackgroundFacesAvailable(deps), exitFaces: sessionBackgroundFacesAvailable(deps) }`) โ€” presence of the
10635
10759
  session background-task doors: `listFace` = `GET /v1/sessions/{sessionId}/background`, `exitFaces` =
10636
10760
  `POST /v1/sessions/{sessionId}/background/stop` (contract ยง16). Both bits use the SAME predicate as the two
10637
- doors' 501 (the session-ownership face `sessionStorage.ownerOf`), so "says yes but 501s" is structurally
10761
+ doors' 501 (the session-ownership face: `sessionStorage.headOf` on server >= 7.135.0, `ownerOf` on <= 7.134.x), so "says yes but 501s" is structurally
10638
10762
  impossible; without it both bits are explicitly `false` and both doors answer 501
10639
10763
  `capability.session_ownership_required`. ENDPOINT-GATE FORM: `false` โ‡’ do not call. True only promises the
10640
10764
  endpoint โ€” not that this replica holds any background row of that session (the registry is in-process).
@@ -11685,7 +11809,7 @@ components:
11685
11809
  modelPlane:
11686
11810
  allOf: [{ $ref: '#/components/schemas/ModelPlane' }]
11687
11811
  description: >-
11688
- 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
11812
+ server >= 7.133.0, ALWAYS present there (appended last on 7.133.0โ€“7.134.x; from server >= 7.135.0 the last key is `aliases` and this one precedes it โ€” key order is not a contract, read by name): which model table this engine SERVES, whether the catalog was adopted, and what the start-up route judge says โ€” read this instead of predicting
11689
11813
  (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.
11690
11814
  candidateRejected:
11691
11815
  allOf: [{ $ref: '#/components/schemas/CandidateRejected' }]
@@ -11697,6 +11821,25 @@ components:
11697
11821
  configuration is clean" (it is also an earlier server): render the real cause only when the key is present; keep the old behaviour when absent.
11698
11822
  Same read as `models`: key present => this `models` is the fallback generation (none of the candidate's models are in it). No capability bit
11699
11823
  (the endpoint is always there; the presence of the key is the presence rule).
11824
+ aliases:
11825
+ type: array
11826
+ items: { $ref: '#/components/schemas/ModelAlias' }
11827
+ description: >
11828
+ server >= 7.135.0, ALWAYS present there (appended LAST; `[]` when none): the model WORDS besides `models[].name` that the server's acceptance gate takes (tier words / `default` / a catalog entry's id spelling, ...).
11829
+ ACCEPTANCE RULE (one rule; a model-word judge reads this key, never infers from a row's id): a model word is submittable IFF its name is in `models[].name` UNION `aliases[].name` AND its row's `atMentionable !== false`
11830
+ (`atMentionable` absent = no allowlist verdict, not "not mentionable"). `aliases` deliberately does not say which row a word lands on. ABSENT = an earlier server (<= 7.134.x): tier words / `default` stay "cannot tell" โ€”
11831
+ do not read the absence as "no aliases". Known boundary (not introduced by this version): a catalog entry literally named `default` does not appear in `models[]`.
11832
+
11833
+ ModelAlias:
11834
+ type: object
11835
+ additionalProperties: false
11836
+ required: [name]
11837
+ description: >
11838
+ One row of `ModelsResponse.aliases` (server >= 7.135.0): a submittable model word that is not a `models[]` row. `name` = the word (never equal to a `models[].name`); `atMentionable` = the SAME projection as on a `models[]`
11839
+ row, present ONLY when the deployment configured an at-mention allowlist (the allowlist verdict for the catalog entry the word resolves to); absent = no allowlist verdict, NOT "not mentionable".
11840
+ properties:
11841
+ name: { type: string }
11842
+ atMentionable: { type: boolean }
11700
11843
 
11701
11844
  CandidateRejected:
11702
11845
  type: object
@@ -12077,6 +12220,8 @@ components:
12077
12220
  name: { type: string, description: "The workflow script's declared `meta.name`." }
12078
12221
  description: { type: string, description: "The workflow script's declared `meta.description` (one-liner)." }
12079
12222
  agentFailures: { type: integer, description: 'Count of agent-runs that ended failed โ€” present only when > 0 (a "completed, with failures" flag without an N+1 get).' }
12223
+ parked: { type: boolean, enum: [true], description: 'server >= 7.135.0 (core 7.55.0 `runParkedOf`) โ€” present, always the literal `true`, ONLY while the run is `running` AND at least one of its agents is parked at an approval gate; otherwise the key is ABSENT (never `false`). Read it as "the run is waiting on a person", not "failed". Older servers never send it.' }
12224
+ errorCode: { type: string, description: 'The run row''s machine code (open set; e.g. `workflow.agent_parked` for a drained parked terminal) โ€” present only when the row has one. (Declared here in 26.1.0: the engine summary has emitted it since before 7.134.0.)' }
12080
12225
  currentPhase: { type: string, description: 'Title of the phase currently executing (absent once terminal).' }
12081
12226
  status: { $ref: '#/components/schemas/WorkflowRunStatus' }
12082
12227
  phaseCount: { type: integer, description: 'Phases recorded.' }
@@ -12255,6 +12400,8 @@ components:
12255
12400
  description: { type: string, description: "The workflow script's declared `meta.description` (redacted, one-liner)." }
12256
12401
  status: { $ref: '#/components/schemas/WorkflowRunStatus' }
12257
12402
  agentFailures: { type: integer, description: 'run-level count of agents whose status ended failed โ€” stamped at run end, only when > 0.' }
12403
+ parked: { type: boolean, enum: [true], description: 'server >= 7.135.0 (core 7.55.0 `runParkedOf`) โ€” present, always the literal `true`, ONLY while the run is `running` AND at least one of its agents is parked at an approval gate; otherwise the key is ABSENT (never `false`). Read it as "the run is waiting on a person", not "failed". Older servers never send it.' }
12404
+ errorCode: { type: string, description: 'The run row''s machine code (open set; the same words as the same-named slot on the fleet row, e.g. `workflow.agent_parked` for a drained parked terminal) โ€” present only when the row has one. (Declared here in 26.1.0: the detail projection has forwarded it since before 7.134.0.)' }
12258
12405
  stats: { $ref: '#/components/schemas/WorkflowRunStats' }
12259
12406
  startedAt: { type: integer }
12260
12407
  endedAt: { type: integer }
@@ -12727,6 +12874,11 @@ components:
12727
12874
  run row has one, the key ABSENT otherwise (never an empty string). A parked workflow run reads `status: "failed"` + `errorCode: "workflow.agent_parked"`
12728
12875
  (parked, not failed; the `status` vocabulary gains no word). Snapshot rows, `workflow` upsert frames and rows merged from the terminal window share one mint
12729
12876
  point; a `?session=` subscriber's demoted rows carry it too. ABSENT = an older server, or no code on this row โ€” fall back to `status`.
12877
+ parked:
12878
+ type: boolean
12879
+ enum: [true]
12880
+ description: >-
12881
+ server >= 7.135.0 (core 7.55.0 `runParkedOf`) โ€” present, always the literal `true`, ONLY while the run is `running` AND at least one of its agents is parked at an approval gate; otherwise the key is ABSENT (never `false`). Read it as "the run is waiting on a person", not "failed". Older servers never send it. On a fleet row the workflow-bus row is now replaced WHOLE on every `workflow` upsert (server >= 7.135.0; no longer merged by `id` into the old row), so a key that left is gone from the row.
12730
12882
  FleetFrame:
12731
12883
  description: >
12732
12884
  One decoded frame off GET /v1/fleet/stream (service streamFleet emit set, src/http/routes/fleet.ts). On the
@@ -14148,12 +14300,20 @@ components:
14148
14300
  server >= 7.92.0 (engine 7.25.0): the session's rule LINEAGE generation. An erase
14149
14301
  (`deleteBySession`) ends the lineage โ€” the next write carries gen+1 โ€” so a reader treats a
14150
14302
  different gen as a new lineage, never a continuation, regardless of rev. Absent on older servers.
14303
+ operatorRules:
14304
+ allOf: [{ $ref: '#/components/schemas/SessionRulesOperatorPart' }]
14305
+ description: >
14306
+ server >= 7.135.0 (engine 7.55.0): the row's OPERATOR PART โ€” the buckets (and their own `grammar` stamp) the last OPERATOR write set; the line a non-operator write may not loosen (`403 loosen_forbidden` on a move inside it).
14307
+ ABSENT = the whole row is the operator's (an operator write; every row stored before 7.135.0, read as before the upgrade: the owner can take nothing back). Minted by the engine's write judge alone, stored whole and read back verbatim; also on the 2c export `policy[].rules`.
14308
+ A non-operator write over an EMPTY row stamps an empty part (stamp only) โ€” the owner can then take back what the owner added. A part ECHOED back is read per door: on `PUT /v1/sessions/{sessionId}/policy` it is ALWAYS ignored (the stored row's part is kept over an existing row; an empty stamped part is minted over none).
14309
+ On the sync-import replay (`POST โ€ฆ/sync/import`, `policy[]` records, which carry `rev`): over an existing row the judge keeps the STORED row's part and ignores the echo; over NO row, a non-operator record that looks like a stored row (carries `rev` >= 1)
14310
+ has its own part adopted (from then on that part IS the row's operator line: taking it back answers 403 `loosen_forbidden`), otherwise an empty stamped part is minted; an operator write never carries a part.
14151
14311
  grammar:
14152
14312
  type: integer
14153
14313
  minimum: 0
14154
14314
  description: >
14155
14315
  server >= 7.129.0 (engine 7.51.2): the rule GRAMMAR the row's entries are read in, stamped by the
14156
- engine's write judge on every row written through it (every row this server writes carries `1`).
14316
+ engine's write judge on every row written through it (server >= 7.135.0 writes `4` โ€” the engine's `SESSION_RULES_GRAMMAR`; 7.129.0 wrote `1`).
14157
14317
  Absent = a row stored before the stamp (grammar 0: an allow entry spelled as a rule, `Bash(rm:*)`,
14158
14318
  named no tool and admitted nothing โ€” the engine keeps reading such a row that way and announces it
14159
14319
  once; the one-time migration rewrites stored rows and stamps them). Echoing a stored body back into
@@ -14174,6 +14334,18 @@ components:
14174
14334
  rev: { type: integer }
14175
14335
  gen: { type: integer }
14176
14336
  grammar: { type: integer }
14337
+ operatorRules: { $ref: '#/components/schemas/SessionRulesOperatorPart' }
14338
+
14339
+ SessionRulesOperatorPart:
14340
+ type: object
14341
+ description: '`StoredSessionRules.operatorRules` (server >= 7.135.0): the three buckets plus their own grammar stamp. The `grammar` is always present (the write judge stamps it).'
14342
+ additionalProperties: false
14343
+ required: [grammar]
14344
+ properties:
14345
+ toolAllow: { type: array, items: { type: string } }
14346
+ toolDeny: { type: array, items: { type: string } }
14347
+ allowDirs: { type: array, items: { type: string } }
14348
+ grammar: { type: integer, minimum: 0 }
14177
14349
 
14178
14350
  McpServerStatus:
14179
14351
  type: object
@@ -14816,7 +14988,18 @@ components:
14816
14988
  fail-closed) and `state.scenario_assignment_unknown` (503, server >= 7.119.0 โ€” the center's scenario assignment for the
14817
14989
  principal is dangling; only re-pointing the assignment, or adding the scenario back to the deployment, helps) and
14818
14990
  `approvals.read_corrupt` (500, server >= 7.134.0 on `GET /v1/approvals` โ€” a queue row's stored body cannot be read; an operator has to repair or
14819
- delete the row). Every other 5xx code keeps the status-based retry.
14991
+ delete the row; server >= 7.135.0 the same judge answers `GET /v1/assistant/inbox` and `GET /v1/assistant/tasks` too) and `workflow.journal_incompatible`
14992
+ (500, server >= 7.135.0 on `GET /v1/workflows/{id}/journal` โ€” a journal row the engine's judge rejects; the body carries `ordinal`; repair the row from the retained copy, never by deleting it).
14993
+ Every other 5xx code keeps the status-based retry.
14994
+ ๐Ÿ”ด server >= 7.135.0 read-failure and store-fault codes (all additive; branch on the code, never the sentence): `503 approvals.read_unavailable` on `GET /v1/approvals`, `/v1/assistant/inbox` and
14995
+ `/v1/assistant/tasks` (the queue cannot be read RIGHT NOW โ€” transient, retried by status; until a read succeeds an answer does NOT mean nothing is pending; it moved from the SSE-only table into the status-bearing catalog, status `-` โ†’ 503) and, on
14996
+ the same three faces, `500 approvals.read_corrupt` (above); `503 state.file_history_unreadable` on the session-sync pull faces (`GET /v1/sessions/{id}/sync/manifest`, `GET /v1/sessions/{id}/sync/history/blobs/{hash}` โ€” the file history
14997
+ cannot be read WHOLE; it is NOT "no history", which stays `200` with `fileHistory: null`); `422 adopt_failed` on `POST /v1/sessions/{id}/sync/import` (the file-history replay hit a store fault; 7.134.x answered `unsupported_format`;
14998
+ NOT a missing blob โ€” that is `422 missing_blob`, now answered in the FIRST phase). The two store-fault codes may carry `permanence` (`transient` = nothing changed, retry the same shape later / `terminal` = repair the store first; absent = the store did not judge).
14999
+ `422 request.unprocessable` now also answers a deny / ask rule whose PARAMETER does not belong to the tool (the sentence carries the engine code `unsupported.param_unknown`, the same word in `detail.problems[].reason`; a stored body that
15000
+ resumes โ‡’ `409 resume_blocked_by_policy` with `blockedBy: request.unprocessable`; 7.134.x accepted such a rule, and it never matched). `400 request.body_shape` also names an unread key inside a `settings.hooks` group / entry in
15001
+ `unknownKeys` as a FULL coordinate (`settings.hooks.PreToolUse.0.hooks.0.onFailure`) on a fresh submit.
15002
+ `409 resume_outcome_invalid` + `field: "feedback"` (`retriable: true`) now answers a release `feedback` that carries an engine `<system-reminder>` tag (refused before the CAS, the card stays pending; fix or drop the words and decide again โ€” it was `409 approval_binding_mismatch` without `field` on 7.134.x). A `feedback` on a deny / policy decision stays `400 request.field_conflict` (`detail.kind: approval_feedback`).
14820
15003
  ๐Ÿ”ด `request.host_posture_refused` (422) has TWO triggers (same code, same status โ€” "you wrote it right, this
14821
15004
  deployment will not grant that host posture"): (1) the write-containment face (`permissionMode: bypassPermissions` โ‡’
14822
15005
  `writeFace: open`; read `capabilities.taskWriteFaceOpen` first); (2) server >= 7.106.0 / S-895: `inheritEnv: "all"` on a
@@ -14880,10 +15063,23 @@ components:
14880
15063
  the ids of the background children rooted at this session that are still alive (count = length) โ€” the SAME
14881
15064
  strings as the `handle` on `GET /v1/agents/roster` rows. Nothing was deleted; wait for them to finish (or stop
14882
15065
  them where they run) and retry the delete verbatim. SDK: `err.extra.remaining` on the ConflictError.
15066
+ permanence:
15067
+ type: string
15068
+ enum: [transient, terminal]
15069
+ description: >-
15070
+ server >= 7.135.0, on `503 state.file_history_unreadable` and `422 adopt_failed` only: the file-history store's own judgment of the fault (the engine's `GovernanceRetryClass`) โ€”
15071
+ `transient` = nothing was changed, retrying the same shape later may succeed; `terminal` = repair the store first. ABSENT = the store did not judge (retry later once; still red โ‡’ look at the store). SDK: `err.extra.permanence`.
15072
+ ordinal:
15073
+ type: integer
15074
+ description: >-
15075
+ server >= 7.135.0, on `500 workflow.journal_incompatible`: the journal ordinal of the FIRST bad row (ascending); `-1` = the refusal names no entry. SDK: `err.extra.ordinal`.
14883
15076
  reason:
14884
15077
  type: string
14885
15078
  description: >-
14886
- A per-code closed word โ€” read it by `errorCode`, never across codes. On 503 `fleet.lease_unavailable` (server >=
15079
+ A per-code closed word โ€” read it by `errorCode`, never across codes. On 400 `request.field_invalid` for a URL that carries a credential (server >= 7.135.0): `url_userinfo` (`user:pass@host`) / `url_credential_param` (a credential-named
15080
+ query / fragment parameter โ€” the names the service's log redactor treats as credentials), beside `field` = the body coordinate (`mcpServers[i].transport.url`, `a2aPeers[i].url`, `a2aPeers[i].cardUrl`,
15081
+ `settings.hooks.<event>[g].hooks[h].url` โ€” the FULL coordinate, not `field: "settings.hooks"` + `detail` โ€” and `settings.webSearch.endpoint`; a `settings.webSearch.endpoint` with userinfo keeps its sentence and gains `field` / `reason`);
15082
+ `a2aPeers` over 32 entries answers `over_cap` + `count` + `cap` on `field: "a2aPeers"`. On 503 `fleet.lease_unavailable` (server >=
14887
15083
  7.106.0 / S-866 โ‘ข): `auth-rejected` (the center refused this engine's credential, or the credential file is
14888
15084
  unreadable) / `unreachable` (unreachable / 5xx / timeout / bad body / any status other than 200 / 409 / 400 / 401 / 403 โ€”
14889
15085
  center 404 / 429 / 3xx included). On 400 `request.field_invalid` for a `settings.permissions` bucket (server >= 7.106.0 /
@@ -15699,6 +15895,12 @@ components:
15699
15895
  K-32 (server >= 7.106.0 / core 7.34.0 design/1111) โ€” `bypassPermissions` RELEASED an ask nobody answered (see
15700
15896
  `ModeRelease`). Absent = this allow was not a mode release (a plain allow / a person's yes / a rule), or an older
15701
15897
  server (<= 7.105.0 stripped it).
15898
+ clearedBy:
15899
+ allOf: [{ $ref: '#/components/schemas/AllowRuleClearance' }]
15900
+ description: >
15901
+ server >= 7.135.0 (core 7.55.0) โ€” the allow layer's account of a call it let run WITHOUT asking: which member's allow rule cleared a standing ask that nobody answered
15902
+ (`AllowRuleClearance`). Present exactly when an allow member cleared the ask; absent on a call nothing asked about, a mode's release or acceptance, the read-only arm's,
15903
+ the classifier's or the sandbox's allow, and a person's yes. Never present together with `released`. Older servers stripped it.
15702
15904
  - type: object
15703
15905
  additionalProperties: false
15704
15906
  required: [kind, deniedBy]
@@ -16156,6 +16358,10 @@ components:
16156
16358
  `MCP_FAILURE_CODES` spellings โ€” including `network` and the `http_<status>` encoding โ€” are gone,
16157
16359
  and an HTTP status now rides the structured `httpStatus` seat on `wiring_manifest.mcp[]` instead of
16158
16360
  being baked into the code. The set as a WHOLE stays open (a tool owns its own codes).
16361
+ ๐Ÿ”ด core 7.55.0 / server >= 7.135.0 โ€” two MCP words minted BEFORE the call is sent (`delivered: "no"`; the server has no mint point, they pass through verbatim): after the engine redials a Streamable HTTP
16362
+ server that LOST its session, `mcp.tool_declaration_changed` (the server now declares the tool differently โ€” safety axis / read-only reading / the "a person must answer" mark / task support โ€” or no longer lists it) and
16363
+ `mcp.tool_listing_unreached` (the post-redial listing walk stopped before reaching the tool, so its new declaration cannot be proved). Every later call to that tool is refused the same way until a listing of the connection
16364
+ reaches it โ€” the way out is `POST /v1/sessions/{id}/mcp/reconnect` (or the engine's refresh); do not resend as-is. The lost-session failure itself stays `http_status` + `httpStatus: 404` + `delivered: "unknown"`.
16159
16365
  Consumers MUST branch with a `default` arm and MUST NOT exhaustively switch on known values.
16160
16366
  The service enforces only a length bound (>128 chars = malformed, the whole key is DROPPED rather
16161
16367
  than truncated โ€” truncating would mint a code core never sent). NOTE: this is a DIFFERENT
@@ -16505,6 +16711,21 @@ components:
16505
16711
  type: object
16506
16712
  additionalProperties: false
16507
16713
  properties: { kind: { type: string, description: 'root | child | resume. Open on read.' } }
16714
+ sessionRules:
16715
+ type: object
16716
+ additionalProperties: false
16717
+ required: [childrenReadLive]
16718
+ description: >
16719
+ server >= 7.135.0 (engine 7.55.0; effective half only, both faces): whether the session-rules layer is handed to the delegated legs. `childrenReadLive: true` = this leg wired the session-rules layer (the deployment has a session-rules
16720
+ store and the task carries a session id) and handed the session's key to every leg it delegates (sync / background / fork / workflow / resume / observer side-car, any depth) โ€” a rule written into the session NOW governs an
16721
+ already-running child's NEXT call; `false` = no rules layer, nothing handed out. The section ABSENT = an older engine (say nothing; do not read it as `false`). An unreadable section (not `{ childrenReadLive: <boolean> }`) is dropped.
16722
+ properties:
16723
+ childrenReadLive: { type: boolean }
16724
+ projectMemory:
16725
+ type: string
16726
+ description: >
16727
+ server >= 7.135.0 (engine 7.55.0; effective half only, both faces): did this leg call the deployment's project-context seat โ€” `on` = it did; `disabled` = the seat is wired but the request's `projectMemory: "disabled"` turned it off;
16728
+ `absent` = the deployment wired no seat (whatever the request said). On `disabled` / `absent` legs the server-minted `instructions` section is ABSENT. Open on read (a new engine word passes through); ABSENT = an older engine.
16508
16729
  ask:
16509
16730
  type: object
16510
16731
  additionalProperties: false
@@ -17096,6 +17317,10 @@ components:
17096
17317
  own failure carries the same code. `{ modelId, boundOrigin, requestOrigin, sessionId, runId }` โ€” both origins in display spelling (scheme + host +
17097
17318
  port, no userinfo / path), never a credential or a digest of one. Once per run per (modelId, boundOrigin, requestOrigin). It replaces the retired
17098
17319
  operator code `route.base_url_changed_key_unchanged` (never on this whitelist). `config.credential_not_withheld` is operator-audience โ€” ops log only.
17320
+ `advisor.not_attached` (server >= 7.135.0 / core 7.55.0; D.3 is now 42 codes) โ€” the request's advisor model did NOT attach (the engine's pairing judge refuses the (advisor, main) pair).
17321
+ `{ advisorModel, mainModel, reason, sessionId, runId? }` โ€” the two model names are the catalog id / the reference the request wrote (display spelling); `reason` is an engine word (`unknown_model` / `advisor_unranked` /
17322
+ `weakest_tier` / `main_unranked` / `pairing` / `hold_unknown`; the server echoes it โ€” read OPEN). Unreachable on this server today: it does not accept `advisorModel` (the request body does not take it, and the
17323
+ `advisor` section is not projected on either `wiring_manifest` face).
17099
17324
  Value change: `permission.rule_row_dropped`'s `detail.rows[].rule` is, since core 7.53.0, the rule's original text (the same reading as
17100
17325
  `allowed_tool_not_granted`'s `entries[].entry`: at most 1 024 code points, longer cut to 1 024 + a truncation mark, CONTROL CHARACTERS RIDE AS DATA โ€”
17101
17326
  neutralize at render); before it was the engine-neutralized single-line form (at most 201 code points). Same keys.
@@ -18300,6 +18525,27 @@ components:
18300
18525
  The closed five-word vocabulary of `AllowRuleShadow.member` (server >= 7.117.0 / core 7.42.0 `ALLOW_RULE_MEMBERS`, verbatim and in the order the engine reads the members). The SDK exports
18301
18526
  it at runtime as `ALLOW_RULE_MEMBERS`. Owner = core; read it as an open string with a default arm (core may add a member and the server follows).
18302
18527
 
18528
+ AllowRuleClearance:
18529
+ type: object
18530
+ additionalProperties: false
18531
+ required: [member, entries]
18532
+ description: >
18533
+ `tool_end.gate.disposition.clearedBy` (server >= 7.135.0 / core 7.55.0 `AllowRuleClearance`): the allow-layer member whose rule CLEARED a standing ask (the pair of `AllowRuleShadow`, which is
18534
+ "matched and could NOT clear"). Same `member` / `entries` / `omitted` shape (minted and capped by the same seat), plus `positions` for a LIST member (`request`, `deployment`): the index of every element of the
18535
+ list the run received that is one of those entries (a string listed twice has both indices), so a host reads which line of its own declaration spoke even where the shown text is capped.
18536
+ properties:
18537
+ member: { $ref: '#/components/schemas/AllowRuleMember' }
18538
+ entries:
18539
+ type: array
18540
+ maxItems: 32
18541
+ items: { type: string, maxLength: 512 }
18542
+ description: 'The entries that member stood on (UNTRUSTED for display, secret-redacted, <= 512 chars each, <= 32 listed).'
18543
+ omitted: { type: integer, minimum: 1, description: 'How many entries were not listed (beyond the cap). Absent = none omitted.' }
18544
+ positions:
18545
+ type: array
18546
+ items: { type: integer, minimum: 0 }
18547
+ description: 'LIST members only: the indices, in the list this run received, of the elements that are those entries. Absent for a non-list member.'
18548
+
18303
18549
  AllowRuleShadow:
18304
18550
  type: object
18305
18551
  additionalProperties: false
@@ -18431,7 +18677,7 @@ components:
18431
18677
  equality). ONE vocabulary for two faces: a card's rule offer (`RuleOffer.match`, the
18432
18678
  server's card schema validates it with this very enum) and a persisted rule row (`GET /v1/rules`).
18433
18679
 
18434
- `exact` = this one command only (`Bash(git status)`). `prefix` = the historical colon-star
18680
+ `exact` = this one command only (`Bash(git status)`). `pattern` (server >= 7.135.0 / core 7.55.0) = the MID-STAR command form (`Bash(git * main)`) โ€” the fourth Bash command form a card edit may persist; the persisted `command` is the written pattern itself. `prefix` = the historical colon-star
18435
18681
  word-boundary prefix (`Bash(git status:*)`) and the ONLY spelling for a compound prefix form; it stays
18436
18682
  a permanent compatibility read. `wildcard` = the trailing space-star form (`Bash(npm run *)`), the
18437
18683
  same predicate as `prefix` on a single command body. `subpath` = the `Read(//abs-dir/**)` directory form,
@@ -18445,11 +18691,11 @@ components:
18445
18691
  always refuses these two words (no network grant is minted through a card). A replica rolled back to 7.125.x reads such a
18446
18692
  row as a damaged row.
18447
18693
 
18448
- ๐Ÿ”ด NINE MEMBERS, closed: the server validates the member on both faces. A consumer type narrower than the
18694
+ ๐Ÿ”ด TEN MEMBERS, closed (server >= 7.135.0 / core 7.55.0 added `pattern`, between `wildcard` and `subpath` โ€” the engine's order): the server validates the member on both faces. A consumer type narrower than the
18449
18695
  mint point is a lie โ€” it lets a `switch` claim exhaustiveness the wire does not honour (SDK <= 16 declared
18450
18696
  four here, so a `path` offer and every 7.118.0 skill-card offer were dropped as malformed; SDK <= 21 declared
18451
18697
  seven, so every domain grant row on `GET /v1/rules` was dropped the same way).
18452
- enum: [exact, prefix, wildcard, subpath, path, name, name_prefix, host, subdomain]
18698
+ enum: [exact, prefix, wildcard, pattern, subpath, path, name, name_prefix, host, subdomain]
18453
18699
 
18454
18700
  RuleOfferUncoveredReason:
18455
18701
  type: string
@@ -19785,6 +20031,12 @@ components:
19785
20031
  runId: { type: string }
19786
20032
  status: { type: string }
19787
20033
  summary: { type: string }
20034
+ completionId:
20035
+ type: string
20036
+ description: >
20037
+ server >= 7.135.0 โ€” the idempotency anchor of this workflow completion: the SAME id rides every notification path of one completion (this frame / `task_notification` / fleet `bg_notification` / inbox replay) and
20038
+ is the dedup key ACROSS paths (the same value domain as `task_notification.completionId`). ABSENT when the engine gave none (an orphan the service terminated itself) โ€” dedup on `runId` as before. The read faces
20039
+ (`GET /v1/workflows/{id}` etc.) deliberately never carry it. Older servers' re-delivered frames have no such key.
19788
20040
 
19789
20041
  # โ”€โ”€ ๐Ÿ”ด #185a (2026-08-08): the DURABLE leg's three APPROVAL arms โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
19790
20042
  # Shape rule for all three: `allOf: [<frame schema>, {the narrowed discriminant}]`. The keys are NOT
@@ -20484,7 +20736,7 @@ components:
20484
20736
  ๐Ÿ”ด ONE RULE FOR EVERY "EFFECTIVE HALF ONLY" KEY (sdk 9.7.0, after a codex adversarial-review [high]):
20485
20737
  this schema's only consumer today is `GET /v1/diagnostics/wiring`'s `static` half, and every key marked
20486
20738
  "effective half only" (`leg`, `ask.effective`, `configFingerprint`, `tools`, `mods`, `lsp`,
20487
- `modelGate`, `autoMode`, `writeProtection`, `readDeny`, `autoConsolidation`, `fileHistory`, `instructions`) is ALWAYS ABSENT on that
20739
+ `modelGate`, `autoMode`, `writeProtection`, `readDeny`, `autoConsolidation`, `fileHistory`, `instructions`, `sessionRules`, `projectMemory`) is ALWAYS ABSENT on that
20488
20740
  wire โ‡’ THEIR ABSENCE HERE CARRIES NO INFORMATION. The per-key absence readings ("absent = not armed",
20489
20741
  "absent = a third state", โ€ฆ) hold for the LIVE FRAME (`Event_wiring_manifest`) and must not be carried
20490
20742
  over to this response. For the facts behind those keys: a tenant reads
@@ -20497,7 +20749,7 @@ components:
20497
20749
  are EFFECTIVE-half only (the live `wiring_manifest` event) โ€” as are the four per-leg sections
20498
20750
  `modelGate`, `autoMode`, `mcp` and `tools`, which describe what THIS leg did and have no static
20499
20751
  counterpart โ€” and the STATIC half
20500
- (GET /v1/diagnostics/wiring) NEVER mints them; every other section, plus `governance`, is always
20752
+ (GET /v1/diagnostics/wiring) NEVER mints them; every other section NOT in the effective-half-only list above, plus `governance`, is always
20501
20753
  present on the static half. Fields stay optional because core may add or drop sections and that
20502
20754
  must not hard-break a client โ€” read by half, and never wait on a key the half never sends.
20503
20755
  ๐Ÿ”ด EVERY section is declared HERE, each saying which half mints it (sdk 8.9.0). Before that, `tools`
@@ -20518,6 +20770,18 @@ components:
20518
20770
  description: 'Which leg emitted it (effective half only; the static half has no leg identity).'
20519
20771
  properties:
20520
20772
  kind: { type: string, enum: [root, child, resume] }
20773
+ sessionRules:
20774
+ type: object
20775
+ required: [childrenReadLive]
20776
+ description: >
20777
+ server >= 7.135.0 (engine 7.55.0; effective half only โ€” the static half never mints it): whether the session-rules layer is handed to the delegated legs. Same reading as on the live frame (`Event_wiring_manifest.sessionRules`):
20778
+ `childrenReadLive: true` = a rule written into the session NOW governs an already-running child's NEXT call; `false` = no rules layer; the section ABSENT = an older engine, or this half.
20779
+ properties:
20780
+ childrenReadLive: { type: boolean }
20781
+ projectMemory:
20782
+ type: string
20783
+ description: >
20784
+ server >= 7.135.0 (engine 7.55.0; effective half only): did the leg call the deployment's project-context seat โ€” `on` / `disabled` (the request's `projectMemory: "disabled"`) / `absent` (no seat wired). Open on read; ABSENT = an older engine, or this half.
20521
20785
  ask:
20522
20786
  type: object
20523
20787
  description: 'Approval seam: wired form, which seat it came from, and (effective half) what it adjudicates to.'
@@ -21285,6 +21549,19 @@ components:
21285
21549
  usageMissing: { type: boolean, enum: [true] }
21286
21550
  stopReason: { type: string }
21287
21551
  errorMessage: { type: string, description: 'A post-stream error (contract: not thrown as HTTP).' }
21552
+ errorCode:
21553
+ type: string
21554
+ description: >-
21555
+ server >= 7.135.0 (core 7.55.0, passed through verbatim; failure only): the engine's brain error code of the failed reply (open on read; today `auth` / `invalid_request` / `rate_limit` / `server` / `network` /
21556
+ `http` / `stream_torn` / `refusal` / `length_empty`). ABSENT on success and on older servers.
21557
+ errorKind:
21558
+ type: string
21559
+ enum: [input_too_long, degenerate, length_empty]
21560
+ description: 'server >= 7.135.0 (failure only): the STRUCTURAL failure kind the reply carried, forwarded as is โ€” `input_too_long` (the provider refused the request as over its window), `degenerate`, `length_empty`. Absent when the reply carried none.'
21561
+ inputTooLongRuledOut:
21562
+ type: boolean
21563
+ enum: [true]
21564
+ description: 'server >= 7.135.0 (failure only): present (always `true`) when the failure was a provider refusal whose minter RULED OUT input-too-long as the explanation (core `AssistantMessage.inputTooLongRuledOut`, passed through). It speaks ONLY to that negative: it does not say the status is 400 / 422 and does not say a verbatim resend repeats the failure (the engine also rules input length out for statuses such as 401 / 429 / 500) โ€” triage on `errorCode`. Absent = no such judgment.'
21288
21565
 
21289
21566
  ToolApprovalDecision:
21290
21567
  type: string
@@ -21463,6 +21740,8 @@ components:
21463
21740
  to settle it): a parked row's `decided` can return to `parked` when the resume leg fails before the approved action starts (the engine reopens the same gate), and
21464
21741
  a cancelled resume reads `void` once the server collects the reopened gate (`parked` in between, or `void` for an instant during the reopen).
21465
21742
  Older servers answered all of these with `parked` + `decidePath`.
21743
+ ๐Ÿ”ด server >= 7.135.0: a gate that was DECIDED and then REOPENED by the engine reads `parked` WITH `reopened` (`ToolApprovalReopened`: the engine's reopen reason + the earlier decision's
21744
+ `decision` / `decidedAtMs` / `decisionNote?`). `reopened` ABSENT = the read cannot say it was reopened (not "confirmed never reopened"). Older servers: always absent.
21466
21745
  oneOf:
21467
21746
  - $ref: '#/components/schemas/ToolApprovalReadPending'
21468
21747
  - $ref: '#/components/schemas/ToolApprovalReadDecided'
@@ -21500,6 +21779,29 @@ components:
21500
21779
  properties:
21501
21780
  state: { type: string, enum: [parked] }
21502
21781
  decidePath: { type: string, description: 'The durable decision path for this ask when it can be built.' }
21782
+ reopened:
21783
+ allOf: [{ $ref: '#/components/schemas/ToolApprovalReopened' }]
21784
+ description: 'server >= 7.135.0: present when the gate was decided and then reopened by the engine (the resume leg failed before the approved action started). Absent = the read cannot say it was reopened.'
21785
+ ToolApprovalReopenReason:
21786
+ type: string
21787
+ description: >
21788
+ `ToolApprovalReopened.reason` (server >= 7.135.0) โ€” the engine's `REOPEN_REASONS`, verbatim and in order (engine-owned; the server echoes a word only when it is in this
21789
+ set, otherwise `reopened` is omitted entirely): `env_failed` (the environment / session side failed) ยท `tool_unavailable` (the approved tool is not there now โ€” decide again) ยท
21790
+ `constraint_rejected` (the decision input was rejected โ€” decide again) ยท `config_refused` (the configuration was refused). Read open on the consumer side: an unknown word is "reopened, reason unknown".
21791
+ enum: [env_failed, tool_unavailable, constraint_rejected, config_refused]
21792
+ ToolApprovalReopened:
21793
+ type: object
21794
+ description: >
21795
+ `ToolApprovalReadParked.reopened` (server >= 7.135.0): evidence that a DECIDED gate was reopened by the engine. `reason` = the engine's reopen reason; `decision` / `decidedAtMs` / `decisionNote?` = the
21796
+ decision made BEFORE the reopen (the same three keys as a `decided` body). An unreadable earlier decision โ‡’ the three keys are absent TOGETHER (only `reason` stays; `decisionNote` may always be absent).
21797
+ Single-instant read: if the row is decided and reopened between the two reads the server re-judges (three rounds in a row โ‡’ falls back to plain `parked`).
21798
+ additionalProperties: false
21799
+ required: [reason]
21800
+ properties:
21801
+ reason: { $ref: '#/components/schemas/ToolApprovalReopenReason' }
21802
+ decision: { type: string, enum: [allow, deny] }
21803
+ decidedAtMs: { type: integer }
21804
+ decisionNote: { type: string }
21503
21805
  ToolApprovalReadVoid:
21504
21806
  allOf:
21505
21807
  - $ref: '#/components/schemas/ToolApprovalReadProjection'
@@ -22217,8 +22519,8 @@ components:
22217
22519
  match:
22218
22520
  allOf: [{ $ref: '#/components/schemas/RuleOfferMatch' }]
22219
22521
  description: >
22220
- The match form โ€” the same seven-member vocabulary as a card's rule offer (`RuleOfferMatch`, the server's
22221
- `PERSISTED_RULE_MATCHES`): `exact` / `prefix` / `wildcard` / `subpath` / `path`, and from server >= 7.118.0
22522
+ The match form โ€” the same ten-member vocabulary as a card's rule offer (`RuleOfferMatch`, the server's
22523
+ `PERSISTED_RULE_MATCHES`): `exact` / `prefix` / `wildcard` / `pattern` (server >= 7.135.0: the mid-star command form) / `subpath` / `path`, and from server >= 7.118.0
22222
22524
  `name` / `name_prefix` (a `Skill(<name>)` / `Skill(<prefix>:*)` rule).
22223
22525
  command: { type: string, description: 'What the matcher actually compares against, parsed out of `rule` at mint time and stored so no consumer re-parses. Trust this + `match`, not your own parse of `rule`.' }
22224
22526
  adds: