@sema-agent/sdk 24.0.0 → 26.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +243 -6
- package/dist/client.d.ts +6 -5
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +8 -6
- package/dist/client.js.map +1 -1
- package/dist/device/index.d.ts +76 -12
- package/dist/device/index.d.ts.map +1 -1
- package/dist/device/index.js +90 -33
- package/dist/device/index.js.map +1 -1
- package/dist/errors.d.ts +162 -30
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +71 -35
- package/dist/errors.js.map +1 -1
- package/dist/events.d.ts +53 -13
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +3 -2
- package/dist/events.js.map +1 -1
- package/dist/health.d.ts +25 -0
- package/dist/health.d.ts.map +1 -1
- package/dist/health.js.map +1 -1
- package/dist/index.d.ts +44 -23
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -6
- package/dist/index.js.map +1 -1
- package/dist/model-route-env/index.d.ts +129 -0
- package/dist/model-route-env/index.d.ts.map +1 -0
- package/dist/model-route-env/index.js +131 -0
- package/dist/model-route-env/index.js.map +1 -0
- package/dist/resources/adoption.d.ts +8 -2
- package/dist/resources/adoption.d.ts.map +1 -1
- package/dist/resources/adoption.js.map +1 -1
- package/dist/resources/approvals.d.ts +38 -4
- package/dist/resources/approvals.d.ts.map +1 -1
- package/dist/resources/approvals.js +19 -4
- package/dist/resources/approvals.js.map +1 -1
- package/dist/resources/fleet.d.ts +73 -18
- package/dist/resources/fleet.d.ts.map +1 -1
- package/dist/resources/fleet.js.map +1 -1
- package/dist/resources/ops.d.ts +113 -0
- package/dist/resources/ops.d.ts.map +1 -1
- package/dist/resources/ops.js +10 -0
- package/dist/resources/ops.js.map +1 -1
- package/dist/resources/principal-inbox.d.ts +62 -0
- package/dist/resources/principal-inbox.d.ts.map +1 -0
- package/dist/resources/principal-inbox.js +51 -0
- package/dist/resources/principal-inbox.js.map +1 -0
- package/dist/resources/rules.d.ts +2 -5
- package/dist/resources/rules.d.ts.map +1 -1
- package/dist/resources/rules.js.map +1 -1
- package/dist/resources/session-sync.d.ts +27 -32
- package/dist/resources/session-sync.d.ts.map +1 -1
- package/dist/resources/session-sync.js +25 -39
- package/dist/resources/session-sync.js.map +1 -1
- package/dist/resources/tool-approvals.d.ts +251 -115
- package/dist/resources/tool-approvals.d.ts.map +1 -1
- package/dist/resources/tool-approvals.js +222 -69
- package/dist/resources/tool-approvals.js.map +1 -1
- package/dist/settings.d.ts +83 -1
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +0 -24
- package/dist/settings.js.map +1 -1
- package/dist/sse.d.ts +5 -1
- package/dist/sse.d.ts.map +1 -1
- package/dist/sse.js +6 -2
- package/dist/sse.js.map +1 -1
- package/dist/sync.d.ts +5 -5
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +13 -55
- package/dist/sync.js.map +1 -1
- package/dist/transport.d.ts +2 -0
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +3 -0
- package/dist/transport.js.map +1 -1
- package/dist/types.d.ts +298 -123
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +10 -3
- package/dist/types.js.map +1 -1
- package/openapi.yaml +1014 -572
- package/package.json +6 -2
- 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:
|
|
@@ -3040,50 +2958,6 @@ paths:
|
|
|
3040
2958
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
3041
2959
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
3042
2960
|
|
|
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
2961
|
/v1/sessions/{sessionId}/sync/history/blobs/{hash}:
|
|
3088
2962
|
parameters:
|
|
3089
2963
|
- $ref: '#/components/parameters/PrincipalHeader'
|
|
@@ -3109,7 +2983,7 @@ paths:
|
|
|
3109
2983
|
→ 404 `not_found.blob`, never a blind read; a server whose file-history store has no blob faces
|
|
3110
2984
|
(e.g. the local file backend) answers the SAME 404 shape (no oracle — "nothing servable" and "not
|
|
3111
2985
|
referenced" are deliberately indistinguishable). 400 `request.id_invalid` on a non-64-hex hash.
|
|
3112
|
-
Owner-scoped (404). Replaces the retired `
|
|
2986
|
+
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
2987
|
responses:
|
|
3114
2988
|
'200':
|
|
3115
2989
|
description: The blob bytes.
|
|
@@ -3142,12 +3016,10 @@ paths:
|
|
|
3142
3016
|
body). The cloud verifies `sha256(body) === :hash` (the content-address integrity guard) BEFORE storing — a
|
|
3143
3017
|
mismatch → 400 `blob hash mismatch`. own-or-FRESH gate. Per-blob cap 64 MiB → 413 mid-stream beyond it. A
|
|
3144
3018
|
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
|
|
3019
|
+
PUSH path is the BARE `blobs/:hash` (the PULL is `history/blobs/:hash`). The blob faces ride the
|
|
3147
3020
|
file-HISTORY store — a deployment whose store has no blob faces (the local file backend) answers 501
|
|
3148
3021
|
`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).
|
|
3022
|
+
rest of the sync surface keeps working (operator disposition: wire a backend with blob faces).
|
|
3151
3023
|
requestBody:
|
|
3152
3024
|
required: true
|
|
3153
3025
|
content:
|
|
@@ -3217,14 +3089,12 @@ paths:
|
|
|
3217
3089
|
x-status: live
|
|
3218
3090
|
summary: PUSH Phase A — classify + stage (mint a stagingId).
|
|
3219
3091
|
description: >
|
|
3220
|
-
2c session-sync PUSH Phase A — a SMALL metadata body (`entryIds,
|
|
3092
|
+
2c session-sync PUSH Phase A — a SMALL metadata body (`entryIds, fileHistory?, policy, anchors, resolution?, logDigest?, title?, pinned?`); the
|
|
3221
3093
|
entries stream in Phase B. The cloud classifies (§7), pre-checks blob presence (a missing referenced hash →
|
|
3222
3094
|
422 `missing_blob` — PUT it first), guards the import lease + any active run (409), then mints a stagingId.
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
`PUT …/sync/blobs/{hash}` first. ≤7.51 servers consume `snapshots` and do not know `fileHistory`. SDK 16.2.0 carries
|
|
3227
|
-
`fileHistory` in `pushSessionBundle` / `importSessionBundle` (and fills `snapshots: []` on the pull side when the manifest omits it); a hand-rolled
|
|
3095
|
+
The file history rides `fileHistory` (a `FileHistoryExport` envelope, `null` / absent = none) and every blob it references must have been
|
|
3096
|
+
`PUT …/sync/blobs/{hash}` first (the `snapshots` key of servers <= 7.51 is outside the support range; a supported server ignores it and sdk 26.0.0
|
|
3097
|
+
no longer declares it). The SDK carries `fileHistory` in `pushSessionBundle` / `importSessionBundle`; a hand-rolled
|
|
3228
3098
|
push must send `fileHistory` itself, and only to a peer whose `capabilities.sessionSyncFileHistory` is not false (server >= 7.116.0 — else the
|
|
3229
3099
|
commit 422s `missing_blob`).
|
|
3230
3100
|
`identical` → `{ relation:"identical", basis, payloadVerified }` with NO stagingId (skip Phase B);
|
|
@@ -3239,15 +3109,14 @@ paths:
|
|
|
3239
3109
|
application/json:
|
|
3240
3110
|
schema:
|
|
3241
3111
|
type: object
|
|
3242
|
-
required: [entryIds,
|
|
3112
|
+
required: [entryIds, policy, anchors]
|
|
3243
3113
|
properties:
|
|
3244
3114
|
entryIds: { type: array, items: { type: string } }
|
|
3245
|
-
snapshots: { type: array, items: { $ref: '#/components/schemas/SyncSnapshot' } }
|
|
3246
3115
|
fileHistory:
|
|
3247
3116
|
oneOf:
|
|
3248
3117
|
- { $ref: '#/components/schemas/FileHistoryExport' }
|
|
3249
3118
|
- { type: 'null' }
|
|
3250
|
-
description: '
|
|
3119
|
+
description: 'the file-history envelope; `null` / absent = none. Every blob it references must already be PUT.'
|
|
3251
3120
|
policy: { type: array, items: { $ref: '#/components/schemas/SessionRulesRecord' } }
|
|
3252
3121
|
anchors: { type: array, items: { $ref: '#/components/schemas/SyncAnchor' } }
|
|
3253
3122
|
title:
|
|
@@ -3324,7 +3193,7 @@ paths:
|
|
|
3324
3193
|
description: >
|
|
3325
3194
|
2c session-sync PUSH Phase B — stream the entry log as an NDJSON REQUEST body into the staged shadow id; the
|
|
3326
3195
|
cloud validates per-line (the streaming invariant gate) then `finish()` (exactly-one-root + leaf-resolvable) →
|
|
3327
|
-
commits (the atomic swap) → replays
|
|
3196
|
+
commits (the atomic swap) → replays fileHistory/policy/anchors. Returns `{ relation }` (the AUTHORITATIVE in-txn
|
|
3328
3197
|
re-classify). Re-POSTing the same stagingId is idempotent. Typed errors: a per-entry / structural violation →
|
|
3329
3198
|
422 `invalid_entries`; a line too large → 413; a commit-time TOCTOU flip to fork/stale → 409 `conflict`
|
|
3330
3199
|
(SyncConflictError); a refused policy loosen → 403 `loosen_forbidden`; a wrong/foreign stagingId → 404.
|
|
@@ -3385,6 +3254,15 @@ paths:
|
|
|
3385
3254
|
application/json:
|
|
3386
3255
|
schema: { $ref: '#/components/schemas/PendingList' }
|
|
3387
3256
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
3257
|
+
'500':
|
|
3258
|
+
description: >-
|
|
3259
|
+
server >= 7.134.0: a queue row whose stored body cannot be read (the store's refusal contract — a retry fails the same way; an operator has to repair
|
|
3260
|
+
or delete that row) answers `500` + `errorCode: "approvals.read_corrupt"` (body `{ error, errorCode }`; the bad row's coordinates stay off the wire) —
|
|
3261
|
+
the same judge and the same code as `GET /v1/approvals/stream`. Do NOT retry it as-is (the SDK's GET retry arm exempts the code). Every other read
|
|
3262
|
+
failure (connection / timeout / store hiccup) is still `500 internal.error`. Older servers answered the corrupt row with `internal.error`.
|
|
3263
|
+
content:
|
|
3264
|
+
application/json:
|
|
3265
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
3388
3266
|
|
|
3389
3267
|
/v1/approvals/stream:
|
|
3390
3268
|
parameters:
|
|
@@ -3424,7 +3302,7 @@ paths:
|
|
|
3424
3302
|
`synced` (and `synced.count` still counts ONLY the durable queue).
|
|
3425
3303
|
🔴 THE TWO WORDS DELIBERATELY DO NOT REUSE `pending`/`resolved`: those are keyed by
|
|
3426
3304
|
`(sessionId, toolCallId)` and decided at `POST /v1/approvals/{sessionId}/decide`, while a live row is
|
|
3427
|
-
keyed by `approvalId` and decided at `POST /v1/tool-approvals/{
|
|
3305
|
+
keyed by `approvalId` and decided at `POST /v1/tool-approvals/{id}/respond`. Sharing one word
|
|
3428
3306
|
would eventually send a live card to the decide door — a 404 with no wire evidence of the mistake.
|
|
3429
3307
|
⚠️ Both live words come from an IN-PROCESS window projection, so their cross-replica reach is exactly
|
|
3430
3308
|
that of `GET /v1/approvals`'s `livePending` — narrower than the durable pair above. The deployment
|
|
@@ -4393,6 +4271,38 @@ paths:
|
|
|
4393
4271
|
'404': { description: no pending question for this id (non-owner, answered, expired, or wrong replica) — no existence oracle. }
|
|
4394
4272
|
'501': { description: AskUserQuestion is not enabled on this worker (ASK_QUESTION_ENABLED). }
|
|
4395
4273
|
|
|
4274
|
+
/v1/tool-approvals/{id}:
|
|
4275
|
+
parameters:
|
|
4276
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
4277
|
+
- { name: id, in: path, required: true, schema: { type: string }, description: 'The ask''s `approvalId` (server >= 7.133.0 always equal to its `askId`; an opaque string).' }
|
|
4278
|
+
get:
|
|
4279
|
+
tags: [questions]
|
|
4280
|
+
operationId: toolApprovalRead
|
|
4281
|
+
x-status: live # server >= 7.133.0; SDK toolApprovals.get consumes it
|
|
4282
|
+
summary: Read ONE live-card ask — where is it now (pending, decided, parked or void)? Pure read.
|
|
4283
|
+
description: >
|
|
4284
|
+
server >= 7.133.0 — the by-ask read door. Same root and same gate as POST /v1/tool-approvals/{id}/respond (one `gatedPrincipal`; an owner-gate miss answers the SAME 404 as an unknown
|
|
4285
|
+
id — no existence oracle), and the state is read the way the answer door reads it: a live registration on THIS replica waiting for the ask => `pending` (the body is the `livePending[]` row projection +
|
|
4286
|
+
`state`); otherwise the row is read by id: `DECIDED` => `decided`, `PARKING` / `PARKED` => `parked`, `DENIED` / `VOID` => `void`. A row still `STREAM_PENDING` with no local registration lives on
|
|
4287
|
+
another replica => 404 `tool_approval.not_pending` + `cause: "unknown_or_other_replica"` (the same answer as a late respond); a deployment with no ask ledger answers the same 404 WITHOUT `cause`.
|
|
4288
|
+
Pure read: zero writes, no settlement, the local registration is untouched. Use it to ask "is this card still alive / was it decided by anyone" instead of inferring it from a respond 404.
|
|
4289
|
+
An older server (<= 7.132.x) has no such door: routing-level 404 `not_found.route` — tell it apart from this door's 404 by `errorCode`, never by status.
|
|
4290
|
+
responses:
|
|
4291
|
+
'200':
|
|
4292
|
+
description: The ask's current state.
|
|
4293
|
+
content:
|
|
4294
|
+
application/json:
|
|
4295
|
+
schema: { $ref: '#/components/schemas/ToolApprovalReadBody' }
|
|
4296
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
4297
|
+
'404':
|
|
4298
|
+
description: >
|
|
4299
|
+
`tool_approval.not_pending` — unknown / not yours (byte-identical) / still pending on another replica (`cause: unknown_or_other_replica`). Past the owner gate the body may carry the additive `cause`
|
|
4300
|
+
(see ToolApprovalNotPendingCause); no ask ledger => no `cause`.
|
|
4301
|
+
content:
|
|
4302
|
+
application/json:
|
|
4303
|
+
schema: { $ref: '#/components/schemas/ToolApprovalNotPendingBody' }
|
|
4304
|
+
'501': { description: 'Live tool-approval HITL not enabled (TOOL_APPROVAL_ENABLED) — `feature.tool_approval_disabled`.' }
|
|
4305
|
+
|
|
4396
4306
|
/v1/tool-approvals/{id}/respond:
|
|
4397
4307
|
parameters:
|
|
4398
4308
|
- $ref: '#/components/parameters/PrincipalHeader'
|
|
@@ -4426,7 +4336,7 @@ paths:
|
|
|
4426
4336
|
verbatim 400 texts, the two capability bits to probe first and the `deny` posture are on
|
|
4427
4337
|
the PersistRuleSelection schema. Allow-family only (on `deny` the whole field is ignored).
|
|
4428
4338
|
|
|
4429
|
-
⚠️ If the frame carried
|
|
4339
|
+
⚠️ If the frame carried no `ruleOffers`, do not send this key —
|
|
4430
4340
|
there is nothing redeemable for THIS card and the answer is necessarily
|
|
4431
4341
|
`rulePersisted:false` + `rule_lane_unavailable` (the missing material short-circuits BEFORE
|
|
4432
4342
|
any candidate comparison, so it is never `rule_not_offered`). Absence has several causes —
|
|
@@ -5295,39 +5205,6 @@ paths:
|
|
|
5295
5205
|
error: { type: string }
|
|
5296
5206
|
missing: { type: array, items: { type: string } }
|
|
5297
5207
|
|
|
5298
|
-
/v1/approvals/{approvalId}:
|
|
5299
|
-
parameters:
|
|
5300
|
-
- $ref: '#/components/parameters/PrincipalHeader'
|
|
5301
|
-
- in: path
|
|
5302
|
-
name: approvalId
|
|
5303
|
-
required: true
|
|
5304
|
-
schema: { type: string }
|
|
5305
|
-
get:
|
|
5306
|
-
tags: [approvals]
|
|
5307
|
-
operationId: approvalsGet
|
|
5308
|
-
x-status: retired # 5.0.0: legacy approval-store leg deleted — NO deployment serves this route.
|
|
5309
|
-
summary: 'RETIRED (server 5.0.0) — by-id approval read; every deployment answers 404. Use GET /v1/approvals.'
|
|
5310
|
-
description: >
|
|
5311
|
-
🔴 RETIRED as of `@sema-agent/server` 5.0.0 ([2354]/[2356] A-5): the legacy approval-store polling leg
|
|
5312
|
-
that served this route was deleted whole, and the durable checkpoint lane never had a by-id read
|
|
5313
|
-
(it can list by scope or look up by capability token — there is no simple-id query). A worker WITH a
|
|
5314
|
-
checkpoint store answers 404 `not_found.route`; a worker WITHOUT one never matches the `/v1/approvals*`
|
|
5315
|
-
prefix at all. Either way: 404 on every deployment, and that 404 is indistinguishable from "no such
|
|
5316
|
-
approval", which is exactly why the SDK stopped calling it (4.0.1 — `approvals.get` now reads
|
|
5317
|
-
`GET /v1/approvals` and filters by `sessionId`, one round trip instead of a guaranteed-404 probe plus
|
|
5318
|
-
the same list call).
|
|
5319
|
-
Kept in the spec as a tombstone rather than deleted: consumers generated against an older spec need to
|
|
5320
|
-
see WHY the path went away. Decide via `POST /v1/approvals/{sessionId}/decide`; list via `GET /v1/approvals`.
|
|
5321
|
-
Historical shape (below) is left intact for the same reason; it describes no live behavior.
|
|
5322
|
-
responses:
|
|
5323
|
-
'200':
|
|
5324
|
-
description: The approval row (full args projection).
|
|
5325
|
-
content:
|
|
5326
|
-
application/json:
|
|
5327
|
-
schema: { $ref: '#/components/schemas/ApprovalRow' }
|
|
5328
|
-
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
5329
|
-
'404': { $ref: '#/components/responses/NotFound' }
|
|
5330
|
-
|
|
5331
5208
|
/v1/memory/export:
|
|
5332
5209
|
parameters:
|
|
5333
5210
|
- $ref: '#/components/parameters/PrincipalHeader'
|
|
@@ -5799,6 +5676,70 @@ paths:
|
|
|
5799
5676
|
'403': { description: 'Multi-tenant worker: outcomes view is operator-only.' }
|
|
5800
5677
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
5801
5678
|
|
|
5679
|
+
/v1/config/catalog:
|
|
5680
|
+
parameters:
|
|
5681
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
5682
|
+
get:
|
|
5683
|
+
tags: [admin]
|
|
5684
|
+
operationId: configCatalog
|
|
5685
|
+
x-status: live # operator lane (capabilities.configCatalog); SDK configCatalog.get consumes it
|
|
5686
|
+
summary: Deployment configuration catalog — every env knob's declaration and this process's effective reading (operator-only).
|
|
5687
|
+
description: >
|
|
5688
|
+
The deployment's configuration catalog (operator lane; probe `capabilities.configCatalog` first). One row per env knob: its declared semantics, which lanes can write it, which lane is in force, the
|
|
5689
|
+
process's effective DISPLAY value and the centre seat (`center`) when the knob is centre-publishable; plus the `retired` tombstone list (env names this process refuses to start on). `Cache-Control: no-store`.
|
|
5690
|
+
Gate: `OPERATOR_PRINCIPALS` — outside the list 403 `auth.operator_only`; empty list on a multi-tenant deployment 501 `capability.operator_principals_required`; `CONFIG_CATALOG_ENABLED=false` 501
|
|
5691
|
+
`feature.config_catalog_disabled`. A server older than 7.53 has no such route (404).
|
|
5692
|
+
`effectiveValue` is a DISPLAY value, never the raw env: `secret` / `opaque` rows report only `{ present, length? }`, a `url` row goes through the display mint (userinfo landed, query values become
|
|
5693
|
+
tokens). Two VERDICT bits on a row are true-only (never `false`; ABSENT = this cell has no such matter): `effectiveValueInvalid` (the effective value falls outside the row's own echo set; `effectiveValue` is then
|
|
5694
|
+
`null`) and, server >= 7.133.0, `effectiveValueWithheld` (a `url` row whose display mint WITHHELD a part — a credential-shaped piece in a query / path segment — so the shown value is not the whole value).
|
|
5695
|
+
responses:
|
|
5696
|
+
'200':
|
|
5697
|
+
description: The catalog.
|
|
5698
|
+
content:
|
|
5699
|
+
application/json:
|
|
5700
|
+
schema: { $ref: '#/components/schemas/ConfigCatalog' }
|
|
5701
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
5702
|
+
'403': { description: 'The caller is not in OPERATOR_PRINCIPALS (`auth.operator_only`).' }
|
|
5703
|
+
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
5704
|
+
|
|
5705
|
+
/v1/principal-inbox/messages:
|
|
5706
|
+
parameters:
|
|
5707
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
5708
|
+
post:
|
|
5709
|
+
tags: [admin]
|
|
5710
|
+
operationId: principalInboxSend
|
|
5711
|
+
x-status: live # server >= 7.133.0; SDK principalInbox.send consumes it
|
|
5712
|
+
summary: Deliver one letter to another principal's inbox in the same organization (cross-principal delivery; default OFF).
|
|
5713
|
+
description: >
|
|
5714
|
+
server >= 7.133.0 — one letter is appended to the recipient principal's `principal.inbox` ONLY by the organization delivery gate: membership judgement -> mint -> inbox bound -> quota -> append, all inside one gate that answers
|
|
5715
|
+
with a receipt or one of four refusal codes. The SENDER is always your verified identity (`from` is not a body key: sending `from` or any unknown key => 400 `request.body_shape` + `unknownKeys`). DEFAULT OFF: without env
|
|
5716
|
+
`PRINCIPAL_INBOX_ASSISTANT` the route answers 501 `capability.unavailable` (no delivery gate is mounted) — probe `capabilities.principalInboxDelivery` (true <=> this door works). THIS VERSION SHIPS THE DELIVERY HALF ONLY:
|
|
5717
|
+
a delivered letter sits in the recipient's box (at most 50 unconfirmed envelopes) and no read verb exists yet. Judging order: the sender's ATTEMPT bound (every attempt is recorded, succeeded or not, BEFORE membership) ->
|
|
5718
|
+
membership (including the mailbox-key judge) -> mint (a sender that cannot be spelled into the envelope's sender slot answers the same `not_member`, no quota consumed) -> inbox bound -> quota deduction -> append. A refused
|
|
5719
|
+
delivery appends nothing. Not auto-retried by the SDK (every attempt consumes the sender's attempt quota).
|
|
5720
|
+
The four refusal codes: `org_delivery.not_member` 404 (ONE sentence, byte-identical body for: different organization / no such person / directory could not answer / `to` is not a principal shape / the recipient cannot be a
|
|
5721
|
+
mailbox key / the sender cannot be spelled into the envelope — the gate is never an existence probe); `org_delivery.rate_limited` 429 (one sentence for three bounds: 60 attempts per sender per hour — a typo'd recipient costs an
|
|
5722
|
+
attempt —, 10 letters per (sender, recipient) pair per hour, 30 letters per recipient per hour; fixed clock-hour windows, up to double across a boundary; hard-coded, no knob); `org_delivery.inbox_full` 409 (the recipient's box already holds
|
|
5723
|
+
50 unconfirmed envelopes — judged BEFORE quota so a full box burns none); `org_delivery.recipient_tombstoned` 410 (the recipient's mailbox is in its pre-delete state).
|
|
5724
|
+
requestBody:
|
|
5725
|
+
required: true
|
|
5726
|
+
content:
|
|
5727
|
+
application/json:
|
|
5728
|
+
schema: { $ref: '#/components/schemas/PrincipalInboxMessage' }
|
|
5729
|
+
responses:
|
|
5730
|
+
'200':
|
|
5731
|
+
description: The letter was appended; `receiptId` is the gate's delivery receipt id.
|
|
5732
|
+
content:
|
|
5733
|
+
application/json:
|
|
5734
|
+
schema: { $ref: '#/components/schemas/PrincipalInboxReceipt' }
|
|
5735
|
+
'400': { description: 'Malformed body: `request.field_invalid` (not an object / empty `to` / empty or over-long `content` / non-boolean `urgent`) or `request.body_shape` (unknown key incl. `from`; body carries `unknownKeys`).' }
|
|
5736
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
5737
|
+
'404': { description: '`org_delivery.not_member` — one byte-identical body for six causes (see the description); the gate is not an existence probe.' }
|
|
5738
|
+
'409': { description: '`org_delivery.inbox_full` — the recipient''s box holds 50 unconfirmed envelopes.' }
|
|
5739
|
+
'410': { description: '`org_delivery.recipient_tombstoned` — the recipient''s mailbox is in its pre-delete state.' }
|
|
5740
|
+
'429': { description: '`org_delivery.rate_limited` — one sentence for the sender-attempt bound, the pair bound and the recipient aggregate bound.' }
|
|
5741
|
+
'501': { description: 'No delivery gate mounted (`PRINCIPAL_INBOX_ASSISTANT` off) — `capability.unavailable`.' }
|
|
5742
|
+
|
|
5802
5743
|
/v1/diagnostics/wiring:
|
|
5803
5744
|
parameters:
|
|
5804
5745
|
- $ref: '#/components/parameters/PrincipalHeader'
|
|
@@ -7064,6 +7005,7 @@ components:
|
|
|
7064
7005
|
promptSummary: { type: string, description: 'a human-readable prompt DIGEST — NOT the full prompt.' }
|
|
7065
7006
|
enabled: { type: boolean }
|
|
7066
7007
|
available: { type: boolean, description: 'whether this deployment can actually RUN the scenario right now — same predicate as the 501 rejection arm on submit; false ⟺ submitting is guaranteed to 501. Independent axis from `enabled` (declared vs runnable).' }
|
|
7008
|
+
attachmentSupplies: { type: array, items: { type: string, enum: [path, content_text, content_image] }, description: 'server >= 7.133.0, always present there: the attachment SUPPLY words this scenario actually carries — a subset of `capabilities.userAttachments.supplies` (`path` only on hands scenarios, where a workspace lives somewhere; a no-hands scenario drops `path`, and a `path` entry is refused by the engine with `config.user_attachment_invalid`). ABSENT = server <= 7.132.x: fall back to `userAttachments.supplies` plus the existing 400 handling.' }
|
|
7067
7009
|
unavailableReason: { type: string, description: 'machine-readable reason, present ONLY when available:false (absent = no reason). Closed set, currently the single member `git_client_unconfigured`; branch on the key, never match the English prose. Additive-open: treat an unknown word as "unavailable, reason unknown".' }
|
|
7068
7010
|
|
|
7069
7011
|
RunStatus:
|
|
@@ -7704,11 +7646,29 @@ components:
|
|
|
7704
7646
|
`permissions.defaultMode`'s specific fold is documented on `TaskRequest.permissionMode`.
|
|
7705
7647
|
🔴 ON THIS SUBMIT FACE THE KEY SET IS CLOSED (server >= 7.57.0 for the sub-tree): accepted top-level keys
|
|
7706
7648
|
are `env` / `hooks` / `model` / `outputStyle` / `permissions` / `skillListingBudgetFraction` /
|
|
7707
|
-
`skillListingMaxDescChars` / `ultracode` / `webSearch
|
|
7708
|
-
`allow` / `ask` / `defaultMode` / `deny` / `disableAutoMode
|
|
7649
|
+
`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
|
|
7650
|
+
`allow` / `ask` / `defaultMode` / `deny` / `disableAutoMode` (server >= 7.133.0 adds `neverAuto`); anything else is 400 `request.body_shape`
|
|
7709
7651
|
with the names in the refusal's `unknownKeys` / `unsupportedKeys` arrays (REFUSED, not dropped — a dropped
|
|
7710
7652
|
key reads as "in effect" to the caller). The object stays OPEN here because the same `SemaSettings`
|
|
7711
7653
|
schema also serves the local `settings.json` wiring, where deferred CC keys are legal.
|
|
7654
|
+
🔴 TIGHTEN-ONLY SEATS (server >= 7.133.0; probe `capabilities.taskSettings.tightenOnlySeats`, an absent bit = an older server that answers 400 `request.body_shape` for these four keys): `readDenyPatterns` (entries: a bare
|
|
7655
|
+
pattern string, or `{ pattern, caseSensitive? }`), `writeProtectedExtra` (a bare name — no `/` reads as `segment`, a `/` reads as `segment-run` — or `{ name, kind: basename | segment | segment-run }`),
|
|
7656
|
+
`complianceAdditionalDenies` (`mcp_servers` | `workflows` | `web_fetch` | `org_memory_mount`) and `permissions.neverAuto` (tool names that ALWAYS ask a human). Each seat = the deployment table UNION the request list, ONLY EVER GROWING
|
|
7657
|
+
(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`).
|
|
7658
|
+
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.
|
|
7659
|
+
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).
|
|
7660
|
+
🔴 `settings.approvalFrameFamilies` (server >= 7.134.0; send it on sight of `capabilities.taskSettings.approvalFrameFamilies === true` — NOT of the top-level
|
|
7661
|
+
`capabilities.approvalFrameFamilies`, which <= 7.133.x already has while answering 400 `request.body_shape` for this key): the approval opening-frame FAMILIES this
|
|
7662
|
+
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
|
|
7663
|
+
repeat) => 400 `request.field_invalid` with `field: "settings.approvalFrameFamilies"`, `reason: "bad_entry"` and `index` (absent for a non-array). BEHAVIOR (BREAKING, wire):
|
|
7664
|
+
a run whose declaration does not contain the family this worker presents (`capabilities.approvalFrameFamilies`) has NO live approval channel — no card of either
|
|
7665
|
+
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
|
|
7666
|
+
policy at once: with a durable store (`capabilities.approvals`) it parks per the operator policy (default; answer through `POST /v1/approvals/{sessionId}/decide`)
|
|
7667
|
+
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
|
|
7668
|
+
rides the persisted body onto resume legs.
|
|
7669
|
+
🔴 `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` /
|
|
7670
|
+
`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 =>
|
|
7671
|
+
400 `request.field_invalid` with `detail: { reason: "wire_key_invalid", key, path }`; the plugin context has one 8192-character budget.
|
|
7712
7672
|
🔴 `skillListingBudgetFraction` (number in (0,1]) / `skillListingMaxDescChars` (positive integer),
|
|
7713
7673
|
server >= 7.78.1: the SKILL-LISTING RENDER BUDGET, passed to the engine with ZERO processing
|
|
7714
7674
|
(no clamping, no conversion, no folding) — ABSENT means the key is not written at all (the server does
|
|
@@ -8374,6 +8334,10 @@ components:
|
|
|
8374
8334
|
cause (`terminal`), not the eight parallel plane keys — see `TerminalCause`.
|
|
8375
8335
|
required: [taskId, sessionId, terminal, stats]
|
|
8376
8336
|
properties:
|
|
8337
|
+
warnings:
|
|
8338
|
+
type: array
|
|
8339
|
+
items: { $ref: '#/components/schemas/EntryProblem' }
|
|
8340
|
+
description: 'server >= 7.133.0, top level of the sync `POST /v1/tasks` 200 body (beside `notice`), present only when non-empty: the accept-time NOTICES for allow-side selector-list entries the engine reads as nothing or that are not in the roster. ABSENT = no notice (older servers too).'
|
|
8377
8341
|
taskId: { type: string }
|
|
8378
8342
|
sessionId: { type: string }
|
|
8379
8343
|
wiringManifest:
|
|
@@ -8676,6 +8640,10 @@ components:
|
|
|
8676
8640
|
taskId: { type: string }
|
|
8677
8641
|
sessionId: { type: string }
|
|
8678
8642
|
status: { $ref: '#/components/schemas/RunStatus' }
|
|
8643
|
+
warnings:
|
|
8644
|
+
type: array
|
|
8645
|
+
items: { $ref: '#/components/schemas/EntryProblem' }
|
|
8646
|
+
description: 'server >= 7.133.0, present only when non-empty: the accept-time NOTICES for allow-side selector-list entries the engine reads as nothing or that are not in the roster (acceptance is unchanged — the engine takes them, only the operator hears afterwards; this makes "this name will not take effect" visible at the call). Same list as the stream `meta` frame''s and the sync 200 body''s. ABSENT = no notice (older servers too).'
|
|
8679
8647
|
|
|
8680
8648
|
SealedKeyCapability:
|
|
8681
8649
|
type: object
|
|
@@ -9545,6 +9513,14 @@ components:
|
|
|
9545
9513
|
redispatched: { type: array, items: { type: string } }
|
|
9546
9514
|
collapsed: { type: string }
|
|
9547
9515
|
|
|
9516
|
+
ApprovalFrameFamily:
|
|
9517
|
+
type: string
|
|
9518
|
+
enum: [tool_approval, approval_request]
|
|
9519
|
+
description: >-
|
|
9520
|
+
The approval opening-frame FAMILY words (closed; the server's `APPROVAL_FRAME_FAMILIES` verbatim and in order): `tool_approval` = the legacy family
|
|
9521
|
+
(live card `tool_approval` / close `tool_approval_complete`); `approval_request` = the card family (`approval_request` / `approval_complete` /
|
|
9522
|
+
batch revoke `approval_revoke`). ONE vocabulary for two faces: the capability `approvalFrameFamilies` (which family this worker presents) and, server >= 7.134.0,
|
|
9523
|
+
the request key `settings.approvalFrameFamilies` (which families this run's client renders).
|
|
9548
9524
|
ApprovalDecision:
|
|
9549
9525
|
type: object
|
|
9550
9526
|
description: HITL decision (`POST /v1/approvals/:sessionId/decide`).
|
|
@@ -9612,30 +9588,17 @@ components:
|
|
|
9612
9588
|
refused by the ENGINE before the CAS (409 `resume_outcome_invalid` + `field: "hostDecision"`, the card stays
|
|
9613
9589
|
pending); outside the closed set ⇒ 400 `request.field_invalid`; any value on a direct-door worker ⇒ 400
|
|
9614
9590
|
`decided_by_not_in_proof`. An older server (no `approvalDecidedBy` bit) refuses the key (400 `request.body_shape`).
|
|
9615
|
-
|
|
9616
|
-
|
|
9617
|
-
|
|
9618
|
-
|
|
9619
|
-
|
|
9620
|
-
|
|
9621
|
-
|
|
9622
|
-
|
|
9623
|
-
|
|
9624
|
-
|
|
9625
|
-
|
|
9626
|
-
properties:
|
|
9627
|
-
id: { type: string }
|
|
9628
|
-
taskId: { type: [string, 'null'] }
|
|
9629
|
-
sessionId: { type: [string, 'null'] }
|
|
9630
|
-
owner: { type: [string, 'null'] }
|
|
9631
|
-
scope: { type: [string, 'null'], description: 'Single-DB fleet scope guard (tenant identity, owner-sourced).' }
|
|
9632
|
-
toolName: { type: string }
|
|
9633
|
-
args: { description: 'The reviewed tool-call args — full fidelity, not redacted (this IS the review surface).' }
|
|
9634
|
-
status: { type: string, enum: [pending, approved, denied, expired] }
|
|
9635
|
-
reason: { type: [string, 'null'] }
|
|
9636
|
-
decidedBy: { type: [string, 'null'] }
|
|
9637
|
-
createdAt: { type: string }
|
|
9638
|
-
decidedAt: { type: [string, 'null'] }
|
|
9591
|
+
feedback:
|
|
9592
|
+
type: string
|
|
9593
|
+
description: >-
|
|
9594
|
+
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
|
|
9595
|
+
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
|
|
9596
|
+
(`decision: "approve"`, `decidedBy` absent or `person`): on a deny or a `decidedBy: "policy"` decision ⇒ 400 `request.field_conflict` with
|
|
9597
|
+
`detail.kind: "approval_feedback"` and `detail.fields` `["feedback", "decision"]` / `["feedback", "decidedBy"]` (a denial's words go in `reason`);
|
|
9598
|
+
a non-string ⇒ 400 `request.field_invalid`; any value on a direct-door worker ⇒ 400 `feedback_not_in_proof` (not inside the signed proof); words
|
|
9599
|
+
carrying `<system-reminder>` ⇒ 409 `approval_binding_mismatch` (the engine's sentence; the card stays pending — ask the person to change the words,
|
|
9600
|
+
do not auto-refetch and re-decide on the code). The same door serves parked background-agent cards and workflow child cards; the live
|
|
9601
|
+
`POST /v1/tool-approvals/{id}/respond` does NOT take it. Servers <= 7.133.x refuse the key (400 `request.body_shape`, `unknownKeys: ["feedback"]`).
|
|
9639
9602
|
|
|
9640
9603
|
Capabilities:
|
|
9641
9604
|
type: object
|
|
@@ -9803,6 +9766,11 @@ components:
|
|
|
9803
9766
|
store backend deliberately wires no image index (the pool is cloud/fleet-only) ⇒ false there.
|
|
9804
9767
|
SCOPE: P1 only — `/v1/images/bakes*` (the P2 bake control plane) is a separate dependency and an
|
|
9805
9768
|
operator/runner-internal face, NOT promised by this bit.
|
|
9769
|
+
principalInboxDelivery:
|
|
9770
|
+
type: boolean
|
|
9771
|
+
description: >-
|
|
9772
|
+
server >= 7.133.0 — the cross-principal delivery door `POST /v1/principal-inbox/messages` is wired (delivery gate present: env `PRINCIPAL_INBOX_ASSISTANT` on and the gate's dependencies in place).
|
|
9773
|
+
True iff that route works; absent gate => the route answers 501 `capability.unavailable`. ABSENT key = server <= 7.132.x (no such route; routing-level 404). Probe this bit; do not trial-by-501.
|
|
9806
9774
|
sharedMemory:
|
|
9807
9775
|
type: boolean
|
|
9808
9776
|
description: >
|
|
@@ -9910,25 +9878,12 @@ components:
|
|
|
9910
9878
|
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.' }
|
|
9911
9879
|
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.' }
|
|
9912
9880
|
workspace:
|
|
9881
|
+
type: boolean
|
|
9882
|
+
enum: [false]
|
|
9913
9883
|
description: >
|
|
9914
|
-
|
|
9915
|
-
|
|
9916
|
-
|
|
9917
|
-
⇒ 413 workspace_file_too_large envelope; the archive endpoint is the escape hatch).
|
|
9918
|
-
🔴 RETIRED (server ≥7.52, S-15/design/381): the whole-tree snapshot epoch this face projected was
|
|
9919
|
-
replaced by per-edited-file rewind history, so this key is now ALWAYS false and the four
|
|
9920
|
-
/v1/sessions/{sessionId}/workspace* routes answer 501 `capability.workspace_retired` (route shape
|
|
9921
|
-
kept as a tombstone — a named refusal, not a 404 that reads like a typo). Consumption posture
|
|
9922
|
-
unchanged: probe this bit, false ⇒ don't render the panel.
|
|
9923
|
-
oneOf:
|
|
9924
|
-
- type: boolean
|
|
9925
|
-
enum: [false]
|
|
9926
|
-
- type: object
|
|
9927
|
-
additionalProperties: true
|
|
9928
|
-
properties:
|
|
9929
|
-
browse: { type: boolean }
|
|
9930
|
-
archive: { type: boolean }
|
|
9931
|
-
maxFileBytes: { type: integer }
|
|
9884
|
+
The workspace-browse bit — ALWAYS `false` on every supported server (>= 7.52.0: the whole-tree snapshot epoch was replaced by
|
|
9885
|
+
per-edited-file rewind history; the four /v1/sessions/{sessionId}/workspace* routes answer 501 `capability.workspace_retired`, route shape kept as a
|
|
9886
|
+
tombstone). sdk 26.0.0 deleted the object arm `{ browse, archive, maxFileBytes }` (servers 1.299–7.51, below the support floor).
|
|
9932
9887
|
sessionEvents:
|
|
9933
9888
|
type: boolean
|
|
9934
9889
|
description: >
|
|
@@ -9973,6 +9928,12 @@ components:
|
|
|
9973
9928
|
lane) or 422 (hosted).
|
|
9974
9929
|
mcp: { type: boolean, description: "MCP servers can be attached." }
|
|
9975
9930
|
mcpInjection: { type: boolean, description: "Per-request MCP injection is accepted." }
|
|
9931
|
+
mcpAssertionHeaderSent:
|
|
9932
|
+
type: boolean
|
|
9933
|
+
description: >-
|
|
9934
|
+
server >= 7.133.0 — the SEND seat self-report (a boolean, today ALWAYS `false`): whether `mcpServers[].transport.assertionHeader` (accepted since server 7.128.0, mutually exclusive with `principalHeader`)
|
|
9935
|
+
is actually SENT to the MCP peer. `false` = the declaration is carried but NOT sent (the same switch drives the key stripping, the operator info line and the probe-door refusal; the disclosure sentences leave when it turns true).
|
|
9936
|
+
ABSENT = an older server, which does not send either — treat it as `false`; never rely on the header for peer authentication.
|
|
9976
9937
|
mcpProbe:
|
|
9977
9938
|
type: boolean
|
|
9978
9939
|
description: >
|
|
@@ -10012,6 +9973,20 @@ components:
|
|
|
10012
9973
|
⚠️ ABSENT = an older server (<7.87.1) ⇒ cannot tell whether the push face carries the live family;
|
|
10013
9974
|
do NOT read "no `live_pending` frame arrived yet" as "this deployment never pushes live" — the two
|
|
10014
9975
|
are indistinguishable on an older server. Fall back to `GET /v1/approvals`'s `livePending` pull face.
|
|
9976
|
+
approvalFrameFamilies:
|
|
9977
|
+
type: array
|
|
9978
|
+
maxItems: 1
|
|
9979
|
+
items: { $ref: '#/components/schemas/ApprovalFrameFamily' }
|
|
9980
|
+
description: >-
|
|
9981
|
+
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:
|
|
9982
|
+
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`
|
|
9983
|
+
(the legacy `tool_approval` / `tool_approval_complete` pair is NOT sent — BREAKING vs <= 7.132.x, which sent both families for one ask and relied on `approvalId` de-duplication;
|
|
9984
|
+
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.
|
|
9985
|
+
`[]` = per-call approvals are off altogether (`TOOL_APPROVAL_ENABLED=false`): no card is presented. Dispatch frame families off THIS array, never off `streamApproval`.
|
|
9986
|
+
KEY ABSENT (not `[]`) = server <= 7.132.x — there a `streamApproval: true` leg sends BOTH families for one ask.
|
|
9987
|
+
🔴 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
|
|
9988
|
+
its submit body declared that family in `settings.approvalFrameFamilies` (send it on sight of `taskSettings.approvalFrameFamilies`, not of this bit — <= 7.133.x
|
|
9989
|
+
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.
|
|
10015
9990
|
streamApproval:
|
|
10016
9991
|
type: boolean
|
|
10017
9992
|
description: >
|
|
@@ -10066,6 +10041,13 @@ components:
|
|
|
10066
10041
|
a server <= 7.118.x returns the archive as written; a 7.119.x server already redacts but does not report the bit. A true
|
|
10067
10042
|
bit does not mean the read face exists (that is `sessions`; without a session audit face that door answers 501 and this
|
|
10068
10043
|
bit is still true), nor that the archive holds no credential (the redaction is read-side only).
|
|
10044
|
+
approvalReadRootGrant:
|
|
10045
|
+
type: boolean
|
|
10046
|
+
description: >-
|
|
10047
|
+
server >= 7.133.0, ALWAYS present, a FACT that varies by deployment — whether this deployment accepts the reply-body key `readRootGrant` ("Yes, and add directory": the card's
|
|
10048
|
+
`readRootCandidate.dir` is registered as a read root together with this one allow). True iff the single-user host-lane posture (the same predicate as `projectContext` / the cwd face); false elsewhere.
|
|
10049
|
+
A client shows the option only when the card carries `readRootCandidate` AND this bit is true. On a non-accepting posture a reply carrying `readRootGrant` is refused
|
|
10050
|
+
`400 request.body_shape` (the ask stays unsettled — answer again without the key). ABSENT = server <= 7.132.x = not accepted; do not send the key.
|
|
10069
10051
|
approvalReach:
|
|
10070
10052
|
type: boolean
|
|
10071
10053
|
description: >-
|
|
@@ -10178,6 +10160,10 @@ components:
|
|
|
10178
10160
|
outputStyle: { type: boolean }
|
|
10179
10161
|
env: { type: boolean, description: 'TRUE on the single-user host lane (server emits `cwdHonored(config)`). It is NOT hard-coded false any more — the old always-false value predated the per-task shellEnv seam and hid a live capability.' }
|
|
10180
10162
|
hooks: { type: boolean, description: 'Single-user deployments only (`requirePrincipal !== true`).' }
|
|
10163
|
+
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.' }
|
|
10164
|
+
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).' }
|
|
10165
|
+
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.' }
|
|
10166
|
+
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.' }
|
|
10181
10167
|
settings:
|
|
10182
10168
|
type: object
|
|
10183
10169
|
description: >
|
|
@@ -10206,6 +10192,7 @@ components:
|
|
|
10206
10192
|
properties:
|
|
10207
10193
|
allowGrant: { type: boolean, description: 'Constant `true` on server >= 7.115.0: `settings.permissions.allow` is read as allow RULES, not as a list. The whole `settings` key being absent = an older server.' }
|
|
10208
10194
|
allowLayer: { type: boolean, description: 'Constant `true` on server >= 7.117.0 (core 7.42.0): the whole `settings.permissions.allow` list goes to the engine allow layer (listed calls are not asked; content forms accepted; entries read as nothing get an `engine_notice` `permission.allowed_tool_not_granted`). Absent = a server without it (7.115.0–7.116.x: allow is a statement; older: a list).' }
|
|
10195
|
+
callerTrust: { type: string, description: 'server >= 7.133.0 — THIS caller''s trust tier (per caller / per credential, NOT a version bit). Request rule tables that take effect without a person answering (`settings.permissions` buckets; a named-invoked skill''s `allowedTools` / `disallowedTools`) are admitted by it: `allow` loosens the no-ask surface and needs `global`; `deny` / `ask` only tighten and are admitted at any tier. Today `global` (single-user turnkey; multi-tenant with the deployment service credential; multi-tenant sign-in token whose scope role >= the governance write role) or `project` (everything else). Read OPEN: an unknown word is treated as `project` (fail-closed). A request below the floor is refused whole with 400 `request.field_invalid` (`detail.problems[].reason: below_trust_floor`, `detail.callerTrust`). ABSENT = server <= 7.132.x: no per-tier admission.' }
|
|
10209
10196
|
memoryWrite:
|
|
10210
10197
|
type: boolean
|
|
10211
10198
|
description: >
|
|
@@ -10268,7 +10255,7 @@ components:
|
|
|
10268
10255
|
additionalProperties: true
|
|
10269
10256
|
properties:
|
|
10270
10257
|
enabled: { type: boolean, enum: [true] }
|
|
10271
|
-
protocolVersion: { type: integer }
|
|
10258
|
+
protocolVersion: { type: integer, description: 'The protocol REVISION this server speaks (the `challenge` frame carries the same value; accepted window [1, 2] on server >= 7.133.0). Server >= 7.133.0 reads 2 (<= 7.132.x read 1). An executor declares the SMALLER of its own highest revision and this value, and takes its outcome-code set from the declared revision (revision 2 added `liveness_no_output`).' }
|
|
10272
10259
|
maxInflightPerDevice: { type: integer }
|
|
10273
10260
|
wsPath: { type: string }
|
|
10274
10261
|
management: { type: boolean, description: 'server >= 7.88.0. `/v1/devices/*` admin verbs present. PRESENT-IFF — absent means a pre-7.88.0 peer (unknown), not false.' }
|
|
@@ -10762,9 +10749,9 @@ components:
|
|
|
10762
10749
|
server >= 7.65.0 (S-167) — the READ containment rung of this deployment AND WHO SET IT (operator face
|
|
10763
10750
|
`GET /v1/diagnostics/wiring` -> `readFace`). `face` is the same boot artefact as the tenant-facing
|
|
10764
10751
|
`capabilities.readFace` (`null` = nothing pinned by this deployment, the engine default is in force).
|
|
10765
|
-
`source` is a CLOSED
|
|
10752
|
+
`source` is a CLOSED five-word set on the server (exhaustive switch — adding a word is a compile error
|
|
10766
10753
|
there): `env` = this machine's READ_FACE env var (wins over everything) · `center` = the config-center
|
|
10767
|
-
readFace domain (restart-to-apply) · `posture` = derived from the single-user turnkey posture
|
|
10754
|
+
readFace domain (restart-to-apply) · `lane` = (server >= 7.133.0) the isolated execution lane's default — with `READ_FACE` absent, e2b / k8s / local-docker mint `open` · `posture` = derived from the single-user turnkey posture
|
|
10768
10755
|
(REQUIRE_PRINCIPAL unset ⇒ `open`; server >= 7.65.0 behaviour change) · `engine-default` = not pinned.
|
|
10769
10756
|
`note` is a human-facing pointer for operators. Minimal disclosure: the deny table is never on this face.
|
|
10770
10757
|
required: [face, source, note]
|
|
@@ -10774,7 +10761,7 @@ components:
|
|
|
10774
10761
|
enum: [open, roots, null]
|
|
10775
10762
|
source:
|
|
10776
10763
|
type: string
|
|
10777
|
-
enum: [env, center, posture, engine-default]
|
|
10764
|
+
enum: [env, center, lane, posture, engine-default]
|
|
10778
10765
|
note:
|
|
10779
10766
|
type: string
|
|
10780
10767
|
|
|
@@ -10791,9 +10778,9 @@ components:
|
|
|
10791
10778
|
`DURABLE_APPROVAL` ON and a 24h approval window when neither is set, so a default FLIP has to be visible
|
|
10792
10779
|
on an operator surface or nobody sees it.
|
|
10793
10780
|
|
|
10794
|
-
`source` is the CLOSED
|
|
10781
|
+
`source` is the CLOSED five-word set (exhaustive switch on the server — adding a word is a compile error
|
|
10795
10782
|
there), in PRECEDENCE order: `env` = pinned by this machine's env var (deployment sovereignty; beats any
|
|
10796
|
-
published value) · `center` = applied from the config-center (restart-to-apply) · `posture` = derived from
|
|
10783
|
+
published value) · `center` = applied from the config-center (restart-to-apply) · `lane` = (server >= 7.133.0) derived from the isolated execution lane (e2b / k8s / local-docker mint a lane default, e.g. `READ_FACE` -> `open`) · `posture` = derived from
|
|
10797
10784
|
the deployment shape (single-user turnkey) · `engine-default` = nothing pinned, the built-in default is in
|
|
10798
10785
|
force. Only `env`/`center` count as "an operator asked for this"; a posture-derived `true` does not.
|
|
10799
10786
|
`note` is PROSE for a human (per-knob, and on the `posture` arm it is that knob's own fact sentence) —
|
|
@@ -10808,7 +10795,7 @@ components:
|
|
|
10808
10795
|
description: 'The knob''s effective value. Typed by the referencing site (boolean / integer / …).'
|
|
10809
10796
|
source:
|
|
10810
10797
|
type: string
|
|
10811
|
-
enum: [env, center, posture, engine-default]
|
|
10798
|
+
enum: [env, center, lane, posture, engine-default]
|
|
10812
10799
|
note: { type: string }
|
|
10813
10800
|
|
|
10814
10801
|
SkillSpec:
|
|
@@ -11630,6 +11617,24 @@ components:
|
|
|
11630
11617
|
`"other"`). This is the "where did those 180 404s actually land" cell. Empty object = zero failures;
|
|
11631
11618
|
key absent = an older server.
|
|
11632
11619
|
|
|
11620
|
+
ModelPlane:
|
|
11621
|
+
type: object
|
|
11622
|
+
additionalProperties: false
|
|
11623
|
+
description: >
|
|
11624
|
+
`GET /v1/models.modelPlane` (server >= 7.133.0; always present; conditional keys are absent, never `null` / empty string) — the single read for "which model table is this engine serving".
|
|
11625
|
+
Two axes. SERVED TABLE: `servedTable` = `env` (the env table minted from MODEL_ID / MODEL_CHEAP_ID — no catalog is in charge: none configured / source unreachable with no last-known-good / the candidate was refused /
|
|
11626
|
+
no enabled entry) | `catalog` (a catalog's model plane landed and replaced the whole env table). CANDIDATE OUTCOME: `catalog` = `refused` (the latest candidate was refused whole — by the candidate judge or the model-plane
|
|
11627
|
+
group; with no previous generation the env table serves, with one the catalog table still serves — who serves is `servedTable`'s answer, NOT merged into this field) | `adopted` (no whole refusal and the catalog table
|
|
11628
|
+
serves; NOT "the latest candidate landed" — that question belongs to the /health generation ledger) | `absent` (no whole refusal and the env table serves). START-UP ROUTE VERDICT: `refusalCode` / `gap` (both ABSENT when the
|
|
11629
|
+
judge passes — the normal state of a running process): `refusalCode` = `config.model_route_endpoint_missing` | `config.gateway_fallback_without_primary`; `gap` = `endpoint_missing` (set MODEL_GATEWAY_BASEURL or the entry's
|
|
11630
|
+
own `baseUrl`) | `lane_credential_missing` (an anthropic-lane choice with neither ANTHROPIC_API_KEY nor ANTHROPIC_AUTH_TOKEN fell into the endpoint-less openai leg). No sentence is carried. Words are server-owned — read OPEN
|
|
11631
|
+
(an unknown word is "unknown", never "passed").
|
|
11632
|
+
required: [servedTable, catalog]
|
|
11633
|
+
properties:
|
|
11634
|
+
servedTable: { type: string, description: '`env` | `catalog` (open on read).' }
|
|
11635
|
+
catalog: { type: string, description: '`absent` | `adopted` | `refused` (open on read).' }
|
|
11636
|
+
refusalCode: { type: string, description: '`config.model_route_endpoint_missing` | `config.gateway_fallback_without_primary` (open on read); ABSENT = the judge passed.' }
|
|
11637
|
+
gap: { type: string, description: '`endpoint_missing` | `lane_credential_missing` (open on read); ABSENT = the judge passed.' }
|
|
11633
11638
|
ModelsResponse:
|
|
11634
11639
|
type: object
|
|
11635
11640
|
description: >
|
|
@@ -11677,6 +11682,11 @@ components:
|
|
|
11677
11682
|
Upgrade window (as it is): an on-disk LKG written by server <= 7.122.x only recorded the dropped entries that had a name AND were
|
|
11678
11683
|
enabled; while serving from it with the center still unreachable this key lists only those rows (possibly `[]`) and the first pull after the
|
|
11679
11684
|
center is reachable fills it in. An unknown `reasonCode` word reads as "dropped, reason unknown".
|
|
11685
|
+
modelPlane:
|
|
11686
|
+
allOf: [{ $ref: '#/components/schemas/ModelPlane' }]
|
|
11687
|
+
description: >-
|
|
11688
|
+
server >= 7.133.0, ALWAYS present there (appended last): which model table this engine SERVES, whether the catalog was adopted, and what the start-up route judge says — read this instead of predicting
|
|
11689
|
+
(the judge's inputs are the serving process's facts; mirroring two of its judgements drifts). ABSENT = an earlier server (<= 7.132.x). Field reading: ModelPlane.
|
|
11680
11690
|
candidateRejected:
|
|
11681
11691
|
allOf: [{ $ref: '#/components/schemas/CandidateRejected' }]
|
|
11682
11692
|
description: >
|
|
@@ -11761,7 +11771,8 @@ components:
|
|
|
11761
11771
|
# 没声明 ⇒ 键缺席 ⇒ 不再是必有键(此前由 `input` 恒算成布尔,对没声明的条目报 false)。
|
|
11762
11772
|
# 🔴 server 7.131.0 起 `reasoning` 同样三态:env 车道 `MODEL_REASONING` / `MODEL_VISION` 缺省 true 退役、中心名册条目不写即不声明
|
|
11763
11773
|
# ⇒ 两键都「未声明 = 键缺席」,`required` 只剩 name / id / provider(sdk 23.1.0;TS 型 `ModelInfo.reasoning?` 早已可选)。
|
|
11764
|
-
required
|
|
11774
|
+
# sdk 26.0.0(支持底线 7.52.0):`routePairing` 进 required(≥7.45 恒铸,此前因地板 3.0.0 不进)。
|
|
11775
|
+
required: [name, id, provider, routePairing]
|
|
11765
11776
|
additionalProperties: false
|
|
11766
11777
|
properties:
|
|
11767
11778
|
name: { type: string, description: 'the @mention handle (user-pickable model name).' }
|
|
@@ -11785,8 +11796,8 @@ components:
|
|
|
11785
11796
|
adjudicated" must stay distinguishable on the wire). Declared here 2026-09-10 alongside `compat`:
|
|
11786
11797
|
this schema is CLOSED, and the key has been minted unconditionally since 7.45 — so every real
|
|
11787
11798
|
/v1/models response has been structurally rejected by a strict validator for the whole time (same
|
|
11788
|
-
class as `hasBidiControls` on InboxRow).
|
|
11789
|
-
|
|
11799
|
+
class as `hasBidiControls` on InboxRow). REQUIRED since sdk 26.0.0 (the support floor 7.52.0 is above 7.45; it
|
|
11800
|
+
was optional only while the floor was 3.0.0).
|
|
11790
11801
|
compat:
|
|
11791
11802
|
$ref: '#/components/schemas/ModelCompat'
|
|
11792
11803
|
description: >-
|
|
@@ -12596,19 +12607,6 @@ components:
|
|
|
12596
12607
|
startedAt: { type: integer }
|
|
12597
12608
|
elapsedMs: { type: integer }
|
|
12598
12609
|
tokens: { type: integer }
|
|
12599
|
-
# tokenDir REMOVED (engine ruling 2026-06-28): dead field, 0 producer / 0 consumer — spec caught up 2026-07-26.
|
|
12600
|
-
queuedCount:
|
|
12601
|
-
type: integer
|
|
12602
|
-
description: >-
|
|
12603
|
-
🔴 PHANTOM KEY — NO server version ever emits it (verified against the published 7.87.0 bytes:
|
|
12604
|
-
the row type declares it and the fleet route's per-key table forwards it, but NO producer writes it
|
|
12605
|
-
— not `publishTask`'s call sites, not core's bg-sink tick, not the durable reconcile leg — and the
|
|
12606
|
-
wire contract does not mention it either). The server deleted it as a DEAD KEY (S-459, zero
|
|
12607
|
-
behavioural impact since `!== undefined` was always false). It stays declared here only because
|
|
12608
|
-
removing an exported field is BREAKING = major-only; it is registered with its deletion condition
|
|
12609
|
-
in `test/_keyset-divergence.ts` (`FLEET_TASK_ROW_PHANTOM_KEYS`) and goes with the next major.
|
|
12610
|
-
DO NOT read it as "queue depth is already reported" — a permanently absent optional key is a FALSE
|
|
12611
|
-
LEAD. Real queue depth needs a NEW key WITH a producer, not this one resurrected.
|
|
12612
12610
|
awaitingPlanApproval: { type: boolean }
|
|
12613
12611
|
currentAction: { type: string, description: "The child's most recent tool intent as one human-readable line (UNTRUSTED argument head, redacted server-side)." }
|
|
12614
12612
|
currentTool:
|
|
@@ -12722,6 +12720,13 @@ components:
|
|
|
12722
12720
|
failedCount: { type: integer }
|
|
12723
12721
|
elapsedMs: { type: integer }
|
|
12724
12722
|
tokens: { type: integer }
|
|
12723
|
+
errorCode:
|
|
12724
|
+
type: string
|
|
12725
|
+
description: >-
|
|
12726
|
+
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
|
|
12727
|
+
run row has one, the key ABSENT otherwise (never an empty string). A parked workflow run reads `status: "failed"` + `errorCode: "workflow.agent_parked"`
|
|
12728
|
+
(parked, not failed; the `status` vocabulary gains no word). Snapshot rows, `workflow` upsert frames and rows merged from the terminal window share one mint
|
|
12729
|
+
point; a `?session=` subscriber's demoted rows carry it too. ABSENT = an older server, or no code on this row — fall back to `status`.
|
|
12725
12730
|
FleetFrame:
|
|
12726
12731
|
description: >
|
|
12727
12732
|
One decoded frame off GET /v1/fleet/stream (service streamFleet emit set, src/http/routes/fleet.ts). On the
|
|
@@ -12898,14 +12903,14 @@ components:
|
|
|
12898
12903
|
ts: { type: integer }
|
|
12899
12904
|
FleetFrame_hook_notice:
|
|
12900
12905
|
description: >
|
|
12901
|
-
Hook observability frame,
|
|
12906
|
+
Hook observability frame, FOUR kinds (server >= 7.133.0 added the third and fourth; all pure observe — never block, resume, or inject anything;
|
|
12902
12907
|
visibility fail-CLOSED like bg_notification: the owner keys ownerScope/ownerSessionId gate delivery
|
|
12903
12908
|
and are STRIPPED before send; wire is FLAT — `send("hook_notice", { type, ...wire, ts })`, the
|
|
12904
12909
|
server-internal `{notice}` nesting never reaches it). codex R1-F4: modeled as the discriminated union
|
|
12905
12910
|
it really is — the common `{type, ts}` frame base composed with each kind's payload arm — so a
|
|
12906
12911
|
generated client gets the SAME per-kind narrowing as the TS type (a second-family frame missing
|
|
12907
|
-
hookName/entryType is invalid, not silently accepted). `kind` is CLOSED
|
|
12908
|
-
OPEN ON READ: degrade an unknown future kind to a generic observability row.
|
|
12912
|
+
hookName/entryType is invalid, not silently accepted). `kind` is CLOSED four-member on the server but
|
|
12913
|
+
OPEN ON READ: degrade an unknown future kind to a generic observability row. DISPATCH on `kind`; never infer a call's outcome from any hook_notice frame (read the tool / approval frames).
|
|
12909
12914
|
oneOf:
|
|
12910
12915
|
- allOf:
|
|
12911
12916
|
- $ref: '#/components/schemas/FleetHookNoticeFrameBase'
|
|
@@ -12913,10 +12918,16 @@ components:
|
|
|
12913
12918
|
- allOf:
|
|
12914
12919
|
- $ref: '#/components/schemas/FleetHookNoticeFrameBase'
|
|
12915
12920
|
- $ref: '#/components/schemas/FleetHookNonBlockingFailure'
|
|
12921
|
+
- allOf:
|
|
12922
|
+
- $ref: '#/components/schemas/FleetHookNoticeFrameBase'
|
|
12923
|
+
- $ref: '#/components/schemas/FleetHookManagedUnrun'
|
|
12924
|
+
- allOf:
|
|
12925
|
+
- $ref: '#/components/schemas/FleetHookNoticeFrameBase'
|
|
12926
|
+
- $ref: '#/components/schemas/FleetHookVerdictUntrusted'
|
|
12916
12927
|
|
|
12917
12928
|
FleetHookNoticeFrameBase:
|
|
12918
12929
|
type: object
|
|
12919
|
-
description: 'The hook_notice frame envelope shared by
|
|
12930
|
+
description: 'The hook_notice frame envelope shared by all four kinds: the SSE frame discriminant and the server timestamp.'
|
|
12920
12931
|
required: [type, ts]
|
|
12921
12932
|
properties:
|
|
12922
12933
|
type: { const: hook_notice }
|
|
@@ -12929,7 +12940,8 @@ components:
|
|
|
12929
12940
|
DISTINCT vocabulary from AgentEvent (the durable log is BLOCK-grained, each frame is a delta to append).
|
|
12930
12941
|
Resumable (each `id:` = the durable task_event.seq; on 416 the server returns `{retainedFrom}` and the SDK
|
|
12931
12942
|
resumes from there after a turns full-sync). Known events: meta / block-thinking-delta / block-content-delta
|
|
12932
|
-
/ tool-call / tool-result /
|
|
12943
|
+
/ tool-call / tool-result / prompt-assembled / cross-boundary-taint (server >= 7.134.0: the device lane's taint row + `seq`, see
|
|
12944
|
+
`Event_cross_boundary_taint`) / turn / done / error (+ heartbeat keep-alives the SDK swallows). Open set.
|
|
12933
12945
|
NOTE: SSE framing on the wire (event name + JSON data + optional id), not a JSON body.
|
|
12934
12946
|
required: [event, data]
|
|
12935
12947
|
additionalProperties: true
|
|
@@ -12946,10 +12958,11 @@ components:
|
|
|
12946
12958
|
description: >
|
|
12947
12959
|
契约调和车1 G(2026-08-02;server [2373]C-12, `src/trace/project.ts` + the meta frame at
|
|
12948
12960
|
`routes/trace-usage.ts`): the frame's own event name, DOUBLE-EMITTED inside `data`. Always
|
|
12949
|
-
present on server >=
|
|
12950
|
-
|
|
12951
|
-
|
|
12952
|
-
|
|
12961
|
+
present on every named frame of a supported server (>= 7.52.0; minted since server 5.0.0) — the SDK's
|
|
12962
|
+
typed arms declare it REQUIRED since 26.0.0; it is not in this schema's `required` only because the one
|
|
12963
|
+
schema also covers heartbeats (`data: {}`, which carry none and which the SDK swallows). Equal to the SSE
|
|
12964
|
+
`event:` line verbatim — its reason to exist is the proxy/relay case that strips the `event:` line and
|
|
12965
|
+
forwards only the data JSON, where this is the only key left to dispatch on.
|
|
12953
12966
|
output:
|
|
12954
12967
|
allOf: [{ $ref: '#/components/schemas/ToolOutput' }]
|
|
12955
12968
|
description: >
|
|
@@ -12965,7 +12978,7 @@ components:
|
|
|
12965
12978
|
# #315(server ≥7.43):第二顶层键 `livePending` = streamApproval 窗内的流内 ask(此前对轮询消费端
|
|
12966
12979
|
# 结构性不可见)。ADDITIVE / tolerate-absent:键缺席=「协调器未装配(本部署无 live 审批面)」或
|
|
12967
12980
|
# 旧 server;空数组=「有面、此刻无待批」——两义可判别,消费端测 presence。**分数组不混编**:
|
|
12968
|
-
# live 行的决议口=POST /v1/tool-approvals/{
|
|
12981
|
+
# live 行的决议口=POST /v1/tool-approvals/{id}/respond,durable 行=decide 口;数组名即路由判据。
|
|
12969
12982
|
# 22.0.0:第三、四顶层键此前漏声明 —— 封闭 schema 上一个真键 = 合法信封被严格校验器拒收。
|
|
12970
12983
|
additionalProperties: false
|
|
12971
12984
|
required: [pending]
|
|
@@ -13020,7 +13033,7 @@ components:
|
|
|
13020
13033
|
type: object
|
|
13021
13034
|
description: >-
|
|
13022
13035
|
A live (in-stream) pending ask inside its streamApproval window (#315, server ≥7.43). Decide it via
|
|
13023
|
-
POST /v1/tool-approvals/{
|
|
13036
|
+
POST /v1/tool-approvals/{id}/respond — NOT the /decide route (that is the durable rows' door).
|
|
13024
13037
|
Optional flags are only-if-true (absent is NEVER encoded as false).
|
|
13025
13038
|
🔴 THE "tool input/args are NOT listed" NARROWING IS GONE (server >= 7.87.0 / S-454 P1). The old reason
|
|
13026
13039
|
was "details ride the stream frame" — but a SUSPENDED ask never has a stream frame (at mint time it had
|
|
@@ -13038,7 +13051,15 @@ components:
|
|
|
13038
13051
|
additionalProperties: true
|
|
13039
13052
|
required: [approvalId, toolName, ts, expiresAtMs]
|
|
13040
13053
|
properties:
|
|
13041
|
-
approvalId: { type: string, description: 'The SAME id the respond endpoint takes (wire uuidv7
|
|
13054
|
+
approvalId: { type: string, description: 'The SAME id the respond endpoint takes. server >= 7.133.0: for an ask that has a ledger row it is ALWAYS the row''s key `askId` (a 64-character lowercase hexadecimal string — an OPAQUE string, never a uuid); on <= 7.132.x a wire uuidv7.' }
|
|
13055
|
+
consumerState:
|
|
13056
|
+
type: string
|
|
13057
|
+
enum: [absent]
|
|
13058
|
+
description: >-
|
|
13059
|
+
server >= 7.133.0, TRUE-ONLY (present as `absent`): no engine leg on THIS replica is waiting for this ask. Present <=> the row was ADOPTED from the ask ledger at start-up (minted by a previous
|
|
13060
|
+
process / another replica, the row still undecided — a "zombie card"): it lists and can be answered (the row becomes decided; the answer ack carries the same bit), but that decision resumes
|
|
13061
|
+
nothing running on this replica — read the run surface to learn whether anything continued. The `live_pending` stream frame and the `pending` arm of GET /v1/tool-approvals/{id} carry it too. After a restart an
|
|
13062
|
+
ask still undecided on the ledger RETURNS to `livePending[]` (it used to vanish and answer 404). Absent = this replica has an engine leg waiting, or an older server.
|
|
13042
13063
|
toolName: { type: string }
|
|
13043
13064
|
ts: { type: number, description: 'Registration time (ms epoch).' }
|
|
13044
13065
|
expiresAtMs: { type: number, description: 'Absolute window deadline — the SAME number minted once for the tool_approval frame (never recomputed).' }
|
|
@@ -13063,9 +13084,13 @@ components:
|
|
|
13063
13084
|
allOf: [{ $ref: '#/components/schemas/ToolApprovalCardWord' }]
|
|
13064
13085
|
description: 'server >= 7.126.0 (core 7.49.0 #1414): which card this ask renders on — the engine-minted card word, the SAME value as `frame.approvalCard` (the row reads the frame''s own seat, no second read), same layer and shape as the operator queue row''s `approvalCard`. It REPLACES `contentKind` (server <= 7.125.x derived it from the tool name; deleted at 7.126.0, no alias). ABSENT = the engine minted none. Display / triage only; it NEVER takes part in decision routing.'
|
|
13065
13086
|
frame:
|
|
13066
|
-
|
|
13087
|
+
oneOf:
|
|
13088
|
+
- $ref: '#/components/schemas/ToolApprovalFrame'
|
|
13089
|
+
- $ref: '#/components/schemas/ApprovalRequestFrame'
|
|
13067
13090
|
description: >-
|
|
13068
|
-
server >= 7.87.0 (S-454 P1; cli L-397) — THE CARD FRAME THIS ASK WOULD HAVE EMITTED.
|
|
13091
|
+
server >= 7.87.0 (S-454 P1; cli L-397) — THE CARD FRAME THIS ASK WOULD HAVE EMITTED. TWO SHAPES, by leg (sdk 25.0.0 declares the union; discriminant
|
|
13092
|
+
`frame.type`): on a leg that speaks the card protocol (`capabilities.approvalFrameFamilies` contains `approval_request`, server >= 7.133.0) it is the
|
|
13093
|
+
`approval_request` frame (card facts under `frame.card`, the ten off-card live facts flat at the top); on a legacy-family leg it is the `tool_approval` frame. It is the SAME
|
|
13069
13094
|
OBJECT as the server's `PendingApproval.frame` (the one construction site in `askBroadcast`, already
|
|
13070
13095
|
redacted / byte-capped / window-keys filled), shared by the suspended and the reachable path — so
|
|
13071
13096
|
this is NOT a second card projection computed for the list, and there is no second redaction surface.
|
|
@@ -13285,6 +13310,7 @@ components:
|
|
|
13285
13310
|
required: [sessionId, scope, createdAt]
|
|
13286
13311
|
additionalProperties: true
|
|
13287
13312
|
properties:
|
|
13313
|
+
checkpointId: { type: string, minLength: 1, maxLength: 128, description: 'server >= 7.133.0: the NON-SECRET stable identity of the pause this row parks on — the join key where the token cannot go; the SAME value as `PausedCause.checkpointId`. Carried on three durable read surfaces (GET /v1/approvals `pending[]` rows, the /v1/approvals/stream `pending` frame, GET /v1/assistant/inbox rows). A row whose value is unreadable or untrusted (non-string / empty / over 128 / contains the token / an old row / an older server) is STILL LISTED with the key ABSENT — read absence as "this row has no joinable identity", never as a required key.' }
|
|
13288
13314
|
sessionId: { type: string, description: 'The decide handle: POST /v1/approvals/:sessionId/decide.' }
|
|
13289
13315
|
scope: { type: string, description: 'Principal; "_" = submitted without one.' }
|
|
13290
13316
|
taskId: { type: ['string', 'null'], description: 'Suspended run holding the session claim (service-pinned) — join key to trace tool-call blocks + context link.' }
|
|
@@ -13339,26 +13365,13 @@ components:
|
|
|
13339
13365
|
is RETIRED: the SQL backend rewrote its parked rows once at the 7.117.0 boot to `[{ member: "persisted", entries: [<old value>] }]`, but the
|
|
13340
13366
|
file backend's pre-upgrade parked rows and run results already on disk (`checkpointGate.riskDescriptor`) keep the old key verbatim
|
|
13341
13367
|
(text still redacted, no structured disclosure) — a consumer reading those must still tolerate the old string.
|
|
13342
|
-
# server 7.16.0 [3684]② — durable 队列行归因两键(与活卡 tool_approval 帧的同名键语义同族、判据不同源;
|
|
13343
|
-
# 两条腿对同一只 ask 的在场性可以不一致,两侧都只认在场、都禁把缺席读成否定)。
|
|
13344
|
-
ruleSuggestions:
|
|
13345
|
-
type: array
|
|
13346
|
-
minItems: 1
|
|
13347
|
-
items: { $ref: '#/components/schemas/RuleSuggestion' }
|
|
13348
|
-
description: >
|
|
13349
|
-
server 7.16.0 ([3684]②), ADDITIVE: the rule candidates minted for this ask, replayed off the durable
|
|
13350
|
-
row (checkpoint.rule_suggestions). DISPLAY-ONLY on this leg — the durable queue has no persistRule
|
|
13351
|
-
redemption; the only redemption path is the LIVE card leg (respond + persistRule). Array order =
|
|
13352
|
-
display order; an EXACT entry, when present, is always first. core contract caps candidates at 2;
|
|
13353
|
-
the server enforces a tolerance cap of 4 on read-back — lay out for 4. Absent = pre-column row
|
|
13354
|
-
(parked before the column existed) OR no candidates; render both as "no candidates", never an error.
|
|
13355
13368
|
ruleOffers:
|
|
13356
13369
|
type: array
|
|
13357
13370
|
minItems: 1
|
|
13358
13371
|
items: { $ref: '#/components/schemas/RuleOffer' }
|
|
13359
13372
|
description: >
|
|
13360
13373
|
server >= 7.46.0 (core 5.58.0, design/375) — the discriminated-union successor of the retired
|
|
13361
|
-
`ruleSuggestions`
|
|
13374
|
+
`ruleSuggestions` (servers <= 7.45; sdk 26.0.0 deleted it) on the DURABLE queue row (core `PendingAction.ruleOffers`, the same
|
|
13362
13375
|
contract as the sync leg's `AskRequest.ruleOffers`). A >= 7.46.0 row carries only the new key;
|
|
13363
13376
|
pre-5.58 rows fail the new narrow read entry by entry and land as "no supply" (no compatibility
|
|
13364
13377
|
read — a stale schema means re-ask).
|
|
@@ -13496,6 +13509,7 @@ components:
|
|
|
13496
13509
|
additionalProperties: false
|
|
13497
13510
|
properties:
|
|
13498
13511
|
sessionId: { type: string, description: 'The suspended task/session id.' }
|
|
13512
|
+
checkpointId: { type: string, minLength: 1, maxLength: 128, description: 'server >= 7.133.0: the NON-SECRET stable identity of the pause this row parks on — the join key where the token cannot go; the SAME value as `PausedCause.checkpointId`. Carried on three durable read surfaces (GET /v1/approvals `pending[]` rows, the /v1/approvals/stream `pending` frame, GET /v1/assistant/inbox rows). A row whose value is unreadable or untrusted (non-string / empty / over 128 / contains the token / an old row / an older server) is STILL LISTED with the key ABSENT — read absence as "this row has no joinable identity", never as a required key.' }
|
|
13499
13513
|
scope: { type: string, description: 'Multi-tenant owner scope.' }
|
|
13500
13514
|
createdAt: { type: number, description: 'Ask creation time (epoch ms).' }
|
|
13501
13515
|
gateKind:
|
|
@@ -13582,25 +13596,20 @@ components:
|
|
|
13582
13596
|
one-off question. The value comes WHOLE from core (`summarizeCheckpoint` echoes it only when the
|
|
13583
13597
|
cause is a member of the engine's set), so the server neither recomputes nor re-screens it. See
|
|
13584
13598
|
ClassifierUnavailable for the open-`cause` rule and the three shapes absence covers.
|
|
13585
|
-
RETIRED: server >= 7.72.0 (core 7.14.0
|
|
13599
|
+
RETIRED: server >= 7.72.0 (core 7.14.0) never mints this key on any of the three faces; its
|
|
13586
13600
|
successor is `disposition.cause` on the gate record (`unavailable` / `parse_error` — a different
|
|
13587
|
-
question, so an old switch does not port). Kept for servers in [7.69.0, 7.72.0) only (sdk 9.2.0)
|
|
13601
|
+
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
|
|
13602
|
+
together with the frame key (floor ≥ 7.106.0, next major); why the frame still declares it: `ToolApprovalFrame.classifierUnavailable`.
|
|
13588
13603
|
ruleStoreUnreadable:
|
|
13589
13604
|
type: string
|
|
13590
13605
|
enum: [store, call]
|
|
13591
13606
|
description: >-
|
|
13592
|
-
server >= 7.72.0 (core 7.14.0 #688 C3), ADDITIVE — WHICH HALF of the rule lane could not be read.
|
|
13593
|
-
|
|
13594
|
-
|
|
13595
|
-
|
|
13596
|
-
|
|
13597
|
-
|
|
13598
|
-
which is not on the card). Absence = `origin` is not that word — NOT an assertion that the rule store
|
|
13599
|
-
is healthy.
|
|
13600
|
-
RETIRED: server >= 7.106.0 (core 7.34.0 C-R172, BREAKING — wire) never mints this key on either face — an
|
|
13601
|
-
unreadable rule store no longer raises a card: that read's rows are SKIPPED by partition and the session
|
|
13602
|
-
stream gets the user-facing `engine_notice` `permission.rule_store_unavailable` (`detail.at` = `prepare` /
|
|
13603
|
-
`call`). Kept for servers in [7.72.0, 7.106.0) only (sdk K-32); `origin: rule_store_unavailable` itself stays.
|
|
13607
|
+
server >= 7.72.0 (core 7.14.0 #688 C3), ADDITIVE — WHICH HALF of the rule lane could not be read (`store` / `call`).
|
|
13608
|
+
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
|
|
13609
|
+
rule, what the two words mean and the retirement wording is on `ToolApprovalFrame.ruleStoreUnreadable`, and this description points to it.
|
|
13610
|
+
RETIRED: server >= 7.106.0 never mints this key on either face (the frame's description names the successor). Kept for
|
|
13611
|
+
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);
|
|
13612
|
+
`origin: rule_store_unavailable` itself stays.
|
|
13604
13613
|
|
|
13605
13614
|
InboxList:
|
|
13606
13615
|
type: object
|
|
@@ -13819,6 +13828,17 @@ components:
|
|
|
13819
13828
|
⚠️ The key name is final: `permissionModeAfter` (the build brief once said `nextPermissionMode`,
|
|
13820
13829
|
which never shipped — zero aliases).
|
|
13821
13830
|
|
|
13831
|
+
SessionAgent:
|
|
13832
|
+
type: object
|
|
13833
|
+
additionalProperties: false
|
|
13834
|
+
description: >-
|
|
13835
|
+
The placement-tuple projection of a delegated subagent's transcript (`SessionRecord.agent`, server >= 7.133.0). Each member is independently optional (only what was stored is projected); `{}` is a legal value
|
|
13836
|
+
(the tuple held only a `kind`). `id` = the tuple's `handle` (the durable agent row's `a…` key — the SAME string as the fleet row's `id` and `GET /v1/agents/roster`'s `handle`); `parentSessionId` = the host
|
|
13837
|
+
session that spawned it; `rootSessionId` = the delegation tree's root host session (a nested grandchild anchors to the root).
|
|
13838
|
+
properties:
|
|
13839
|
+
id: { type: string }
|
|
13840
|
+
parentSessionId: { type: string }
|
|
13841
|
+
rootSessionId: { type: string }
|
|
13822
13842
|
SessionRecord:
|
|
13823
13843
|
type: object
|
|
13824
13844
|
description: >
|
|
@@ -13851,6 +13871,14 @@ components:
|
|
|
13851
13871
|
$ref: '#/components/schemas/SessionWindow'
|
|
13852
13872
|
promptEpoch:
|
|
13853
13873
|
$ref: '#/components/schemas/PromptEpoch'
|
|
13874
|
+
agent:
|
|
13875
|
+
allOf: [{ $ref: '#/components/schemas/SessionAgent' }]
|
|
13876
|
+
description: >-
|
|
13877
|
+
server >= 7.133.0, top-level additive key — WHICH delegated subagent's transcript this session is (read by terminal side-chain projections that join a fleet frame's `transcriptId`). Only the stored placement
|
|
13878
|
+
tuple is projected (minted by the engine when a BACKGROUND subagent with a durable agent row creates the session on a store that declares placements; a synchronously delegated subagent's transcript is not in
|
|
13879
|
+
the host store and has no tuple); the three members are each optional. 🔴 READ IT AS "WAS IT STORED", NEVER AS "IS IT A SUBAGENT": key PRESENT = a delegated subagent's transcript (even `{}` when the tuple held only a
|
|
13880
|
+
kind); key ABSENT has three causes — an older server (<= 7.132.x) / an ordinary session / a deployment whose store keeps no placement tuple (the local backend: the copy parked from the private short-TTL store is an
|
|
13881
|
+
ordinary session row). Absence is NOT "not a subagent": fall back to the identity the fleet row (`id`) / `GET /v1/agents/roster` (`handle`) already carry.
|
|
13854
13882
|
userInputUndrained:
|
|
13855
13883
|
type: array
|
|
13856
13884
|
description: >-
|
|
@@ -14476,26 +14504,6 @@ components:
|
|
|
14476
14504
|
parentId: { type: [string, 'null'] }
|
|
14477
14505
|
type: { type: string, description: Entry discriminator (message / leaf / compaction / …) — opaque to the SDK. }
|
|
14478
14506
|
|
|
14479
|
-
SyncSnapshot:
|
|
14480
|
-
type: object
|
|
14481
|
-
description: >
|
|
14482
|
-
One E19 file snapshot keyed by a `SessionTreeEntry.id`. `manifest` = `[relPath, blobHash]` tuples; the blob
|
|
14483
|
-
BYTES are NOT inlined (they ride the content-addressed blob routes).
|
|
14484
|
-
# 封闭(census 批2 第三段,2026-07-30):server 侧类型+铸造点(`session-sync.ts` PULL 出 /
|
|
14485
|
-
# `routes/session-sync.ts` PUSH 校验形)都恰是这两键。
|
|
14486
|
-
additionalProperties: false
|
|
14487
|
-
required: [key, manifest]
|
|
14488
|
-
properties:
|
|
14489
|
-
key: { type: string }
|
|
14490
|
-
manifest:
|
|
14491
|
-
type: array
|
|
14492
|
-
items:
|
|
14493
|
-
type: array
|
|
14494
|
-
minItems: 2
|
|
14495
|
-
maxItems: 2
|
|
14496
|
-
items: { type: string }
|
|
14497
|
-
description: A `[relPath, blobHash]` tuple.
|
|
14498
|
-
|
|
14499
14507
|
SyncAnchor:
|
|
14500
14508
|
type: object
|
|
14501
14509
|
description: One E18 resume-at anchor. `owner` is RE-KEYED to the importing principal on a PUSH (§9).
|
|
@@ -14523,31 +14531,28 @@ components:
|
|
|
14523
14531
|
description: >
|
|
14524
14532
|
2c session-sync `GET …/sync/manifest` payload (wrapped server-side as `{ manifest }`). The THIN cross-backend
|
|
14525
14533
|
snapshot: entry IDS (oldest-first, NOT payloads) + the file-history envelope + policy + anchors + leaf.
|
|
14526
|
-
|
|
14527
|
-
|
|
14528
|
-
|
|
14529
|
-
|
|
14530
|
-
|
|
14531
|
-
`
|
|
14532
|
-
(`GET …/sync/history/blobs/{hash}` / `PUT …/sync/blobs/{hash}`; probe `capabilities.sessionSyncFileHistory` on BOTH ends).
|
|
14533
|
-
The SDK's `manifest()` fills `snapshots: []` when the wire omits it.
|
|
14534
|
+
The server's `exportSessionManifest` mints `{ sessionId, entryIds, entryCount, leafId, fileHistory, policy, anchors }` (server >= 7.125.0 adds the
|
|
14535
|
+
session row's two writable bits `title` / `pinned`, present-iff the session is a row of the list face — see the two properties). `fileHistory`
|
|
14536
|
+
(core's versioned `FileHistoryExport` envelope; `null` = no history / the source store cannot export) is ALWAYS present on a supported server
|
|
14537
|
+
(>= 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
|
|
14538
|
+
never ride the manifest: content-addressed, via the history blob faces (`GET …/sync/history/blobs/{hash}` / `PUT …/sync/blobs/{hash}`; probe
|
|
14539
|
+
`capabilities.sessionSyncFileHistory` on BOTH ends). The SDK returns the wire verbatim.
|
|
14534
14540
|
# 封闭(census 批2 第三段,2026-07-30;16.2.0 按 7.116.0 发布字节重核):铸造点 `session-sync.ts`(exportSessionManifest 尾部
|
|
14535
14541
|
# 字面量)恰是 `{ sessionId, entryIds, entryCount, leafId, fileHistory, policy, anchors }` 7 键(≥7.52),无 `snapshots`;
|
|
14536
14542
|
# server ≥7.125.0 另有 `title` / `pinned` 两键(会话行可写位,present-iff 会话是列表面上的行;条件展开,不进 required);
|
|
14537
|
-
#
|
|
14543
|
+
# sdk 26.0.0(支持底线 7.52.0):`fileHistory` 进 required(支持面内恒铸),`snapshots` 删(≤7.51 才发)。
|
|
14538
14544
|
additionalProperties: false
|
|
14539
|
-
required: [sessionId, entryIds, entryCount, leafId, policy, anchors]
|
|
14545
|
+
required: [sessionId, entryIds, entryCount, leafId, fileHistory, policy, anchors]
|
|
14540
14546
|
properties:
|
|
14541
14547
|
sessionId: { type: string }
|
|
14542
14548
|
entryIds: { type: array, items: { type: string }, description: Oldest-first, verbatim cross-backend. }
|
|
14543
14549
|
entryCount: { type: integer, description: '= entryIds.length (the `/sync/entries` trailer count).' }
|
|
14544
14550
|
leafId: { type: [string, 'null'] }
|
|
14545
|
-
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`.' }
|
|
14546
14551
|
fileHistory:
|
|
14547
14552
|
oneOf:
|
|
14548
14553
|
- { $ref: '#/components/schemas/FileHistoryExport' }
|
|
14549
14554
|
- { type: 'null' }
|
|
14550
|
-
description: '
|
|
14555
|
+
description: 'the per-edited-file rewind history envelope; `null` = no history / the source store cannot export. Always present on a supported server.'
|
|
14551
14556
|
policy: { type: array, items: { $ref: '#/components/schemas/SessionRulesRecord' } }
|
|
14552
14557
|
anchors: { type: array, items: { $ref: '#/components/schemas/SyncAnchor' } }
|
|
14553
14558
|
title:
|
|
@@ -14610,20 +14615,18 @@ components:
|
|
|
14610
14615
|
SessionBundle:
|
|
14611
14616
|
type: object
|
|
14612
14617
|
description: >
|
|
14613
|
-
The portable state of ONE session, ready to replay into another backend. Blobs are NOT inlined (
|
|
14614
|
-
|
|
14615
|
-
assembles this from a manifest + the streamed entries for a PUSH.
|
|
14616
|
-
|
|
14617
|
-
required: [sessionId, entries, snapshots, policy, anchors]
|
|
14618
|
+
The portable state of ONE session, ready to replay into another backend. Blobs are NOT inlined (the file-history
|
|
14619
|
+
envelope carries only blob hashes; the bytes stream through the content-addressed blob routes). The SDK
|
|
14620
|
+
assembles this from a manifest + the streamed entries for a PUSH.
|
|
14621
|
+
required: [sessionId, entries, fileHistory, policy, anchors]
|
|
14618
14622
|
properties:
|
|
14619
14623
|
sessionId: { type: string }
|
|
14620
14624
|
entries: { type: array, items: { $ref: '#/components/schemas/SyncEntry' } }
|
|
14621
|
-
snapshots: { type: array, items: { $ref: '#/components/schemas/SyncSnapshot' } }
|
|
14622
14625
|
fileHistory:
|
|
14623
14626
|
oneOf:
|
|
14624
14627
|
- { $ref: '#/components/schemas/FileHistoryExport' }
|
|
14625
14628
|
- { type: 'null' }
|
|
14626
|
-
description: '
|
|
14629
|
+
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).'
|
|
14627
14630
|
policy: { type: array, items: { $ref: '#/components/schemas/SessionRulesRecord' } }
|
|
14628
14631
|
anchors: { type: array, items: { $ref: '#/components/schemas/SyncAnchor' } }
|
|
14629
14632
|
|
|
@@ -14811,8 +14814,9 @@ components:
|
|
|
14811
14814
|
<= 7.105.0 answered the synchronous leg 200 + a failed body), `corrupt` (500 — the session rule store
|
|
14812
14815
|
cannot read its own bytes), `internal.memory_control_plane_corrupt` (500 — the memory control plane refuses
|
|
14813
14816
|
fail-closed) and `state.scenario_assignment_unknown` (503, server >= 7.119.0 — the center's scenario assignment for the
|
|
14814
|
-
principal is dangling; only re-pointing the assignment, or adding the scenario back to the deployment, helps)
|
|
14815
|
-
|
|
14817
|
+
principal is dangling; only re-pointing the assignment, or adding the scenario back to the deployment, helps) and
|
|
14818
|
+
`approvals.read_corrupt` (500, server >= 7.134.0 on `GET /v1/approvals` — a queue row's stored body cannot be read; an operator has to repair or
|
|
14819
|
+
delete the row). Every other 5xx code keeps the status-based retry.
|
|
14816
14820
|
🔴 `request.host_posture_refused` (422) has TWO triggers (same code, same status — "you wrote it right, this
|
|
14817
14821
|
deployment will not grant that host posture"): (1) the write-containment face (`permissionMode: bypassPermissions` ⇒
|
|
14818
14822
|
`writeFace: open`; read `capabilities.taskWriteFaceOpen` first); (2) server >= 7.106.0 / S-895: `inheritEnv: "all"` on a
|
|
@@ -14885,8 +14889,8 @@ components:
|
|
|
14885
14889
|
center 404 / 429 / 3xx included). On 400 `request.field_invalid` for a `settings.permissions` bucket (server >= 7.106.0 /
|
|
14886
14890
|
S-904): `over_cap` (more than `cap` entries; beside `count` + `cap`) / `bad_entry` (a non-string or empty item; beside
|
|
14887
14891
|
`index`); server >= 7.132.0 widens that group to every tool-name list on the body, the agent definitions and the two host-path lists, and adds
|
|
14888
|
-
`too_long` (a text over its character cap; beside `count` + `cap`) — closed
|
|
14889
|
-
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.)
|
|
14892
|
+
`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).
|
|
14893
|
+
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.)
|
|
14890
14894
|
retryAfterSec:
|
|
14891
14895
|
type: number
|
|
14892
14896
|
description: >-
|
|
@@ -14913,6 +14917,10 @@ components:
|
|
|
14913
14917
|
`null` and `""` count), the refusal landed before any decision, and `field` is the literal `settledBy`; the three doors now answer
|
|
14914
14918
|
the SAME sentence. Every such 400 carries it, even when another defect in the same body was hit first (the sentence then names that
|
|
14915
14919
|
defect). Branch on the code plus `field`, never on the sentence; a bad body WITHOUT that key carries no `field`. SDK: `err.extra.field`.
|
|
14920
|
+
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
|
|
14921
|
+
`[i]`, record members `.key`: `mcpServers[0].transport.headers.Authorization`, `settings.hooks.PreToolUse[0].hooks[0].headers.Authorization`; the key
|
|
14922
|
+
name is not a secret, the value never enters the refusal); on 400 `request.field_invalid` for the request key `settings.approvalFrameFamilies` it is
|
|
14923
|
+
that literal (with `reason: "bad_entry"` and the bad `index`; no `index` for a non-array).
|
|
14916
14924
|
detail:
|
|
14917
14925
|
description: >
|
|
14918
14926
|
🔴 SHAPES differ by code — branch on `field` (or the `errorCode`), never guess from the type of `detail`. On 503
|
|
@@ -15006,6 +15014,14 @@ components:
|
|
|
15006
15014
|
properties:
|
|
15007
15015
|
reason: { type: string, enum: [unnamed_refusal] }
|
|
15008
15016
|
path: { $ref: '#/components/schemas/SettingsHooksConfigPath' }
|
|
15017
|
+
- type: object
|
|
15018
|
+
description: 'one of the three wire keys this service reads itself is malformed (server >= 7.133.0): the group `source` / a command entry''s `plugin` / `args`; `key` names it, `path` is its coordinate. The whole request is refused (they used to be stripped silently as unknown keys).'
|
|
15019
|
+
required: [reason, key, path]
|
|
15020
|
+
additionalProperties: false
|
|
15021
|
+
properties:
|
|
15022
|
+
reason: { type: string, enum: [wire_key_invalid] }
|
|
15023
|
+
key: { type: string }
|
|
15024
|
+
path: { $ref: '#/components/schemas/SettingsHooksConfigPath' }
|
|
15009
15025
|
- type: object
|
|
15010
15026
|
description: 'server 7.115.0–7.125.x: the dead-alias matcher refusal before `reason` existed.'
|
|
15011
15027
|
required: [event, alias, canonical]
|
|
@@ -15027,13 +15043,42 @@ components:
|
|
|
15027
15043
|
properties:
|
|
15028
15044
|
scenario: { type: string }
|
|
15029
15045
|
source: { type: string, enum: [ruling, deployment] }
|
|
15046
|
+
- type: object
|
|
15047
|
+
description: >-
|
|
15048
|
+
server >= 7.133.0 — entry problems on a tool-name list / settings-rule refusal (400 `request.field_invalid`, 422): `problems` (at most 1000, each EntryProblem) + `total` (the true count; `total > problems.length` => cut).
|
|
15049
|
+
With `callerTrust` it is the below-trust-floor refusal: the caller's admission tier (`global` | `project`, read open; the same words as `capabilities.settings.permissions.callerTrust`).
|
|
15050
|
+
required: [problems, total]
|
|
15051
|
+
additionalProperties: false
|
|
15052
|
+
properties:
|
|
15053
|
+
problems: { type: array, items: { $ref: '#/components/schemas/EntryProblem' } }
|
|
15054
|
+
total: { type: integer }
|
|
15055
|
+
callerTrust: { type: string }
|
|
15056
|
+
- type: object
|
|
15057
|
+
description: >-
|
|
15058
|
+
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 |
|
|
15059
|
+
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 |
|
|
15060
|
+
approval_feedback (server >= 7.134.0: `feedback` on a deny — fields `["feedback", "decision"]` — or on a policy decision — `["feedback", "decidedBy"]`));
|
|
15061
|
+
`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).
|
|
15062
|
+
required: [kind, fields]
|
|
15063
|
+
additionalProperties: false
|
|
15064
|
+
properties:
|
|
15065
|
+
kind: { type: string }
|
|
15066
|
+
fields: { type: array, items: { type: string } }
|
|
15067
|
+
count: { type: integer }
|
|
15068
|
+
cap: { type: integer }
|
|
15069
|
+
- type: object
|
|
15070
|
+
description: '409 `conflict.session_placed` (server >= 7.133.0): the session sync commit targeted a session that holds a delegated agent''s transcript. `placement_kind` is the stored placement kind (today only `subagent`; read open).'
|
|
15071
|
+
required: [placement_kind]
|
|
15072
|
+
additionalProperties: false
|
|
15073
|
+
properties:
|
|
15074
|
+
placement_kind: { type: string }
|
|
15030
15075
|
- type: string
|
|
15031
15076
|
count:
|
|
15032
15077
|
type: integer
|
|
15033
|
-
description: '`reason: over_cap` / `too_long`
|
|
15078
|
+
description: '`reason: over_cap` / `too_long` / `too_large` — how much was sent (entries / characters / bytes; every capped face since >= 7.132.0). ABSENT on the unmeasured form (a body read that stopped at the cap). SDK: `err.extra.count`.'
|
|
15034
15079
|
cap:
|
|
15035
15080
|
type: integer
|
|
15036
|
-
description: '`reason: over_cap` / `too_long`
|
|
15081
|
+
description: '`reason: over_cap` / `too_long` / `too_large` — the cap (1000 for a permissions bucket; per face otherwise; bytes on a too_large 413). SDK: `err.extra.cap`.'
|
|
15037
15082
|
index:
|
|
15038
15083
|
type: integer
|
|
15039
15084
|
description: '`reason: bad_entry` only — the index of the first bad item; ABSENT when the list itself is not an array. SDK: `err.extra.index`.'
|
|
@@ -15058,6 +15103,8 @@ components:
|
|
|
15058
15103
|
the engine's `detail.code` passes through as `blockedBy` (today only `checkpoint.config_refused` carries it — the closed set is core's
|
|
15059
15104
|
`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"),
|
|
15060
15105
|
next to `field` and `reason`, absent keys not minted (SDK `ResumeRefusalExtra`).
|
|
15106
|
+
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
|
|
15107
|
+
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).
|
|
15061
15108
|
|
|
15062
15109
|
ResumeLegConflictError:
|
|
15063
15110
|
description: >
|
|
@@ -15141,6 +15188,9 @@ components:
|
|
|
15141
15188
|
# 🔴 census 轴一(2026-07-30)补的 4 臂:server 今天就真发,spec/SDK 两侧此前同缺(详见各 schema 处的注)。
|
|
15142
15189
|
- $ref: '#/components/schemas/Event_context_usage'
|
|
15143
15190
|
- $ref: '#/components/schemas/Event_config_assembled'
|
|
15191
|
+
# 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
|
|
15192
|
+
# on the live stream and never on the `GET /v1/runs/{taskId}` poll object; the trace SSE twin is the `cross-boundary-taint` frame.
|
|
15193
|
+
- $ref: '#/components/schemas/Event_cross_boundary_taint'
|
|
15144
15194
|
- $ref: '#/components/schemas/Event_needs_review'
|
|
15145
15195
|
- $ref: '#/components/schemas/Event_message_committed'
|
|
15146
15196
|
# [2373]A-2(2026-08-03,server 5.0.0):compaction_outcome 三腿同源批的 spec 半场(SDK 臂 20867e1 同批)。
|
|
@@ -15169,6 +15219,11 @@ components:
|
|
|
15169
15219
|
- $ref: '#/components/schemas/Event_tool_approval_complete'
|
|
15170
15220
|
- $ref: '#/components/schemas/Event_approval_request'
|
|
15171
15221
|
- $ref: '#/components/schemas/Event_approval_revoke'
|
|
15222
|
+
# server >= 7.133.0 (sdk 25.0.0): the card family's single-card CLOSE frame (`approval_complete`; one frame family per leg — it replaces the legacy
|
|
15223
|
+
# `tool_approval_complete` on a leg where `capabilities.approvalFrameFamilies` = ["approval_request"]) and the LIVE-ONLY `session_titled` frame (a session
|
|
15224
|
+
# title was written; never ledgered, no eventId).
|
|
15225
|
+
- $ref: '#/components/schemas/Event_approval_complete'
|
|
15226
|
+
- $ref: '#/components/schemas/Event_session_titled'
|
|
15172
15227
|
# sdk 9.1.0: two LIVE-leg arms the service has been emitting while neither this union nor the
|
|
15173
15228
|
# SDK's declared them — `text_end` since server 7.66.0 (segment boundary; retires the idle-flush
|
|
15174
15229
|
# heuristic) and `tool_roster_delta` since server 7.70.0 (run-time roster change). A generated client
|
|
@@ -15214,6 +15269,7 @@ components:
|
|
|
15214
15269
|
failed: '#/components/schemas/Event_failed'
|
|
15215
15270
|
context_usage: '#/components/schemas/Event_context_usage'
|
|
15216
15271
|
config_assembled: '#/components/schemas/Event_config_assembled'
|
|
15272
|
+
cross_boundary_taint: '#/components/schemas/Event_cross_boundary_taint'
|
|
15217
15273
|
needs_review: '#/components/schemas/Event_needs_review'
|
|
15218
15274
|
message_committed: '#/components/schemas/Event_message_committed'
|
|
15219
15275
|
compaction_outcome: '#/components/schemas/Event_compaction_outcome'
|
|
@@ -15230,6 +15286,8 @@ components:
|
|
|
15230
15286
|
tool_approval_complete: '#/components/schemas/Event_tool_approval_complete'
|
|
15231
15287
|
approval_request: '#/components/schemas/Event_approval_request'
|
|
15232
15288
|
approval_revoke: '#/components/schemas/Event_approval_revoke'
|
|
15289
|
+
approval_complete: '#/components/schemas/Event_approval_complete'
|
|
15290
|
+
session_titled: '#/components/schemas/Event_session_titled'
|
|
15233
15291
|
text_end: '#/components/schemas/Event_text_end'
|
|
15234
15292
|
tool_roster_delta: '#/components/schemas/Event_tool_roster_delta'
|
|
15235
15293
|
reasoning_end: '#/components/schemas/Event_reasoning_end'
|
|
@@ -15259,13 +15317,18 @@ components:
|
|
|
15259
15317
|
condition: durable-ledger leg only (no ledger => neither header nor frame). sessionId rides along
|
|
15260
15318
|
(needed to bind subsequent turns when the server minted a fresh session). Always precedes any content
|
|
15261
15319
|
frame. NOT an engine event — server-minted, no EventIdentity.
|
|
15262
|
-
# 封闭:铸造点 routes/tasks.ts 是逐字写死的三键字面量(sessionId 条件在场)。
|
|
15320
|
+
# 封闭:铸造点 routes/tasks.ts 是逐字写死的三键字面量(sessionId 条件在场);server >= 7.133.0 起第四个条件键 `warnings`(有才发)。
|
|
15263
15321
|
additionalProperties: false
|
|
15264
15322
|
required: [type, taskId]
|
|
15265
15323
|
properties:
|
|
15266
15324
|
type: { const: meta }
|
|
15267
15325
|
taskId: { type: string }
|
|
15268
15326
|
sessionId: { type: string }
|
|
15327
|
+
warnings:
|
|
15328
|
+
type: array
|
|
15329
|
+
items: { $ref: '#/components/schemas/EntryProblem' }
|
|
15330
|
+
description: 'server >= 7.133.0, present only when non-empty: the accept-time NOTICES for entries of the allow-side selector lists (root / agent `allowTools`) the engine reads as nothing or that are not in the roster. Same list as the 202 receipt''s and the sync 200 body''s `warnings`. ABSENT = no notice (older servers too).'
|
|
15331
|
+
|
|
15269
15332
|
Event_reasoning:
|
|
15270
15333
|
type: object
|
|
15271
15334
|
description: durable stream only — turn-aggregated thinking.
|
|
@@ -15433,10 +15496,10 @@ components:
|
|
|
15433
15496
|
# 每只闭集 = 引擎今天的词 ∪ 引擎退役词表里那一集。这五张 enum 与引擎 `RETIRED_MEMBERS` 逐集逐词同(由 `server-7129-wire` 对拍);卡面 / 队列行 / 请求面的同名闭集只取今天的词。
|
|
15434
15497
|
GateRecordAskClass:
|
|
15435
15498
|
type: string
|
|
15436
|
-
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]
|
|
15499
|
+
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]
|
|
15437
15500
|
description: >-
|
|
15438
15501
|
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:
|
|
15439
|
-
today's `AskClass` set (twenty-
|
|
15502
|
+
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`,
|
|
15440
15503
|
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`);
|
|
15441
15504
|
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`.
|
|
15442
15505
|
Render a known archive word normally and an unknown one generically; never drop the record because the word is not in today's set.
|
|
@@ -15454,9 +15517,9 @@ components:
|
|
|
15454
15517
|
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`.
|
|
15455
15518
|
GateRecordPersistedRuleMandate:
|
|
15456
15519
|
type: string
|
|
15457
|
-
enum: [operator_always, tool_marks, probe_mandate, probe_unanswered, write_protection, target_unresolved, write_protection_unresolved]
|
|
15520
|
+
enum: [operator_always, tool_marks, probe_mandate, probe_unanswered, write_protection, target_unresolved, cross_boundary_taint, write_protection_unresolved]
|
|
15458
15521
|
description: >-
|
|
15459
|
-
`released.mandate` on a GATE RECORD (archive read, server >= 7.129.0): today's
|
|
15522
|
+
`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`.
|
|
15460
15523
|
GateRecordAskOrigin:
|
|
15461
15524
|
type: string
|
|
15462
15525
|
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]
|
|
@@ -15687,9 +15750,10 @@ components:
|
|
|
15687
15750
|
`UNATTENDED_PERMISSION_MODE=dontAsk`).
|
|
15688
15751
|
AskClass:
|
|
15689
15752
|
type: string
|
|
15690
|
-
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]
|
|
15753
|
+
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]
|
|
15691
15754
|
description: >
|
|
15692
|
-
core `ASK_CLASSES` (TWENTY-
|
|
15755
|
+
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
|
|
15756
|
+
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
|
|
15693
15757
|
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
|
|
15694
15758
|
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
|
|
15695
15759
|
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
|
|
@@ -15857,6 +15921,29 @@ components:
|
|
|
15857
15921
|
WHO ASKED. Same-present/same-absent as `settlement` (I1). CLOSED here (archive read, `GateRecordAskOrigin`) — see the
|
|
15858
15922
|
note above.
|
|
15859
15923
|
|
|
15924
|
+
McpProbeFaceRow:
|
|
15925
|
+
# server 7.133.0: the face''s row = the wiring-manifest row projection + TWO face-only optional keys. The manifest row is CLOSED (so an `allOf` over it would reject the two extra keys), hence a
|
|
15926
|
+
# standalone closed schema whose per-key shapes are `$ref`s into WiringManifestMcpEntry's own properties (one source per shape); the key SET is pinned by test (manifest row keys + the two).
|
|
15927
|
+
type: object
|
|
15928
|
+
description: >
|
|
15929
|
+
One row of the no-run MCP status face (GET /v1/capabilities/mcp and POST /v1/capabilities/mcp/probe): the SAME projection a prepared leg pushes onto `wiring_manifest.mcp[]`, PLUS two optional keys only this
|
|
15930
|
+
face carries (server >= 7.133.0). `toolNames` = the tool names this server will mount (the same probe reading `servers[i].tools`, engine namespace form `mcp__<server>__<tool>`, after the schema gate and the per-server
|
|
15931
|
+
allow-list; the server neither composes nor renames), at most 200 per server, the reading-order prefix. ABSENT = an older server or no `tools` in that server's reading — NEVER invent `[]` (it would read as "this server
|
|
15932
|
+
has no tools"); a present `[]` is real. `toolNamesTruncated` = TRUE-ONLY: the list was cut (more than 200; exactly 200 is NOT cut and the key stays absent, never `false`) — when true `toolCount` is the true count, not
|
|
15933
|
+
`toolNames.length`. A misaligned reading (not an array / count differs from the row count) answers 500 `internal.error` on both doors instead of shipping a list that could be paired wrongly.
|
|
15934
|
+
additionalProperties: false
|
|
15935
|
+
required: [name, status]
|
|
15936
|
+
properties:
|
|
15937
|
+
name: { $ref: '#/components/schemas/WiringManifestMcpEntry/properties/name' }
|
|
15938
|
+
status: { $ref: '#/components/schemas/WiringManifestMcpEntry/properties/status' }
|
|
15939
|
+
source: { $ref: '#/components/schemas/WiringManifestMcpEntry/properties/source' }
|
|
15940
|
+
toolCount: { $ref: '#/components/schemas/WiringManifestMcpEntry/properties/toolCount' }
|
|
15941
|
+
errorCode: { $ref: '#/components/schemas/WiringManifestMcpEntry/properties/errorCode' }
|
|
15942
|
+
delivered: { $ref: '#/components/schemas/WiringManifestMcpEntry/properties/delivered' }
|
|
15943
|
+
httpStatus: { $ref: '#/components/schemas/WiringManifestMcpEntry/properties/httpStatus' }
|
|
15944
|
+
liveness: { $ref: '#/components/schemas/WiringManifestMcpEntry/properties/liveness' }
|
|
15945
|
+
toolNames: { type: array, items: { type: string }, maxItems: 200 }
|
|
15946
|
+
toolNamesTruncated: { type: boolean, enum: [true] }
|
|
15860
15947
|
McpProbeFace:
|
|
15861
15948
|
type: object
|
|
15862
15949
|
description: >
|
|
@@ -15882,8 +15969,8 @@ components:
|
|
|
15882
15969
|
description: 'How long this reading may be reused (seconds; 30s per identity + declaration set server-side). A hit inside the window does NOT count against the rate limit.'
|
|
15883
15970
|
servers:
|
|
15884
15971
|
type: array
|
|
15885
|
-
items: { $ref: '#/components/schemas/
|
|
15886
|
-
description: 'One row per declaration,
|
|
15972
|
+
items: { $ref: '#/components/schemas/McpProbeFaceRow' }
|
|
15973
|
+
description: 'One row per declaration, the wiring-manifest row shape plus (server >= 7.133.0) this face''s two optional keys, index-aligned (see this schema''s description).'
|
|
15887
15974
|
|
|
15888
15975
|
McpLivenessState:
|
|
15889
15976
|
type: string
|
|
@@ -16654,6 +16741,12 @@ components:
|
|
|
16654
16741
|
(`capabilities.rewindFiles`); only a run the REQUEST word turned off cannot restore itself.
|
|
16655
16742
|
OPEN on read: the server does not enumerate the word (a present non-string is dropped), a third word passes
|
|
16656
16743
|
through — treat it as unknown. ABSENT = an older engine / external derivation, never "capturing".
|
|
16744
|
+
permissionMode:
|
|
16745
|
+
allOf: [{ $ref: '#/components/schemas/WiringManifestPermissionMode' }]
|
|
16746
|
+
description: >-
|
|
16747
|
+
server >= 7.133.0 (core 7.53.0; TENANT and OPERATOR faces alike) — this leg's PERMISSION-MODE receipt `{ requested?, effective, foldedBy? }`; the shell renders its mode indicator from `effective`
|
|
16748
|
+
(`foldedBy` present = the requested mode was folded), never from the word it sent. ABSENT = an older engine / server, not an assertion of the default mode. Effective half only (this frame);
|
|
16749
|
+
the same section rides the non-streaming `wiringManifest` receipt and the ledger row. Section shape: WiringManifestPermissionMode.
|
|
16657
16750
|
instructions:
|
|
16658
16751
|
allOf: [{ $ref: '#/components/schemas/WiringManifestInstructions' }]
|
|
16659
16752
|
description: >
|
|
@@ -16878,6 +16971,16 @@ components:
|
|
|
16878
16971
|
pathTarget: { $ref: '#/components/schemas/McpToolPathTarget' }
|
|
16879
16972
|
ruleFace: { $ref: '#/components/schemas/McpToolRuleFace' }
|
|
16880
16973
|
renderHints: { $ref: '#/components/schemas/McpToolRenderHints' }
|
|
16974
|
+
boundaryLegs:
|
|
16975
|
+
description: >-
|
|
16976
|
+
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
|
|
16977
|
+
`delta.roster.entries[]` carries it) — the LEGS a call of this tool travels (core `BOUNDARY_LEGS`, in that order: `env` = inside the execution
|
|
16978
|
+
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
|
|
16979
|
+
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
|
|
16980
|
+
it. ABSENT = an older server (<= 7.133.x).
|
|
16981
|
+
oneOf:
|
|
16982
|
+
- { type: string, const: per_call }
|
|
16983
|
+
- { type: array, items: { type: string, enum: [env, outside_read, outside_write] } }
|
|
16881
16984
|
McpToolPathTarget:
|
|
16882
16985
|
type: object
|
|
16883
16986
|
required: [param, access]
|
|
@@ -17533,7 +17636,7 @@ components:
|
|
|
17533
17636
|
payload carried `type` (the read boundary emits `{type, ...data}`) so runtime discrimination worked,
|
|
17534
17637
|
but a generated client had nothing to switch on and dropped approval cards. That gap is closed.
|
|
17535
17638
|
The shell renders `tool_approval` as the three-choice card and dismisses it on `tool_approval_complete`.
|
|
17536
|
-
Respond via POST /v1/tool-approvals/{
|
|
17639
|
+
Respond via POST /v1/tool-approvals/{id}/respond.
|
|
17537
17640
|
required: [type, approvalId]
|
|
17538
17641
|
additionalProperties: true
|
|
17539
17642
|
properties:
|
|
@@ -17579,44 +17682,6 @@ components:
|
|
|
17579
17682
|
governance shell gate sits at the "classify" tier, a shell ask may come from the classifier or from
|
|
17580
17683
|
another gate; the server leaves the key absent rather than guessing). Never render absence as
|
|
17581
17684
|
"this gate can be bypassed by a posture".
|
|
17582
|
-
ruleSuggestions:
|
|
17583
|
-
type: array
|
|
17584
|
-
minItems: 1 # 空数组不可能出现:server 的合取项②要求 `req.ruleSuggestions` **非空**,否则整键省略(缺席 = 本卡无此选项)
|
|
17585
|
-
# 刻意**不**声明 maxItems:server 的 `.max(4)` 是**卡** schema(`ApprovalCardSchema`)上的约束,这条
|
|
17586
|
-
# live 帧腿没有它。在这里抄一个上界 = 声明一条 server 并不执行的约束(照 spec 生成的严格客户端会
|
|
17587
|
-
# 拒收一条合法帧)。数量上界的真源是引擎:core 5.22.0 的 `suggestRulesForCommand` **至多铸一条,且恒
|
|
17588
|
-
# `match: "exact"`**(不写死上界是给将来的 miner 留位,不是暗示今天会有更多)。
|
|
17589
|
-
items: { $ref: '#/components/schemas/RuleSuggestion' }
|
|
17590
|
-
description: >
|
|
17591
|
-
server #154 车二 (core 5.18.0, design/179), ADDITIVE, "tool_approval" only: the rule candidates
|
|
17592
|
-
the ENGINE minted for this ask. Render them as the "don't ask again" options and echo the chosen
|
|
17593
|
-
one back as `persistRule.rule` on POST /v1/tool-approvals/{id}/respond.
|
|
17594
|
-
|
|
17595
|
-
🔴 PRESENCE IS THE PROMISE: the key is present ONLY on a deployment that actually has a
|
|
17596
|
-
permission-rule store wired (the same object behind the engine's `permissionRules.storeWired`
|
|
17597
|
-
manifest bit). A deployment without one OMITS it rather than offering an option with nowhere to
|
|
17598
|
-
redeem — an unredeemable "don't ask again" is a wire lie, worse than absence.
|
|
17599
|
-
|
|
17600
|
-
🔴 THE CONVERSE DOES NOT HOLD: absence means "THIS CARD has no redeemable candidate", and three
|
|
17601
|
-
causes produce it — no rule store wired, the engine minted no candidate for this command (compound /
|
|
17602
|
-
redirect / substitution), or the command bytes were unreadable. One frame cannot tell them apart, so
|
|
17603
|
-
never diagnose the deployment's rule lane from a single absence, and never synthesize a candidate.
|
|
17604
|
-
|
|
17605
|
-
📏 HOW MANY, TODAY: core 5.22.0's miner (`suggestRulesForCommand`) emits AT MOST ONE candidate and
|
|
17606
|
-
always `match: "exact"` — no shipped producer mints the broader prefix option. `prefix` stays in the
|
|
17607
|
-
closed vocabulary because the wire validator accepts it and a later miner may emit it; render what
|
|
17608
|
-
arrives, but do NOT build a UI that assumes a narrow/broad PAIR is on offer.
|
|
17609
|
-
|
|
17610
|
-
🔴 The values are the engine's VERBATIM (closed `match` vocabulary + engine-minted text); the
|
|
17611
|
-
server neither re-mints nor re-orders them. A hand-built rule string is refused at redemption
|
|
17612
|
-
(`ruleRefusal: "rule_not_offered"`) — by construction, so a click on a harmless command cannot be
|
|
17613
|
-
turned into a rule for something else.
|
|
17614
|
-
|
|
17615
|
-
⚠️ REDEMPTION SCOPE: the key also rides the durable card replay, but its only redemption channel is
|
|
17616
|
-
the LIVE respond leg keyed by this frame's `approvalId` — an in-process map on ONE replica. Same
|
|
17617
|
-
replica ⇒ redeemable; after a restart / on another replica that `approvalId` 404s and the person
|
|
17618
|
-
must use the durable decide leg, which carries NO rule field in v1 (its body is strict — an extra
|
|
17619
|
-
`persistRule` is a loud 400, not a silent drop).
|
|
17620
17685
|
ruleOffers:
|
|
17621
17686
|
type: array
|
|
17622
17687
|
minItems: 1
|
|
@@ -17624,8 +17689,8 @@ components:
|
|
|
17624
17689
|
description: >
|
|
17625
17690
|
server >= 7.46.0 (core 5.58.0, design/375), "tool_approval" only — the ENGINE-minted "don't ask
|
|
17626
17691
|
again" OPTIONS for this ask, as a DISCRIMINATED UNION (`single` | `batch`). It is the BREAKING
|
|
17627
|
-
REPLACEMENT of `ruleSuggestions`
|
|
17628
|
-
alias
|
|
17692
|
+
REPLACEMENT of the triple-shaped `ruleSuggestions` of servers <= 7.45: a >= 7.46.0 engine mints the old key nowhere and there is no
|
|
17693
|
+
alias (sdk 26.0.0 deleted the old key with the support floor 7.52.0).
|
|
17629
17694
|
|
|
17630
17695
|
🔴 READ `kind` TO TELL THE ARMS APART, never a positional assumption. ORDER IS CONTRACT: a
|
|
17631
17696
|
whole-string exact `single` is index 0 when present and a `batch` is always last; core's own
|
|
@@ -17640,23 +17705,15 @@ components:
|
|
|
17640
17705
|
suppress its persistence actions entirely (core's normative clause) — a compacted index means the
|
|
17641
17706
|
k-th option the person clicked is not the k-th rule the server redeems.
|
|
17642
17707
|
|
|
17643
|
-
🔴 PRESENCE IS THE PROMISE
|
|
17708
|
+
🔴 PRESENCE IS THE PROMISE: present only where a permission-rule store
|
|
17644
17709
|
is really wired. The converse does not hold — a wired deployment still omits it for mandated /
|
|
17645
17710
|
requiresRealApproval / allow_outranked / ask_rule_matched / hook-produced / inherited-unresolved / ancestor-resolved /
|
|
17646
17711
|
anonymous asks. Absence reads as "THIS CARD has no such option", never as "this deployment has no
|
|
17647
17712
|
rule lane" (that is `capabilities.permissionRules`) and never as "your rule broke" (that is
|
|
17648
|
-
`allowRuleShadowed
|
|
17713
|
+
`allowRuleShadowed`).
|
|
17649
17714
|
|
|
17650
17715
|
⚠️ REDEMPTION SCOPE is the live respond leg keyed by this frame's `approvalId` only; a replayed
|
|
17651
17716
|
offer on another replica is not a promise that it is still redeemable.
|
|
17652
|
-
persistedRuleShadowed:
|
|
17653
|
-
type: string
|
|
17654
|
-
description: >-
|
|
17655
|
-
server >= 7.13.0 (core 5.25.0 #144), ADDITIVE, "tool_approval" only — this ask MATCHED one of the caller's persisted allow
|
|
17656
|
-
rules and that rule COULD NOT silence it; the value is the rule's VERBATIM text (secret-redacted + length-bounded; UNTRUSTED
|
|
17657
|
-
for display, like `message`). Absent != "you have no rule"; deliberately separate from `governanceForced`.
|
|
17658
|
-
RETIRED: server >= 7.117.0 (core 7.42.0, BREAKING rename, no alias) never mints this key on any face; its successor is
|
|
17659
|
-
`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).
|
|
17660
17717
|
allowRuleShadowed:
|
|
17661
17718
|
type: array
|
|
17662
17719
|
items: { $ref: '#/components/schemas/AllowRuleShadow' }
|
|
@@ -17929,22 +17986,23 @@ components:
|
|
|
17929
17986
|
|
|
17930
17987
|
# ── design/172 流内审批协议(#151)—— 已随 server 7.3.0 上线;开关 STREAM_APPROVAL_ENABLED 的默认值
|
|
17931
17988
|
# 分两段:server ≤7.4.0 默认 OFF,≥7.5.0 默认 ON(BREAKING,见 server CHANGELOG)。──
|
|
17932
|
-
# 与上面的 ToolApprovalFrame
|
|
17933
|
-
#
|
|
17989
|
+
# 与上面的 ToolApprovalFrame 是两个帧族,**一条腿只走一个**(server >= 7.133.0,capabilities.approvalFrameFamilies):呈卡协议上场的腿上一只 ask 只出本族的帧、
|
|
17990
|
+
# 旧族一帧都不发,消费端不再按 `approvalId` 去重;≤7.132.x 的呈卡腿两族并发(先 tool_approval、后 approval_request,同带 `approvalId`)⇒ 按它去重、优先渲染新帧。
|
|
17934
17991
|
classifierUnavailable:
|
|
17935
17992
|
$ref: '#/components/schemas/ClassifierUnavailable'
|
|
17936
17993
|
description: >-
|
|
17937
|
-
server >= 7.69.0 (core 7.10.0
|
|
17994
|
+
server >= 7.69.0 (core 7.10.0), ADDITIVE, present only when the classifier was consulted and
|
|
17938
17995
|
could not run — WHY this ask reached a human. See ClassifierUnavailable for the open-`cause` rule and
|
|
17939
17996
|
the three shapes absence covers.
|
|
17940
|
-
RETIRED: server >= 7.72.0 (core 7.14.0
|
|
17997
|
+
RETIRED: server >= 7.72.0 (core 7.14.0) never mints this key on any of the three faces; its
|
|
17941
17998
|
successor is `disposition.cause` on the gate record (`unavailable` / `parse_error` — a different
|
|
17942
|
-
question, so an old switch does not port). Kept
|
|
17999
|
+
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
|
|
18000
|
+
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.
|
|
17943
18001
|
ruleStoreUnreadable:
|
|
17944
18002
|
type: string
|
|
17945
18003
|
enum: [store, call]
|
|
17946
18004
|
description: >-
|
|
17947
|
-
server >= 7.72.0 (core 7.14.0
|
|
18005
|
+
server >= 7.72.0 (core 7.14.0), ADDITIVE — WHICH HALF of the rule lane could not be read.
|
|
17948
18006
|
Present iff `origin === "rule_store_unavailable"`: `store` = the wired rule store could not be read
|
|
17949
18007
|
(look at the store / network); `call` = the store was read but THIS command could not be read against
|
|
17950
18008
|
the person's deny/ask rows (look at the command spelling). One `origin` word covers both facts, and
|
|
@@ -17952,10 +18010,11 @@ components:
|
|
|
17952
18010
|
frame and the inbox row; the `card_json` card deliberately does NOT carry it (it travels with `origin`,
|
|
17953
18011
|
which is not on the card). Absence = `origin` is not that word — NOT an assertion that the rule store
|
|
17954
18012
|
is healthy.
|
|
17955
|
-
RETIRED: server >= 7.106.0 (core 7.34.0
|
|
17956
|
-
unreadable rule store no longer raises a card
|
|
17957
|
-
|
|
17958
|
-
|
|
18013
|
+
RETIRED: server >= 7.106.0 (core 7.34.0, BREAKING — wire) never mints this key on either face — an
|
|
18014
|
+
unreadable rule store no longer raises a card; the signal moved to the session stream's user-facing
|
|
18015
|
+
`engine_notice` `permission.rule_store_unavailable` (`detail.at` = `prepare` / `call`; what the run does while the rules cannot be read is
|
|
18016
|
+
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
|
|
18017
|
+
declares the same key; it goes with the next major once the support floor is >= 7.106.0 (`origin: rule_store_unavailable` itself stays).
|
|
17959
18018
|
readRootCandidate:
|
|
17960
18019
|
type: object
|
|
17961
18020
|
required: [dir, clearsThisAsk]
|
|
@@ -17972,6 +18031,14 @@ components:
|
|
|
17972
18031
|
(a malformed shape, or a `dir` over 1024 UTF-16 code units ⇒ the key is ABSENT — never truncated,
|
|
17973
18032
|
because a truncated directory is a DIFFERENT directory and adding it would not clear this ask;
|
|
17974
18033
|
see the STRING-LENGTH UNIT convention in `info.description` for this `maxLength`'s counting unit).
|
|
18034
|
+
covers:
|
|
18035
|
+
allOf: [{ $ref: '#/components/schemas/ReadRootCover' }]
|
|
18036
|
+
description: >-
|
|
18037
|
+
Optional (engine core 7.22.0 `ReadRootGrantCandidate.covers`; the server carries it through key by key). ABSENT ⇒ `dir` is a DIRECTORY
|
|
18038
|
+
(adding it to the read roots releases what lies under it). `exact` ⇒ `dir` is the path the command NAMED, offered verbatim
|
|
18039
|
+
(a file or one concrete path, not a directory containing it): the consumer ADDS the string itself — expanding it into a
|
|
18040
|
+
pattern grants nothing — and must not render the offer as "add directory". The domain is the single word `exact`
|
|
18041
|
+
(a word outside it makes the server drop the whole candidate). The `approval_request` frame reads this very definition.
|
|
17975
18042
|
clearsThisAsk:
|
|
17976
18043
|
type: boolean
|
|
17977
18044
|
enum: [true]
|
|
@@ -18052,8 +18119,23 @@ components:
|
|
|
18052
18119
|
approvalId:
|
|
18053
18120
|
type: string
|
|
18054
18121
|
description: >
|
|
18055
|
-
|
|
18056
|
-
|
|
18122
|
+
The id of POST /v1/tool-approvals/{id}/respond and GET /v1/tool-approvals/{id}. server >= 7.133.0: ALWAYS EQUAL TO `askId` (the row's key is the ask's one wire
|
|
18123
|
+
identity; a 64-character lowercase hexadecimal string — treat it as an OPAQUE string, never parse it as a uuid); the matching close frame `approval_complete` carries it too.
|
|
18124
|
+
On server <= 7.132.x it is the legacy bridge / dedupe key (a uuid, the same value as the parallel tool_approval frame).
|
|
18125
|
+
# ── server >= 7.133.0: the TEN off-card LIVE facts on the opening frame's top level (one frame family per leg) ──────────────────────────
|
|
18126
|
+
# Same keys, same shapes, same values as on the legacy `tool_approval` frame (each is a `$ref` into ToolApprovalFrame's own property — one
|
|
18127
|
+
# source per shape). Only the frame minted LIVE carries them (and what the durable ledger recorded of that frame): a disconnect-replay frame
|
|
18128
|
+
# (rebuilt from `card_json`) never does. Absence means "this frame does not carry it", not "there is none". See APPROVAL_REQUEST_LIVE_FACT_KEYS (sdk) / the server's twin.
|
|
18129
|
+
mandated: { $ref: '#/components/schemas/ToolApprovalFrame/properties/mandated' }
|
|
18130
|
+
mandate: { $ref: '#/components/schemas/ToolApprovalFrame/properties/mandate' }
|
|
18131
|
+
origin: { $ref: '#/components/schemas/ToolApprovalFrame/properties/origin' }
|
|
18132
|
+
boundGrantDigest: { $ref: '#/components/schemas/ToolApprovalFrame/properties/boundGrantDigest' }
|
|
18133
|
+
boundGrants: { $ref: '#/components/schemas/ToolApprovalFrame/properties/boundGrants' }
|
|
18134
|
+
classifierNoRuling: { $ref: '#/components/schemas/ToolApprovalFrame/properties/classifierNoRuling' }
|
|
18135
|
+
boundGrantAbsence: { $ref: '#/components/schemas/ToolApprovalFrame/properties/boundGrantAbsence' }
|
|
18136
|
+
approvalCard: { $ref: '#/components/schemas/ToolApprovalFrame/properties/approvalCard' }
|
|
18137
|
+
readRootCandidate: { $ref: '#/components/schemas/ToolApprovalFrame/properties/readRootCandidate' }
|
|
18138
|
+
probeReason: { $ref: '#/components/schemas/ToolApprovalFrame/properties/probeReason' }
|
|
18057
18139
|
|
|
18058
18140
|
ApprovalCard:
|
|
18059
18141
|
type: object
|
|
@@ -18086,33 +18168,15 @@ components:
|
|
|
18086
18168
|
posture. Semantics, the ABSENT != false clause and the discrimination boundary are stated verbatim on
|
|
18087
18169
|
ToolApprovalFrame.governanceForced. It sits at the card's top level rather than inside `risk` because
|
|
18088
18170
|
`risk` describes the ENGINE's judgement of the call, while this key says WHO imposed the gate.
|
|
18089
|
-
ruleSuggestions:
|
|
18090
|
-
type: array
|
|
18091
|
-
minItems: 1 # 同帧腿:空数组不可能出现(素材同源),缺席才是「本卡无此选项」
|
|
18092
|
-
maxItems: 4 # 卡形上**真有**这条上界(server `ApprovalCardSchema` 的 `.max(4)`);live 帧腿没有,故只写在这里
|
|
18093
|
-
items: { $ref: '#/components/schemas/RuleSuggestion' }
|
|
18094
|
-
description: >
|
|
18095
|
-
ADDITIVE (#154 车二 / core 5.18.0 design/179): the rule candidates the ENGINE minted for this ask —
|
|
18096
|
-
the card's "don't ask again" options; echo the chosen one back as `persistRule.rule`. Semantics,
|
|
18097
|
-
the PRESENCE-IS-THE-PROMISE clause (present only where a rule store is wired) and the redemption
|
|
18098
|
-
scope are stated verbatim on ToolApprovalFrame.ruleSuggestions — the live frame, the stored
|
|
18099
|
-
`card_json` and the replayed frame carry the SAME material.
|
|
18100
18171
|
ruleOffers:
|
|
18101
18172
|
type: array
|
|
18102
18173
|
minItems: 1
|
|
18103
18174
|
items: { $ref: '#/components/schemas/RuleOffer' }
|
|
18104
18175
|
description: >
|
|
18105
|
-
server >= 7.46.0 (core 5.58.0, design/375) — the discriminated-union replacement of
|
|
18106
|
-
`ruleSuggestions`
|
|
18176
|
+
server >= 7.46.0 (core 5.58.0, design/375) — the discriminated-union replacement of the retired
|
|
18177
|
+
`ruleSuggestions` (servers <= 7.45; sdk 26.0.0 deleted it). Same material as the live frame and the stored `card_json` (the server
|
|
18107
18178
|
projects all three through one `copyRuleOffer`), so semantics, order-is-contract, presence-is-the-
|
|
18108
18179
|
promise and the per-leg index rules are stated verbatim on ToolApprovalFrame.ruleOffers.
|
|
18109
|
-
persistedRuleShadowed:
|
|
18110
|
-
type: string
|
|
18111
|
-
description: >-
|
|
18112
|
-
server >= 7.13.0 (core 5.25.0 #144), ADDITIVE — the persisted rule this ask matched but which could not silence it; frame and
|
|
18113
|
-
card carry the SAME material (see ToolApprovalFrame.persistedRuleShadowed).
|
|
18114
|
-
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
|
|
18115
|
-
carrying it); its successor is `allowRuleShadowed`. Kept for servers in [7.13.0, 7.117.0) only (sdk 16.3.0).
|
|
18116
18180
|
allowRuleShadowed:
|
|
18117
18181
|
type: array
|
|
18118
18182
|
items: { $ref: '#/components/schemas/AllowRuleShadow' }
|
|
@@ -18147,35 +18211,13 @@ components:
|
|
|
18147
18211
|
classifierUnavailable:
|
|
18148
18212
|
$ref: '#/components/schemas/ClassifierUnavailable'
|
|
18149
18213
|
description: >-
|
|
18150
|
-
|
|
18151
|
-
|
|
18152
|
-
|
|
18153
|
-
RETIRED: server >= 7.72.0 (core 7.14.0 #688) never mints this key on any of the three faces; its
|
|
18214
|
+
(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
|
|
18215
|
+
shapes absence covers.
|
|
18216
|
+
RETIRED: server >= 7.72.0 (core 7.14.0) never mints this key on any of the three faces; its
|
|
18154
18217
|
successor is `disposition.cause` on the gate record (`unavailable` / `parse_error` — a different
|
|
18155
|
-
question, so an old switch does not port).
|
|
18156
|
-
|
|
18157
|
-
RuleSuggestion:
|
|
18158
|
-
type: object
|
|
18159
|
-
description: >
|
|
18160
|
-
One ENGINE-minted rule candidate (#154 车二 / core 5.18.0 `RuleSuggestion`) — what a single click on
|
|
18161
|
-
"don't ask again" would persist.
|
|
18162
|
-
|
|
18163
|
-
🔴 THE TEXT COMES FROM THE ENGINE, NOT THE SHELL: the server derives it from the adjudicated command's
|
|
18164
|
-
own bytes; a client may only echo one of these back verbatim (`persistRule.rule`). Reporting text that
|
|
18165
|
-
is not among the candidates is refused (`ruleRefusal: "rule_not_offered"`) rather than guessed at, so
|
|
18166
|
-
"click on a harmless command, get a rule for something else" is not spellable on this path.
|
|
18167
|
-
# 封闭:core `RuleSuggestion` 恰三键(rule/match/command),server 逐字透传不重铸。
|
|
18168
|
-
additionalProperties: false
|
|
18169
|
-
required: [rule, match, command]
|
|
18170
|
-
properties:
|
|
18171
|
-
rule: { type: string, maxLength: 512, description: 'The canonical rule text to echo back on redemption (server MAX_RULE_TEXT_CHARS = 512).' }
|
|
18172
|
-
match:
|
|
18173
|
-
type: string
|
|
18174
|
-
enum: [exact, prefix]
|
|
18175
|
-
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.'
|
|
18176
|
-
command: { type: string, maxLength: 512, description: 'The command pattern inside the rule, for a shell that would rather not re-parse `rule`.' }
|
|
18218
|
+
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
|
|
18219
|
+
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`.)
|
|
18177
18220
|
|
|
18178
|
-
# ─── S-139(sdk 8.3.0):对 server 7.60.0 整体重对账带进来的四张型面 ───────────────────────
|
|
18179
18221
|
ClassifierUnavailable:
|
|
18180
18222
|
# 8.9.0:三面(tool_approval 帧 / ApprovalCard / InboxRow)同形同值 ⇒ 具名单源,内联三份会各自漂。
|
|
18181
18223
|
type: object
|
|
@@ -18290,7 +18332,7 @@ components:
|
|
|
18290
18332
|
`ApprovalCard.ruleOffersAbsence`; server >= 7.55.0 / core #490 fix ②, S-15). The engine names which
|
|
18291
18333
|
door is shut: `mandated` = this ask must be confirmed every time (🔴 this arm must NOT point the user
|
|
18292
18334
|
at writing a rule — a rule would not silence it, and saying so sends them into a loop); `allow_outranked` = an
|
|
18293
|
-
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
|
|
18335
|
+
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`);
|
|
18294
18336
|
`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
|
|
18295
18337
|
`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).
|
|
18296
18338
|
🔴 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
|
|
@@ -18316,12 +18358,21 @@ components:
|
|
|
18316
18358
|
and a word outside the set all read as ABSENT — a card without this word must not be treated as "approving it grants nothing else"; whether to render "allow for this session" is `rememberRefused`.
|
|
18317
18359
|
Display metadata, never an adjudication input. CLOSED: the server narrows with the engine's members, anything else reads as absent.
|
|
18318
18360
|
|
|
18361
|
+
ReadRootCover:
|
|
18362
|
+
type: string
|
|
18363
|
+
enum: [exact]
|
|
18364
|
+
description: >
|
|
18365
|
+
The closed vocabulary of `readRootCandidate.covers` (engine core 7.22.0 `ReadRootGrantCandidate.covers`; the server's narrow read `readReadRootCandidate` drops the whole
|
|
18366
|
+
candidate for a word outside it). One word today: `exact` — `dir` is the path the command NAMED, not a directory containing it. The SDK exports the same table as
|
|
18367
|
+
`READ_ROOT_COVERS` and its `isApprovalRequestFrameV1` judges `covers` by membership in it; a source-level anchor reads the word off the server's minting site and the engine's type.
|
|
18319
18368
|
PersistedRuleMandate:
|
|
18320
18369
|
type: string
|
|
18321
|
-
enum: [operator_always, tool_marks, probe_mandate, probe_unanswered, write_protection, target_unresolved]
|
|
18370
|
+
enum: [operator_always, tool_marks, probe_mandate, probe_unanswered, write_protection, target_unresolved, cross_boundary_taint]
|
|
18322
18371
|
description: >
|
|
18323
18372
|
WHICH WORD a mandated ask stands on (server >= 7.102.0 / S-721 / core 7.32.0 #1092) — core's
|
|
18324
|
-
`PERSISTED_RULE_MANDATES`
|
|
18373
|
+
`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
|
|
18374
|
+
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
|
|
18375
|
+
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
|
|
18325
18376
|
`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 —
|
|
18326
18377
|
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
|
|
18327
18378
|
`tool_approval` frame, the `livePending` row top level, the operator queue row (`GET /v1/approvals`
|
|
@@ -18375,9 +18426,9 @@ components:
|
|
|
18375
18426
|
RuleOfferMatch:
|
|
18376
18427
|
type: string
|
|
18377
18428
|
description: >
|
|
18378
|
-
The MATCH FORM of a persisted rule —
|
|
18379
|
-
|
|
18380
|
-
|
|
18429
|
+
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
|
|
18430
|
+
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
|
|
18431
|
+
equality). ONE vocabulary for two faces: a card's rule offer (`RuleOffer.match`, the
|
|
18381
18432
|
server's card schema validates it with this very enum) and a persisted rule row (`GET /v1/rules`).
|
|
18382
18433
|
|
|
18383
18434
|
`exact` = this one command only (`Bash(git status)`). `prefix` = the historical colon-star
|
|
@@ -18580,8 +18631,7 @@ components:
|
|
|
18580
18631
|
maxLength: 512
|
|
18581
18632
|
description: >
|
|
18582
18633
|
With `edited` absent or false this is the CANDIDATE ARM: echo back, VERBATIM, the `rule` of the
|
|
18583
|
-
chosen `kind: "single"` entry of the frame's `ruleOffers
|
|
18584
|
-
`ruleSuggestions`). Do not hand-build it, re-case it or append `:*` — the server locates it by
|
|
18634
|
+
chosen `kind: "single"` entry of the frame's `ruleOffers`. Do not hand-build it, re-case it or append `:*` — the server locates it by
|
|
18585
18635
|
text and refuses otherwise (`rule_not_offered`). With `edited: true` this is the FREE-TEXT ARM
|
|
18586
18636
|
and the string is whatever the person typed.
|
|
18587
18637
|
edited:
|
|
@@ -18729,6 +18779,40 @@ components:
|
|
|
18729
18779
|
depth: { type: integer, description: 'Delegation depth (a direct child of the host = 1).' }
|
|
18730
18780
|
agentName: { type: string, description: 'Child agent name. UNTRUSTED — spawning-model free text (server-redacted and length-bounded).' }
|
|
18731
18781
|
|
|
18782
|
+
ApprovalCompleteFrame:
|
|
18783
|
+
type: object
|
|
18784
|
+
description: >
|
|
18785
|
+
The CLOSE frame of the card family (`approval_complete`; server >= 7.133.0; SSE, `type` IS the event name; the durable leg appends the same frame, so it replays through
|
|
18786
|
+
GET /v1/runs/:id/events as the `Event_approval_complete` arm). ONE FRAME FAMILY PER LEG: on a leg where the card protocol is on (`capabilities.approvalFrameFamilies`
|
|
18787
|
+
= `["approval_request"]`) one ask travels only `approval_request` -> `approval_complete` / `approval_revoke`; the legacy `tool_approval` / `tool_approval_complete` pair is NOT sent
|
|
18788
|
+
(BREAKING vs <= 7.132.x, which sent both families for one ask; remedy on the worker: `STREAM_APPROVAL_ENABLED=false`).
|
|
18789
|
+
Same meaning as the legacy `tool_approval_complete` (one settle, one verdict: the `outcome` three words, the non-human `cause`, the park-route `parked`); dismiss the card by the opening frame's
|
|
18790
|
+
`askId`. Division of labour with ApprovalRevokeFrame: the revoke frame is BATCH-level (siblings voided by a winner), this one is the SINGLE card's end (answered / window expired / park route / cancelled);
|
|
18791
|
+
one ask can receive both — clearing by `askId` is enough. The gate is "this connection's opening frame was actually delivered". The frame is a notification, the row is the source of truth: a lost
|
|
18792
|
+
frame is compensated by the open-stream preamble (see ApprovalRequestFrame). THIS SCHEMA DESCRIBES v1 ONLY (read it as ApprovalFrameEnvelope first; the SDK's `isApprovalCompleteFrameV1` narrows).
|
|
18793
|
+
required: [type, schemaVersion, askId, taskId, approvalId, outcome, serverNowMs]
|
|
18794
|
+
additionalProperties: true
|
|
18795
|
+
properties:
|
|
18796
|
+
type: { type: string, enum: [approval_complete] }
|
|
18797
|
+
schemaVersion: { type: integer, enum: [1], description: 'This schema describes v1 only (see ApprovalFrameEnvelope for the unknown-version read).' }
|
|
18798
|
+
askId: { type: string, description: 'The durable identity — dismiss the card by it (the same key as the opening frame''s `askId`).' }
|
|
18799
|
+
taskId: { type: string, description: 'wire run id (same as the opening frame).' }
|
|
18800
|
+
approvalId: { type: string, description: 'Always equal to `askId` (same as the opening frame).' }
|
|
18801
|
+
outcome:
|
|
18802
|
+
type: string
|
|
18803
|
+
enum: [allowed, denied, expired]
|
|
18804
|
+
description: 'How the ask settled — the same three words as the legacy frame: `allowed` / `denied` = a decision; `expired` = a non-human ending (the reason is in `cause`).'
|
|
18805
|
+
cause:
|
|
18806
|
+
allOf: [{ $ref: '#/components/schemas/ApprovalSettleCause' }]
|
|
18807
|
+
description: >-
|
|
18808
|
+
The non-human ending''s cause (present only on `outcome: "expired"`; the fourteen words of the approval seat''s ending-cause set, the same word as on the audit settle row). A human answer carries none.
|
|
18809
|
+
ABSENCE IS NOT EVIDENCE.
|
|
18810
|
+
parked:
|
|
18811
|
+
type: boolean
|
|
18812
|
+
enum: [true]
|
|
18813
|
+
description: 'TRUE-ONLY: this ask itself ended on the park route (row moved to PARKING / PARKED; a late answer is reachable). Absent = no evidence — NEVER read as a real denial.'
|
|
18814
|
+
serverNowMs: { type: integer, description: 'Server clock at mint time.' }
|
|
18815
|
+
|
|
18732
18816
|
ApprovalRevokeFrame:
|
|
18733
18817
|
type: object
|
|
18734
18818
|
description: >
|
|
@@ -19028,7 +19112,7 @@ components:
|
|
|
19028
19112
|
description: >
|
|
19029
19113
|
server >= 7.126.0 (sdk 21.1.0 `APPROVAL_DECIDE_GATE_KINDS`, verbatim and in order; single owner = server
|
|
19030
19114
|
`APPROVAL_GATE_KINDS`) — the parked gate kinds the DURABLE decision door `POST /v1/approvals/{sessionId}/decide`
|
|
19031
|
-
accepts (the live `POST /v1/tool-approvals/{
|
|
19115
|
+
accepts (the live `POST /v1/tool-approvals/{id}/respond` answers the same card family). A parked row of
|
|
19032
19116
|
kind `plan_review` goes to the plan-review door instead (`PlanReviewGateKind`); `resource_limit` / `needs_review` /
|
|
19033
19117
|
`task_done` have no human decision door (resume / terminal). Clients route a pending row by reading this set,
|
|
19034
19118
|
never by a hand-written copy of it.
|
|
@@ -19043,7 +19127,7 @@ components:
|
|
|
19043
19127
|
enum: [person, policy]
|
|
19044
19128
|
description: >
|
|
19045
19129
|
server >= 7.118.0 (`APPROVAL_DECIDED_BY`, verbatim and in order) — WHO made an approval decision, one key on all three decision
|
|
19046
|
-
doors (live `POST /v1/tool-approvals/{
|
|
19130
|
+
doors (live `POST /v1/tool-approvals/{id}/respond`, durable `POST /v1/tasks/{taskId}/asks/{askId}/decision`, durable
|
|
19047
19131
|
`POST /v1/approvals/{sessionId}/decide`). `person` = a person decided through the approval channel (ABSENT means this);
|
|
19048
19132
|
`policy` = this deployment's own policy decided (a rule / posture answering on someone's behalf, a headless lane) — BOTH
|
|
19049
19133
|
directions: the engine records `tool_end.gate.settlement.kind` = `policy_allowed` / `policy_refused` (`who.party: "policy"`).
|
|
@@ -19135,6 +19219,20 @@ components:
|
|
|
19135
19219
|
`updatedInputForwarded` this field IS persisted, so an idempotent replay DOES promise byte-identity.
|
|
19136
19220
|
The 409 `conflict.ask_decided` body echoes the same field — and there it is always the FIRST
|
|
19137
19221
|
decision's note, not the one the rejected request carried.
|
|
19222
|
+
noteRecorded:
|
|
19223
|
+
type: boolean
|
|
19224
|
+
description: >
|
|
19225
|
+
server >= 7.133.0, ADDITIVE: did the `note` THIS request carried land as the row's text? Present ONLY when the request carried a `note`. `true` <=> the row's `decisionNote` is
|
|
19226
|
+
verbatim the text this request submitted (text that happens to be identical counts even if someone else wrote it first); `false` = not confirmed (the row holds another reason /
|
|
19227
|
+
the store could not be read back). Same shape and meaning as `noteRecorded` on the live respond ack and on its replayed arm (one judgement for the three doors). Carried by the
|
|
19228
|
+
first-decision and idempotent-replay forms; the PARKED redemption leg does not carry it (read it as unknown). ABSENT is not `false`.
|
|
19229
|
+
consumerState:
|
|
19230
|
+
type: string
|
|
19231
|
+
enum: [absent]
|
|
19232
|
+
description: >
|
|
19233
|
+
TRUE-ONLY disclosure (present as `absent`): no engine leg on THIS replica is waiting for this ask — the decision is on the row (an idempotent replay will read it, the audit surface keeps it)
|
|
19234
|
+
but nothing running here resumes because of it (the holder of the suspended promise may be another replica, or this replica restarted and its local window is empty). Read the RUN surface to
|
|
19235
|
+
learn whether anything continued; NEVER take a 200 without this key as proof the run continued (older servers never send it). Not a 409 / 410: the decision DID take effect.
|
|
19138
19236
|
|
|
19139
19237
|
QuestionFrame:
|
|
19140
19238
|
type: object
|
|
@@ -19471,6 +19569,38 @@ components:
|
|
|
19471
19569
|
fields: {}
|
|
19472
19570
|
overrideReasons: {}
|
|
19473
19571
|
ts: {}
|
|
19572
|
+
CrossBoundaryTaintRow:
|
|
19573
|
+
type: object
|
|
19574
|
+
description: >
|
|
19575
|
+
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
|
|
19576
|
+
(`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
|
|
19577
|
+
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);
|
|
19578
|
+
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`)
|
|
19579
|
+
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
|
|
19580
|
+
such key.
|
|
19581
|
+
# 不封闭(`additionalProperties: true`):本 schema 是 `Event_cross_boundary_taint` 的 allOf 目标(帧上多一个 `type`),封闭会把合法帧判成违约(同 PromptManifestRow)。
|
|
19582
|
+
additionalProperties: true
|
|
19583
|
+
required: [runId, phase, taint, governed]
|
|
19584
|
+
properties:
|
|
19585
|
+
runId: { type: string, description: 'the leg''s run id.' }
|
|
19586
|
+
phase: { type: string, enum: [prepare, resume, observed], description: 'core `permission.cross_boundary_taint` `phase` (engine closed set).' }
|
|
19587
|
+
taint:
|
|
19588
|
+
type: array
|
|
19589
|
+
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).'
|
|
19590
|
+
items: { type: string, enum: [targetExecutionArmed, offTargetReadObserved] }
|
|
19591
|
+
governed: { type: boolean, description: 'the tree has run under the `multi_tenant` posture since it armed (derived from `REQUIRE_PRINCIPAL`).' }
|
|
19592
|
+
toolName: { type: string, description: '`observed` only: the call that let the off-target read in.' }
|
|
19593
|
+
toolCallId: { type: string, description: '`observed` only: that call''s id.' }
|
|
19594
|
+
Event_cross_boundary_taint:
|
|
19595
|
+
description: >
|
|
19596
|
+
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
|
|
19597
|
+
seq). Never on the live stream, never on the `GET /v1/runs/{taskId}` poll object; an older server drops the frame on the spot.
|
|
19598
|
+
allOf:
|
|
19599
|
+
- $ref: '#/components/schemas/CrossBoundaryTaintRow'
|
|
19600
|
+
- type: object
|
|
19601
|
+
required: [type]
|
|
19602
|
+
properties:
|
|
19603
|
+
type: { const: cross_boundary_taint }
|
|
19474
19604
|
Event_needs_review:
|
|
19475
19605
|
type: object
|
|
19476
19606
|
description: >
|
|
@@ -19669,7 +19799,7 @@ components:
|
|
|
19669
19799
|
Event_tool_approval:
|
|
19670
19800
|
description: >
|
|
19671
19801
|
DURABLE-leg replay of the legacy tool-approval OPEN frame. Same payload as ToolApprovalFrame with the
|
|
19672
|
-
discriminant narrowed to `tool_approval`. Settle it via POST /v1/tool-approvals/{
|
|
19802
|
+
discriminant narrowed to `tool_approval`. Settle it via POST /v1/tool-approvals/{id}/respond.
|
|
19673
19803
|
Anchor the card on `toolCallId` (NOT `approvalId`); render `governanceForced` as a non-bypassable
|
|
19674
19804
|
"forced by governance" badge. UNTRUSTED for display: `message`/`args` are redacted but model-authored.
|
|
19675
19805
|
allOf:
|
|
@@ -19692,9 +19822,11 @@ components:
|
|
|
19692
19822
|
type: { type: string, enum: [tool_approval_complete] }
|
|
19693
19823
|
Event_approval_request:
|
|
19694
19824
|
description: >
|
|
19695
|
-
The design/172 in-stream approval card as an AgentEvent arm.
|
|
19696
|
-
|
|
19697
|
-
`
|
|
19825
|
+
The design/172 in-stream approval card as an AgentEvent arm. ONE FRAME FAMILY PER LEG (server >= 7.133.0,
|
|
19826
|
+
`capabilities.approvalFrameFamilies`): on a leg where the card protocol is on, one ask emits only this family
|
|
19827
|
+
(`approval_request` -> `approval_complete` / `approval_revoke`) and none of the legacy `tool_approval` family,
|
|
19828
|
+
so there is nothing to deduplicate. On server <= 7.132.x the two families ran side by side (legacy first) carrying
|
|
19829
|
+
the SAME `approvalId` — there, dedupe on it and prefer this arm. The decision key is `askId`; settle via
|
|
19698
19830
|
POST /v1/tasks/{taskId}/asks/{askId}/decision.
|
|
19699
19831
|
|
|
19700
19832
|
🔴 THIS ARM IS THE OPEN ENVELOPE, NOT `ApprovalRequestFrame`, AND THAT IS DELIBERATE. The v1 frame
|
|
@@ -19741,6 +19873,37 @@ components:
|
|
|
19741
19873
|
properties:
|
|
19742
19874
|
type: { type: string, enum: [approval_revoke] }
|
|
19743
19875
|
|
|
19876
|
+
Event_approval_complete:
|
|
19877
|
+
description: >
|
|
19878
|
+
The card family's single-card CLOSE frame as an AgentEvent arm (server >= 7.133.0). One frame family per leg: with `capabilities.approvalFrameFamilies` = `["approval_request"]` this frame
|
|
19879
|
+
REPLACES the legacy `tool_approval_complete` (one ask no longer travels two families). Live SSE and the durable ledger replay carry the same shape (same settle, same verdict: `outcome`,
|
|
19880
|
+
non-human `cause`, `parked`).
|
|
19881
|
+
🔴 THIS ARM IS THE OPEN ENVELOPE, NOT `ApprovalCompleteFrame` — same ruling as Event_approval_request / Event_approval_revoke: narrow with `schemaVersion == 1` (SDK:
|
|
19882
|
+
`isApprovalCompleteFrameV1`) before touching `outcome` / `cause` / `parked`; a frame that does not narrow still dismisses the card by its `askId` (when it has one) — never guess the outcome.
|
|
19883
|
+
🔴 Consumption discipline: (a) dismiss by the opening frame's `askId`; (b) a ledger-replayed frame is for TIMELINE RENDERING ONLY — the card-set baseline stays the open-stream preamble;
|
|
19884
|
+
(c) NEVER derive the run's fate from this frame (read the run row / checkpoint face); (d) it does not conflict with ApprovalRevokeFrame — one ask can receive both.
|
|
19885
|
+
allOf:
|
|
19886
|
+
- $ref: '#/components/schemas/ApprovalFrameEnvelope'
|
|
19887
|
+
- type: object
|
|
19888
|
+
required: [type]
|
|
19889
|
+
properties:
|
|
19890
|
+
type: { type: string, enum: [approval_complete] }
|
|
19891
|
+
Event_session_titled:
|
|
19892
|
+
description: >
|
|
19893
|
+
server >= 7.133.0 — the session title was WRITTEN (contract §4a-ttl): the title generator wrote the title into the session row and minted this frame on THE RUN'S LIVE STREAM afterwards.
|
|
19894
|
+
🔴 LIVE-ONLY, NEVER STORED: the session row (`GET /v1/sessions` list row `title`) is the single source of the title; this frame only says "the row just changed". It is not ledgered
|
|
19895
|
+
(GET /v1/runs/:id/events replay never carries it), not part of any replay surface, and has NO `eventId` (no SSE `id:` line, no part in the `Last-Event-ID` cursor). Only a leg holding a live stream
|
|
19896
|
+
receives it (today `POST /v1/tasks/stream`); the sync-JSON leg and the durable background leg structurally cannot. NOT sent when the session already has a title, on a user rename / pin
|
|
19897
|
+
(`PATCH /v1/sessions/{id}` does not go through the generator), with `SESSION_AUTO_TITLE=false`, after `done`, or once the connection is gone. THE FRAME MAY NOT ARRIVE — the title is whatever
|
|
19898
|
+
the session row says: refresh on the frame, read the row on cold start / another window / reconnect. `title` has passed the generator's redaction and cleaning and is the SAME value as the row's
|
|
19899
|
+
(still UNTRUSTED for display — model-authored text).
|
|
19900
|
+
type: object
|
|
19901
|
+
required: [type, sessionId, title]
|
|
19902
|
+
additionalProperties: false
|
|
19903
|
+
properties:
|
|
19904
|
+
type: { type: string, enum: [session_titled] }
|
|
19905
|
+
sessionId: { type: string, description: 'The session whose row was just written.' }
|
|
19906
|
+
title: { type: string, description: 'The title that landed in the session row (redacted and cleaned; bounded by the server''s TITLE_MAX_CHARS).' }
|
|
19744
19907
|
# ── resources/*.ts schema backfill, batch 2 (2026-07-28) — the 28-item WIRE_PENDING ratchet zeroed.
|
|
19745
19908
|
# Each schema below is cross-checked against the server source (file:line cited in its description
|
|
19746
19909
|
# or in the SDK resource JSDoc it mirrors). Wire objects are OPEN sets (additionalProperties: true).
|
|
@@ -19765,6 +19928,10 @@ components:
|
|
|
19765
19928
|
sessionId: { type: string, description: '`resolved` frames: which pending settled.' }
|
|
19766
19929
|
toolCallId: { type: string, description: '`resolved` frames: the settled pending''s bound tool-call id (absent when the pending carried none).' }
|
|
19767
19930
|
count: { type: integer, description: '`synced` frames: how many pendings the connect-time snapshot carried.' }
|
|
19931
|
+
checkpointId: { type: string, description: '`pending` frames only (server >= 7.133.0): the spread PendingCheckpoint row''s `checkpointId` (key absent when the row has no trusted value — read as "no joinable identity").' }
|
|
19932
|
+
consumerState: { type: string, enum: [absent], description: '`live_pending` frames only (server >= 7.133.0): the spread LivePendingRow''s true-only `consumerState` (no engine leg on this replica is waiting for the ask).' }
|
|
19933
|
+
errorCode: { type: string, description: '`error` frames: the machine code (`STREAM_MAX_DURATION` / `draining` / server >= 7.133.0 `approvals.read_unavailable` — the stream STAYS open, do NOT reconnect on it — / `approvals.read_corrupt` — a corrupt row was read, the server closes the stream after the frame).' }
|
|
19934
|
+
message: { type: string, description: '`error` frames: the human sentence (redacted); never match on it.' }
|
|
19768
19935
|
hasBidiControls:
|
|
19769
19936
|
type: boolean
|
|
19770
19937
|
enum: [true]
|
|
@@ -19885,20 +20052,24 @@ components:
|
|
|
19885
20052
|
|
|
19886
20053
|
FleetHookNotice:
|
|
19887
20054
|
description: >
|
|
19888
|
-
Hook observability payload,
|
|
20055
|
+
Hook observability payload, FOUR kinds (server >= 7.133.0 added `hook_managed_unrun` and `hook_verdict_untrusted`; service fleet-bus.ts `HookNotice` minus the wire-stripped
|
|
19889
20056
|
ownerScope/ownerSessionId — the payload view of FleetFrame_hook_notice; a discriminated union on
|
|
19890
20057
|
`kind`, mirroring the SDK type verbatim). Both kinds are pure observe — never block, resume, or
|
|
19891
20058
|
inject. Visibility is fail-CLOSED: a ?session= subscriber only receives notices provably owned by
|
|
19892
|
-
that session. `kind` is a CLOSED
|
|
20059
|
+
that session. `kind` is a CLOSED four-member set on the server (exhaustive-switch discipline) but
|
|
19893
20060
|
OPEN ON READ: degrade an unknown future kind to a generic observability row, never crash.
|
|
19894
20061
|
oneOf:
|
|
19895
20062
|
- $ref: '#/components/schemas/FleetHookDecisionUnavailable'
|
|
19896
20063
|
- $ref: '#/components/schemas/FleetHookNonBlockingFailure'
|
|
20064
|
+
- $ref: '#/components/schemas/FleetHookManagedUnrun'
|
|
20065
|
+
- $ref: '#/components/schemas/FleetHookVerdictUntrusted'
|
|
19897
20066
|
discriminator:
|
|
19898
20067
|
propertyName: kind
|
|
19899
20068
|
mapping:
|
|
19900
20069
|
hook_decision_unavailable: '#/components/schemas/FleetHookDecisionUnavailable'
|
|
19901
20070
|
hook_non_blocking_failure: '#/components/schemas/FleetHookNonBlockingFailure'
|
|
20071
|
+
hook_managed_unrun: '#/components/schemas/FleetHookManagedUnrun'
|
|
20072
|
+
hook_verdict_untrusted: '#/components/schemas/FleetHookVerdictUntrusted'
|
|
19902
20073
|
|
|
19903
20074
|
FleetHookDecisionUnavailable:
|
|
19904
20075
|
type: object
|
|
@@ -19933,7 +20104,7 @@ components:
|
|
|
19933
20104
|
reason:
|
|
19934
20105
|
# 🔴 NO `enum` — same open-on-read ruling as kind 1 (SDK type is `... | (string & {})`).
|
|
19935
20106
|
type: string
|
|
19936
|
-
description: 'Machine-branchable failure shape. CLOSED server-side, OPEN ON READ — KNOWN WORDS TODAY: exit_nonzero | timeout | spawn_failed | bad_json.'
|
|
20107
|
+
description: 'Machine-branchable failure shape. CLOSED server-side, OPEN ON READ — KNOWN WORDS TODAY: exit_nonzero | timeout | spawn_failed | bad_json | matcher_over_budget (server >= 7.133.0: the group matcher / the entry `if` name part did not finish evaluating within its 1000 ms budget on this name — NOT a broken hook script but an unevaluable selection condition; the server treats it as selected, the entry still runs; ONE frame per group, `if` one per entry, no exitCode / stderr).'
|
|
19937
20108
|
hookName: { type: string, description: 'Hook identity (statusMessage, else command/URL/prompt; redacted + bounded, http entries stripped of query/fragment). Always sent.' }
|
|
19938
20109
|
entryType: { type: string, description: 'command | http | prompt | agent — without it, hookName''s nature is a guess. Closed server-side, open on read. Always sent.' }
|
|
19939
20110
|
exitCode: { type: integer, description: 'Absent for timeout/spawn_failed (no exit code exists — the server never mints a legit-looking 0).' }
|
|
@@ -19941,6 +20112,57 @@ components:
|
|
|
19941
20112
|
stderr: { type: string, description: 'Redacted + truncated stderr digest (512-char cap incl. marker). Empty stderr => absent.' }
|
|
19942
20113
|
detail: { type: string, description: 'Redacted supplement (e.g. where an exit-2 had no venue). Additive / tolerate-absent.' }
|
|
19943
20114
|
|
|
20115
|
+
FleetHookManagedUnrun:
|
|
20116
|
+
type: object
|
|
20117
|
+
description: >
|
|
20118
|
+
Kind 3 (server >= 7.133.0): a MANAGED hook did not complete — the group carries a `source`; managed groups (`managedPolicy` / `managedPlugin:<name>`) are the organization policy's guard and run in an independent lane
|
|
20119
|
+
after the unmanaged rewrites, on the final input. A synchronous managed entry that gave neither a legal verdict (exit 0 with no verdict / a closed-set verdict) nor a legal veto (exit 2 / `decision:"block"` /
|
|
20120
|
+
`continue:false`) is "unrun": on `PreToolUse` the conclusion folds to `ask` when nothing vetoed (a person in the loop; unmanaged allows are not trusted; any phase's veto is still the last vote), on other events the
|
|
20121
|
+
conclusion is unchanged — but this frame is ALWAYS sent. One frame per unrun managed entry. The frame does NOT assert how the call ended (read the tool / approval frames).
|
|
20122
|
+
required: [kind, event, reason, source, hookName, entryType]
|
|
20123
|
+
additionalProperties: true
|
|
20124
|
+
properties:
|
|
20125
|
+
kind: { type: string, enum: [hook_managed_unrun] }
|
|
20126
|
+
event: { type: string, description: 'Which hook event. On PreToolUse this entry sets the managed lane: with no veto the call folds to `ask`.' }
|
|
20127
|
+
reason:
|
|
20128
|
+
# NO `enum` — same open-on-read ruling as kinds 1 and 2.
|
|
20129
|
+
type: string
|
|
20130
|
+
description: >-
|
|
20131
|
+
Why it did not complete. CLOSED on the server (thirteen words), OPEN ON READ — KNOWN WORDS TODAY: matcher_invalid | llm_carrier_unavailable | llm_cap | powershell_unsupported | if_unevaluable | budget_exhausted |
|
|
20132
|
+
spawn_failed | timeout | exit_nonzero | bad_json | decision_out_of_set | evidence_unavailable | rewrite_unsettled. `input_truncated` / `matcher_over_budget` are NOT in this kind (they belong to kind 4).
|
|
20133
|
+
source: { type: string, description: 'The group''s source word (`managedPolicy` or `managedPlugin:<name>`; the name is trace only, never a criterion).' }
|
|
20134
|
+
hookName: { type: string, description: 'Hook identity (same rule as kind 2); redacted + bounded.' }
|
|
20135
|
+
entryType: { type: string, description: 'command | http | prompt | agent — closed server-side, open on read.' }
|
|
20136
|
+
toolName: { type: string, description: 'Present on tool events.' }
|
|
20137
|
+
detail: { type: string, description: 'Redacted supplement.' }
|
|
20138
|
+
|
|
20139
|
+
FleetHookVerdictUntrusted:
|
|
20140
|
+
type: object
|
|
20141
|
+
description: >
|
|
20142
|
+
Kind 4 (server >= 7.133.0): a hook's VERDICT IS UNTRUSTED — one rule: an entry that did not get the complete payload, or was pulled in BY SELECTION by an over-budget matcher, has its VETO count (deny / exit 2 /
|
|
20143
|
+
`decision:"block"` / `continue:false` / ask) and its ALLOW and `updatedInput` NOT count. The rule holds on both lanes, so there is one frame; reason, lane and identity are machine fields. Emission condition per lane:
|
|
20144
|
+
managed = the entry ran with an untrusted cause (sent whatever it judged; `PreToolUse` then folds to `ask` when nothing vetoed); unmanaged = it gave an allow or a real rewrite (an identity echo does not count) that was
|
|
20145
|
+
dropped. Today only on `PreToolUse` (the event with an allow word). THE FRAME DOES NOT ASSERT HOW THE CALL ENDED: a managed entry that itself vetoes ends in deny with the frame sent; another trusted allow in the group still
|
|
20146
|
+
lets the call through — read the call's own result. NEVER infer a call outcome from any hook_notice frame.
|
|
20147
|
+
required: [kind, event, reason, lane, hookName, entryType]
|
|
20148
|
+
additionalProperties: true
|
|
20149
|
+
properties:
|
|
20150
|
+
kind: { type: string, enum: [hook_verdict_untrusted] }
|
|
20151
|
+
event: { type: string, description: 'Which hook event (today always PreToolUse).' }
|
|
20152
|
+
reason:
|
|
20153
|
+
# NO `enum` — same open-on-read ruling as kinds 1–3.
|
|
20154
|
+
type: string
|
|
20155
|
+
description: >-
|
|
20156
|
+
Why the verdict is untrusted. CLOSED on the server (two words), OPEN ON READ — KNOWN WORDS TODAY: input_truncated (the entry did not get the full payload: `tool_input` over the cap was replaced by a truncation marker, or a prompt /
|
|
20157
|
+
agent entry''s whole payload text was cut at the same cap) | matcher_over_budget (the group matcher / entry `if` name part ran past its budget and the entry ran BY SELECTION; the same moment also sends a kind-2 frame
|
|
20158
|
+
`matcher_over_budget`).
|
|
20159
|
+
lane: { type: string, description: 'The lane the entry sits in, read off the source table: `managedPolicy` / `managedPlugin:*` => `managed`, everything else and absent => `unmanaged`. Closed (two words) server-side, open on read. Read THIS field, never re-derive from `source`.' }
|
|
20160
|
+
hookName: { type: string, description: 'Hook identity (same rule as kind 2); redacted + bounded.' }
|
|
20161
|
+
entryType: { type: string, description: 'command | http | prompt | agent — closed server-side, open on read.' }
|
|
20162
|
+
toolName: { type: string, description: 'Present on tool events.' }
|
|
20163
|
+
source: { type: string, description: 'The source word the group declared (both lanes give it; ABSENT = the group declared none, so only unmanaged). Trace only, never a criterion.' }
|
|
20164
|
+
detail: { type: string, description: 'Redacted supplement (human-facing; machines read `reason` / `lane`).' }
|
|
20165
|
+
|
|
19944
20166
|
BakeStatus:
|
|
19945
20167
|
# 新(census 批2 四段)——server `BakeStatus`(plugins/store-contracts.ts), the COARSE lifecycle
|
|
19946
20168
|
# bakeView/claimResponse/bakesCreate all key on.
|
|
@@ -20450,6 +20672,11 @@ components:
|
|
|
20450
20672
|
K-24 (server >= 7.104.0 / S-724) — does this leg capture file history (`on` / `disabled`, OPEN word). EFFECTIVE
|
|
20451
20673
|
HALF ONLY ⇒ ALWAYS ABSENT ON THIS ENDPOINT; the deployment-level reading is `capabilities.fileHistoryCapture`.
|
|
20452
20674
|
Full per-key contract: see Event_wiring_manifest.fileHistory.
|
|
20675
|
+
permissionMode:
|
|
20676
|
+
allOf: [{ $ref: '#/components/schemas/WiringManifestPermissionMode' }]
|
|
20677
|
+
description: >-
|
|
20678
|
+
core 7.53.0, server >= 7.133.0 — the leg's permission-mode receipt (section shape WiringManifestPermissionMode). EFFECTIVE HALF ONLY ⇒ this (static) face NEVER carries it, and absence here carries no
|
|
20679
|
+
information; the reading holds only on a live `wiring_manifest` frame.
|
|
20453
20680
|
instructions:
|
|
20454
20681
|
allOf: [{ $ref: '#/components/schemas/WiringManifestInstructions' }]
|
|
20455
20682
|
description: >
|
|
@@ -20521,6 +20748,24 @@ components:
|
|
|
20521
20748
|
type: array
|
|
20522
20749
|
description: 'The selected chain member(s), spelled as the chain spells them (relative to the cwd); `[]` = the chain was walked, nothing was selected.'
|
|
20523
20750
|
items: { type: string, enum: ['CLAUDE.md', '.claude/CLAUDE.md', 'AGENTS.md', '.claude/AGENTS.md'] }
|
|
20751
|
+
WiringManifestPermissionMode:
|
|
20752
|
+
# server 7.133.0 / core 7.53.0:同一段形被 live 帧(两张脸)、静态诊断面、`POST /v1/tasks` 的 `wiringManifest` 回执与账本行引用 ⇒ 具名单源(`spec-field-drift-gate` 也按「导出的 interface 必有同名 schema」逼出它)。
|
|
20753
|
+
type: object
|
|
20754
|
+
additionalProperties: false
|
|
20755
|
+
description: >
|
|
20756
|
+
The `wiring_manifest.permissionMode` section (core 7.53.0 `PermissionModeReceipt`; server >= 7.133.0 projects it on BOTH faces — tenant and operator — and the same section rides the `wiringManifest`
|
|
20757
|
+
receipt of the `POST /v1/tasks` 200 body and the `/events` ledger row; the server judges SHAPE only: `effective` a non-empty string, `requested` / `foldedBy` absent or non-empty strings, else the whole section
|
|
20758
|
+
is dropped). The engine gives the same three-member receipt on EVERY mode question: `requested` = the mode word the caller declared (ABSENT = declared nothing — a root inherits `default`, a child inherits its
|
|
20759
|
+
parent); `effective` = the mode the leg actually runs under (the request clamped under a parent's ceiling, then folded to `default` where the deployment denies it or the classifier face is absent) — ALWAYS present,
|
|
20760
|
+
the shell renders its mode indicator from it; `foldedBy` = PRESENT IFF `requested` and `effective` differ — `operator_deny` (the deployment / organization ceiling denies the mode) / `face_absent` (`auto` needs
|
|
20761
|
+
a classifier face this leg lacks) / `inherited_ceiling` (a delegated child clamped under its parent's ceiling); read OPEN (a fourth word passes through as "folded, cause unknown"). ABSENT section = an older
|
|
20762
|
+
engine / server (<= 7.132.x), not an assertion of the default mode. This is ONE added key on a closed object: a consumer validating `wiring_manifest` strictly against sdk <= 24.0.0's spec fails the whole frame
|
|
20763
|
+
on 7.133.0+ — upgrade to sdk 25.0.0 or relax unknown keys on this section.
|
|
20764
|
+
required: [effective]
|
|
20765
|
+
properties:
|
|
20766
|
+
requested: { type: string, description: 'The mode word the caller declared (a PermissionMode word today; read open). ABSENT = declared nothing.' }
|
|
20767
|
+
effective: { type: string, description: 'The mode the leg actually runs under (a PermissionMode word today; read open). Always present.' }
|
|
20768
|
+
foldedBy: { type: string, description: 'Present iff `requested` and `effective` differ: `operator_deny` / `face_absent` / `inherited_ceiling` (engine words; read OPEN — an unknown word means "folded, cause unknown").' }
|
|
20524
20769
|
WiringManifestModEntry:
|
|
20525
20770
|
# server 7.132.0 / core 7.52.0:取代钩子席位行形(删,零别名)。同一份行形被 live 帧与静态诊断面两处引用 ⇒ 具名;两形各自具名(⑤bis 名单逐形对账)。
|
|
20526
20771
|
description: >
|
|
@@ -21050,10 +21295,225 @@ components:
|
|
|
21050
21295
|
remember for this session's category (fs-write tools share ONE category: approving Write covers
|
|
21051
21296
|
Edit/NotebookEdit; other tools are per-tool) — the CC "don't ask again this session" semantic.
|
|
21052
21297
|
|
|
21298
|
+
ConfigCatalog:
|
|
21299
|
+
type: object
|
|
21300
|
+
description: >
|
|
21301
|
+
The 200 body of GET /v1/config/catalog (server `ConfigCatalogWire`) — a PROJECTION, left open on read (the producer ships independently and adds keys additively between SDK releases; the request side has no body).
|
|
21302
|
+
required: [catalogVersion, envLaneTiming, restartSlices, centerDomains, summary, rows, retired]
|
|
21303
|
+
additionalProperties: true
|
|
21304
|
+
properties:
|
|
21305
|
+
catalogVersion: { type: integer, enum: [1] }
|
|
21306
|
+
envLaneTiming: { type: string, description: 'The env lane''s uniform timing (`deploy`: env is immutable at runtime, so changing env = changing the deployment). Open on read.' }
|
|
21307
|
+
restartSlices: { type: array, items: { type: string }, description: 'The restart-slice names a centre write can land in (open on read).' }
|
|
21308
|
+
centerDomains: { type: array, items: { type: string }, description: 'The contract package''s centre-configuration domains.' }
|
|
21309
|
+
summary:
|
|
21310
|
+
type: object
|
|
21311
|
+
required: [total, envHeld, secret, centerManaged]
|
|
21312
|
+
additionalProperties: true
|
|
21313
|
+
properties:
|
|
21314
|
+
total: { type: integer }
|
|
21315
|
+
envHeld: { type: integer }
|
|
21316
|
+
secret: { type: integer }
|
|
21317
|
+
centerManaged: { type: integer }
|
|
21318
|
+
rows: { type: array, items: { $ref: '#/components/schemas/ConfigCatalogRow' } }
|
|
21319
|
+
retired: { type: array, items: { $ref: '#/components/schemas/ConfigCatalogRetiredEnv' }, description: 'Tombstoned env names this process refuses to start on (always present, may be empty). A name is never in `rows` and here at once.' }
|
|
21320
|
+
ConfigCatalogRow:
|
|
21321
|
+
type: object
|
|
21322
|
+
description: >
|
|
21323
|
+
One catalog row (server `ConfigCatalogRow`): a deployment knob's static declaration plus this process's effective reading. OPEN on read (projection). The two VERDICT bits are the true-only family:
|
|
21324
|
+
`effectiveValueInvalid` (the effective value fell outside the row's echo set; `effectiveValue` is `null`) and `effectiveValueWithheld` (server >= 7.133.0) — see the property.
|
|
21325
|
+
required: [name, domain, type, semantics, writeLanes, effectiveLane, envHeld, envTiming, effectiveValue, valueClass, danger]
|
|
21326
|
+
additionalProperties: true
|
|
21327
|
+
properties:
|
|
21328
|
+
name: { type: string, description: 'The knob (env) name; the catalog is sorted by it.' }
|
|
21329
|
+
domain: { type: string, description: 'One of the nine configuration groups (store / modelPlane / approval / memory / auth / orchestration / limitsHttp / observability / integrations). Open on read.' }
|
|
21330
|
+
type: { type: string, description: 'Value type: boolean | number | string | enum | csv | json | path | url | secret | opaque. Open on read.' }
|
|
21331
|
+
semantics: { type: string, description: 'Human sentence (true to the read site).' }
|
|
21332
|
+
writeLanes: { type: array, items: { type: string, enum: [env, center] }, description: 'Which lanes can write this knob.' }
|
|
21333
|
+
effectiveLane: { type: string, description: 'The lane actually in force: env | center | default. Open on read.' }
|
|
21334
|
+
envHeld: { type: boolean, description: 'This machine''s env sets it explicitly.' }
|
|
21335
|
+
envTiming: { type: string, description: '`deploy` (changing env = changing the deployment) | `inert` (retired / zero-consumer key). Open on read.' }
|
|
21336
|
+
staticDefault: { type: string }
|
|
21337
|
+
derivedDefaultNote: { type: string }
|
|
21338
|
+
effectiveValue:
|
|
21339
|
+
description: 'This process''s effective DISPLAY value: a scalar / a word list / `null` (unset) / `{ present, length? }` (`secret` / `opaque` rows report presence and length only).'
|
|
21340
|
+
oneOf:
|
|
21341
|
+
- { type: [string, number, boolean, 'null'] }
|
|
21342
|
+
- { type: array, items: { type: string } }
|
|
21343
|
+
- { type: object, required: [present], additionalProperties: true, properties: { present: { type: boolean }, length: { type: integer } } }
|
|
21344
|
+
valueClass: { type: string, description: '`plain` (echoable) | `secret` | `opaque` (the value never goes on the wire). Open on read.' }
|
|
21345
|
+
danger: { type: array, items: { type: string }, description: 'The risk axes of a wrong setting: security | billing | data (open on read).' }
|
|
21346
|
+
center: { $ref: '#/components/schemas/ConfigCatalogRowCenter' }
|
|
21347
|
+
centerDomain: { type: string }
|
|
21348
|
+
centerPath: { type: string }
|
|
21349
|
+
scope: { type: string, description: '`bootstrap-only` = read only at start-up.' }
|
|
21350
|
+
enumValues: { type: array, items: { type: string }, description: 'An `enum` row''s writable word set.' }
|
|
21351
|
+
effectiveValueInvalid:
|
|
21352
|
+
type: boolean
|
|
21353
|
+
enum: [true]
|
|
21354
|
+
description: 'TRUE-ONLY verdict bit: the effective value fell OUTSIDE the row''s own echo set (or a boolean without a declared mapping) — `effectiveValue` is then `null` (echoing an out-of-set value would teach a consumer to write back an illegal word). Enum rows only. ABSENT = this cell has no such matter.'
|
|
21355
|
+
effectiveValueWithheld:
|
|
21356
|
+
type: boolean
|
|
21357
|
+
enum: [true]
|
|
21358
|
+
description: >-
|
|
21359
|
+
server >= 7.133.0, TRUE-ONLY verdict bit on `type: url` rows (present only when true, never `false`): the display mint judged this cell (single value) or any item of it (list) WITHHELD — a part the position rule
|
|
21360
|
+
would have shown (the whole query / a path segment) was NOT shown because it held a credential-shaped piece. The mint owns the verdict; the catalog does not judge again. So the shown value is not the whole value:
|
|
21361
|
+
a catalog page / `sema config show` marks the row ("part of this value is withheld"), and must not read the shown value as "the value is exactly that". ABSENT = nothing withheld (older servers never send it).
|
|
21362
|
+
The standard position rule (userinfo landed, query values become tokens) holds for every row and is NOT a withholding; a value that became a placeholder as a whole means the row's display rule cannot show it
|
|
21363
|
+
(not a hierarchical URL; the two outbound-proxy rows show only scheme://host[:port]) — also not this bit.
|
|
21364
|
+
ConfigCatalogRowCenter:
|
|
21365
|
+
type: object
|
|
21366
|
+
description: 'A catalog row''s centre seat: whether the knob is centre-publishable, who wins, when it takes effect. Present only on rows with a centre seat.'
|
|
21367
|
+
required: [domain, path, precedence, timing, managedNow, effectiveNow]
|
|
21368
|
+
additionalProperties: true
|
|
21369
|
+
properties:
|
|
21370
|
+
domain: { type: string }
|
|
21371
|
+
path: { type: string, description: 'Path in the domain (`a.b` into objects; `xs[].c` into array elements).' }
|
|
21372
|
+
precedence: { type: string, description: '`center-wins` | `env-wins` (open on read).' }
|
|
21373
|
+
timing: { type: string, description: '`hot` | `restart` | `inert` (open on read; not `deploy`).' }
|
|
21374
|
+
restartSlice: { type: string }
|
|
21375
|
+
restartReason: { type: string }
|
|
21376
|
+
managedNow: { type: boolean, description: 'The centre''s latest effective config currently MANAGES this key (no config pipeline => always false).' }
|
|
21377
|
+
effectiveNow: { type: boolean, description: 'The centre write is in force right now (managed AND not held down by an env placeholder).' }
|
|
21378
|
+
ConfigCatalogRetiredEnv:
|
|
21379
|
+
type: object
|
|
21380
|
+
description: 'A tombstoned env name (the contract package''s RETIRED_ENVS wire projection): a name this process refuses to start on. `renamed` carries only `replacement`; `replaced` carries `replacement` + `reason` (how the value semantics changed); `removed` carries only `reason`.'
|
|
21381
|
+
required: [name, kind]
|
|
21382
|
+
additionalProperties: true
|
|
21383
|
+
properties:
|
|
21384
|
+
name: { type: string }
|
|
21385
|
+
kind: { type: string, description: '`renamed` | `replaced` | `removed` (open on read).' }
|
|
21386
|
+
replacement: { type: string }
|
|
21387
|
+
reason: { type: string }
|
|
21388
|
+
EntryProblem:
|
|
21389
|
+
type: object
|
|
21390
|
+
description: >
|
|
21391
|
+
One ENTRY PROBLEM (server >= 7.133.0): a refusal body's `detail.problems[]` element and an accept-time `warnings[]` element share this shape. `code` = the refusal code the problem belongs to (in `warnings[]`: the engine's
|
|
21392
|
+
reading code for that entry); `field` = the coordinate of the list holding the entry in the request body (`settings.permissions.allow`, `skills[i].allowedTools` …); `entry` = the entry string (passed through the server''s
|
|
21393
|
+
redaction mouth — not a guarantee of your original bytes); `reason` = the machine reason — mostly the ENGINE''s word verbatim (why it read the entry as nothing); the server mints two of its own, `not_in_roster` (accept-time
|
|
21394
|
+
warnings only: the name in an allow-side selector list is not in the roster and is no protocol namespace name — the engine accepts it anyway) and `home_unresolvable` (settings-rule 422: a `~/` path rule on a deployment that cannot
|
|
21395
|
+
resolve the execution environment''s home); and `below_trust_floor` (the caller''s trust tier is below the member''s admission floor — see ErrorResponse.detail.callerTrust). Read OPEN.
|
|
21396
|
+
required: [code, field, entry, reason]
|
|
21397
|
+
additionalProperties: false
|
|
21398
|
+
properties:
|
|
21399
|
+
code: { type: string }
|
|
21400
|
+
field: { type: string }
|
|
21401
|
+
entry: { type: string }
|
|
21402
|
+
reason: { type: string }
|
|
21403
|
+
PrincipalInboxMessage:
|
|
21404
|
+
type: object
|
|
21405
|
+
description: 'The request body of POST /v1/principal-inbox/messages: a CLOSED three-key shape (`from` is minted by the server from the verified identity and cannot be sent).'
|
|
21406
|
+
required: [to, content]
|
|
21407
|
+
additionalProperties: false
|
|
21408
|
+
properties:
|
|
21409
|
+
to: { type: string, minLength: 1, description: 'The recipient principal string. Must belong to the sender''s organization (the membership judgement reads the organization directory); a failed judgement is 404 `org_delivery.not_member`, byte-identical to "no such person".' }
|
|
21410
|
+
content: { type: string, minLength: 1, maxLength: 32000, description: 'The letter body (characters; over 32000 => 400 `request.field_invalid`).' }
|
|
21411
|
+
urgent: { type: boolean, description: 'The urgent flag on the envelope (absent = ordinary).' }
|
|
21412
|
+
PrincipalInboxReceipt:
|
|
21413
|
+
type: object
|
|
21414
|
+
description: 'The 200 receipt: the letter was appended. `receiptId` = the gate-minted delivery receipt id (the envelope''s `gateReceiptId` carries the same value).'
|
|
21415
|
+
required: [receiptId]
|
|
21416
|
+
additionalProperties: false
|
|
21417
|
+
properties:
|
|
21418
|
+
receiptId: { type: string }
|
|
21419
|
+
OrgDeliveryRefusalCode:
|
|
21420
|
+
type: string
|
|
21421
|
+
enum: [org_delivery.not_member, org_delivery.rate_limited, org_delivery.inbox_full, org_delivery.recipient_tombstoned]
|
|
21422
|
+
description: >
|
|
21423
|
+
The four cross-principal delivery refusal codes (server >= 7.133.0; the engine's closed `ORG_DELIVERY_REFUSAL_CODES`, one frozen sentence each — machines read the CODE): `org_delivery.not_member` (404),
|
|
21424
|
+
`org_delivery.rate_limited` (429), `org_delivery.inbox_full` (409), `org_delivery.recipient_tombstoned` (410). See POST /v1/principal-inbox/messages.
|
|
21425
|
+
ToolApprovalReadState:
|
|
21426
|
+
type: string
|
|
21427
|
+
enum: [pending, decided, parked, void]
|
|
21428
|
+
description: >
|
|
21429
|
+
`state` of GET /v1/tool-approvals/{id} (server >= 7.133.0), a CLOSED four-word set — read it exhaustively: `pending` = a live registration on this replica is waiting (answer door open);
|
|
21430
|
+
`decided` = the row is DECIDED; `parked` = PARKING / PARKED (a late answer redeems the parked card); `void` = DENIED / VOID (nobody decided it, the answer has nowhere to land).
|
|
21431
|
+
ApprovalsStreamReadErrorCode:
|
|
21432
|
+
type: string
|
|
21433
|
+
enum: [approvals.read_unavailable, approvals.read_corrupt]
|
|
21434
|
+
description: >
|
|
21435
|
+
The `errorCode` of the two READ-FAILURE `error` frames on GET /v1/approvals/stream (server >= 7.133.0). `approvals.read_unavailable` = this tick's approval read failed (the store was momentarily
|
|
21436
|
+
unreadable) — the stream STAYS open and the next tick reads again; it is NOT a terminal frame and NOT a reconnect signal (do not reconnect on it, and do not read it as "nothing pending").
|
|
21437
|
+
`approvals.read_corrupt` = a corrupt row was read — the server closes the stream after the frame; deal with that row before reopening. Branch on the code, never match the message.
|
|
21438
|
+
Older servers (<= 7.132.x) answer a read failure with nothing but heartbeats.
|
|
21439
|
+
ToolApprovalReadProjection:
|
|
21440
|
+
type: object
|
|
21441
|
+
description: >
|
|
21442
|
+
The row projection shared by the three non-`pending` arms of GET /v1/tool-approvals/{id} (server >= 7.133.0): a SUBSET of the `livePending[]` row keys — one reader, one key set. `frame` = the disconnect-replay
|
|
21443
|
+
opening frame (an `approval_request` frame; ABSENT when the card cannot be read back — never invented).
|
|
21444
|
+
required: [approvalId, ts, expiresAtMs]
|
|
21445
|
+
additionalProperties: true
|
|
21446
|
+
properties:
|
|
21447
|
+
approvalId: { type: string }
|
|
21448
|
+
ts: { type: number, description: 'Registration time (ms epoch).' }
|
|
21449
|
+
expiresAtMs: { type: number, description: 'Absolute window deadline (cast once, never recomputed).' }
|
|
21450
|
+
sessionId: { type: string }
|
|
21451
|
+
toolName: { type: string }
|
|
21452
|
+
frame: { $ref: '#/components/schemas/ApprovalRequestFrame' }
|
|
21453
|
+
ToolApprovalReadBody:
|
|
21454
|
+
description: >
|
|
21455
|
+
The 200 body of GET /v1/tool-approvals/{id} (server >= 7.133.0) — a four-arm union discriminated by `state` (closed, read it exhaustively): `pending` = a live registration on this replica is waiting
|
|
21456
|
+
(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
|
|
21457
|
+
(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
|
|
21458
|
+
late-answer 404's, see ToolApprovalNotPendingCause).
|
|
21459
|
+
🔴 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`
|
|
21460
|
+
(+ `decidePath`; ONLY this state renders "go decide"); the gate settled by a person / policy ⇒ `decided` (no `decidePath`); the gate expired (a cancel expires it
|
|
21461
|
+
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
|
|
21462
|
+
readable direction / time ⇒ 404 `tool_approval.not_pending` WITHOUT `cause`; the gate unreadable (store failure) ⇒ falls back to `parked`. Known boundary (re-read
|
|
21463
|
+
to settle it): a parked row's `decided` can return to `parked` when the resume leg fails before the approved action starts (the engine reopens the same gate), and
|
|
21464
|
+
a cancelled resume reads `void` once the server collects the reopened gate (`parked` in between, or `void` for an instant during the reopen).
|
|
21465
|
+
Older servers answered all of these with `parked` + `decidePath`.
|
|
21466
|
+
oneOf:
|
|
21467
|
+
- $ref: '#/components/schemas/ToolApprovalReadPending'
|
|
21468
|
+
- $ref: '#/components/schemas/ToolApprovalReadDecided'
|
|
21469
|
+
- $ref: '#/components/schemas/ToolApprovalReadParked'
|
|
21470
|
+
- $ref: '#/components/schemas/ToolApprovalReadVoid'
|
|
21471
|
+
discriminator:
|
|
21472
|
+
propertyName: state
|
|
21473
|
+
mapping:
|
|
21474
|
+
pending: '#/components/schemas/ToolApprovalReadPending'
|
|
21475
|
+
decided: '#/components/schemas/ToolApprovalReadDecided'
|
|
21476
|
+
parked: '#/components/schemas/ToolApprovalReadParked'
|
|
21477
|
+
void: '#/components/schemas/ToolApprovalReadVoid'
|
|
21478
|
+
ToolApprovalReadPending:
|
|
21479
|
+
allOf:
|
|
21480
|
+
- $ref: '#/components/schemas/LivePendingRow'
|
|
21481
|
+
- type: object
|
|
21482
|
+
required: [state]
|
|
21483
|
+
properties:
|
|
21484
|
+
state: { type: string, enum: [pending] }
|
|
21485
|
+
ToolApprovalReadDecided:
|
|
21486
|
+
allOf:
|
|
21487
|
+
- $ref: '#/components/schemas/ToolApprovalReadProjection'
|
|
21488
|
+
- type: object
|
|
21489
|
+
required: [state, decision, decidedAtMs]
|
|
21490
|
+
properties:
|
|
21491
|
+
state: { type: string, enum: [decided] }
|
|
21492
|
+
decision: { type: string, enum: [allow, deny] }
|
|
21493
|
+
decidedAtMs: { type: integer }
|
|
21494
|
+
decisionNote: { type: string }
|
|
21495
|
+
ToolApprovalReadParked:
|
|
21496
|
+
allOf:
|
|
21497
|
+
- $ref: '#/components/schemas/ToolApprovalReadProjection'
|
|
21498
|
+
- type: object
|
|
21499
|
+
required: [state]
|
|
21500
|
+
properties:
|
|
21501
|
+
state: { type: string, enum: [parked] }
|
|
21502
|
+
decidePath: { type: string, description: 'The durable decision path for this ask when it can be built.' }
|
|
21503
|
+
ToolApprovalReadVoid:
|
|
21504
|
+
allOf:
|
|
21505
|
+
- $ref: '#/components/schemas/ToolApprovalReadProjection'
|
|
21506
|
+
- type: object
|
|
21507
|
+
required: [state]
|
|
21508
|
+
properties:
|
|
21509
|
+
state: { type: string, enum: [void] }
|
|
21510
|
+
cause:
|
|
21511
|
+
allOf: [{ $ref: '#/components/schemas/AskRowEndingCause' }]
|
|
21512
|
+
description: 'The row''s recorded ending cause (absent for a legacy row with no cause written — never invented).'
|
|
21053
21513
|
ToolApprovalRespondApplied:
|
|
21054
21514
|
type: object
|
|
21055
21515
|
description: >
|
|
21056
|
-
The APPLIED arm of the 200 ack of POST /v1/tool-approvals/{
|
|
21516
|
+
The APPLIED arm of the 200 ack of POST /v1/tool-approvals/{id}/respond (server tool-approval.ts — exact
|
|
21057
21517
|
shape, `decision` echoed): the decision settled a live gate. Owner-gated with a 404 and NO existence oracle: non-owner and
|
|
21058
21518
|
unknown/settled/expired/wrong-replica ids are indistinguishable. See ToolApprovalRespondAck for the replayed arm.
|
|
21059
21519
|
# 封闭(census 批2 第三段,2026-07-30):铸造点 `tool-approval.ts` 三恒在键 + 两条件键
|
|
@@ -21069,6 +21529,13 @@ components:
|
|
|
21069
21529
|
approvalId: { type: string }
|
|
21070
21530
|
delivery: { type: string, enum: [applied] }
|
|
21071
21531
|
decision: { $ref: '#/components/schemas/ToolApprovalDecision' }
|
|
21532
|
+
consumerState:
|
|
21533
|
+
type: string
|
|
21534
|
+
enum: [absent]
|
|
21535
|
+
description: >-
|
|
21536
|
+
server >= 7.133.0, TRUE-ONLY: no engine leg on THIS replica was waiting for the ask this answer settled — the same bit, with the same meaning, as `LivePendingRow.consumerState` and the
|
|
21537
|
+
durable decision ack's. Present <=> the answered entry was ADOPTED from the ask ledger at start-up. The decision lands on the row as usual, but nothing running here resumes because of it — read the run
|
|
21538
|
+
surface; do not read the 200 as "the run continued". Absent = a local engine leg was waiting, or an older server.
|
|
21072
21539
|
rememberApplied:
|
|
21073
21540
|
type: boolean
|
|
21074
21541
|
description: >
|
|
@@ -21160,7 +21627,7 @@ components:
|
|
|
21160
21627
|
ToolApprovalRespondReplayed:
|
|
21161
21628
|
type: object
|
|
21162
21629
|
description: >
|
|
21163
|
-
The REPLAYED arm of the 200 ack of POST /v1/tool-approvals/{
|
|
21630
|
+
The REPLAYED arm of the 200 ack of POST /v1/tool-approvals/{id}/respond: the ask was already DECIDED
|
|
21164
21631
|
(by anyone) when this answer arrived, so the server replays the FIRST decision — `decision` is the row's two-word
|
|
21165
21632
|
value (`allow_session` was folded to `allow` when it landed), `decidedAtMs` the first decision's clock,
|
|
21166
21633
|
`decisionNote` only when the row has one, `rememberApplied: false` only when THIS request asked `allow_session`.
|
|
@@ -21176,14 +21643,57 @@ components:
|
|
|
21176
21643
|
decidedAtMs: { type: integer }
|
|
21177
21644
|
decisionNote: { type: string }
|
|
21178
21645
|
rememberApplied: { type: boolean, enum: [false] }
|
|
21646
|
+
noteRecorded: { type: boolean, description: 'server >= 7.133.0 (two doors, one shape): present ONLY when THIS request carried a `note`; `true` <=> the row''s text is verbatim what this request submitted, `false` = not confirmed (another reason is on the row / no store / read-back failed). A late answer''s note never overwrites the first decision''s reason — that is `decisionNote`.' }
|
|
21647
|
+
rulePersisted: { type: boolean, enum: [false], description: 'server >= 7.133.0: present (as `false`) only when this late answer carried `persistRule`: the rule is redeemed on the FIRST decision only, a late answer never saves one.' }
|
|
21648
|
+
ruleRefusal:
|
|
21649
|
+
type: string
|
|
21650
|
+
enum: [rule_lane_unavailable]
|
|
21651
|
+
description: 'with `rulePersisted: false`; on this arm always `rule_lane_unavailable` (the only word the published late-answer path mints).'
|
|
21652
|
+
ToolApprovalRespondParkedRedeemed:
|
|
21653
|
+
type: object
|
|
21654
|
+
description: >
|
|
21655
|
+
The PARKED-REDEEMED arm of the 200 ack of POST /v1/tool-approvals/{id}/respond (server >= 7.133.0; server tool-approval.ts `redeemParkedAsk` /
|
|
21656
|
+
`replayRedeemReceipt`): the card's window had run out and the ask moved to background waiting (PARKED); this late answer — on another replica or after a restart —
|
|
21657
|
+
goes through the EXISTING durable redemption leg (the same CAS as POST /v1/approvals/{sessionId}/decide) instead of the old 404 `tool_approval.not_pending`
|
|
21658
|
+
(`cause: unknown_or_other_replica`). Body = the redemption leg's receipt (ApprovalDecisionResult's keys — `status` / `taskId` / `sessionId` / ... as they come) plus the three keys
|
|
21659
|
+
this door adds: `approvalId` / `askId` (the same 64-hex value) and the discriminant `accepted`. `decision` is the two-word direction on the REPLAY form only (`allow` / `deny`,
|
|
21660
|
+
`allow_session` folded to `allow`); `rememberApplied: false` appears only when this request asked `allow_session` (a parked redemption cannot remember a session grant);
|
|
21661
|
+
`rulePersisted` / `ruleRefusal` / `persistedRule*` are the `persistRule` outcome on the redemption leg (a rule that fails to store NEVER flips the verdict).
|
|
21662
|
+
`idempotent: true` appears only on the REPLAY (the same already-redeemed card answered again returns the first redemption's receipt).
|
|
21663
|
+
CLOSED like its two sibling arms (the census keeps this op's 200 body closed): the key set is ApprovalDecisionResult's own keys (one source per shape, by `$ref`) plus the added ones.
|
|
21664
|
+
required: [approvalId, askId, accepted]
|
|
21665
|
+
additionalProperties: false
|
|
21666
|
+
properties:
|
|
21667
|
+
approvalId: { type: string }
|
|
21668
|
+
askId: { type: string }
|
|
21669
|
+
accepted: { type: string, enum: [parked_redeemed] }
|
|
21670
|
+
status: { $ref: '#/components/schemas/ApprovalDecisionResult/properties/status' }
|
|
21671
|
+
taskId: { $ref: '#/components/schemas/ApprovalDecisionResult/properties/taskId' }
|
|
21672
|
+
sessionId: { $ref: '#/components/schemas/ApprovalDecisionResult/properties/sessionId' }
|
|
21673
|
+
bindingEnforced: { $ref: '#/components/schemas/ApprovalDecisionResult/properties/bindingEnforced' }
|
|
21674
|
+
decision: { type: string, description: 'REPLAY form: `allow` / `deny` (`allow_session` folded to `allow`). FIRST redemption: whatever the redemption leg''s own receipt says (`approve` / `deny` today) — read it open.' }
|
|
21675
|
+
errorCode: { $ref: '#/components/schemas/ApprovalDecisionResult/properties/errorCode' }
|
|
21676
|
+
errorMessage: { $ref: '#/components/schemas/ApprovalDecisionResult/properties/errorMessage' }
|
|
21677
|
+
retriable: { $ref: '#/components/schemas/ApprovalDecisionResult/properties/retriable' }
|
|
21678
|
+
idempotent: { $ref: '#/components/schemas/ApprovalDecisionResult/properties/idempotent' }
|
|
21679
|
+
executionOutcome: { $ref: '#/components/schemas/ApprovalDecisionResult/properties/executionOutcome' }
|
|
21680
|
+
rememberApplied: { type: boolean, enum: [false] }
|
|
21681
|
+
rulePersisted: { $ref: '#/components/schemas/ToolApprovalRespondApplied/properties/rulePersisted' }
|
|
21682
|
+
ruleRefusal: { $ref: '#/components/schemas/ToolApprovalRespondApplied/properties/ruleRefusal' }
|
|
21683
|
+
persistedRule: { $ref: '#/components/schemas/ToolApprovalRespondApplied/properties/persistedRule' }
|
|
21684
|
+
persistedRules: { $ref: '#/components/schemas/ToolApprovalRespondApplied/properties/persistedRules' }
|
|
21685
|
+
persistedRuleAnchors: { $ref: '#/components/schemas/ToolApprovalRespondApplied/properties/persistedRuleAnchors' }
|
|
21179
21686
|
ToolApprovalRespondAck:
|
|
21180
21687
|
description: >
|
|
21181
|
-
The 200 ack of POST /v1/tool-approvals/{
|
|
21688
|
+
The 200 ack of POST /v1/tool-approvals/{id}/respond — a discriminated union: the decision was APPLIED to a live
|
|
21182
21689
|
gate (ToolApprovalRespondApplied, `delivery: "applied"`) or the ask was already decided and the first decision is
|
|
21183
|
-
REPLAYED (ToolApprovalRespondReplayed, `idempotent: true`).
|
|
21690
|
+
REPLAYED (ToolApprovalRespondReplayed, `idempotent: true`), or (server >= 7.133.0, sdk 25.0.0) the answer REDEEMED a PARKED card through the durable redemption
|
|
21691
|
+
leg (ToolApprovalRespondParkedRedeemed, `accepted: "parked_redeemed"`). The discriminants are `delivery` / `idempotent` / `accepted` (a replayed redemption carries
|
|
21692
|
+
BOTH `accepted: "parked_redeemed"` and `idempotent: true` — read `accepted` first).
|
|
21184
21693
|
oneOf:
|
|
21185
21694
|
- $ref: '#/components/schemas/ToolApprovalRespondApplied'
|
|
21186
21695
|
- $ref: '#/components/schemas/ToolApprovalRespondReplayed'
|
|
21696
|
+
- $ref: '#/components/schemas/ToolApprovalRespondParkedRedeemed'
|
|
21187
21697
|
UsageMetric:
|
|
21188
21698
|
type: string
|
|
21189
21699
|
enum: [tasks, tokensIn, tokensOut, costUsd]
|
|
@@ -21346,74 +21856,6 @@ components:
|
|
|
21346
21856
|
delivery: { type: string, const: applied, description: 'Literal "applied" on today''s server.' }
|
|
21347
21857
|
marker: { type: string, description: 'Engine-minted delivery marker.' }
|
|
21348
21858
|
|
|
21349
|
-
WorkspaceSnapshotPage:
|
|
21350
|
-
# 4.3.0(CAPS-OPS-8):把 GET …/workspace 的 200 信封提成具名 schema。SDK 侧此前是 `list()` 返回位上的
|
|
21351
|
-
# **内联闭合字面量**,两道 spec 门(field-drift 只扫 types.ts / resource-schema 只扫 resources 的 exported
|
|
21352
|
-
# type)对方法返回位的内联形都是盲的 —— 于是它漏了这里一直标为 required 的 `sessionId`,CI 全绿。
|
|
21353
|
-
type: object
|
|
21354
|
-
description: >
|
|
21355
|
-
GET /v1/sessions/{sessionId}/workspace 的 200 信封(server src/http/routes/sessions.ts)。
|
|
21356
|
-
⚠️ `total` = **本页行数**(`snapshots.length`),不是快照总数,且这条腿没有任何分页游标 —— 与同族
|
|
21357
|
-
`WorkspaceTreePage.total`(真·全量)**同名反义**。`latest` 与展示行同源(防 ghost key)。
|
|
21358
|
-
additionalProperties: false
|
|
21359
|
-
required: [sessionId, snapshots, total]
|
|
21360
|
-
properties:
|
|
21361
|
-
sessionId: { type: string }
|
|
21362
|
-
snapshots:
|
|
21363
|
-
type: array
|
|
21364
|
-
items: { $ref: '#/components/schemas/WorkspaceSnapshotRow' }
|
|
21365
|
-
latest: { type: string, description: 'Newest snapshot key; absent when the session has none.' }
|
|
21366
|
-
total: { type: integer, description: 'ROWS ON THIS PAGE — not the snapshot count.' }
|
|
21367
|
-
|
|
21368
|
-
WorkspaceTreePage:
|
|
21369
|
-
# 4.3.0(CAPS-OPS-8):同上,tree 腿的 200 信封。
|
|
21370
|
-
type: object
|
|
21371
|
-
description: >
|
|
21372
|
-
GET /v1/sessions/{sessionId}/workspace/{key}/tree 的 200 信封(server src/http/routes/sessions.ts)。
|
|
21373
|
-
`total` 在这条腿上是**全量**(`all.length`)—— 与 `WorkspaceSnapshotPage.total` 语义相反。
|
|
21374
|
-
`key` 回的是解析后的真实键(请求可传 `latest` 别名)。
|
|
21375
|
-
additionalProperties: false
|
|
21376
|
-
required: [sessionId, key, entries, total]
|
|
21377
|
-
properties:
|
|
21378
|
-
sessionId: { type: string }
|
|
21379
|
-
key: { type: string, description: 'The RESOLVED snapshot key (a `latest` alias in the request comes back resolved).' }
|
|
21380
|
-
entries:
|
|
21381
|
-
type: array
|
|
21382
|
-
items: { $ref: '#/components/schemas/WorkspaceTreeEntry' }
|
|
21383
|
-
total: { type: integer, description: 'FULL entry count (not the page size).' }
|
|
21384
|
-
nextOffset: { type: integer, description: 'Present ⇒ more pages; send it back as ?offset=.' }
|
|
21385
|
-
|
|
21386
|
-
WorkspaceSnapshotRow:
|
|
21387
|
-
type: object
|
|
21388
|
-
description: >
|
|
21389
|
-
One snapshot row of GET /v1/sessions/{sessionId}/workspace (envelope `{sessionId, snapshots,
|
|
21390
|
-
latest?, total}`; server src/http/routes/sessions.ts — newest first, uuidv7 keys). E19 snapshot store's
|
|
21391
|
-
read-only projection: each row is the freeze at a turn boundary, NOT a live workspace.
|
|
21392
|
-
required: [key, files]
|
|
21393
|
-
additionalProperties: true
|
|
21394
|
-
properties:
|
|
21395
|
-
key: { type: string, description: 'Snapshot key (uuidv7 entry id; "latest" is an accepted alias on the sub-routes).' }
|
|
21396
|
-
files: { type: integer, description: 'Manifest row count.' }
|
|
21397
|
-
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).' }
|
|
21398
|
-
|
|
21399
|
-
WorkspaceTreeEntry:
|
|
21400
|
-
type: object
|
|
21401
|
-
description: >
|
|
21402
|
-
One file entry of GET /v1/sessions/{sessionId}/workspace/{key}/tree (envelope `{sessionId, key,
|
|
21403
|
-
entries, total, nextOffset?}`; server src/http/routes/sessions.ts — stable path order, offset paging).
|
|
21404
|
-
required: [path, hash]
|
|
21405
|
-
additionalProperties: true
|
|
21406
|
-
properties:
|
|
21407
|
-
path: { type: string, description: 'The relPath — pass VERBATIM as ?path= on the file sub-route.' }
|
|
21408
|
-
hash: { type: string, description: 'Content address (sha256) — enables cross-snapshot change highlighting ([1894]②).' }
|
|
21409
|
-
size: { type: integer, description: 'Present only when the store has a sizes face (SQL backends).' }
|
|
21410
|
-
|
|
21411
|
-
# ═══════════════════════════════════════════════════════════════════════════════════════════════
|
|
21412
|
-
# #154 车二(design/179 §7)—— 持久权限规则的 CC settings 导入面。形按 server 真码 + core 原形亲读
|
|
21413
|
-
# (`src/http/routes/rules.ts` / `src/rules-consent.ts`;preview/result 两形是 core `ImportPreview` /
|
|
21414
|
-
# `ImportResult` 的原样透传)。
|
|
21415
|
-
# ═══════════════════════════════════════════════════════════════════════════════════════════════
|
|
21416
|
-
|
|
21417
21859
|
CcImportSettingsLayer:
|
|
21418
21860
|
type: string
|
|
21419
21861
|
enum: [userSettings, projectSettings, localSettings]
|
|
@@ -22141,7 +22583,7 @@ components:
|
|
|
22141
22583
|
|
|
22142
22584
|
AdoptionNotMigratedFace:
|
|
22143
22585
|
type: string
|
|
22144
|
-
enum: [usage-window, cost-quota, rate-limit, approval-exemption-grantor, approval-ask-decision-actor, image-bake-requested-by]
|
|
22586
|
+
enum: [usage-window, cost-quota, rate-limit, approval-exemption-grantor, approval-ask-decision-actor, image-bake-requested-by, permission-rule-approval-history, permission-rule-import-ticket, org-delivery-quota]
|
|
22145
22587
|
description: >
|
|
22146
22588
|
FROZEN machine-readable vocabulary of the faces the server deliberately does not migrate. Renaming or
|
|
22147
22589
|
removing a member is a wire break; adding one is a new behaviour face. Consumers assert POSITIVELY on
|