@sema-agent/sdk 25.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.
- package/README.md +151 -3
- package/dist/client.d.ts +0 -4
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +0 -5
- package/dist/client.js.map +1 -1
- package/dist/errors.d.ts +108 -26
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +57 -35
- package/dist/errors.js.map +1 -1
- package/dist/events.d.ts +41 -4
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js.map +1 -1
- package/dist/index.d.ts +28 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -3
- package/dist/index.js.map +1 -1
- package/dist/resources/approvals.d.ts +19 -3
- package/dist/resources/approvals.d.ts.map +1 -1
- package/dist/resources/approvals.js +8 -3
- package/dist/resources/approvals.js.map +1 -1
- package/dist/resources/fleet.d.ts +12 -12
- package/dist/resources/fleet.d.ts.map +1 -1
- package/dist/resources/fleet.js.map +1 -1
- package/dist/resources/rules.d.ts +3 -6
- package/dist/resources/rules.d.ts.map +1 -1
- package/dist/resources/rules.js.map +1 -1
- package/dist/resources/runs.d.ts +4 -1
- package/dist/resources/runs.d.ts.map +1 -1
- package/dist/resources/runs.js +4 -1
- package/dist/resources/runs.js.map +1 -1
- package/dist/resources/session-sync.d.ts +36 -34
- package/dist/resources/session-sync.d.ts.map +1 -1
- package/dist/resources/session-sync.js +34 -41
- package/dist/resources/session-sync.js.map +1 -1
- package/dist/resources/sessions.d.ts +5 -3
- package/dist/resources/sessions.d.ts.map +1 -1
- package/dist/resources/sessions.js +5 -3
- package/dist/resources/sessions.js.map +1 -1
- package/dist/resources/side-query.d.ts +8 -0
- package/dist/resources/side-query.d.ts.map +1 -1
- package/dist/resources/side-query.js.map +1 -1
- package/dist/resources/tasks.d.ts +2 -0
- package/dist/resources/tasks.d.ts.map +1 -1
- package/dist/resources/tasks.js +2 -0
- package/dist/resources/tasks.js.map +1 -1
- package/dist/resources/tool-approvals.d.ts +106 -101
- package/dist/resources/tool-approvals.d.ts.map +1 -1
- package/dist/resources/tool-approvals.js +58 -37
- package/dist/resources/tool-approvals.js.map +1 -1
- package/dist/settings.d.ts +14 -1
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +0 -24
- package/dist/settings.js.map +1 -1
- package/dist/sync.d.ts +5 -5
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +15 -55
- package/dist/sync.js.map +1 -1
- package/dist/transport.d.ts +5 -0
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +7 -0
- package/dist/transport.js.map +1 -1
- package/dist/types.d.ts +236 -129
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -1
- package/openapi.yaml +566 -559
- package/package.json +2 -3
- package/dist/resources/workspace.d.ts +0 -95
- package/dist/resources/workspace.d.ts.map +0 -1
- package/dist/resources/workspace.js +0 -60
- package/dist/resources/workspace.js.map +0 -1
package/openapi.yaml
CHANGED
|
@@ -172,7 +172,7 @@ tags:
|
|
|
172
172
|
themself said yes"), never operator. GATED AND OFF BY DEFAULT: the whole lane hangs on
|
|
173
173
|
PERMISSION_RULES_ENABLED (default false) plus a wired rule store — so on a stock deployment BOTH doors
|
|
174
174
|
answer 501 `capability.rule_store_required`, and so does the single-card half (the ask frame simply
|
|
175
|
-
carries no `
|
|
175
|
+
carries no `ruleOffers`). Read the observed frame / an actual response, never a version number.
|
|
176
176
|
The SINGLE-CARD half of the same lane is `persistRule` on POST /v1/tool-approvals/{id}/respond.
|
|
177
177
|
- name: adoption
|
|
178
178
|
description: >
|
|
@@ -2280,38 +2280,16 @@ paths:
|
|
|
2280
2280
|
get:
|
|
2281
2281
|
tags: [workspace]
|
|
2282
2282
|
operationId: workspaceList
|
|
2283
|
-
x-status: retired #
|
|
2284
|
-
summary: 'RETIRED (server 7.52.0, S-15) — snapshot listing;
|
|
2285
|
-
description: >
|
|
2286
|
-
🔴 RETIRED as of `@sema-agent/server` 7.52.0 (S-15/design/381): the E19 whole-tree snapshot store this
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
predicate). There is NO 1:1 replacement path — a future "rewind coverage" browse would be a NEW face
|
|
2292
|
-
with tracked-set semantics, not this name wired back. Related live faces: rewind restore rides
|
|
2293
|
-
`TaskRequest.restoreFiles` (probe `capabilities.restoreFiles`); per-file history blobs ride
|
|
2294
|
-
`GET /v1/sessions/{sessionId}/sync/history/blobs/{hash}`.
|
|
2295
|
-
Historical shape (servers 1.299–7.51): read-only projection over the E19 snapshot store. Rows carry
|
|
2296
|
-
`{key, files, bytes?}`; `latest` = the newest snapshot key. Owner-scoped (non-owner → 404, no
|
|
2297
|
-
oracle); 501 without the snapshot-store read faces (probe `capabilities.workspace`).
|
|
2298
|
-
parameters:
|
|
2299
|
-
- in: query
|
|
2300
|
-
name: limit
|
|
2301
|
-
required: false
|
|
2302
|
-
schema: { type: integer, minimum: 1, maximum: 100 }
|
|
2283
|
+
x-status: retired # server 7.52.0: whole-tree snapshot epoch retired; sdk 26.0.0 deleted the verb (support floor 7.52.0).
|
|
2284
|
+
summary: 'RETIRED (server 7.52.0, S-15) — snapshot listing; every supported server (>= 7.52.0) answers 501 capability.workspace_retired.'
|
|
2285
|
+
description: >
|
|
2286
|
+
🔴 RETIRED as of `@sema-agent/server` 7.52.0 (S-15/design/381): the E19 whole-tree snapshot store this face projected was replaced by per-edited-file
|
|
2287
|
+
rewind HISTORY (only files the agent edited — there is no "whole workspace" manifest to browse). The route SHAPE is kept server-side as a tombstone:
|
|
2288
|
+
every supported server answers 501 `capability.workspace_retired` (a named refusal, not a 404 that reads like a typo), and `capabilities.workspace` is
|
|
2289
|
+
always `false` (same predicate). No 1:1 replacement: rewind restore rides `TaskRequest.restoreFiles` (probe `capabilities.restoreFiles`); per-file
|
|
2290
|
+
history blobs ride `GET /v1/sessions/{sessionId}/sync/history/blobs/{hash}`. sdk 26.0.0 deleted `client.workspace` (its 200 shapes only existed on 1.299–7.51).
|
|
2303
2291
|
responses:
|
|
2304
|
-
'200':
|
|
2305
|
-
description: The snapshot listing.
|
|
2306
|
-
content:
|
|
2307
|
-
application/json:
|
|
2308
|
-
# 封闭两层(census 批2 第三段,2026-07-30):铸造点 `routes/sessions.ts`(信封与行两处)。条件键=信封 `latest`(零快照即省略)、行 `bytes`(blobSizes 面缺席=诚实省略)。
|
|
2309
|
-
# 4.3.0:形提成具名 `WorkspaceSnapshotPage`(SDK 同批把返回位的内联字面量也提成了 exported type,
|
|
2310
|
-
# 让 spec-resource-schema-gate 罩得住 —— CAPS-OPS-8)。
|
|
2311
|
-
schema: { $ref: '#/components/schemas/WorkspaceSnapshotPage' }
|
|
2312
|
-
'400': { $ref: '#/components/responses/BadRequest' }
|
|
2313
2292
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
2314
|
-
'404': { $ref: '#/components/responses/NotFound' }
|
|
2315
2293
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
2316
2294
|
|
|
2317
2295
|
/v1/sessions/{sessionId}/workspace/{key}/tree:
|
|
@@ -2325,38 +2303,19 @@ paths:
|
|
|
2325
2303
|
name: key
|
|
2326
2304
|
required: true
|
|
2327
2305
|
schema: { type: string }
|
|
2328
|
-
description: 'Snapshot key (entryId), or the alias `latest` = the newest snapshot.'
|
|
2329
2306
|
get:
|
|
2330
2307
|
tags: [workspace]
|
|
2331
2308
|
operationId: workspaceTree
|
|
2332
|
-
x-status: retired #
|
|
2333
|
-
summary: 'RETIRED (server 7.52.0, S-15) — snapshot file tree;
|
|
2334
|
-
description: >
|
|
2335
|
-
🔴 RETIRED as of server 7.52.0
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
parameters:
|
|
2341
|
-
- in: query
|
|
2342
|
-
name: limit
|
|
2343
|
-
required: false
|
|
2344
|
-
schema: { type: integer, minimum: 1, maximum: 2000 }
|
|
2345
|
-
- in: query
|
|
2346
|
-
name: offset
|
|
2347
|
-
required: false
|
|
2348
|
-
schema: { type: integer, minimum: 0 }
|
|
2309
|
+
x-status: retired # server 7.52.0: whole-tree snapshot epoch retired; sdk 26.0.0 deleted the verb (support floor 7.52.0).
|
|
2310
|
+
summary: 'RETIRED (server 7.52.0, S-15) — snapshot file tree; every supported server (>= 7.52.0) answers 501 capability.workspace_retired.'
|
|
2311
|
+
description: >
|
|
2312
|
+
🔴 RETIRED as of `@sema-agent/server` 7.52.0 (S-15/design/381): the E19 whole-tree snapshot store this face projected was replaced by per-edited-file
|
|
2313
|
+
rewind HISTORY (only files the agent edited — there is no "whole workspace" manifest to browse). The route SHAPE is kept server-side as a tombstone:
|
|
2314
|
+
every supported server answers 501 `capability.workspace_retired` (a named refusal, not a 404 that reads like a typo), and `capabilities.workspace` is
|
|
2315
|
+
always `false` (same predicate). No 1:1 replacement: rewind restore rides `TaskRequest.restoreFiles` (probe `capabilities.restoreFiles`); per-file
|
|
2316
|
+
history blobs ride `GET /v1/sessions/{sessionId}/sync/history/blobs/{hash}`. sdk 26.0.0 deleted `client.workspace` (its 200 shapes only existed on 1.299–7.51).
|
|
2349
2317
|
responses:
|
|
2350
|
-
'200':
|
|
2351
|
-
description: One page of the tree.
|
|
2352
|
-
content:
|
|
2353
|
-
application/json:
|
|
2354
|
-
# 封闭两层(census 批2 第三段,2026-07-30):铸造点 `routes/sessions.ts`(信封与行两处)。条件键=信封 `nextOffset`(尾页省略)、行 `size`(blobSizes 面缺席省略)。
|
|
2355
|
-
# 4.3.0:形提成具名 `WorkspaceTreePage`(理由同 list 腿 —— CAPS-OPS-8)。
|
|
2356
|
-
schema: { $ref: '#/components/schemas/WorkspaceTreePage' }
|
|
2357
|
-
'400': { $ref: '#/components/responses/BadRequest' }
|
|
2358
2318
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
2359
|
-
'404': { $ref: '#/components/responses/NotFound' }
|
|
2360
2319
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
2361
2320
|
|
|
2362
2321
|
/v1/sessions/{sessionId}/workspace/{key}/file:
|
|
@@ -2373,46 +2332,16 @@ paths:
|
|
|
2373
2332
|
get:
|
|
2374
2333
|
tags: [workspace]
|
|
2375
2334
|
operationId: workspaceFile
|
|
2376
|
-
x-status: retired #
|
|
2377
|
-
summary: 'RETIRED (server 7.52.0, S-15) — single
|
|
2378
|
-
description: >
|
|
2379
|
-
🔴 RETIRED as of server 7.52.0
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
untrusted; `x-content-type-options: nosniff` always set) + the `x-sema-content-binary` discriminator
|
|
2385
|
-
header (first-8KiB NUL sniff, [1894]③). Capped at `capabilities.workspace.maxFileBytes` (default
|
|
2386
|
-
8MiB, WORKSPACE_FILE_MAX_BYTES) — over-cap ⇒ 413 STRUCTURED envelope
|
|
2387
|
-
`{errorCode: "workspace_file_too_large", sizeBytes, limit}` ([1894]①; archive was the escape hatch).
|
|
2388
|
-
With the SQL size index the 413 fired BEFORE fetching the bytes.
|
|
2389
|
-
parameters:
|
|
2390
|
-
- in: query
|
|
2391
|
-
name: path
|
|
2392
|
-
required: true
|
|
2393
|
-
schema: { type: string }
|
|
2394
|
-
description: The tree's relPath, verbatim.
|
|
2335
|
+
x-status: retired # server 7.52.0: whole-tree snapshot epoch retired; sdk 26.0.0 deleted the verb (support floor 7.52.0).
|
|
2336
|
+
summary: 'RETIRED (server 7.52.0, S-15) — snapshot single file; every supported server (>= 7.52.0) answers 501 capability.workspace_retired.'
|
|
2337
|
+
description: >
|
|
2338
|
+
🔴 RETIRED as of `@sema-agent/server` 7.52.0 (S-15/design/381): the E19 whole-tree snapshot store this face projected was replaced by per-edited-file
|
|
2339
|
+
rewind HISTORY (only files the agent edited — there is no "whole workspace" manifest to browse). The route SHAPE is kept server-side as a tombstone:
|
|
2340
|
+
every supported server answers 501 `capability.workspace_retired` (a named refusal, not a 404 that reads like a typo), and `capabilities.workspace` is
|
|
2341
|
+
always `false` (same predicate). No 1:1 replacement: rewind restore rides `TaskRequest.restoreFiles` (probe `capabilities.restoreFiles`); per-file
|
|
2342
|
+
history blobs ride `GET /v1/sessions/{sessionId}/sync/history/blobs/{hash}`. sdk 26.0.0 deleted `client.workspace` (its 200 shapes only existed on 1.299–7.51).
|
|
2395
2343
|
responses:
|
|
2396
|
-
'200':
|
|
2397
|
-
description: The file bytes.
|
|
2398
|
-
content:
|
|
2399
|
-
application/octet-stream:
|
|
2400
|
-
schema: { type: string, format: binary }
|
|
2401
|
-
'400': { $ref: '#/components/responses/BadRequest' }
|
|
2402
2344
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
2403
|
-
'404': { $ref: '#/components/responses/NotFound' }
|
|
2404
|
-
'413':
|
|
2405
|
-
description: 'Structured over-cap envelope: {error, errorCode: "workspace_file_too_large", sizeBytes, limit}.'
|
|
2406
|
-
content:
|
|
2407
|
-
application/json:
|
|
2408
|
-
schema:
|
|
2409
|
-
type: object
|
|
2410
|
-
required: [error, errorCode, sizeBytes, limit]
|
|
2411
|
-
properties:
|
|
2412
|
-
error: { type: string }
|
|
2413
|
-
errorCode: { type: string, enum: [workspace_file_too_large] }
|
|
2414
|
-
sizeBytes: { type: integer }
|
|
2415
|
-
limit: { type: integer }
|
|
2416
2345
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
2417
2346
|
|
|
2418
2347
|
/v1/sessions/{sessionId}/workspace/{key}/archive:
|
|
@@ -2429,28 +2358,17 @@ paths:
|
|
|
2429
2358
|
get:
|
|
2430
2359
|
tags: [workspace]
|
|
2431
2360
|
operationId: workspaceArchive
|
|
2432
|
-
x-status: retired #
|
|
2433
|
-
summary: 'RETIRED (server 7.52.0, S-15) —
|
|
2434
|
-
description: >
|
|
2435
|
-
🔴 RETIRED as of server 7.52.0
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
import) ⇒ 502 (retryable). A blob vanishing MID-stream destroyed the connection (honest truncation)
|
|
2441
|
-
rather than faking a complete archive.
|
|
2361
|
+
x-status: retired # server 7.52.0: whole-tree snapshot epoch retired; sdk 26.0.0 deleted the verb (support floor 7.52.0).
|
|
2362
|
+
summary: 'RETIRED (server 7.52.0, S-15) — snapshot tar archive; every supported server (>= 7.52.0) answers 501 capability.workspace_retired.'
|
|
2363
|
+
description: >
|
|
2364
|
+
🔴 RETIRED as of `@sema-agent/server` 7.52.0 (S-15/design/381): the E19 whole-tree snapshot store this face projected was replaced by per-edited-file
|
|
2365
|
+
rewind HISTORY (only files the agent edited — there is no "whole workspace" manifest to browse). The route SHAPE is kept server-side as a tombstone:
|
|
2366
|
+
every supported server answers 501 `capability.workspace_retired` (a named refusal, not a 404 that reads like a typo), and `capabilities.workspace` is
|
|
2367
|
+
always `false` (same predicate). No 1:1 replacement: rewind restore rides `TaskRequest.restoreFiles` (probe `capabilities.restoreFiles`); per-file
|
|
2368
|
+
history blobs ride `GET /v1/sessions/{sessionId}/sync/history/blobs/{hash}`. sdk 26.0.0 deleted `client.workspace` (its 200 shapes only existed on 1.299–7.51).
|
|
2442
2369
|
responses:
|
|
2443
|
-
'200':
|
|
2444
|
-
description: The tar stream.
|
|
2445
|
-
content:
|
|
2446
|
-
application/x-tar:
|
|
2447
|
-
schema: { type: string, format: binary }
|
|
2448
|
-
'400': { $ref: '#/components/responses/BadRequest' }
|
|
2449
2370
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
2450
|
-
'404': { $ref: '#/components/responses/NotFound' }
|
|
2451
|
-
'422': { description: 'Snapshot contains paths not representable in ustar (names the first 5).' }
|
|
2452
2371
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
2453
|
-
'502': { description: 'Snapshot is missing blobs (raced a reap or a partial import) — retry or pick another snapshot.' }
|
|
2454
2372
|
|
|
2455
2373
|
/v1/sessions/{sessionId}/policy:
|
|
2456
2374
|
parameters:
|
|
@@ -2510,7 +2428,7 @@ paths:
|
|
|
2510
2428
|
summary: Write the per-session tool-permission rules (tighten-only).
|
|
2511
2429
|
description: >
|
|
2512
2430
|
E6 — write the per-session rules. 🔴 TIGHTEN-ONLY for a normal principal: a loosen (remove a deny / widen
|
|
2513
|
-
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
|
|
2514
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
|
|
2515
2433
|
key, including the retired `commandAllow` / `commandDeny`, → 400 `rules.invalid_shape` on server >= 7.129.0) → malformed body 400. ownerless
|
|
2516
2434
|
session → 409; non-owner → 404. 501 without a session-policy store.
|
|
@@ -2538,6 +2456,9 @@ paths:
|
|
|
2538
2456
|
# write judge stamps the current grammar). `grammar` newer than the engine's ⇒ `400 rules.invalid_shape`.
|
|
2539
2457
|
rev: { type: integer, minimum: 0 }
|
|
2540
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' }
|
|
2541
2462
|
expectedRev: { type: integer, minimum: 0, description: CAS — reject if the stored rev differs. }
|
|
2542
2463
|
responses:
|
|
2543
2464
|
'200':
|
|
@@ -2574,7 +2495,8 @@ paths:
|
|
|
2574
2495
|
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
2575
2496
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
2576
2497
|
'403':
|
|
2577
|
-
description:
|
|
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).
|
|
2578
2500
|
content:
|
|
2579
2501
|
application/json:
|
|
2580
2502
|
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
@@ -2663,7 +2585,7 @@ paths:
|
|
|
2663
2585
|
narrow it, switch with a default arm; `httpStatus` 401 is a re-authorize door); `reason` is HUMAN TEXT
|
|
2664
2586
|
(server-redacted, length-bounded) and must NOT be parsed.
|
|
2665
2587
|
Gates, the same family as steer / capture-optout: 501 `capability.run_store_required` (no durable run
|
|
2666
|
-
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
|
|
2667
2589
|
`request.path_malformed` (bad percent-encoding in the id) · 404 `not_found.session` — unknown session
|
|
2668
2590
|
AND someone else's session answer the SAME code and SAME message (deliberate: no existence oracle); an
|
|
2669
2591
|
EXPLICIT operator may re-dial any tenant's session · 422 `steering.invalid_content` · 409
|
|
@@ -2729,7 +2651,7 @@ paths:
|
|
|
2729
2651
|
the model's description, falling back to the launch command text (content family: redacted, cut at 500
|
|
2730
2652
|
chars). At most 500 rows (core `list()`'s own cap). 🔴 IN-PROCESS: only rows on THIS replica.
|
|
2731
2653
|
Zero model work: not billable, not refused by drain. Gates (same family as `mcp/reconnect`): 501
|
|
2732
|
-
`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
|
|
2733
2655
|
session AND someone else's session answer the SAME code and SAME message (no existence oracle); an EXPLICIT
|
|
2734
2656
|
operator may read any session · 400 `request.path_malformed`. Probe with `capabilities.background.listFace`;
|
|
2735
2657
|
ABSENT key = an older server, where this path is 404 `not_found.route` (same status, different code).
|
|
@@ -3002,6 +2924,14 @@ paths:
|
|
|
3002
2924
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
3003
2925
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
3004
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' }
|
|
3005
2935
|
|
|
3006
2936
|
/v1/sessions/{sessionId}/sync/entries:
|
|
3007
2937
|
parameters:
|
|
@@ -3040,50 +2970,6 @@ paths:
|
|
|
3040
2970
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
3041
2971
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
3042
2972
|
|
|
3043
|
-
/v1/sessions/{sessionId}/sync/snapshots/{key}/blobs/{hash}:
|
|
3044
|
-
parameters:
|
|
3045
|
-
- $ref: '#/components/parameters/PrincipalHeader'
|
|
3046
|
-
- in: path
|
|
3047
|
-
name: sessionId
|
|
3048
|
-
required: true
|
|
3049
|
-
schema: { type: string }
|
|
3050
|
-
- in: path
|
|
3051
|
-
name: key
|
|
3052
|
-
required: true
|
|
3053
|
-
schema: { type: string }
|
|
3054
|
-
description: The snapshot key (= a `SessionTreeEntry.id`).
|
|
3055
|
-
- in: path
|
|
3056
|
-
name: hash
|
|
3057
|
-
required: true
|
|
3058
|
-
schema: { type: string, pattern: '^[0-9a-f]{64}$' }
|
|
3059
|
-
description: The content-address (SHA-256, 64 lowercase-hex).
|
|
3060
|
-
get:
|
|
3061
|
-
tags: [sessions]
|
|
3062
|
-
operationId: sessionSyncGetBlob
|
|
3063
|
-
x-status: retired # S-15 (server 7.52.0): the E19-era snapshot-scoped PULL arm was REPLACED by GET …/sync/history/blobs/{hash} — ≥7.52 this path 404s (not_found.route).
|
|
3064
|
-
summary: 'RETIRED (server 7.52.0, S-15) — snapshot-scoped blob PULL; use GET …/sync/history/blobs/{hash}. Live on ≤7.51 only.'
|
|
3065
|
-
description: >
|
|
3066
|
-
🔴 RETIRED as of `@sema-agent/server` 7.52.0 (S-15/design/381): a blob is scoped by the session's
|
|
3067
|
-
HISTORY GRAPH now, not by a per-snapshot manifest key, so this arm was replaced by
|
|
3068
|
-
`GET /v1/sessions/{sessionId}/sync/history/blobs/{hash}` (sessionSyncGetHistoryBlob — same
|
|
3069
|
-
octet-stream body, same no-oracle 404 posture, scope check = "referenced by this session's
|
|
3070
|
-
exportHistory envelope"). On ≥7.52 this path no longer matches the sync route family and answers 404
|
|
3071
|
-
`not_found.route`. SDK: `sessions.sync.getHistoryBlob` (the legacy `getBlob` verb stays, deprecated,
|
|
3072
|
-
for ≤7.51 servers inside the support range).
|
|
3073
|
-
Historical shape (≤7.51): one content-addressed blob's RAW bytes; the cloud served a hash ONLY if
|
|
3074
|
-
THIS session's `:key` manifest referenced it (foreign/unreferenced → 404 `blob not found`). 400 on a
|
|
3075
|
-
non-64-hex hash or an undecodable key/hash. Owner-scoped (404).
|
|
3076
|
-
responses:
|
|
3077
|
-
'200':
|
|
3078
|
-
description: The blob bytes.
|
|
3079
|
-
content:
|
|
3080
|
-
application/octet-stream:
|
|
3081
|
-
schema: { type: string, format: binary }
|
|
3082
|
-
'400': { $ref: '#/components/responses/BadRequest' }
|
|
3083
|
-
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
3084
|
-
'404': { $ref: '#/components/responses/NotFound' }
|
|
3085
|
-
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
3086
|
-
|
|
3087
2973
|
/v1/sessions/{sessionId}/sync/history/blobs/{hash}:
|
|
3088
2974
|
parameters:
|
|
3089
2975
|
- $ref: '#/components/parameters/PrincipalHeader'
|
|
@@ -3109,7 +2995,7 @@ paths:
|
|
|
3109
2995
|
→ 404 `not_found.blob`, never a blind read; a server whose file-history store has no blob faces
|
|
3110
2996
|
(e.g. the local file backend) answers the SAME 404 shape (no oracle — "nothing servable" and "not
|
|
3111
2997
|
referenced" are deliberately indistinguishable). 400 `request.id_invalid` on a non-64-hex hash.
|
|
3112
|
-
Owner-scoped (404). Replaces the retired `
|
|
2998
|
+
Owner-scoped (404). Replaces the retired snapshot-scoped PULL arm of servers <= 7.51 (below the support floor; sdk 26.0.0 deleted `getBlob` and its path).
|
|
3113
2999
|
responses:
|
|
3114
3000
|
'200':
|
|
3115
3001
|
description: The blob bytes.
|
|
@@ -3120,6 +3006,14 @@ paths:
|
|
|
3120
3006
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
3121
3007
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
3122
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' }
|
|
3123
3017
|
|
|
3124
3018
|
/v1/sessions/{sessionId}/sync/blobs/{hash}:
|
|
3125
3019
|
parameters:
|
|
@@ -3142,12 +3036,10 @@ paths:
|
|
|
3142
3036
|
body). The cloud verifies `sha256(body) === :hash` (the content-address integrity guard) BEFORE storing — a
|
|
3143
3037
|
mismatch → 400 `blob hash mismatch`. own-or-FRESH gate. Per-blob cap 64 MiB → 413 mid-stream beyond it. A
|
|
3144
3038
|
backend write failure (e.g. a MinIO non-2xx) → 502 `blob store write failed`. 204 on success (no body). The
|
|
3145
|
-
PUSH path is the BARE `blobs/:hash` (the PULL is `history/blobs/:hash`
|
|
3146
|
-
`snapshots/:key/blobs/:hash` before). 🔴 server ≥7.52 (S-15 换店): the blob faces ride the
|
|
3039
|
+
PUSH path is the BARE `blobs/:hash` (the PULL is `history/blobs/:hash`). The blob faces ride the
|
|
3147
3040
|
file-HISTORY store — a deployment whose store has no blob faces (the local file backend) answers 501
|
|
3148
3041
|
`capability.snapshot_store_required` ("blob upload requires a durable file-history store") while the
|
|
3149
|
-
rest of the sync surface keeps working
|
|
3150
|
-
code, same operator disposition (wire a backend with blob faces).
|
|
3042
|
+
rest of the sync surface keeps working (operator disposition: wire a backend with blob faces).
|
|
3151
3043
|
requestBody:
|
|
3152
3044
|
required: true
|
|
3153
3045
|
content:
|
|
@@ -3217,16 +3109,17 @@ paths:
|
|
|
3217
3109
|
x-status: live
|
|
3218
3110
|
summary: PUSH Phase A — classify + stage (mint a stagingId).
|
|
3219
3111
|
description: >
|
|
3220
|
-
2c session-sync PUSH Phase A — a SMALL metadata body (`entryIds,
|
|
3112
|
+
2c session-sync PUSH Phase A — a SMALL metadata body (`entryIds, fileHistory?, policy, anchors, resolution?, logDigest?, title?, pinned?`); the
|
|
3221
3113
|
entries stream in Phase B. The cloud classifies (§7), pre-checks blob presence (a missing referenced hash →
|
|
3222
|
-
422 `missing_blob` — PUT it first
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
`
|
|
3228
|
-
|
|
3229
|
-
|
|
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`.
|
|
3118
|
+
The file history rides `fileHistory` (a `FileHistoryExport` envelope, `null` / absent = none) and every blob it references must have been
|
|
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
|
|
3120
|
+
no longer declares it). The SDK carries `fileHistory` in `pushSessionBundle` / `importSessionBundle`; a hand-rolled
|
|
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).
|
|
3230
3123
|
`identical` → `{ relation:"identical", basis, payloadVerified }` with NO stagingId (skip Phase B);
|
|
3231
3124
|
`basis` is `"entry-ids+digest"` when the caller supplied a comparable digest and it matched, else
|
|
3232
3125
|
`"entry-ids"` (id-set equality only) — see the `ImportStaged` schema, which has carried these two
|
|
@@ -3239,15 +3132,14 @@ paths:
|
|
|
3239
3132
|
application/json:
|
|
3240
3133
|
schema:
|
|
3241
3134
|
type: object
|
|
3242
|
-
required: [entryIds,
|
|
3135
|
+
required: [entryIds, policy, anchors]
|
|
3243
3136
|
properties:
|
|
3244
3137
|
entryIds: { type: array, items: { type: string } }
|
|
3245
|
-
snapshots: { type: array, items: { $ref: '#/components/schemas/SyncSnapshot' } }
|
|
3246
3138
|
fileHistory:
|
|
3247
3139
|
oneOf:
|
|
3248
3140
|
- { $ref: '#/components/schemas/FileHistoryExport' }
|
|
3249
3141
|
- { type: 'null' }
|
|
3250
|
-
description: '
|
|
3142
|
+
description: 'the file-history envelope; `null` / absent = none. Every blob it references must already be PUT.'
|
|
3251
3143
|
policy: { type: array, items: { $ref: '#/components/schemas/SessionRulesRecord' } }
|
|
3252
3144
|
anchors: { type: array, items: { $ref: '#/components/schemas/SyncAnchor' } }
|
|
3253
3145
|
title:
|
|
@@ -3300,7 +3192,12 @@ paths:
|
|
|
3300
3192
|
content:
|
|
3301
3193
|
application/json:
|
|
3302
3194
|
schema: { $ref: '#/components/schemas/SyncConflict' }
|
|
3303
|
-
'422':
|
|
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' }
|
|
3304
3201
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
3305
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 起)
|
|
3306
3203
|
|
|
@@ -3324,7 +3221,7 @@ paths:
|
|
|
3324
3221
|
description: >
|
|
3325
3222
|
2c session-sync PUSH Phase B — stream the entry log as an NDJSON REQUEST body into the staged shadow id; the
|
|
3326
3223
|
cloud validates per-line (the streaming invariant gate) then `finish()` (exactly-one-root + leaf-resolvable) →
|
|
3327
|
-
commits (the atomic swap) → replays
|
|
3224
|
+
commits (the atomic swap) → replays fileHistory/policy/anchors. Returns `{ relation }` (the AUTHORITATIVE in-txn
|
|
3328
3225
|
re-classify). Re-POSTing the same stagingId is idempotent. Typed errors: a per-entry / structural violation →
|
|
3329
3226
|
422 `invalid_entries`; a line too large → 413; a commit-time TOCTOU flip to fork/stale → 409 `conflict`
|
|
3330
3227
|
(SyncConflictError); a refused policy loosen → 403 `loosen_forbidden`; a wrong/foreign stagingId → 404.
|
|
@@ -3342,7 +3239,8 @@ paths:
|
|
|
3342
3239
|
'400': { $ref: '#/components/responses/BadRequest' }
|
|
3343
3240
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
3344
3241
|
'403':
|
|
3345
|
-
description:
|
|
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.
|
|
3346
3244
|
content:
|
|
3347
3245
|
application/json:
|
|
3348
3246
|
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
@@ -3385,6 +3283,22 @@ paths:
|
|
|
3385
3283
|
application/json:
|
|
3386
3284
|
schema: { $ref: '#/components/schemas/PendingList' }
|
|
3387
3285
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
3286
|
+
'500':
|
|
3287
|
+
description: >-
|
|
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
|
|
3289
|
+
or delete that row) answers `500` + `errorCode: "approvals.read_corrupt"` (body `{ error, errorCode }`; the bad row's coordinates stay off the wire) —
|
|
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
|
|
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`.
|
|
3299
|
+
content:
|
|
3300
|
+
application/json:
|
|
3301
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
3388
3302
|
|
|
3389
3303
|
/v1/approvals/stream:
|
|
3390
3304
|
parameters:
|
|
@@ -3829,6 +3743,20 @@ paths:
|
|
|
3829
3743
|
application/json:
|
|
3830
3744
|
schema: { $ref: '#/components/schemas/InboxList' }
|
|
3831
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' }
|
|
3832
3760
|
|
|
3833
3761
|
/v1/assistant/tasks:
|
|
3834
3762
|
parameters:
|
|
@@ -3850,6 +3778,20 @@ paths:
|
|
|
3850
3778
|
application/json:
|
|
3851
3779
|
schema: { $ref: '#/components/schemas/AssistantTaskList' }
|
|
3852
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' }
|
|
3853
3795
|
|
|
3854
3796
|
/v1/assistant/tasks/{taskId}/resume:
|
|
3855
3797
|
parameters:
|
|
@@ -4458,7 +4400,7 @@ paths:
|
|
|
4458
4400
|
verbatim 400 texts, the two capability bits to probe first and the `deny` posture are on
|
|
4459
4401
|
the PersistRuleSelection schema. Allow-family only (on `deny` the whole field is ignored).
|
|
4460
4402
|
|
|
4461
|
-
⚠️ If the frame carried
|
|
4403
|
+
⚠️ If the frame carried no `ruleOffers`, do not send this key —
|
|
4462
4404
|
there is nothing redeemable for THIS card and the answer is necessarily
|
|
4463
4405
|
`rulePersisted:false` + `rule_lane_unavailable` (the missing material short-circuits BEFORE
|
|
4464
4406
|
any candidate comparison, so it is never `rule_not_offered`). Absence has several causes —
|
|
@@ -5327,39 +5269,6 @@ paths:
|
|
|
5327
5269
|
error: { type: string }
|
|
5328
5270
|
missing: { type: array, items: { type: string } }
|
|
5329
5271
|
|
|
5330
|
-
/v1/approvals/{approvalId}:
|
|
5331
|
-
parameters:
|
|
5332
|
-
- $ref: '#/components/parameters/PrincipalHeader'
|
|
5333
|
-
- in: path
|
|
5334
|
-
name: approvalId
|
|
5335
|
-
required: true
|
|
5336
|
-
schema: { type: string }
|
|
5337
|
-
get:
|
|
5338
|
-
tags: [approvals]
|
|
5339
|
-
operationId: approvalsGet
|
|
5340
|
-
x-status: retired # 5.0.0: legacy approval-store leg deleted — NO deployment serves this route.
|
|
5341
|
-
summary: 'RETIRED (server 5.0.0) — by-id approval read; every deployment answers 404. Use GET /v1/approvals.'
|
|
5342
|
-
description: >
|
|
5343
|
-
🔴 RETIRED as of `@sema-agent/server` 5.0.0 ([2354]/[2356] A-5): the legacy approval-store polling leg
|
|
5344
|
-
that served this route was deleted whole, and the durable checkpoint lane never had a by-id read
|
|
5345
|
-
(it can list by scope or look up by capability token — there is no simple-id query). A worker WITH a
|
|
5346
|
-
checkpoint store answers 404 `not_found.route`; a worker WITHOUT one never matches the `/v1/approvals*`
|
|
5347
|
-
prefix at all. Either way: 404 on every deployment, and that 404 is indistinguishable from "no such
|
|
5348
|
-
approval", which is exactly why the SDK stopped calling it (4.0.1 — `approvals.get` now reads
|
|
5349
|
-
`GET /v1/approvals` and filters by `sessionId`, one round trip instead of a guaranteed-404 probe plus
|
|
5350
|
-
the same list call).
|
|
5351
|
-
Kept in the spec as a tombstone rather than deleted: consumers generated against an older spec need to
|
|
5352
|
-
see WHY the path went away. Decide via `POST /v1/approvals/{sessionId}/decide`; list via `GET /v1/approvals`.
|
|
5353
|
-
Historical shape (below) is left intact for the same reason; it describes no live behavior.
|
|
5354
|
-
responses:
|
|
5355
|
-
'200':
|
|
5356
|
-
description: The approval row (full args projection).
|
|
5357
|
-
content:
|
|
5358
|
-
application/json:
|
|
5359
|
-
schema: { $ref: '#/components/schemas/ApprovalRow' }
|
|
5360
|
-
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
5361
|
-
'404': { $ref: '#/components/responses/NotFound' }
|
|
5362
|
-
|
|
5363
5272
|
/v1/memory/export:
|
|
5364
5273
|
parameters:
|
|
5365
5274
|
- $ref: '#/components/parameters/PrincipalHeader'
|
|
@@ -6076,6 +5985,14 @@ paths:
|
|
|
6076
5985
|
schema: { $ref: '#/components/schemas/WorkflowJournalPage' }
|
|
6077
5986
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
6078
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' }
|
|
6079
5996
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
6080
5997
|
|
|
6081
5998
|
/v1/workflows/{workflowRunId}/agents/{label}/steer:
|
|
@@ -7161,8 +7078,32 @@ components:
|
|
|
7161
7078
|
enabled: { type: boolean }
|
|
7162
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).' }
|
|
7163
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`.
|
|
7164
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".' }
|
|
7165
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
|
+
|
|
7166
7107
|
RunStatus:
|
|
7167
7108
|
type: string
|
|
7168
7109
|
enum: [running, completed, failed, suspended, needs_review, blocked]
|
|
@@ -7445,7 +7386,7 @@ components:
|
|
|
7445
7386
|
CC Rewind parity (E18). Fork the conversation FROM this user-message SessionTreeEntry.id (non-destructive
|
|
7446
7387
|
branch via setLeafId). With `restoreFiles: true` ⇒ "both" (also restore the working tree — the retired spelling
|
|
7447
7388
|
`rewindFiles` still rides the wire but its restore sense is core's typed migration rejection naming `restoreFiles`). Alone ⇒
|
|
7448
|
-
"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);
|
|
7449
7390
|
unknown anchor ⇒ 4xx, capability-off ⇒ 501. Cannot combine with a durable resume.
|
|
7450
7391
|
resumeAtMode:
|
|
7451
7392
|
type: string
|
|
@@ -7479,7 +7420,7 @@ components:
|
|
|
7479
7420
|
kind of handle as `resumeAt`, resolved through the SAME anchor store — typically the `taskId` from that turn's
|
|
7480
7421
|
202 receipt (the server keys the user-message anchor by the run's taskId). Server `src/boot/resolve-spec.ts:686-697`:
|
|
7481
7422
|
`resumeAnchorStore.resolve(sessionId, handle, principal)` → entry id; unknown handle ⇒ 404
|
|
7482
|
-
`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`) ⇒
|
|
7483
7424
|
501. ⚠️ The 501 is NOT the capability's whole predicate: `capabilities.rewindFilesTo` also requires the
|
|
7484
7425
|
file-history store; without it the server does not 501 — core refuses at prepare with
|
|
7485
7426
|
`rewind.store_unconfigured` (server >= 7.104.0 / S-724: a 409 error body on the synchronous `POST /v1/tasks` when
|
|
@@ -7801,7 +7742,7 @@ components:
|
|
|
7801
7742
|
`permissions.defaultMode`'s specific fold is documented on `TaskRequest.permissionMode`.
|
|
7802
7743
|
🔴 ON THIS SUBMIT FACE THE KEY SET IS CLOSED (server >= 7.57.0 for the sub-tree): accepted top-level keys
|
|
7803
7744
|
are `env` / `hooks` / `model` / `outputStyle` / `permissions` / `skillListingBudgetFraction` /
|
|
7804
|
-
`skillListingMaxDescChars` / `ultracode` / `webSearch` (server >= 7.133.0 adds the three TIGHTEN-ONLY seats `complianceAdditionalDenies` / `readDenyPatterns` / `writeProtectedExtra`), and accepted `permissions.*` keys are
|
|
7745
|
+
`skillListingMaxDescChars` / `ultracode` / `webSearch` (server >= 7.133.0 adds the three TIGHTEN-ONLY seats `complianceAdditionalDenies` / `readDenyPatterns` / `writeProtectedExtra`; server >= 7.134.0 adds `approvalFrameFamilies`), and accepted `permissions.*` keys are
|
|
7805
7746
|
`allow` / `ask` / `defaultMode` / `deny` / `disableAutoMode` (server >= 7.133.0 adds `neverAuto`); anything else is 400 `request.body_shape`
|
|
7806
7747
|
with the names in the refusal's `unknownKeys` / `unsupportedKeys` arrays (REFUSED, not dropped — a dropped
|
|
7807
7748
|
key reads as "in effect" to the caller). The object stays OPEN here because the same `SemaSettings`
|
|
@@ -7812,6 +7753,15 @@ components:
|
|
|
7812
7753
|
(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
7754
|
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
7755
|
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).
|
|
7756
|
+
🔴 `settings.approvalFrameFamilies` (server >= 7.134.0; send it on sight of `capabilities.taskSettings.approvalFrameFamilies === true` — NOT of the top-level
|
|
7757
|
+
`capabilities.approvalFrameFamilies`, which <= 7.133.x already has while answering 400 `request.body_shape` for this key): the approval opening-frame FAMILIES this
|
|
7758
|
+
run's client RENDERS — an array of `tool_approval` / `approval_request`, each once (`[]` reads as absent). Bad value (not an array / a word outside the set / a
|
|
7759
|
+
repeat) => 400 `request.field_invalid` with `field: "settings.approvalFrameFamilies"`, `reason: "bad_entry"` and `index` (absent for a non-array). BEHAVIOR (BREAKING, wire):
|
|
7760
|
+
a run whose declaration does not contain the family this worker presents (`capabilities.approvalFrameFamilies`) has NO live approval channel — no card of either
|
|
7761
|
+
family (no fallback, no double send), no replay in the stream preamble of the sync stream or of `GET /v1/runs/{taskId}/events`; every ask goes to the unattended
|
|
7762
|
+
policy at once: with a durable store (`capabilities.approvals`) it parks per the operator policy (default; answer through `POST /v1/approvals/{sessionId}/decide`)
|
|
7763
|
+
or is refused naming the missing family; with NO durable store the gated tool is refused on the spot and the run finishes (no card, no decide). The declaration
|
|
7764
|
+
rides the persisted body onto resume legs.
|
|
7815
7765
|
🔴 `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
7766
|
`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
7767
|
400 `request.field_invalid` with `detail: { reason: "wire_key_invalid", key, path }`; the plugin context has one 8192-character budget.
|
|
@@ -8066,6 +8016,15 @@ components:
|
|
|
8066
8016
|
combination); `taskWriteFaceOpen` is NOT its reading. Residual (server S-908): the binding is keyed by SESSION and written
|
|
8067
8017
|
after the run is claimed (a refused request writes nothing); on deployments without a run store two concurrently accepted
|
|
8068
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"]`).
|
|
8069
8028
|
attachments:
|
|
8070
8029
|
type: object
|
|
8071
8030
|
description: >
|
|
@@ -9659,6 +9618,14 @@ components:
|
|
|
9659
9618
|
redispatched: { type: array, items: { type: string } }
|
|
9660
9619
|
collapsed: { type: string }
|
|
9661
9620
|
|
|
9621
|
+
ApprovalFrameFamily:
|
|
9622
|
+
type: string
|
|
9623
|
+
enum: [tool_approval, approval_request]
|
|
9624
|
+
description: >-
|
|
9625
|
+
The approval opening-frame FAMILY words (closed; the server's `APPROVAL_FRAME_FAMILIES` verbatim and in order): `tool_approval` = the legacy family
|
|
9626
|
+
(live card `tool_approval` / close `tool_approval_complete`); `approval_request` = the card family (`approval_request` / `approval_complete` /
|
|
9627
|
+
batch revoke `approval_revoke`). ONE vocabulary for two faces: the capability `approvalFrameFamilies` (which family this worker presents) and, server >= 7.134.0,
|
|
9628
|
+
the request key `settings.approvalFrameFamilies` (which families this run's client renders).
|
|
9662
9629
|
ApprovalDecision:
|
|
9663
9630
|
type: object
|
|
9664
9631
|
description: HITL decision (`POST /v1/approvals/:sessionId/decide`).
|
|
@@ -9726,30 +9693,17 @@ components:
|
|
|
9726
9693
|
refused by the ENGINE before the CAS (409 `resume_outcome_invalid` + `field: "hostDecision"`, the card stays
|
|
9727
9694
|
pending); outside the closed set ⇒ 400 `request.field_invalid`; any value on a direct-door worker ⇒ 400
|
|
9728
9695
|
`decided_by_not_in_proof`. An older server (no `approvalDecidedBy` bit) refuses the key (400 `request.body_shape`).
|
|
9729
|
-
|
|
9730
|
-
|
|
9731
|
-
|
|
9732
|
-
|
|
9733
|
-
|
|
9734
|
-
|
|
9735
|
-
|
|
9736
|
-
|
|
9737
|
-
|
|
9738
|
-
|
|
9739
|
-
|
|
9740
|
-
properties:
|
|
9741
|
-
id: { type: string }
|
|
9742
|
-
taskId: { type: [string, 'null'] }
|
|
9743
|
-
sessionId: { type: [string, 'null'] }
|
|
9744
|
-
owner: { type: [string, 'null'] }
|
|
9745
|
-
scope: { type: [string, 'null'], description: 'Single-DB fleet scope guard (tenant identity, owner-sourced).' }
|
|
9746
|
-
toolName: { type: string }
|
|
9747
|
-
args: { description: 'The reviewed tool-call args — full fidelity, not redacted (this IS the review surface).' }
|
|
9748
|
-
status: { type: string, enum: [pending, approved, denied, expired] }
|
|
9749
|
-
reason: { type: [string, 'null'] }
|
|
9750
|
-
decidedBy: { type: [string, 'null'] }
|
|
9751
|
-
createdAt: { type: string }
|
|
9752
|
-
decidedAt: { type: [string, 'null'] }
|
|
9696
|
+
feedback:
|
|
9697
|
+
type: string
|
|
9698
|
+
description: >-
|
|
9699
|
+
server >= 7.134.0 (core 7.54.0) — what the approving PERSON writes in the card's "and tell the agent what to do next" input: on the resumed
|
|
9700
|
+
leg it reaches the model as the user's own words (unfenced, unbounded — like the reference's accepted-prompt feedback). ONLY on a person's approve
|
|
9701
|
+
(`decision: "approve"`, `decidedBy` absent or `person`): on a deny or a `decidedBy: "policy"` decision ⇒ 400 `request.field_conflict` with
|
|
9702
|
+
`detail.kind: "approval_feedback"` and `detail.fields` `["feedback", "decision"]` / `["feedback", "decidedBy"]` (a denial's words go in `reason`);
|
|
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
|
|
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
|
|
9706
|
+
`POST /v1/tool-approvals/{id}/respond` does NOT take it. Servers <= 7.133.x refuse the key (400 `request.body_shape`, `unknownKeys: ["feedback"]`).
|
|
9753
9707
|
|
|
9754
9708
|
Capabilities:
|
|
9755
9709
|
type: object
|
|
@@ -9958,7 +9912,7 @@ components:
|
|
|
9958
9912
|
type: boolean
|
|
9959
9913
|
description: >-
|
|
9960
9914
|
Code-only rewind (`TaskRequest.rewindFilesTo`) is wired — mint `routes/capabilities.ts:839` =
|
|
9961
|
-
`Boolean(deps.fileHistoryStore && deps.resumeAnchorStore && deps.sessionStorage?.getLeafId)
|
|
9915
|
+
`Boolean(deps.fileHistoryStore && deps.resumeAnchorStore && headReadOf(deps.sessionStorage))` (<= 7.134.x last conjunct: `deps.sessionStorage?.getLeafId`): it needs the
|
|
9962
9916
|
file-history store (to restore) AND the anchor map (to resolve the target HANDLE). Corrected in sdk 11.4.0
|
|
9963
9917
|
(board [8437] U3): the store is the FILE-HISTORY store (not the pre-S-15 whole-tree snapshot store).
|
|
9964
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
|
|
@@ -10029,32 +9983,20 @@ components:
|
|
|
10029
9983
|
leaseClient: { type: boolean, description: 'server >= 7.121.0, a version bit (always true, rides with the `fleet` object): this binary carries the serving-lease CLIENT — when the engine is a fleet member (`FLEET_GENERATION`) and the center declares in its heartbeat reply that it answers renewals (`servingLease`), the engine renews and is governed by the lease. Deliberately NOT named `servingLease` (that is the center''s bit): this says the client exists, not that a lease is in force — read `/health.leaseState` for that. Absent = an older worker.' }
|
|
10030
9984
|
generationEcho: { type: boolean, description: 'server >= 7.121.0, a version bit (always true, rides with the `fleet` object): this binary echoes env `FLEET_GENERATION` as `generation` on the heartbeat and on `/health` (with `leaseState` / `leaseUntil`), and the drain endpoint `POST /v1/admin/drain` compares the four-part identity including `expectedGeneration`. Absent = an older worker.' }
|
|
10031
9985
|
workspace:
|
|
9986
|
+
type: boolean
|
|
9987
|
+
enum: [false]
|
|
10032
9988
|
description: >
|
|
10033
|
-
|
|
10034
|
-
|
|
10035
|
-
|
|
10036
|
-
⇒ 413 workspace_file_too_large envelope; the archive endpoint is the escape hatch).
|
|
10037
|
-
🔴 RETIRED (server ≥7.52, S-15/design/381): the whole-tree snapshot epoch this face projected was
|
|
10038
|
-
replaced by per-edited-file rewind history, so this key is now ALWAYS false and the four
|
|
10039
|
-
/v1/sessions/{sessionId}/workspace* routes answer 501 `capability.workspace_retired` (route shape
|
|
10040
|
-
kept as a tombstone — a named refusal, not a 404 that reads like a typo). Consumption posture
|
|
10041
|
-
unchanged: probe this bit, false ⇒ don't render the panel.
|
|
10042
|
-
oneOf:
|
|
10043
|
-
- type: boolean
|
|
10044
|
-
enum: [false]
|
|
10045
|
-
- type: object
|
|
10046
|
-
additionalProperties: true
|
|
10047
|
-
properties:
|
|
10048
|
-
browse: { type: boolean }
|
|
10049
|
-
archive: { type: boolean }
|
|
10050
|
-
maxFileBytes: { type: integer }
|
|
9989
|
+
The workspace-browse bit — ALWAYS `false` on every supported server (>= 7.52.0: the whole-tree snapshot epoch was replaced by
|
|
9990
|
+
per-edited-file rewind history; the four /v1/sessions/{sessionId}/workspace* routes answer 501 `capability.workspace_retired`, route shape kept as a
|
|
9991
|
+
tombstone). sdk 26.0.0 deleted the object arm `{ browse, archive, maxFileBytes }` (servers 1.299–7.51, below the support floor).
|
|
10051
9992
|
sessionEvents:
|
|
10052
9993
|
type: boolean
|
|
10053
9994
|
description: >
|
|
10054
9995
|
🔴 契约调和车1 H② (2026-08-02): server HAS been emitting this key (the `sessionEvents` bit in `src/http/routes/capabilities.ts`)
|
|
10055
9996
|
while both this spec and the SDK type were missing it. Gates `GET /v1/sessions/{sessionId}/events`
|
|
10056
|
-
(the session-level SSE head subscription). Predicate is a
|
|
10057
|
-
plus a session store exposing BOTH `getLeafId` and `ownerOf
|
|
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
|
|
10058
10000
|
predicate, so "says yes <=> route works" holds here. False/absent => fall back to polling
|
|
10059
10001
|
`GET /v1/sessions/{sessionId}/head`. NOTE (server [2373]A-5): no consumer is wired to it yet — it is
|
|
10060
10002
|
a prepared slot, correct to gate on, wrong to read as "widely consumed".
|
|
@@ -10140,7 +10082,7 @@ components:
|
|
|
10140
10082
|
approvalFrameFamilies:
|
|
10141
10083
|
type: array
|
|
10142
10084
|
maxItems: 1
|
|
10143
|
-
items: {
|
|
10085
|
+
items: { $ref: '#/components/schemas/ApprovalFrameFamily' }
|
|
10144
10086
|
description: >-
|
|
10145
10087
|
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
10088
|
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`
|
|
@@ -10148,6 +10090,9 @@ components:
|
|
|
10148
10090
|
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
10091
|
`[]` = per-call approvals are off altogether (`TOOL_APPROVAL_ENABLED=false`): no card is presented. Dispatch frame families off THIS array, never off `streamApproval`.
|
|
10150
10092
|
KEY ABSENT (not `[]`) = server <= 7.132.x — there a `streamApproval: true` leg sends BOTH families for one ask.
|
|
10093
|
+
🔴 server >= 7.134.0: this bit says which family THIS WORKER presents, no longer that you will receive cards — a run has a live approval channel only when
|
|
10094
|
+
its submit body declared that family in `settings.approvalFrameFamilies` (send it on sight of `taskSettings.approvalFrameFamilies`, not of this bit — <= 7.133.x
|
|
10095
|
+
already had this bit yet answers 400 `request.body_shape` for the key). The value does not change with a declaration. The words are the request key's words.
|
|
10151
10096
|
streamApproval:
|
|
10152
10097
|
type: boolean
|
|
10153
10098
|
description: >
|
|
@@ -10324,6 +10269,7 @@ components:
|
|
|
10324
10269
|
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
10270
|
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
10271
|
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.' }
|
|
10272
|
+
approvalFrameFamilies: { type: boolean, description: 'server >= 7.134.0, constant true — the request key `settings.approvalFrameFamilies` (which approval opening-frame families this run''s client renders) is accepted. THIS is the trigger bit for that key, NOT the top-level `approvalFrameFamilies`: servers <= 7.133.x already carry the top-level bit yet answer 400 `request.body_shape` (`unknownKeys: ["settings.approvalFrameFamilies"]`) for the key. ABSENT = do not send it.' }
|
|
10327
10273
|
settings:
|
|
10328
10274
|
type: object
|
|
10329
10275
|
description: >
|
|
@@ -10382,11 +10328,29 @@ components:
|
|
|
10382
10328
|
— SQL backends only; `DB_BACKEND=local` is always false). Advertises exactly the two file-history blob faces
|
|
10383
10329
|
`PUT /v1/sessions/{sessionId}/sync/blobs/{hash}` and `GET /v1/sessions/{sessionId}/sync/history/blobs/{hash}`.
|
|
10384
10330
|
READ IT ON BOTH ENDS of a sync: if either is false, sync the conversation only — do not push `fileHistory`, do
|
|
10385
|
-
not PUT blobs (a `fileHistory` pushed to a peer without the blob faces 422s `missing_blob` after
|
|
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
|
|
10386
10332
|
false the two routes answer 501 `capability.snapshot_store_required` (any session id, ahead of the owner check).
|
|
10387
10333
|
A blob that the session history graph does not reference / whose bytes are gone stays a 404 `not_found.blob`.
|
|
10388
10334
|
ABSENT = an older server (<= 7.115.x): the bit cannot be read there (a `DB_BACKEND=local` deployment with
|
|
10389
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: []`).
|
|
10390
10354
|
|
|
10391
10355
|
# ─── S-139(sdk 8.3.0):对 server 7.60.0 的整体重对账补齐的 19 位 ───────────────────────
|
|
10392
10356
|
# 行号 = server 7.60.0 树的 `src/http/routes/capabilities.ts`。语义与谓词的全文在
|
|
@@ -10673,7 +10637,7 @@ components:
|
|
|
10673
10637
|
type: boolean
|
|
10674
10638
|
description: >
|
|
10675
10639
|
server >= 7.85.0 (S-381 / core 7.22.0 #857; mint `routes/capabilities.ts:264` =
|
|
10676
|
-
`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
|
|
10677
10641
|
`POST /v1/sessions/{sessionId}/mcp/reconnect`. FACILITY DEPENDENCY, two conjuncts: a durable run
|
|
10678
10642
|
ledger (the verb reaches the live stream through the run row) AND the session-ownership face (the
|
|
10679
10643
|
inbound owner gate is fail-closed; without it the whole door is 501). Both 501s derive from the SAME
|
|
@@ -10794,7 +10758,7 @@ components:
|
|
|
10794
10758
|
sessionBackgroundFacesAvailable(deps), exitFaces: sessionBackgroundFacesAvailable(deps) }`) — presence of the
|
|
10795
10759
|
session background-task doors: `listFace` = `GET /v1/sessions/{sessionId}/background`, `exitFaces` =
|
|
10796
10760
|
`POST /v1/sessions/{sessionId}/background/stop` (contract §16). Both bits use the SAME predicate as the two
|
|
10797
|
-
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
|
|
10798
10762
|
impossible; without it both bits are explicitly `false` and both doors answer 501
|
|
10799
10763
|
`capability.session_ownership_required`. ENDPOINT-GATE FORM: `false` ⇒ do not call. True only promises the
|
|
10800
10764
|
endpoint — not that this replica holds any background row of that session (the registry is in-process).
|
|
@@ -11845,7 +11809,7 @@ components:
|
|
|
11845
11809
|
modelPlane:
|
|
11846
11810
|
allOf: [{ $ref: '#/components/schemas/ModelPlane' }]
|
|
11847
11811
|
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
|
|
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
|
|
11849
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.
|
|
11850
11814
|
candidateRejected:
|
|
11851
11815
|
allOf: [{ $ref: '#/components/schemas/CandidateRejected' }]
|
|
@@ -11857,6 +11821,25 @@ components:
|
|
|
11857
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.
|
|
11858
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
|
|
11859
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 }
|
|
11860
11843
|
|
|
11861
11844
|
CandidateRejected:
|
|
11862
11845
|
type: object
|
|
@@ -11931,7 +11914,8 @@ components:
|
|
|
11931
11914
|
# 没声明 ⇒ 键缺席 ⇒ 不再是必有键(此前由 `input` 恒算成布尔,对没声明的条目报 false)。
|
|
11932
11915
|
# 🔴 server 7.131.0 起 `reasoning` 同样三态:env 车道 `MODEL_REASONING` / `MODEL_VISION` 缺省 true 退役、中心名册条目不写即不声明
|
|
11933
11916
|
# ⇒ 两键都「未声明 = 键缺席」,`required` 只剩 name / id / provider(sdk 23.1.0;TS 型 `ModelInfo.reasoning?` 早已可选)。
|
|
11934
|
-
required
|
|
11917
|
+
# sdk 26.0.0(支持底线 7.52.0):`routePairing` 进 required(≥7.45 恒铸,此前因地板 3.0.0 不进)。
|
|
11918
|
+
required: [name, id, provider, routePairing]
|
|
11935
11919
|
additionalProperties: false
|
|
11936
11920
|
properties:
|
|
11937
11921
|
name: { type: string, description: 'the @mention handle (user-pickable model name).' }
|
|
@@ -11955,8 +11939,8 @@ components:
|
|
|
11955
11939
|
adjudicated" must stay distinguishable on the wire). Declared here 2026-09-10 alongside `compat`:
|
|
11956
11940
|
this schema is CLOSED, and the key has been minted unconditionally since 7.45 — so every real
|
|
11957
11941
|
/v1/models response has been structurally rejected by a strict validator for the whole time (same
|
|
11958
|
-
class as `hasBidiControls` on InboxRow).
|
|
11959
|
-
|
|
11942
|
+
class as `hasBidiControls` on InboxRow). REQUIRED since sdk 26.0.0 (the support floor 7.52.0 is above 7.45; it
|
|
11943
|
+
was optional only while the floor was 3.0.0).
|
|
11960
11944
|
compat:
|
|
11961
11945
|
$ref: '#/components/schemas/ModelCompat'
|
|
11962
11946
|
description: >-
|
|
@@ -12236,6 +12220,8 @@ components:
|
|
|
12236
12220
|
name: { type: string, description: "The workflow script's declared `meta.name`." }
|
|
12237
12221
|
description: { type: string, description: "The workflow script's declared `meta.description` (one-liner)." }
|
|
12238
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.)' }
|
|
12239
12225
|
currentPhase: { type: string, description: 'Title of the phase currently executing (absent once terminal).' }
|
|
12240
12226
|
status: { $ref: '#/components/schemas/WorkflowRunStatus' }
|
|
12241
12227
|
phaseCount: { type: integer, description: 'Phases recorded.' }
|
|
@@ -12414,6 +12400,8 @@ components:
|
|
|
12414
12400
|
description: { type: string, description: "The workflow script's declared `meta.description` (redacted, one-liner)." }
|
|
12415
12401
|
status: { $ref: '#/components/schemas/WorkflowRunStatus' }
|
|
12416
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.)' }
|
|
12417
12405
|
stats: { $ref: '#/components/schemas/WorkflowRunStats' }
|
|
12418
12406
|
startedAt: { type: integer }
|
|
12419
12407
|
endedAt: { type: integer }
|
|
@@ -12766,19 +12754,6 @@ components:
|
|
|
12766
12754
|
startedAt: { type: integer }
|
|
12767
12755
|
elapsedMs: { type: integer }
|
|
12768
12756
|
tokens: { type: integer }
|
|
12769
|
-
# tokenDir REMOVED (engine ruling 2026-06-28): dead field, 0 producer / 0 consumer — spec caught up 2026-07-26.
|
|
12770
|
-
queuedCount:
|
|
12771
|
-
type: integer
|
|
12772
|
-
description: >-
|
|
12773
|
-
🔴 PHANTOM KEY — NO server version ever emits it (verified against the published 7.87.0 bytes:
|
|
12774
|
-
the row type declares it and the fleet route's per-key table forwards it, but NO producer writes it
|
|
12775
|
-
— not `publishTask`'s call sites, not core's bg-sink tick, not the durable reconcile leg — and the
|
|
12776
|
-
wire contract does not mention it either). The server deleted it as a DEAD KEY (S-459, zero
|
|
12777
|
-
behavioural impact since `!== undefined` was always false). It stays declared here only because
|
|
12778
|
-
removing an exported field is BREAKING = major-only; it is registered with its deletion condition
|
|
12779
|
-
in `test/_keyset-divergence.ts` (`FLEET_TASK_ROW_PHANTOM_KEYS`) and goes with the next major.
|
|
12780
|
-
DO NOT read it as "queue depth is already reported" — a permanently absent optional key is a FALSE
|
|
12781
|
-
LEAD. Real queue depth needs a NEW key WITH a producer, not this one resurrected.
|
|
12782
12757
|
awaitingPlanApproval: { type: boolean }
|
|
12783
12758
|
currentAction: { type: string, description: "The child's most recent tool intent as one human-readable line (UNTRUSTED argument head, redacted server-side)." }
|
|
12784
12759
|
currentTool:
|
|
@@ -12892,6 +12867,18 @@ components:
|
|
|
12892
12867
|
failedCount: { type: integer }
|
|
12893
12868
|
elapsedMs: { type: integer }
|
|
12894
12869
|
tokens: { type: integer }
|
|
12870
|
+
errorCode:
|
|
12871
|
+
type: string
|
|
12872
|
+
description: >-
|
|
12873
|
+
server >= 7.134.0 — the workflow run's MACHINE code (open set; the same words as the same-named slot on `GET /v1/workflows/{id}`): carried verbatim when the
|
|
12874
|
+
run row has one, the key ABSENT otherwise (never an empty string). A parked workflow run reads `status: "failed"` + `errorCode: "workflow.agent_parked"`
|
|
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
|
|
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.
|
|
12895
12882
|
FleetFrame:
|
|
12896
12883
|
description: >
|
|
12897
12884
|
One decoded frame off GET /v1/fleet/stream (service streamFleet emit set, src/http/routes/fleet.ts). On the
|
|
@@ -13105,7 +13092,8 @@ components:
|
|
|
13105
13092
|
DISTINCT vocabulary from AgentEvent (the durable log is BLOCK-grained, each frame is a delta to append).
|
|
13106
13093
|
Resumable (each `id:` = the durable task_event.seq; on 416 the server returns `{retainedFrom}` and the SDK
|
|
13107
13094
|
resumes from there after a turns full-sync). Known events: meta / block-thinking-delta / block-content-delta
|
|
13108
|
-
/ tool-call / tool-result /
|
|
13095
|
+
/ tool-call / tool-result / prompt-assembled / cross-boundary-taint (server >= 7.134.0: the device lane's taint row + `seq`, see
|
|
13096
|
+
`Event_cross_boundary_taint`) / turn / done / error (+ heartbeat keep-alives the SDK swallows). Open set.
|
|
13109
13097
|
NOTE: SSE framing on the wire (event name + JSON data + optional id), not a JSON body.
|
|
13110
13098
|
required: [event, data]
|
|
13111
13099
|
additionalProperties: true
|
|
@@ -13122,10 +13110,11 @@ components:
|
|
|
13122
13110
|
description: >
|
|
13123
13111
|
契约调和车1 G(2026-08-02;server [2373]C-12, `src/trace/project.ts` + the meta frame at
|
|
13124
13112
|
`routes/trace-usage.ts`): the frame's own event name, DOUBLE-EMITTED inside `data`. Always
|
|
13125
|
-
present on server >=
|
|
13126
|
-
|
|
13127
|
-
|
|
13128
|
-
|
|
13113
|
+
present on every named frame of a supported server (>= 7.52.0; minted since server 5.0.0) — the SDK's
|
|
13114
|
+
typed arms declare it REQUIRED since 26.0.0; it is not in this schema's `required` only because the one
|
|
13115
|
+
schema also covers heartbeats (`data: {}`, which carry none and which the SDK swallows). Equal to the SSE
|
|
13116
|
+
`event:` line verbatim — its reason to exist is the proxy/relay case that strips the `event:` line and
|
|
13117
|
+
forwards only the data JSON, where this is the only key left to dispatch on.
|
|
13129
13118
|
output:
|
|
13130
13119
|
allOf: [{ $ref: '#/components/schemas/ToolOutput' }]
|
|
13131
13120
|
description: >
|
|
@@ -13528,26 +13517,13 @@ components:
|
|
|
13528
13517
|
is RETIRED: the SQL backend rewrote its parked rows once at the 7.117.0 boot to `[{ member: "persisted", entries: [<old value>] }]`, but the
|
|
13529
13518
|
file backend's pre-upgrade parked rows and run results already on disk (`checkpointGate.riskDescriptor`) keep the old key verbatim
|
|
13530
13519
|
(text still redacted, no structured disclosure) — a consumer reading those must still tolerate the old string.
|
|
13531
|
-
# server 7.16.0 [3684]② — durable 队列行归因两键(与活卡 tool_approval 帧的同名键语义同族、判据不同源;
|
|
13532
|
-
# 两条腿对同一只 ask 的在场性可以不一致,两侧都只认在场、都禁把缺席读成否定)。
|
|
13533
|
-
ruleSuggestions:
|
|
13534
|
-
type: array
|
|
13535
|
-
minItems: 1
|
|
13536
|
-
items: { $ref: '#/components/schemas/RuleSuggestion' }
|
|
13537
|
-
description: >
|
|
13538
|
-
server 7.16.0 ([3684]②), ADDITIVE: the rule candidates minted for this ask, replayed off the durable
|
|
13539
|
-
row (checkpoint.rule_suggestions). DISPLAY-ONLY on this leg — the durable queue has no persistRule
|
|
13540
|
-
redemption; the only redemption path is the LIVE card leg (respond + persistRule). Array order =
|
|
13541
|
-
display order; an EXACT entry, when present, is always first. core contract caps candidates at 2;
|
|
13542
|
-
the server enforces a tolerance cap of 4 on read-back — lay out for 4. Absent = pre-column row
|
|
13543
|
-
(parked before the column existed) OR no candidates; render both as "no candidates", never an error.
|
|
13544
13520
|
ruleOffers:
|
|
13545
13521
|
type: array
|
|
13546
13522
|
minItems: 1
|
|
13547
13523
|
items: { $ref: '#/components/schemas/RuleOffer' }
|
|
13548
13524
|
description: >
|
|
13549
13525
|
server >= 7.46.0 (core 5.58.0, design/375) — the discriminated-union successor of the retired
|
|
13550
|
-
`ruleSuggestions`
|
|
13526
|
+
`ruleSuggestions` (servers <= 7.45; sdk 26.0.0 deleted it) on the DURABLE queue row (core `PendingAction.ruleOffers`, the same
|
|
13551
13527
|
contract as the sync leg's `AskRequest.ruleOffers`). A >= 7.46.0 row carries only the new key;
|
|
13552
13528
|
pre-5.58 rows fail the new narrow read entry by entry and land as "no supply" (no compatibility
|
|
13553
13529
|
read — a stale schema means re-ask).
|
|
@@ -13772,25 +13748,20 @@ components:
|
|
|
13772
13748
|
one-off question. The value comes WHOLE from core (`summarizeCheckpoint` echoes it only when the
|
|
13773
13749
|
cause is a member of the engine's set), so the server neither recomputes nor re-screens it. See
|
|
13774
13750
|
ClassifierUnavailable for the open-`cause` rule and the three shapes absence covers.
|
|
13775
|
-
RETIRED: server >= 7.72.0 (core 7.14.0
|
|
13751
|
+
RETIRED: server >= 7.72.0 (core 7.14.0) never mints this key on any of the three faces; its
|
|
13776
13752
|
successor is `disposition.cause` on the gate record (`unavailable` / `parse_error` — a different
|
|
13777
|
-
question, so an old switch does not port). Kept for servers in [7.69.0, 7.72.0) only (sdk 9.2.0)
|
|
13753
|
+
question, so an old switch does not port). Kept for servers in [7.69.0, 7.72.0) only (sdk 9.2.0); the row key retires
|
|
13754
|
+
together with the frame key (floor ≥ 7.106.0, next major); why the frame still declares it: `ToolApprovalFrame.classifierUnavailable`.
|
|
13778
13755
|
ruleStoreUnreadable:
|
|
13779
13756
|
type: string
|
|
13780
13757
|
enum: [store, call]
|
|
13781
13758
|
description: >-
|
|
13782
|
-
server >= 7.72.0 (core 7.14.0 #688 C3), ADDITIVE — WHICH HALF of the rule lane could not be read.
|
|
13783
|
-
|
|
13784
|
-
|
|
13785
|
-
|
|
13786
|
-
|
|
13787
|
-
|
|
13788
|
-
which is not on the card). Absence = `origin` is not that word — NOT an assertion that the rule store
|
|
13789
|
-
is healthy.
|
|
13790
|
-
RETIRED: server >= 7.106.0 (core 7.34.0 C-R172, BREAKING — wire) never mints this key on either face — an
|
|
13791
|
-
unreadable rule store no longer raises a card: that read's rows are SKIPPED by partition and the session
|
|
13792
|
-
stream gets the user-facing `engine_notice` `permission.rule_store_unavailable` (`detail.at` = `prepare` /
|
|
13793
|
-
`call`). Kept for servers in [7.72.0, 7.106.0) only (sdk K-32); `origin: rule_store_unavailable` itself stays.
|
|
13759
|
+
server >= 7.72.0 (core 7.14.0 #688 C3), ADDITIVE — WHICH HALF of the rule lane could not be read (`store` / `call`).
|
|
13760
|
+
The same key, with the same two words and the same reading, as on the live frame; the authoritative statement of the shape, the presence
|
|
13761
|
+
rule, what the two words mean and the retirement wording is on `ToolApprovalFrame.ruleStoreUnreadable`, and this description points to it.
|
|
13762
|
+
RETIRED: server >= 7.106.0 never mints this key on either face (the frame's description names the successor). Kept for
|
|
13763
|
+
servers in [7.72.0, 7.106.0) only (sdk K-32); the row key retires together with the frame key (floor ≥ 7.106.0, next major);
|
|
13764
|
+
`origin: rule_store_unavailable` itself stays.
|
|
13794
13765
|
|
|
13795
13766
|
InboxList:
|
|
13796
13767
|
type: object
|
|
@@ -14329,12 +14300,20 @@ components:
|
|
|
14329
14300
|
server >= 7.92.0 (engine 7.25.0): the session's rule LINEAGE generation. An erase
|
|
14330
14301
|
(`deleteBySession`) ends the lineage — the next write carries gen+1 — so a reader treats a
|
|
14331
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.
|
|
14332
14311
|
grammar:
|
|
14333
14312
|
type: integer
|
|
14334
14313
|
minimum: 0
|
|
14335
14314
|
description: >
|
|
14336
14315
|
server >= 7.129.0 (engine 7.51.2): the rule GRAMMAR the row's entries are read in, stamped by the
|
|
14337
|
-
engine's write judge on every row written through it (
|
|
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`).
|
|
14338
14317
|
Absent = a row stored before the stamp (grammar 0: an allow entry spelled as a rule, `Bash(rm:*)`,
|
|
14339
14318
|
named no tool and admitted nothing — the engine keeps reading such a row that way and announces it
|
|
14340
14319
|
once; the one-time migration rewrites stored rows and stamps them). Echoing a stored body back into
|
|
@@ -14355,6 +14334,18 @@ components:
|
|
|
14355
14334
|
rev: { type: integer }
|
|
14356
14335
|
gen: { type: integer }
|
|
14357
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 }
|
|
14358
14349
|
|
|
14359
14350
|
McpServerStatus:
|
|
14360
14351
|
type: object
|
|
@@ -14685,26 +14676,6 @@ components:
|
|
|
14685
14676
|
parentId: { type: [string, 'null'] }
|
|
14686
14677
|
type: { type: string, description: Entry discriminator (message / leaf / compaction / …) — opaque to the SDK. }
|
|
14687
14678
|
|
|
14688
|
-
SyncSnapshot:
|
|
14689
|
-
type: object
|
|
14690
|
-
description: >
|
|
14691
|
-
One E19 file snapshot keyed by a `SessionTreeEntry.id`. `manifest` = `[relPath, blobHash]` tuples; the blob
|
|
14692
|
-
BYTES are NOT inlined (they ride the content-addressed blob routes).
|
|
14693
|
-
# 封闭(census 批2 第三段,2026-07-30):server 侧类型+铸造点(`session-sync.ts` PULL 出 /
|
|
14694
|
-
# `routes/session-sync.ts` PUSH 校验形)都恰是这两键。
|
|
14695
|
-
additionalProperties: false
|
|
14696
|
-
required: [key, manifest]
|
|
14697
|
-
properties:
|
|
14698
|
-
key: { type: string }
|
|
14699
|
-
manifest:
|
|
14700
|
-
type: array
|
|
14701
|
-
items:
|
|
14702
|
-
type: array
|
|
14703
|
-
minItems: 2
|
|
14704
|
-
maxItems: 2
|
|
14705
|
-
items: { type: string }
|
|
14706
|
-
description: A `[relPath, blobHash]` tuple.
|
|
14707
|
-
|
|
14708
14679
|
SyncAnchor:
|
|
14709
14680
|
type: object
|
|
14710
14681
|
description: One E18 resume-at anchor. `owner` is RE-KEYED to the importing principal on a PUSH (§9).
|
|
@@ -14732,31 +14703,28 @@ components:
|
|
|
14732
14703
|
description: >
|
|
14733
14704
|
2c session-sync `GET …/sync/manifest` payload (wrapped server-side as `{ manifest }`). The THIN cross-backend
|
|
14734
14705
|
snapshot: entry IDS (oldest-first, NOT payloads) + the file-history envelope + policy + anchors + leaf.
|
|
14735
|
-
|
|
14736
|
-
|
|
14737
|
-
|
|
14738
|
-
|
|
14739
|
-
|
|
14740
|
-
`
|
|
14741
|
-
(`GET …/sync/history/blobs/{hash}` / `PUT …/sync/blobs/{hash}`; probe `capabilities.sessionSyncFileHistory` on BOTH ends).
|
|
14742
|
-
The SDK's `manifest()` fills `snapshots: []` when the wire omits it.
|
|
14706
|
+
The server's `exportSessionManifest` mints `{ sessionId, entryIds, entryCount, leafId, fileHistory, policy, anchors }` (server >= 7.125.0 adds the
|
|
14707
|
+
session row's two writable bits `title` / `pinned`, present-iff the session is a row of the list face — see the two properties). `fileHistory`
|
|
14708
|
+
(core's versioned `FileHistoryExport` envelope; `null` = no history / the source store cannot export) is ALWAYS present on a supported server
|
|
14709
|
+
(>= 7.52.0); the retired E19 whole-tree snapshot key `snapshots` of servers <= 7.51 is below the support floor (sdk 26.0.0 deleted it). The blob BYTES
|
|
14710
|
+
never ride the manifest: content-addressed, via the history blob faces (`GET …/sync/history/blobs/{hash}` / `PUT …/sync/blobs/{hash}`; probe
|
|
14711
|
+
`capabilities.sessionSyncFileHistory` on BOTH ends). The SDK returns the wire verbatim.
|
|
14743
14712
|
# 封闭(census 批2 第三段,2026-07-30;16.2.0 按 7.116.0 发布字节重核):铸造点 `session-sync.ts`(exportSessionManifest 尾部
|
|
14744
14713
|
# 字面量)恰是 `{ sessionId, entryIds, entryCount, leafId, fileHistory, policy, anchors }` 7 键(≥7.52),无 `snapshots`;
|
|
14745
14714
|
# server ≥7.125.0 另有 `title` / `pinned` 两键(会话行可写位,present-iff 会话是列表面上的行;条件展开,不进 required);
|
|
14746
|
-
#
|
|
14715
|
+
# sdk 26.0.0(支持底线 7.52.0):`fileHistory` 进 required(支持面内恒铸),`snapshots` 删(≤7.51 才发)。
|
|
14747
14716
|
additionalProperties: false
|
|
14748
|
-
required: [sessionId, entryIds, entryCount, leafId, policy, anchors]
|
|
14717
|
+
required: [sessionId, entryIds, entryCount, leafId, fileHistory, policy, anchors]
|
|
14749
14718
|
properties:
|
|
14750
14719
|
sessionId: { type: string }
|
|
14751
14720
|
entryIds: { type: array, items: { type: string }, description: Oldest-first, verbatim cross-backend. }
|
|
14752
14721
|
entryCount: { type: integer, description: '= entryIds.length (the `/sync/entries` trailer count).' }
|
|
14753
14722
|
leafId: { type: [string, 'null'] }
|
|
14754
|
-
snapshots: { type: array, items: { $ref: '#/components/schemas/SyncSnapshot' }, description: 'server <= 7.51 ONLY (the retired E19 snapshot epoch); server >= 7.52 does not send it — see `fileHistory`.' }
|
|
14755
14723
|
fileHistory:
|
|
14756
14724
|
oneOf:
|
|
14757
14725
|
- { $ref: '#/components/schemas/FileHistoryExport' }
|
|
14758
14726
|
- { type: 'null' }
|
|
14759
|
-
description: '
|
|
14727
|
+
description: 'the per-edited-file rewind history envelope; `null` = no history / the source store cannot export. Always present on a supported server.'
|
|
14760
14728
|
policy: { type: array, items: { $ref: '#/components/schemas/SessionRulesRecord' } }
|
|
14761
14729
|
anchors: { type: array, items: { $ref: '#/components/schemas/SyncAnchor' } }
|
|
14762
14730
|
title:
|
|
@@ -14819,20 +14787,18 @@ components:
|
|
|
14819
14787
|
SessionBundle:
|
|
14820
14788
|
type: object
|
|
14821
14789
|
description: >
|
|
14822
|
-
The portable state of ONE session, ready to replay into another backend. Blobs are NOT inlined (
|
|
14823
|
-
|
|
14824
|
-
assembles this from a manifest + the streamed entries for a PUSH.
|
|
14825
|
-
|
|
14826
|
-
required: [sessionId, entries, snapshots, policy, anchors]
|
|
14790
|
+
The portable state of ONE session, ready to replay into another backend. Blobs are NOT inlined (the file-history
|
|
14791
|
+
envelope carries only blob hashes; the bytes stream through the content-addressed blob routes). The SDK
|
|
14792
|
+
assembles this from a manifest + the streamed entries for a PUSH.
|
|
14793
|
+
required: [sessionId, entries, fileHistory, policy, anchors]
|
|
14827
14794
|
properties:
|
|
14828
14795
|
sessionId: { type: string }
|
|
14829
14796
|
entries: { type: array, items: { $ref: '#/components/schemas/SyncEntry' } }
|
|
14830
|
-
snapshots: { type: array, items: { $ref: '#/components/schemas/SyncSnapshot' } }
|
|
14831
14797
|
fileHistory:
|
|
14832
14798
|
oneOf:
|
|
14833
14799
|
- { $ref: '#/components/schemas/FileHistoryExport' }
|
|
14834
14800
|
- { type: 'null' }
|
|
14835
|
-
description: '
|
|
14801
|
+
description: 'the file-history envelope (same as `SessionManifest.fileHistory`; `null` = none, or the pull did not carry it because the source has no blob faces).'
|
|
14836
14802
|
policy: { type: array, items: { $ref: '#/components/schemas/SessionRulesRecord' } }
|
|
14837
14803
|
anchors: { type: array, items: { $ref: '#/components/schemas/SyncAnchor' } }
|
|
14838
14804
|
|
|
@@ -15020,8 +14986,20 @@ components:
|
|
|
15020
14986
|
<= 7.105.0 answered the synchronous leg 200 + a failed body), `corrupt` (500 — the session rule store
|
|
15021
14987
|
cannot read its own bytes), `internal.memory_control_plane_corrupt` (500 — the memory control plane refuses
|
|
15022
14988
|
fail-closed) and `state.scenario_assignment_unknown` (503, server >= 7.119.0 — the center's scenario assignment for the
|
|
15023
|
-
principal is dangling; only re-pointing the assignment, or adding the scenario back to the deployment, helps)
|
|
15024
|
-
|
|
14989
|
+
principal is dangling; only re-pointing the assignment, or adding the scenario back to the deployment, helps) and
|
|
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
|
|
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`).
|
|
15025
15003
|
🔴 `request.host_posture_refused` (422) has TWO triggers (same code, same status — "you wrote it right, this
|
|
15026
15004
|
deployment will not grant that host posture"): (1) the write-containment face (`permissionMode: bypassPermissions` ⇒
|
|
15027
15005
|
`writeFace: open`; read `capabilities.taskWriteFaceOpen` first); (2) server >= 7.106.0 / S-895: `inheritEnv: "all"` on a
|
|
@@ -15085,17 +15063,30 @@ components:
|
|
|
15085
15063
|
the ids of the background children rooted at this session that are still alive (count = length) — the SAME
|
|
15086
15064
|
strings as the `handle` on `GET /v1/agents/roster` rows. Nothing was deleted; wait for them to finish (or stop
|
|
15087
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`.
|
|
15088
15076
|
reason:
|
|
15089
15077
|
type: string
|
|
15090
15078
|
description: >-
|
|
15091
|
-
A per-code closed word — read it by `errorCode`, never across codes. On
|
|
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 >=
|
|
15092
15083
|
7.106.0 / S-866 ③): `auth-rejected` (the center refused this engine's credential, or the credential file is
|
|
15093
15084
|
unreadable) / `unreachable` (unreachable / 5xx / timeout / bad body / any status other than 200 / 409 / 400 / 401 / 403 —
|
|
15094
15085
|
center 404 / 429 / 3xx included). On 400 `request.field_invalid` for a `settings.permissions` bucket (server >= 7.106.0 /
|
|
15095
15086
|
S-904): `over_cap` (more than `cap` entries; beside `count` + `cap`) / `bad_entry` (a non-string or empty item; beside
|
|
15096
15087
|
`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
|
|
15097
15088
|
`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).
|
|
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.)
|
|
15089
|
+
On 409 refusals of a resume (server >= 7.132.0) and on 422 `parked_resume.startup_failed` (server >= 7.134.0 — the parked background agent's pre-claim failure; absent when the engine gave none): the engine's `CheckpointErrorReason` arm (`detail.reason` passed through; see `blockedBy`). On 409 `conflict.checkpoint_ctx_unsealable` (server >= 7.134.0): why the request's credentials cannot be sealed into, or unsealed from, the stored resume context — a closed five-word set (SDK `CHECKPOINT_CTX_UNSEALABLE_REASONS`); which doors can answer which word is written once, in the SDK documentation of `CHECKPOINT_CTX_UNSEALABLE_REASONS`, not here: `no_key_store` (this server has no sealed-key store) / `key_not_held` (sealed with a key this server does not hold — another replica's, or a replaced sealed-key directory) / `unopenable` (the sealed value is damaged or belongs to another session) / `plaintext` (an older server stored it unsealed) / `unrecognized_seal` (a seal format this version does not know) — the way out depends on the door and the `reason`: `key_not_held` (a resume door) — have the operator restore the sealed-key directory that sealed it and restart that replica (it reads its keys only at startup), then resend the same request; `no_key_store` on the submit door — give the service a writable sealed-key directory (`<data root>/sealed-key/`, read at startup), or resubmit WITHOUT those credentials — resending the same submit body is refused again; `no_key_store` on a resume door — the same way out as `key_not_held`: restore the directory that sealed it and restart, then resend (a fresh empty directory does not help: it holds no such key, so the next read says `key_not_held`); `unopenable` / `plaintext` / `unrecognized_seal` — submit the work as a new task (cancel the still-suspended one first); never auto-retry, never match the sentence. A resume door's refusal decides nothing and starts no resume (the card stays pending; the known exception is a terminal run's steer race arm, which first opens a wake card and, when the second read fails for any of the five reasons, withdraws it and then answers 409 — if the withdrawal fails the server only logs it); a submit door's refusal lands before the task runs (nothing runs; a run row already opened for it is closed as `failed` and its idempotency key released, both best-effort — a failed step is only logged by the server and the answer is still the 409). 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.)
|
|
15099
15090
|
retryAfterSec:
|
|
15100
15091
|
type: number
|
|
15101
15092
|
description: >-
|
|
@@ -15122,6 +15113,10 @@ components:
|
|
|
15122
15113
|
`null` and `""` count), the refusal landed before any decision, and `field` is the literal `settledBy`; the three doors now answer
|
|
15123
15114
|
the SAME sentence. Every such 400 carries it, even when another defect in the same body was hit first (the sentence then names that
|
|
15124
15115
|
defect). Branch on the code plus `field`, never on the sentence; a bad body WITHOUT that key carries no `field`. SDK: `err.extra.field`.
|
|
15116
|
+
server >= 7.134.0: on 409 `conflict.checkpoint_ctx_unsealable` `field` is the SUBMIT-BODY PATH of the credential slot that cannot be sealed or unsealed (arrays
|
|
15117
|
+
`[i]`, record members `.key`: `mcpServers[0].transport.headers.Authorization`, `settings.hooks.PreToolUse[0].hooks[0].headers.Authorization`; the key
|
|
15118
|
+
name is not a secret, the value never enters the refusal); on 400 `request.field_invalid` for the request key `settings.approvalFrameFamilies` it is
|
|
15119
|
+
that literal (with `reason: "bad_entry"` and the bad `index`; no `index` for a non-array).
|
|
15125
15120
|
detail:
|
|
15126
15121
|
description: >
|
|
15127
15122
|
🔴 SHAPES differ by code — branch on `field` (or the `errorCode`), never guess from the type of `detail`. On 503
|
|
@@ -15256,8 +15251,9 @@ components:
|
|
|
15256
15251
|
callerTrust: { type: string }
|
|
15257
15252
|
- type: object
|
|
15258
15253
|
description: >-
|
|
15259
|
-
server >= 7.133.0 — 400 `request.field_conflict`: the request body contradicts itself. `kind` = which constraint (a CLOSED fourteen
|
|
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
|
|
15254
|
+
server >= 7.133.0 — 400 `request.field_conflict`: the request body contradicts itself. `kind` = which constraint (a CLOSED set — fourteen words, fifteen from server >= 7.134.0 — read open: cascade_verify | image_profile | resume_anchor |
|
|
15255
|
+
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 |
|
|
15256
|
+
approval_feedback (server >= 7.134.0: `feedback` on a deny — fields `["feedback", "decision"]` — or on a policy decision — `["feedback", "decidedBy"]`));
|
|
15261
15257
|
`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
15258
|
required: [kind, fields]
|
|
15263
15259
|
additionalProperties: false
|
|
@@ -15303,6 +15299,8 @@ components:
|
|
|
15303
15299
|
the engine's `detail.code` passes through as `blockedBy` (today only `checkpoint.config_refused` carries it — the closed set is core's
|
|
15304
15300
|
`CHECKPOINT_CONFIG_REFUSAL_CODES`, 40 words since core 7.53.0; read it as "the errorCode a fresh run would report for the same bad configuration"),
|
|
15305
15301
|
next to `field` and `reason`, absent keys not minted (SDK `ResumeRefusalExtra`).
|
|
15302
|
+
server >= 7.134.0 / core 7.54.0: the SAME three keys on 422 `parked_resume.startup_failed` (a parked background agent's leg refused before the claim — the
|
|
15303
|
+
engine now hands the failure's code / field / reason to the redemption receipt; absent when the engine gave none; <= 7.133.x carried only `cause` there).
|
|
15306
15304
|
|
|
15307
15305
|
ResumeLegConflictError:
|
|
15308
15306
|
description: >
|
|
@@ -15386,6 +15384,9 @@ components:
|
|
|
15386
15384
|
# 🔴 census 轴一(2026-07-30)补的 4 臂:server 今天就真发,spec/SDK 两侧此前同缺(详见各 schema 处的注)。
|
|
15387
15385
|
- $ref: '#/components/schemas/Event_context_usage'
|
|
15388
15386
|
- $ref: '#/components/schemas/Event_config_assembled'
|
|
15387
|
+
# server >= 7.134.0 (core 7.54.0): the device lane's cross-boundary taint row — DURABLE only (the `GET /v1/runs/{taskId}/events` ledger replay), never
|
|
15388
|
+
# on the live stream and never on the `GET /v1/runs/{taskId}` poll object; the trace SSE twin is the `cross-boundary-taint` frame.
|
|
15389
|
+
- $ref: '#/components/schemas/Event_cross_boundary_taint'
|
|
15389
15390
|
- $ref: '#/components/schemas/Event_needs_review'
|
|
15390
15391
|
- $ref: '#/components/schemas/Event_message_committed'
|
|
15391
15392
|
# [2373]A-2(2026-08-03,server 5.0.0):compaction_outcome 三腿同源批的 spec 半场(SDK 臂 20867e1 同批)。
|
|
@@ -15464,6 +15465,7 @@ components:
|
|
|
15464
15465
|
failed: '#/components/schemas/Event_failed'
|
|
15465
15466
|
context_usage: '#/components/schemas/Event_context_usage'
|
|
15466
15467
|
config_assembled: '#/components/schemas/Event_config_assembled'
|
|
15468
|
+
cross_boundary_taint: '#/components/schemas/Event_cross_boundary_taint'
|
|
15467
15469
|
needs_review: '#/components/schemas/Event_needs_review'
|
|
15468
15470
|
message_committed: '#/components/schemas/Event_message_committed'
|
|
15469
15471
|
compaction_outcome: '#/components/schemas/Event_compaction_outcome'
|
|
@@ -15690,10 +15692,10 @@ components:
|
|
|
15690
15692
|
# 每只闭集 = 引擎今天的词 ∪ 引擎退役词表里那一集。这五张 enum 与引擎 `RETIRED_MEMBERS` 逐集逐词同(由 `server-7129-wire` 对拍);卡面 / 队列行 / 请求面的同名闭集只取今天的词。
|
|
15691
15693
|
GateRecordAskClass:
|
|
15692
15694
|
type: string
|
|
15693
|
-
enum: [content_question, hook, ancestor, dangerous_delete, unverifiable_delete, unverifiable_shell_text, transcript_write, org_rule, rule_unreadable, ask_rule, denial_limit_fallback, classifier_no_ruling, requires_user_interaction, operator_forced, shell_floor, target_unresolved, probe, write_protection, tool_marks_mcp, tool_marks_a2a, policy_rra, artifact_shared_read, mode, unstated, ordinary, write_protection_unresolved, governance_unavailable]
|
|
15695
|
+
enum: [content_question, hook, ancestor, dangerous_delete, unverifiable_delete, unverifiable_shell_text, transcript_write, org_rule, rule_unreadable, ask_rule, denial_limit_fallback, classifier_no_ruling, requires_user_interaction, cross_boundary, operator_forced, shell_floor, target_unresolved, probe, write_protection, tool_marks_mcp, tool_marks_a2a, policy_rra, artifact_shared_read, mode, unstated, ordinary, write_protection_unresolved, governance_unavailable]
|
|
15694
15696
|
description: >-
|
|
15695
15697
|
The class word on a GATE RECORD (`tool_end.gate.disposition.askClass` / `.classes` on the mode-refusal arm, `ModeRelease.askClass` / `.classes`) — server >= 7.129.0 reads the record as an ARCHIVE:
|
|
15696
|
-
today's `AskClass` set (twenty-
|
|
15698
|
+
today's `AskClass` set (twenty-six words; `cross_boundary` from server >= 7.134.0) PLUS the words the engine retired (`write_protection_unresolved`, retired in core 7.49.0 — renamed `target_unresolved`; `governance_unavailable`,
|
|
15697
15699
|
retired in core 7.51.0 — merged into `rule_unreadable`). A record the previous engine generation legally wrote is replayed byte for byte (a 7.50.x mode refusal can carry `governance_unavailable`);
|
|
15698
15700
|
a word NO engine generation ever minted (a case variant, the empty string, a future word) withholds the whole `gate` and counts `server.trace.gate-outcome-defective`.
|
|
15699
15701
|
Render a known archive word normally and an unknown one generically; never drop the record because the word is not in today's set.
|
|
@@ -15711,9 +15713,9 @@ components:
|
|
|
15711
15713
|
OMITTED `reason` for the two retired words (`released` was still sent); from 7.129.0 they arrive verbatim on the replay faces. Same reading as `GateRecordAskClass`.
|
|
15712
15714
|
GateRecordPersistedRuleMandate:
|
|
15713
15715
|
type: string
|
|
15714
|
-
enum: [operator_always, tool_marks, probe_mandate, probe_unanswered, write_protection, target_unresolved, write_protection_unresolved]
|
|
15716
|
+
enum: [operator_always, tool_marks, probe_mandate, probe_unanswered, write_protection, target_unresolved, cross_boundary_taint, write_protection_unresolved]
|
|
15715
15717
|
description: >-
|
|
15716
|
-
`released.mandate` on a GATE RECORD (archive read, server >= 7.129.0): today's
|
|
15718
|
+
`released.mandate` on a GATE RECORD (archive read, server >= 7.129.0): today's seven `PersistedRuleMandate` words (`cross_boundary_taint` from server >= 7.134.0) PLUS the retired `write_protection_unresolved` (core 7.49.0 rename). Same reading as `GateRecordAskClass`.
|
|
15717
15719
|
GateRecordAskOrigin:
|
|
15718
15720
|
type: string
|
|
15719
15721
|
enum: [content_question, plan_review, ancestor_marked, org_unavailable, org_rule, rule_store_unavailable, hook, ask_rule, denial_limit_fallback, shell_gate_tighten, safety_tighten, policy, unresolvable]
|
|
@@ -15893,6 +15895,12 @@ components:
|
|
|
15893
15895
|
K-32 (server >= 7.106.0 / core 7.34.0 design/1111) — `bypassPermissions` RELEASED an ask nobody answered (see
|
|
15894
15896
|
`ModeRelease`). Absent = this allow was not a mode release (a plain allow / a person's yes / a rule), or an older
|
|
15895
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.
|
|
15896
15904
|
- type: object
|
|
15897
15905
|
additionalProperties: false
|
|
15898
15906
|
required: [kind, deniedBy]
|
|
@@ -15944,9 +15952,10 @@ components:
|
|
|
15944
15952
|
`UNATTENDED_PERMISSION_MODE=dontAsk`).
|
|
15945
15953
|
AskClass:
|
|
15946
15954
|
type: string
|
|
15947
|
-
enum: [content_question, hook, ancestor, dangerous_delete, unverifiable_delete, unverifiable_shell_text, transcript_write, org_rule, rule_unreadable, ask_rule, denial_limit_fallback, classifier_no_ruling, requires_user_interaction, operator_forced, shell_floor, target_unresolved, probe, write_protection, tool_marks_mcp, tool_marks_a2a, policy_rra, artifact_shared_read, mode, unstated, ordinary]
|
|
15955
|
+
enum: [content_question, hook, ancestor, dangerous_delete, unverifiable_delete, unverifiable_shell_text, transcript_write, org_rule, rule_unreadable, ask_rule, denial_limit_fallback, classifier_no_ruling, requires_user_interaction, cross_boundary, operator_forced, shell_floor, target_unresolved, probe, write_protection, tool_marks_mcp, tool_marks_a2a, policy_rra, artifact_shared_read, mode, unstated, ordinary]
|
|
15948
15956
|
description: >
|
|
15949
|
-
core `ASK_CLASSES` (TWENTY-
|
|
15957
|
+
core `ASK_CLASSES` (TWENTY-SIX words as of core 7.54.0 / server >= 7.134.0: `cross_boundary` joined after `requires_user_interaction` — the device lane's
|
|
15958
|
+
cross-boundary confirmation, see below; TWENTY-FIVE as of core 7.51.0 / server >= 7.129.0: `governance_unavailable` was merged into `rule_unreadable`, no alias — sdk 23.0.0; a stored card / parked row written by server <= 7.128.x is rewritten once by the server at boot. The gate record reads the ARCHIVE form, see `GateRecordAskClass`. History: twenty-two words as of core 7.40.0, twenty-three as of core 7.41.0, twenty-four as of core 7.43.0, twenty-five as of core 7.45.0, twenty-six as of core 7.49.0 — 7.34.0 minted twenty; `unverifiable_shell_text` joined in 7.49.0 #1505 and rides server >= 7.126.0, where the
|
|
15950
15959
|
unresolved-write-target class was also RENAMED to `target_unresolved` (#955; zero alias — the previous spelling is in the SDK README's 22.0.0 migration table); `shell_floor` joined in 7.39.0 #1260 and rides
|
|
15951
15960
|
server >= 7.113.0; `unstated` joined in 7.40.0 and rides server >= 7.115.0; `requires_user_interaction` joined in 7.41.0 and rides
|
|
15952
15961
|
server >= 7.116.0; `classifier_no_ruling` joined in 7.43.0 #1353 and rides server >= 7.118.0; `dangerous_delete` joined in 7.45.0 and rides
|
|
@@ -16349,6 +16358,10 @@ components:
|
|
|
16349
16358
|
`MCP_FAILURE_CODES` spellings — including `network` and the `http_<status>` encoding — are gone,
|
|
16350
16359
|
and an HTTP status now rides the structured `httpStatus` seat on `wiring_manifest.mcp[]` instead of
|
|
16351
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"`.
|
|
16352
16365
|
Consumers MUST branch with a `default` arm and MUST NOT exhaustively switch on known values.
|
|
16353
16366
|
The service enforces only a length bound (>128 chars = malformed, the whole key is DROPPED rather
|
|
16354
16367
|
than truncated — truncating would mint a code core never sent). NOTE: this is a DIFFERENT
|
|
@@ -16698,6 +16711,21 @@ components:
|
|
|
16698
16711
|
type: object
|
|
16699
16712
|
additionalProperties: false
|
|
16700
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.
|
|
16701
16729
|
ask:
|
|
16702
16730
|
type: object
|
|
16703
16731
|
additionalProperties: false
|
|
@@ -17164,6 +17192,16 @@ components:
|
|
|
17164
17192
|
pathTarget: { $ref: '#/components/schemas/McpToolPathTarget' }
|
|
17165
17193
|
ruleFace: { $ref: '#/components/schemas/McpToolRuleFace' }
|
|
17166
17194
|
renderHints: { $ref: '#/components/schemas/McpToolRenderHints' }
|
|
17195
|
+
boundaryLegs:
|
|
17196
|
+
description: >-
|
|
17197
|
+
core 7.54.0 / server >= 7.134.0 (a REQUIRED engine member: every row of `wiring_manifest.tools.entries[]` and of a `tool_roster_delta` frame's
|
|
17198
|
+
`delta.roster.entries[]` carries it) — the LEGS a call of this tool travels (core `BOUNDARY_LEGS`, in that order: `env` = inside the execution
|
|
17199
|
+
environment / on the device, `outside_read` = a read beyond it, `outside_write` = a write beyond it), or `per_call` when its dispatch gives the legs
|
|
17200
|
+
per call (the engine's own definitions and the protocol tools state them; every other row follows its `effect`). The device lane's launch gate reads
|
|
17201
|
+
it. ABSENT = an older server (<= 7.133.x).
|
|
17202
|
+
oneOf:
|
|
17203
|
+
- { type: string, const: per_call }
|
|
17204
|
+
- { type: array, items: { type: string, enum: [env, outside_read, outside_write] } }
|
|
17167
17205
|
McpToolPathTarget:
|
|
17168
17206
|
type: object
|
|
17169
17207
|
required: [param, access]
|
|
@@ -17279,6 +17317,10 @@ components:
|
|
|
17279
17317
|
own failure carries the same code. `{ modelId, boundOrigin, requestOrigin, sessionId, runId }` — both origins in display spelling (scheme + host +
|
|
17280
17318
|
port, no userinfo / path), never a credential or a digest of one. Once per run per (modelId, boundOrigin, requestOrigin). It replaces the retired
|
|
17281
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).
|
|
17282
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
|
|
17283
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 —
|
|
17284
17326
|
neutralize at render); before it was the engine-neutralized single-line form (at most 201 code points). Same keys.
|
|
@@ -17865,44 +17907,6 @@ components:
|
|
|
17865
17907
|
governance shell gate sits at the "classify" tier, a shell ask may come from the classifier or from
|
|
17866
17908
|
another gate; the server leaves the key absent rather than guessing). Never render absence as
|
|
17867
17909
|
"this gate can be bypassed by a posture".
|
|
17868
|
-
ruleSuggestions:
|
|
17869
|
-
type: array
|
|
17870
|
-
minItems: 1 # 空数组不可能出现:server 的合取项②要求 `req.ruleSuggestions` **非空**,否则整键省略(缺席 = 本卡无此选项)
|
|
17871
|
-
# 刻意**不**声明 maxItems:server 的 `.max(4)` 是**卡** schema(`ApprovalCardSchema`)上的约束,这条
|
|
17872
|
-
# live 帧腿没有它。在这里抄一个上界 = 声明一条 server 并不执行的约束(照 spec 生成的严格客户端会
|
|
17873
|
-
# 拒收一条合法帧)。数量上界的真源是引擎:core 5.22.0 的 `suggestRulesForCommand` **至多铸一条,且恒
|
|
17874
|
-
# `match: "exact"`**(不写死上界是给将来的 miner 留位,不是暗示今天会有更多)。
|
|
17875
|
-
items: { $ref: '#/components/schemas/RuleSuggestion' }
|
|
17876
|
-
description: >
|
|
17877
|
-
server #154 车二 (core 5.18.0, design/179), ADDITIVE, "tool_approval" only: the rule candidates
|
|
17878
|
-
the ENGINE minted for this ask. Render them as the "don't ask again" options and echo the chosen
|
|
17879
|
-
one back as `persistRule.rule` on POST /v1/tool-approvals/{id}/respond.
|
|
17880
|
-
|
|
17881
|
-
🔴 PRESENCE IS THE PROMISE: the key is present ONLY on a deployment that actually has a
|
|
17882
|
-
permission-rule store wired (the same object behind the engine's `permissionRules.storeWired`
|
|
17883
|
-
manifest bit). A deployment without one OMITS it rather than offering an option with nowhere to
|
|
17884
|
-
redeem — an unredeemable "don't ask again" is a wire lie, worse than absence.
|
|
17885
|
-
|
|
17886
|
-
🔴 THE CONVERSE DOES NOT HOLD: absence means "THIS CARD has no redeemable candidate", and three
|
|
17887
|
-
causes produce it — no rule store wired, the engine minted no candidate for this command (compound /
|
|
17888
|
-
redirect / substitution), or the command bytes were unreadable. One frame cannot tell them apart, so
|
|
17889
|
-
never diagnose the deployment's rule lane from a single absence, and never synthesize a candidate.
|
|
17890
|
-
|
|
17891
|
-
📏 HOW MANY, TODAY: core 5.22.0's miner (`suggestRulesForCommand`) emits AT MOST ONE candidate and
|
|
17892
|
-
always `match: "exact"` — no shipped producer mints the broader prefix option. `prefix` stays in the
|
|
17893
|
-
closed vocabulary because the wire validator accepts it and a later miner may emit it; render what
|
|
17894
|
-
arrives, but do NOT build a UI that assumes a narrow/broad PAIR is on offer.
|
|
17895
|
-
|
|
17896
|
-
🔴 The values are the engine's VERBATIM (closed `match` vocabulary + engine-minted text); the
|
|
17897
|
-
server neither re-mints nor re-orders them. A hand-built rule string is refused at redemption
|
|
17898
|
-
(`ruleRefusal: "rule_not_offered"`) — by construction, so a click on a harmless command cannot be
|
|
17899
|
-
turned into a rule for something else.
|
|
17900
|
-
|
|
17901
|
-
⚠️ REDEMPTION SCOPE: the key also rides the durable card replay, but its only redemption channel is
|
|
17902
|
-
the LIVE respond leg keyed by this frame's `approvalId` — an in-process map on ONE replica. Same
|
|
17903
|
-
replica ⇒ redeemable; after a restart / on another replica that `approvalId` 404s and the person
|
|
17904
|
-
must use the durable decide leg, which carries NO rule field in v1 (its body is strict — an extra
|
|
17905
|
-
`persistRule` is a loud 400, not a silent drop).
|
|
17906
17910
|
ruleOffers:
|
|
17907
17911
|
type: array
|
|
17908
17912
|
minItems: 1
|
|
@@ -17910,8 +17914,8 @@ components:
|
|
|
17910
17914
|
description: >
|
|
17911
17915
|
server >= 7.46.0 (core 5.58.0, design/375), "tool_approval" only — the ENGINE-minted "don't ask
|
|
17912
17916
|
again" OPTIONS for this ask, as a DISCRIMINATED UNION (`single` | `batch`). It is the BREAKING
|
|
17913
|
-
REPLACEMENT of `ruleSuggestions`
|
|
17914
|
-
alias
|
|
17917
|
+
REPLACEMENT of the triple-shaped `ruleSuggestions` of servers <= 7.45: a >= 7.46.0 engine mints the old key nowhere and there is no
|
|
17918
|
+
alias (sdk 26.0.0 deleted the old key with the support floor 7.52.0).
|
|
17915
17919
|
|
|
17916
17920
|
🔴 READ `kind` TO TELL THE ARMS APART, never a positional assumption. ORDER IS CONTRACT: a
|
|
17917
17921
|
whole-string exact `single` is index 0 when present and a `batch` is always last; core's own
|
|
@@ -17926,23 +17930,15 @@ components:
|
|
|
17926
17930
|
suppress its persistence actions entirely (core's normative clause) — a compacted index means the
|
|
17927
17931
|
k-th option the person clicked is not the k-th rule the server redeems.
|
|
17928
17932
|
|
|
17929
|
-
🔴 PRESENCE IS THE PROMISE
|
|
17933
|
+
🔴 PRESENCE IS THE PROMISE: present only where a permission-rule store
|
|
17930
17934
|
is really wired. The converse does not hold — a wired deployment still omits it for mandated /
|
|
17931
17935
|
requiresRealApproval / allow_outranked / ask_rule_matched / hook-produced / inherited-unresolved / ancestor-resolved /
|
|
17932
17936
|
anonymous asks. Absence reads as "THIS CARD has no such option", never as "this deployment has no
|
|
17933
17937
|
rule lane" (that is `capabilities.permissionRules`) and never as "your rule broke" (that is
|
|
17934
|
-
`allowRuleShadowed
|
|
17938
|
+
`allowRuleShadowed`).
|
|
17935
17939
|
|
|
17936
17940
|
⚠️ REDEMPTION SCOPE is the live respond leg keyed by this frame's `approvalId` only; a replayed
|
|
17937
17941
|
offer on another replica is not a promise that it is still redeemable.
|
|
17938
|
-
persistedRuleShadowed:
|
|
17939
|
-
type: string
|
|
17940
|
-
description: >-
|
|
17941
|
-
server >= 7.13.0 (core 5.25.0 #144), ADDITIVE, "tool_approval" only — this ask MATCHED one of the caller's persisted allow
|
|
17942
|
-
rules and that rule COULD NOT silence it; the value is the rule's VERBATIM text (secret-redacted + length-bounded; UNTRUSTED
|
|
17943
|
-
for display, like `message`). Absent != "you have no rule"; deliberately separate from `governanceForced`.
|
|
17944
|
-
RETIRED: server >= 7.117.0 (core 7.42.0, BREAKING rename, no alias) never mints this key on any face; its successor is
|
|
17945
|
-
`allowRuleShadowed` (all five allow-layer members, not just the rule store). Kept for servers in [7.13.0, 7.117.0) only (sdk 16.3.0).
|
|
17946
17942
|
allowRuleShadowed:
|
|
17947
17943
|
type: array
|
|
17948
17944
|
items: { $ref: '#/components/schemas/AllowRuleShadow' }
|
|
@@ -18220,17 +18216,18 @@ components:
|
|
|
18220
18216
|
classifierUnavailable:
|
|
18221
18217
|
$ref: '#/components/schemas/ClassifierUnavailable'
|
|
18222
18218
|
description: >-
|
|
18223
|
-
server >= 7.69.0 (core 7.10.0
|
|
18219
|
+
server >= 7.69.0 (core 7.10.0), ADDITIVE, present only when the classifier was consulted and
|
|
18224
18220
|
could not run — WHY this ask reached a human. See ClassifierUnavailable for the open-`cause` rule and
|
|
18225
18221
|
the three shapes absence covers.
|
|
18226
|
-
RETIRED: server >= 7.72.0 (core 7.14.0
|
|
18222
|
+
RETIRED: server >= 7.72.0 (core 7.14.0) never mints this key on any of the three faces; its
|
|
18227
18223
|
successor is `disposition.cause` on the gate record (`unavailable` / `parse_error` — a different
|
|
18228
|
-
question, so an old switch does not port). Kept
|
|
18224
|
+
question, so an old switch does not port). Kept on the frame because servers 7.69–7.71 are inside the supported range and still
|
|
18225
|
+
send it, and the closed inbox row declares the same key (frame and row read the same); it goes with the next major once the support floor is >= 7.106.0.
|
|
18229
18226
|
ruleStoreUnreadable:
|
|
18230
18227
|
type: string
|
|
18231
18228
|
enum: [store, call]
|
|
18232
18229
|
description: >-
|
|
18233
|
-
server >= 7.72.0 (core 7.14.0
|
|
18230
|
+
server >= 7.72.0 (core 7.14.0), ADDITIVE — WHICH HALF of the rule lane could not be read.
|
|
18234
18231
|
Present iff `origin === "rule_store_unavailable"`: `store` = the wired rule store could not be read
|
|
18235
18232
|
(look at the store / network); `call` = the store was read but THIS command could not be read against
|
|
18236
18233
|
the person's deny/ask rows (look at the command spelling). One `origin` word covers both facts, and
|
|
@@ -18238,10 +18235,11 @@ components:
|
|
|
18238
18235
|
frame and the inbox row; the `card_json` card deliberately does NOT carry it (it travels with `origin`,
|
|
18239
18236
|
which is not on the card). Absence = `origin` is not that word — NOT an assertion that the rule store
|
|
18240
18237
|
is healthy.
|
|
18241
|
-
RETIRED: server >= 7.106.0 (core 7.34.0
|
|
18242
|
-
unreadable rule store no longer raises a card
|
|
18243
|
-
|
|
18244
|
-
|
|
18238
|
+
RETIRED: server >= 7.106.0 (core 7.34.0, BREAKING — wire) never mints this key on either face — an
|
|
18239
|
+
unreadable rule store no longer raises a card; the signal moved to the session stream's user-facing
|
|
18240
|
+
`engine_notice` `permission.rule_store_unavailable` (`detail.at` = `prepare` / `call`; what the run does while the rules cannot be read is
|
|
18241
|
+
that notice's own text). Kept on the frame because servers 7.72–7.105 are inside the supported range and still send it, and the closed inbox row
|
|
18242
|
+
declares the same key; it goes with the next major once the support floor is >= 7.106.0 (`origin: rule_store_unavailable` itself stays).
|
|
18245
18243
|
readRootCandidate:
|
|
18246
18244
|
type: object
|
|
18247
18245
|
required: [dir, clearsThisAsk]
|
|
@@ -18395,33 +18393,15 @@ components:
|
|
|
18395
18393
|
posture. Semantics, the ABSENT != false clause and the discrimination boundary are stated verbatim on
|
|
18396
18394
|
ToolApprovalFrame.governanceForced. It sits at the card's top level rather than inside `risk` because
|
|
18397
18395
|
`risk` describes the ENGINE's judgement of the call, while this key says WHO imposed the gate.
|
|
18398
|
-
ruleSuggestions:
|
|
18399
|
-
type: array
|
|
18400
|
-
minItems: 1 # 同帧腿:空数组不可能出现(素材同源),缺席才是「本卡无此选项」
|
|
18401
|
-
maxItems: 4 # 卡形上**真有**这条上界(server `ApprovalCardSchema` 的 `.max(4)`);live 帧腿没有,故只写在这里
|
|
18402
|
-
items: { $ref: '#/components/schemas/RuleSuggestion' }
|
|
18403
|
-
description: >
|
|
18404
|
-
ADDITIVE (#154 车二 / core 5.18.0 design/179): the rule candidates the ENGINE minted for this ask —
|
|
18405
|
-
the card's "don't ask again" options; echo the chosen one back as `persistRule.rule`. Semantics,
|
|
18406
|
-
the PRESENCE-IS-THE-PROMISE clause (present only where a rule store is wired) and the redemption
|
|
18407
|
-
scope are stated verbatim on ToolApprovalFrame.ruleSuggestions — the live frame, the stored
|
|
18408
|
-
`card_json` and the replayed frame carry the SAME material.
|
|
18409
18396
|
ruleOffers:
|
|
18410
18397
|
type: array
|
|
18411
18398
|
minItems: 1
|
|
18412
18399
|
items: { $ref: '#/components/schemas/RuleOffer' }
|
|
18413
18400
|
description: >
|
|
18414
|
-
server >= 7.46.0 (core 5.58.0, design/375) — the discriminated-union replacement of
|
|
18415
|
-
`ruleSuggestions`
|
|
18401
|
+
server >= 7.46.0 (core 5.58.0, design/375) — the discriminated-union replacement of the retired
|
|
18402
|
+
`ruleSuggestions` (servers <= 7.45; sdk 26.0.0 deleted it). Same material as the live frame and the stored `card_json` (the server
|
|
18416
18403
|
projects all three through one `copyRuleOffer`), so semantics, order-is-contract, presence-is-the-
|
|
18417
18404
|
promise and the per-leg index rules are stated verbatim on ToolApprovalFrame.ruleOffers.
|
|
18418
|
-
persistedRuleShadowed:
|
|
18419
|
-
type: string
|
|
18420
|
-
description: >-
|
|
18421
|
-
server >= 7.13.0 (core 5.25.0 #144), ADDITIVE — the persisted rule this ask matched but which could not silence it; frame and
|
|
18422
|
-
card carry the SAME material (see ToolApprovalFrame.persistedRuleShadowed).
|
|
18423
|
-
RETIRED: server >= 7.117.0 (core 7.42.0) never mints this key on any face (its strict card schema even refuses a stored card
|
|
18424
|
-
carrying it); its successor is `allowRuleShadowed`. Kept for servers in [7.13.0, 7.117.0) only (sdk 16.3.0).
|
|
18425
18405
|
allowRuleShadowed:
|
|
18426
18406
|
type: array
|
|
18427
18407
|
items: { $ref: '#/components/schemas/AllowRuleShadow' }
|
|
@@ -18456,35 +18436,13 @@ components:
|
|
|
18456
18436
|
classifierUnavailable:
|
|
18457
18437
|
$ref: '#/components/schemas/ClassifierUnavailable'
|
|
18458
18438
|
description: >-
|
|
18459
|
-
|
|
18460
|
-
|
|
18461
|
-
|
|
18462
|
-
RETIRED: server >= 7.72.0 (core 7.14.0 #688) never mints this key on any of the three faces; its
|
|
18439
|
+
(minted by servers [7.69.0, 7.72.0), core 7.10.0 #616) WHY this ask reached a human. See ClassifierUnavailable for the open-`cause` rule and the three
|
|
18440
|
+
shapes absence covers.
|
|
18441
|
+
RETIRED: server >= 7.72.0 (core 7.14.0) never mints this key on any of the three faces; its
|
|
18463
18442
|
successor is `disposition.cause` on the gate record (`unavailable` / `parse_error` — a different
|
|
18464
|
-
question, so an old switch does not port).
|
|
18465
|
-
|
|
18466
|
-
RuleSuggestion:
|
|
18467
|
-
type: object
|
|
18468
|
-
description: >
|
|
18469
|
-
One ENGINE-minted rule candidate (#154 车二 / core 5.18.0 `RuleSuggestion`) — what a single click on
|
|
18470
|
-
"don't ask again" would persist.
|
|
18471
|
-
|
|
18472
|
-
🔴 THE TEXT COMES FROM THE ENGINE, NOT THE SHELL: the server derives it from the adjudicated command's
|
|
18473
|
-
own bytes; a client may only echo one of these back verbatim (`persistRule.rule`). Reporting text that
|
|
18474
|
-
is not among the candidates is refused (`ruleRefusal: "rule_not_offered"`) rather than guessed at, so
|
|
18475
|
-
"click on a harmless command, get a rule for something else" is not spellable on this path.
|
|
18476
|
-
# 封闭:core `RuleSuggestion` 恰三键(rule/match/command),server 逐字透传不重铸。
|
|
18477
|
-
additionalProperties: false
|
|
18478
|
-
required: [rule, match, command]
|
|
18479
|
-
properties:
|
|
18480
|
-
rule: { type: string, maxLength: 512, description: 'The canonical rule text to echo back on redemption (server MAX_RULE_TEXT_CHARS = 512).' }
|
|
18481
|
-
match:
|
|
18482
|
-
type: string
|
|
18483
|
-
enum: [exact, prefix]
|
|
18484
|
-
description: '`exact` = this one command only (`Bash(git status)`); `prefix` = word-boundary prefix (`Bash(git status:*)`). Closed vocabulary — an unknown value is not a candidate this contract describes; do not render it as redeemable.'
|
|
18485
|
-
command: { type: string, maxLength: 512, description: 'The command pattern inside the rule, for a shell that would rather not re-parse `rule`.' }
|
|
18443
|
+
question, so an old switch does not port). It stays declared on the CARD because the pinned server's card schema (`ApprovalCardSchema`, server 7.134.0) still accepts it: a still-pending `card_json` row a
|
|
18444
|
+
7.69–7.71 server wrote replays verbatim onto `approval_request.card` / the replay frame. (The FRAME's same-named key stays declared too; why, and until when: `ToolApprovalFrame.classifierUnavailable`.)
|
|
18486
18445
|
|
|
18487
|
-
# ─── S-139(sdk 8.3.0):对 server 7.60.0 整体重对账带进来的四张型面 ───────────────────────
|
|
18488
18446
|
ClassifierUnavailable:
|
|
18489
18447
|
# 8.9.0:三面(tool_approval 帧 / ApprovalCard / InboxRow)同形同值 ⇒ 具名单源,内联三份会各自漂。
|
|
18490
18448
|
type: object
|
|
@@ -18567,6 +18525,27 @@ components:
|
|
|
18567
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
|
|
18568
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).
|
|
18569
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
|
+
|
|
18570
18549
|
AllowRuleShadow:
|
|
18571
18550
|
type: object
|
|
18572
18551
|
additionalProperties: false
|
|
@@ -18599,7 +18578,7 @@ components:
|
|
|
18599
18578
|
`ApprovalCard.ruleOffersAbsence`; server >= 7.55.0 / core #490 fix ②, S-15). The engine names which
|
|
18600
18579
|
door is shut: `mandated` = this ask must be confirmed every time (🔴 this arm must NOT point the user
|
|
18601
18580
|
at writing a rule — a rule would not silence it, and saying so sends them into a loop); `allow_outranked` = an
|
|
18602
|
-
allow-layer member matched this call but the ask sits at a higher tier, so another allow rule cannot clear it (the machine-readable twin of `allowRuleShadowed
|
|
18581
|
+
allow-layer member matched this call but the ask sits at a higher tier, so another allow rule cannot clear it (the machine-readable twin of `allowRuleShadowed`);
|
|
18603
18582
|
`ask_rule_matched` = an explicit `ask` rule matched this call (same frame `origin: ask_rule`): it keeps asking, an allow rule would not silence it either; a card from the deployment name-list gate
|
|
18604
18583
|
`APPROVAL_REQUIRE` / the supervisor route that reports an explicit ask-rule hit lands here too; `lane_cannot_speak` = a rule COULD clear this ask but the lane cannot speak this time (the engine could mint no coverable form for this command).
|
|
18605
18584
|
🔴 sdk 23.0.0 BREAKING (server >= 7.129.0 / engine 7.51.0, `RULE_OFFERS_ABSENCES`): the former `shadowed` meant two things and is split into the middle two words, no alias; a card / parked row that a server <= 7.128.x
|
|
@@ -18634,10 +18613,12 @@ components:
|
|
|
18634
18613
|
`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.
|
|
18635
18614
|
PersistedRuleMandate:
|
|
18636
18615
|
type: string
|
|
18637
|
-
enum: [operator_always, tool_marks, probe_mandate, probe_unanswered, write_protection, target_unresolved]
|
|
18616
|
+
enum: [operator_always, tool_marks, probe_mandate, probe_unanswered, write_protection, target_unresolved, cross_boundary_taint]
|
|
18638
18617
|
description: >
|
|
18639
18618
|
WHICH WORD a mandated ask stands on (server >= 7.102.0 / S-721 / core 7.32.0 #1092) — core's
|
|
18640
|
-
`PERSISTED_RULE_MANDATES`
|
|
18619
|
+
`PERSISTED_RULE_MANDATES` (seven words since core 7.54.0 / server >= 7.134.0: `cross_boundary_taint` — the run executes on a device outside the deployment's
|
|
18620
|
+
trust boundary and the deployment is multi-tenant, so an org-side write, or after an off-target read a call bound for the device, is confirmed by the deployment's
|
|
18621
|
+
approver per call; no standing allow rule and no automatic lane answers it; core puts it at the HEAD of the mandate ladder, the array keeps core's constant order), transcribed verbatim and in order (core 7.32.1
|
|
18641
18622
|
`dist/core/ask-origin.d.ts:349`; the vocabulary's owner is core; the sixth word is `target_unresolved` from core 7.49.0 #955 / server >= 7.126.0 —
|
|
18642
18623
|
server 7.122.0–7.125.x sent the previous spelling, which reads as out of set here, see the SDK README's 22.0.0 migration table). Rides FOUR faces with one value: the
|
|
18643
18624
|
`tool_approval` frame, the `livePending` row top level, the operator queue row (`GET /v1/approvals`
|
|
@@ -18691,12 +18672,12 @@ components:
|
|
|
18691
18672
|
RuleOfferMatch:
|
|
18692
18673
|
type: string
|
|
18693
18674
|
description: >
|
|
18694
|
-
The MATCH FORM of a persisted rule —
|
|
18695
|
-
|
|
18696
|
-
|
|
18675
|
+
The MATCH FORM of a persisted rule — core's `PERSISTED_RULE_MATCHES`, verbatim and in order (the vocabulary is OWNED BY CORE: a package-root
|
|
18676
|
+
value since core 7.54.0, which server >= 7.134.0 reads directly instead of keeping its own transcription; earlier servers transcribed it and pinned set
|
|
18677
|
+
equality). ONE vocabulary for two faces: a card's rule offer (`RuleOffer.match`, the
|
|
18697
18678
|
server's card schema validates it with this very enum) and a persisted rule row (`GET /v1/rules`).
|
|
18698
18679
|
|
|
18699
|
-
`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
|
|
18700
18681
|
word-boundary prefix (`Bash(git status:*)`) and the ONLY spelling for a compound prefix form; it stays
|
|
18701
18682
|
a permanent compatibility read. `wildcard` = the trailing space-star form (`Bash(npm run *)`), the
|
|
18702
18683
|
same predicate as `prefix` on a single command body. `subpath` = the `Read(//abs-dir/**)` directory form,
|
|
@@ -18710,11 +18691,11 @@ components:
|
|
|
18710
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
|
|
18711
18692
|
row as a damaged row.
|
|
18712
18693
|
|
|
18713
|
-
🔴
|
|
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
|
|
18714
18695
|
mint point is a lie — it lets a `switch` claim exhaustiveness the wire does not honour (SDK <= 16 declared
|
|
18715
18696
|
four here, so a `path` offer and every 7.118.0 skill-card offer were dropped as malformed; SDK <= 21 declared
|
|
18716
18697
|
seven, so every domain grant row on `GET /v1/rules` was dropped the same way).
|
|
18717
|
-
enum: [exact, prefix, wildcard, subpath, path, name, name_prefix, host, subdomain]
|
|
18698
|
+
enum: [exact, prefix, wildcard, pattern, subpath, path, name, name_prefix, host, subdomain]
|
|
18718
18699
|
|
|
18719
18700
|
RuleOfferUncoveredReason:
|
|
18720
18701
|
type: string
|
|
@@ -18896,8 +18877,7 @@ components:
|
|
|
18896
18877
|
maxLength: 512
|
|
18897
18878
|
description: >
|
|
18898
18879
|
With `edited` absent or false this is the CANDIDATE ARM: echo back, VERBATIM, the `rule` of the
|
|
18899
|
-
chosen `kind: "single"` entry of the frame's `ruleOffers
|
|
18900
|
-
`ruleSuggestions`). Do not hand-build it, re-case it or append `:*` — the server locates it by
|
|
18880
|
+
chosen `kind: "single"` entry of the frame's `ruleOffers`. Do not hand-build it, re-case it or append `:*` — the server locates it by
|
|
18901
18881
|
text and refuses otherwise (`rule_not_offered`). With `edited: true` this is the FREE-TEXT ARM
|
|
18902
18882
|
and the string is whatever the person typed.
|
|
18903
18883
|
edited:
|
|
@@ -19835,6 +19815,38 @@ components:
|
|
|
19835
19815
|
fields: {}
|
|
19836
19816
|
overrideReasons: {}
|
|
19837
19817
|
ts: {}
|
|
19818
|
+
CrossBoundaryTaintRow:
|
|
19819
|
+
type: object
|
|
19820
|
+
description: >
|
|
19821
|
+
server >= 7.134.0 / core 7.54.0 — the device lane's CROSS-BOUNDARY TAINT row: the engine's `permission.cross_boundary_taint` frame minus its envelope
|
|
19822
|
+
(`kind` / `version` / `taskId` / `ts`). Metadata only. Emitted ONLY on a tree that executes on a target device (one row when an armed tree prepares a
|
|
19823
|
+
leg — `prepare`, one on a durable resume leg — `resume`, one when this leg first lets an off-target read into the tree — `observed`, with that call);
|
|
19824
|
+
the host lane never emits it. Two exits, one row: the ledger event `cross_boundary_taint` on `GET /v1/runs/{taskId}/events` (`Event_cross_boundary_taint`)
|
|
19825
|
+
and the trace SSE `cross-boundary-taint` frame on `GET /v1/tasks/{taskId}/stream` (the row + `seq`). The `GET /v1/runs/{taskId}` poll object carries NO
|
|
19826
|
+
such key.
|
|
19827
|
+
# 不封闭(`additionalProperties: true`):本 schema 是 `Event_cross_boundary_taint` 的 allOf 目标(帧上多一个 `type`),封闭会把合法帧判成违约(同 PromptManifestRow)。
|
|
19828
|
+
additionalProperties: true
|
|
19829
|
+
required: [runId, phase, taint, governed]
|
|
19830
|
+
properties:
|
|
19831
|
+
runId: { type: string, description: 'the leg''s run id.' }
|
|
19832
|
+
phase: { type: string, enum: [prepare, resume, observed], description: 'core `permission.cross_boundary_taint` `phase` (engine closed set).' }
|
|
19833
|
+
taint:
|
|
19834
|
+
type: array
|
|
19835
|
+
description: 'The tree''s taint bits after the event — core `CROSS_BOUNDARY_TAINT_BITS` (`targetExecutionArmed` = the tree is armed to execute on the target; `offTargetReadObserved` = off-target read content was let into the tree).'
|
|
19836
|
+
items: { type: string, enum: [targetExecutionArmed, offTargetReadObserved] }
|
|
19837
|
+
governed: { type: boolean, description: 'the tree has run under the `multi_tenant` posture since it armed (derived from `REQUIRE_PRINCIPAL`).' }
|
|
19838
|
+
toolName: { type: string, description: '`observed` only: the call that let the off-target read in.' }
|
|
19839
|
+
toolCallId: { type: string, description: '`observed` only: that call''s id.' }
|
|
19840
|
+
Event_cross_boundary_taint:
|
|
19841
|
+
description: >
|
|
19842
|
+
DURABLE only (server >= 7.134.0 / core 7.54.0) — the ledger event of a `CrossBoundaryTaintRow` on `GET /v1/runs/{taskId}/events` (SSE `id:` = the row's
|
|
19843
|
+
seq). Never on the live stream, never on the `GET /v1/runs/{taskId}` poll object; an older server drops the frame on the spot.
|
|
19844
|
+
allOf:
|
|
19845
|
+
- $ref: '#/components/schemas/CrossBoundaryTaintRow'
|
|
19846
|
+
- type: object
|
|
19847
|
+
required: [type]
|
|
19848
|
+
properties:
|
|
19849
|
+
type: { const: cross_boundary_taint }
|
|
19838
19850
|
Event_needs_review:
|
|
19839
19851
|
type: object
|
|
19840
19852
|
description: >
|
|
@@ -20019,6 +20031,12 @@ components:
|
|
|
20019
20031
|
runId: { type: string }
|
|
20020
20032
|
status: { type: string }
|
|
20021
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.
|
|
20022
20040
|
|
|
20023
20041
|
# ── 🔴 #185a (2026-08-08): the DURABLE leg's three APPROVAL arms ────────────────────────────────
|
|
20024
20042
|
# Shape rule for all three: `allOf: [<frame schema>, {the narrowed discriminant}]`. The keys are NOT
|
|
@@ -20718,7 +20736,7 @@ components:
|
|
|
20718
20736
|
🔴 ONE RULE FOR EVERY "EFFECTIVE HALF ONLY" KEY (sdk 9.7.0, after a codex adversarial-review [high]):
|
|
20719
20737
|
this schema's only consumer today is `GET /v1/diagnostics/wiring`'s `static` half, and every key marked
|
|
20720
20738
|
"effective half only" (`leg`, `ask.effective`, `configFingerprint`, `tools`, `mods`, `lsp`,
|
|
20721
|
-
`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
|
|
20722
20740
|
wire ⇒ THEIR ABSENCE HERE CARRIES NO INFORMATION. The per-key absence readings ("absent = not armed",
|
|
20723
20741
|
"absent = a third state", …) hold for the LIVE FRAME (`Event_wiring_manifest`) and must not be carried
|
|
20724
20742
|
over to this response. For the facts behind those keys: a tenant reads
|
|
@@ -20731,7 +20749,7 @@ components:
|
|
|
20731
20749
|
are EFFECTIVE-half only (the live `wiring_manifest` event) — as are the four per-leg sections
|
|
20732
20750
|
`modelGate`, `autoMode`, `mcp` and `tools`, which describe what THIS leg did and have no static
|
|
20733
20751
|
counterpart — and the STATIC half
|
|
20734
|
-
(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
|
|
20735
20753
|
present on the static half. Fields stay optional because core may add or drop sections and that
|
|
20736
20754
|
must not hard-break a client — read by half, and never wait on a key the half never sends.
|
|
20737
20755
|
🔴 EVERY section is declared HERE, each saying which half mints it (sdk 8.9.0). Before that, `tools`
|
|
@@ -20752,6 +20770,18 @@ components:
|
|
|
20752
20770
|
description: 'Which leg emitted it (effective half only; the static half has no leg identity).'
|
|
20753
20771
|
properties:
|
|
20754
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.
|
|
20755
20785
|
ask:
|
|
20756
20786
|
type: object
|
|
20757
20787
|
description: 'Approval seam: wired form, which seat it came from, and (effective half) what it adjudicates to.'
|
|
@@ -21519,6 +21549,19 @@ components:
|
|
|
21519
21549
|
usageMissing: { type: boolean, enum: [true] }
|
|
21520
21550
|
stopReason: { type: string }
|
|
21521
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.'
|
|
21522
21565
|
|
|
21523
21566
|
ToolApprovalDecision:
|
|
21524
21567
|
type: string
|
|
@@ -21690,6 +21733,15 @@ components:
|
|
|
21690
21733
|
(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
21734
|
(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
21735
|
late-answer 404's, see ToolApprovalNotPendingCause).
|
|
21736
|
+
🔴 server >= 7.134.0: a PARKED row passes the gate judge (the same one the answer door's 409 `approval_stale` reads) — the gate still open ⇒ `parked`
|
|
21737
|
+
(+ `decidePath`; ONLY this state renders "go decide"); the gate settled by a person / policy ⇒ `decided` (no `decidePath`); the gate expired (a cancel expires it
|
|
21738
|
+
first) / superseded / settled by the host SLA sweep, the window or nobody-to-answer ⇒ `void` WITHOUT `cause`; the checkpoint row gone, or settled without a
|
|
21739
|
+
readable direction / time ⇒ 404 `tool_approval.not_pending` WITHOUT `cause`; the gate unreadable (store failure) ⇒ falls back to `parked`. Known boundary (re-read
|
|
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
|
|
21741
|
+
a cancelled resume reads `void` once the server collects the reopened gate (`parked` in between, or `void` for an instant during the reopen).
|
|
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.
|
|
21693
21745
|
oneOf:
|
|
21694
21746
|
- $ref: '#/components/schemas/ToolApprovalReadPending'
|
|
21695
21747
|
- $ref: '#/components/schemas/ToolApprovalReadDecided'
|
|
@@ -21727,6 +21779,29 @@ components:
|
|
|
21727
21779
|
properties:
|
|
21728
21780
|
state: { type: string, enum: [parked] }
|
|
21729
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 }
|
|
21730
21805
|
ToolApprovalReadVoid:
|
|
21731
21806
|
allOf:
|
|
21732
21807
|
- $ref: '#/components/schemas/ToolApprovalReadProjection'
|
|
@@ -22083,74 +22158,6 @@ components:
|
|
|
22083
22158
|
delivery: { type: string, const: applied, description: 'Literal "applied" on today''s server.' }
|
|
22084
22159
|
marker: { type: string, description: 'Engine-minted delivery marker.' }
|
|
22085
22160
|
|
|
22086
|
-
WorkspaceSnapshotPage:
|
|
22087
|
-
# 4.3.0(CAPS-OPS-8):把 GET …/workspace 的 200 信封提成具名 schema。SDK 侧此前是 `list()` 返回位上的
|
|
22088
|
-
# **内联闭合字面量**,两道 spec 门(field-drift 只扫 types.ts / resource-schema 只扫 resources 的 exported
|
|
22089
|
-
# type)对方法返回位的内联形都是盲的 —— 于是它漏了这里一直标为 required 的 `sessionId`,CI 全绿。
|
|
22090
|
-
type: object
|
|
22091
|
-
description: >
|
|
22092
|
-
GET /v1/sessions/{sessionId}/workspace 的 200 信封(server src/http/routes/sessions.ts)。
|
|
22093
|
-
⚠️ `total` = **本页行数**(`snapshots.length`),不是快照总数,且这条腿没有任何分页游标 —— 与同族
|
|
22094
|
-
`WorkspaceTreePage.total`(真·全量)**同名反义**。`latest` 与展示行同源(防 ghost key)。
|
|
22095
|
-
additionalProperties: false
|
|
22096
|
-
required: [sessionId, snapshots, total]
|
|
22097
|
-
properties:
|
|
22098
|
-
sessionId: { type: string }
|
|
22099
|
-
snapshots:
|
|
22100
|
-
type: array
|
|
22101
|
-
items: { $ref: '#/components/schemas/WorkspaceSnapshotRow' }
|
|
22102
|
-
latest: { type: string, description: 'Newest snapshot key; absent when the session has none.' }
|
|
22103
|
-
total: { type: integer, description: 'ROWS ON THIS PAGE — not the snapshot count.' }
|
|
22104
|
-
|
|
22105
|
-
WorkspaceTreePage:
|
|
22106
|
-
# 4.3.0(CAPS-OPS-8):同上,tree 腿的 200 信封。
|
|
22107
|
-
type: object
|
|
22108
|
-
description: >
|
|
22109
|
-
GET /v1/sessions/{sessionId}/workspace/{key}/tree 的 200 信封(server src/http/routes/sessions.ts)。
|
|
22110
|
-
`total` 在这条腿上是**全量**(`all.length`)—— 与 `WorkspaceSnapshotPage.total` 语义相反。
|
|
22111
|
-
`key` 回的是解析后的真实键(请求可传 `latest` 别名)。
|
|
22112
|
-
additionalProperties: false
|
|
22113
|
-
required: [sessionId, key, entries, total]
|
|
22114
|
-
properties:
|
|
22115
|
-
sessionId: { type: string }
|
|
22116
|
-
key: { type: string, description: 'The RESOLVED snapshot key (a `latest` alias in the request comes back resolved).' }
|
|
22117
|
-
entries:
|
|
22118
|
-
type: array
|
|
22119
|
-
items: { $ref: '#/components/schemas/WorkspaceTreeEntry' }
|
|
22120
|
-
total: { type: integer, description: 'FULL entry count (not the page size).' }
|
|
22121
|
-
nextOffset: { type: integer, description: 'Present ⇒ more pages; send it back as ?offset=.' }
|
|
22122
|
-
|
|
22123
|
-
WorkspaceSnapshotRow:
|
|
22124
|
-
type: object
|
|
22125
|
-
description: >
|
|
22126
|
-
One snapshot row of GET /v1/sessions/{sessionId}/workspace (envelope `{sessionId, snapshots,
|
|
22127
|
-
latest?, total}`; server src/http/routes/sessions.ts — newest first, uuidv7 keys). E19 snapshot store's
|
|
22128
|
-
read-only projection: each row is the freeze at a turn boundary, NOT a live workspace.
|
|
22129
|
-
required: [key, files]
|
|
22130
|
-
additionalProperties: true
|
|
22131
|
-
properties:
|
|
22132
|
-
key: { type: string, description: 'Snapshot key (uuidv7 entry id; "latest" is an accepted alias on the sub-routes).' }
|
|
22133
|
-
files: { type: integer, description: 'Manifest row count.' }
|
|
22134
|
-
bytes: { type: integer, description: 'Total snapshot bytes — present only when the store has a sizes face (SQL backends); the local store honestly omits it ([1894]② additive).' }
|
|
22135
|
-
|
|
22136
|
-
WorkspaceTreeEntry:
|
|
22137
|
-
type: object
|
|
22138
|
-
description: >
|
|
22139
|
-
One file entry of GET /v1/sessions/{sessionId}/workspace/{key}/tree (envelope `{sessionId, key,
|
|
22140
|
-
entries, total, nextOffset?}`; server src/http/routes/sessions.ts — stable path order, offset paging).
|
|
22141
|
-
required: [path, hash]
|
|
22142
|
-
additionalProperties: true
|
|
22143
|
-
properties:
|
|
22144
|
-
path: { type: string, description: 'The relPath — pass VERBATIM as ?path= on the file sub-route.' }
|
|
22145
|
-
hash: { type: string, description: 'Content address (sha256) — enables cross-snapshot change highlighting ([1894]②).' }
|
|
22146
|
-
size: { type: integer, description: 'Present only when the store has a sizes face (SQL backends).' }
|
|
22147
|
-
|
|
22148
|
-
# ═══════════════════════════════════════════════════════════════════════════════════════════════
|
|
22149
|
-
# #154 车二(design/179 §7)—— 持久权限规则的 CC settings 导入面。形按 server 真码 + core 原形亲读
|
|
22150
|
-
# (`src/http/routes/rules.ts` / `src/rules-consent.ts`;preview/result 两形是 core `ImportPreview` /
|
|
22151
|
-
# `ImportResult` 的原样透传)。
|
|
22152
|
-
# ═══════════════════════════════════════════════════════════════════════════════════════════════
|
|
22153
|
-
|
|
22154
22161
|
CcImportSettingsLayer:
|
|
22155
22162
|
type: string
|
|
22156
22163
|
enum: [userSettings, projectSettings, localSettings]
|
|
@@ -22512,8 +22519,8 @@ components:
|
|
|
22512
22519
|
match:
|
|
22513
22520
|
allOf: [{ $ref: '#/components/schemas/RuleOfferMatch' }]
|
|
22514
22521
|
description: >
|
|
22515
|
-
The match form — the same
|
|
22516
|
-
`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
|
|
22517
22524
|
`name` / `name_prefix` (a `Skill(<name>)` / `Skill(<prefix>:*)` rule).
|
|
22518
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`.' }
|
|
22519
22526
|
adds:
|