@sema-agent/sdk 22.0.3 → 23.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 +76 -1
- package/dist/errors.d.ts +40 -23
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +38 -21
- package/dist/errors.js.map +1 -1
- package/dist/events.d.ts +47 -5
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js.map +1 -1
- package/dist/index.d.ts +16 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -3
- package/dist/index.js.map +1 -1
- package/dist/resources/approvals.d.ts +5 -2
- package/dist/resources/approvals.d.ts.map +1 -1
- package/dist/resources/approvals.js.map +1 -1
- package/dist/resources/fleet.d.ts +4 -0
- package/dist/resources/fleet.d.ts.map +1 -1
- package/dist/resources/fleet.js.map +1 -1
- package/dist/resources/tasks.d.ts +2 -2
- package/dist/resources/tasks.js +2 -2
- package/dist/resources/tool-approvals.d.ts +77 -25
- package/dist/resources/tool-approvals.d.ts.map +1 -1
- package/dist/resources/tool-approvals.js +45 -14
- package/dist/resources/tool-approvals.js.map +1 -1
- package/dist/settings.d.ts +4 -4
- package/dist/types.d.ts +155 -57
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +36 -0
- package/dist/types.js.map +1 -1
- package/openapi.yaml +279 -115
- package/package.json +1 -1
package/openapi.yaml
CHANGED
|
@@ -423,7 +423,7 @@ paths:
|
|
|
423
423
|
prepare-time refusal code that HAS a status in the server's per-code triage table (`resume_at.*` — `resume_at.not_found` is 404, the rest 422 —, governance codes,
|
|
424
424
|
`rewind.store_unconfigured` / `rewind.capture_disabled` / `rewind.child_scope_unsupported` / `rewind_snapshot.unresolvable`, the
|
|
425
425
|
registered `config.*` codes incl. the three roster-bound ones and — server >= 7.116.0 — `config.invalid_checkpoint_store` (400) and
|
|
426
|
-
`config.user_invoked_skill_unknown` (400: top-level `userInvokedSkills` named a skill this run does not carry) and — server >= 7.117.0 — `config.image_invalid` (400: an inline image failed the engine's byte admission; `error` starts with `TaskSpec.images: `) and — server >= 7.126.0 — `config.tool_face_control` (400: a name in `allowTools` / `excludeTools` that no tool of this run answers to
|
|
426
|
+
`config.user_invoked_skill_unknown` (400: top-level `userInvokedSkills` named a skill this run does not carry) and — server >= 7.117.0 — `config.image_invalid` (400: an inline image failed the engine's byte admission; `error` starts with `TaskSpec.images: `) and — server >= 7.126.0 — `config.tool_face_control` (400: `allowTools: ["mcp__*"]`, `excludeTools: ["*"]` (and, on server 7.126.0–7.128.x only, a name in `allowTools` / `excludeTools` that no tool of this run answers to — server >= 7.129.0 runs those as written and announces each once with the operator row `runner_config_advisory`), or an `excludeTools` entry that reaches a protocol tool this leg depends on; the engine rejects before the run starts, never silently ignoring the name; `error` is the engine's sentence naming the entry, its list and why), `corrupt_session`; codes outside the table, e.g. `rewind.invalid_spec`, stay
|
|
427
427
|
200 with a failed body) answers its 4xx / 5xx ERROR BODY only when the whole submission did ZERO WORK (cumulative
|
|
428
428
|
`stats.turns === 0` AND `stats.tokens === 0` — the common case: nothing billed, not idempotency-cached). When a
|
|
429
429
|
`verify` / `cascade` run's earlier tier already ran turns and only the last tier is refused, the SAME code arrives
|
|
@@ -2511,7 +2511,8 @@ paths:
|
|
|
2511
2511
|
description: >
|
|
2512
2512
|
E6 — write the per-session rules. 🔴 TIGHTEN-ONLY for a normal principal: a loosen (remove a deny / widen
|
|
2513
2513
|
an allowlist or allowDirs) → 403 `loosen_forbidden` (operator-only). `expectedRev` = optimistic-concurrency
|
|
2514
|
-
CAS; a stale rev → 409 `conflict`. Each rule field must be `string[]`
|
|
2514
|
+
CAS; a stale rev → 409 `conflict`. Each rule field must be `string[]` and the body carries only the three buckets (`toolAllow` / `toolDeny` / `allowDirs`; any other
|
|
2515
|
+
key, including the retired `commandAllow` / `commandDeny`, → 400 `rules.invalid_shape` on server >= 7.129.0) → malformed body 400. ownerless
|
|
2515
2516
|
session → 409; non-owner → 404. 501 without a session-policy store.
|
|
2516
2517
|
requestBody:
|
|
2517
2518
|
required: true
|
|
@@ -2521,7 +2522,22 @@ paths:
|
|
|
2521
2522
|
type: object
|
|
2522
2523
|
required: [rules]
|
|
2523
2524
|
properties:
|
|
2524
|
-
rules
|
|
2525
|
+
# 🔴 请求体里的 `rules` 收闭(server >= 7.129.0:写口的判官拒一切不是桶的键,含退役命令桶 ⇒ 400 `rules.invalid_shape`)。
|
|
2526
|
+
# `SessionPermissionRules` 本体保持开放(响应 / 同步面的 `StoredSessionRules` 经 allOf 引它并再加 `rev` / `gen`,本体收闭会把合法的 `rev` 判成违约);
|
|
2527
|
+
# 所以收闭落在**这一个引用点**:allOf + 本地镜像三桶 + `additionalProperties:false`(spec.test 的通用「封闭 allOf 必须镜像分支键」门看守镜像完整)。
|
|
2528
|
+
rules:
|
|
2529
|
+
allOf: [{ $ref: '#/components/schemas/SessionPermissionRules' }]
|
|
2530
|
+
additionalProperties: false
|
|
2531
|
+
properties:
|
|
2532
|
+
toolAllow: { type: array, items: { type: string } }
|
|
2533
|
+
toolDeny: { type: array, items: { type: string } }
|
|
2534
|
+
allowDirs: { type: array, items: { type: string } }
|
|
2535
|
+
# server >= 7.129.0 (engine 7.51.2): a stored body echoed back (GET → edit → PUT) may carry its `rev` and `grammar`;
|
|
2536
|
+
# the engine reads the entries in THAT grammar (`rev` without `grammar` = grammar 0 ⇒ a rule-spelled allow entry
|
|
2537
|
+
# answers `400 rules.inert_entry` reason `earlier_grammar`; the way out is to send the three buckets alone — the
|
|
2538
|
+
# write judge stamps the current grammar). `grammar` newer than the engine's ⇒ `400 rules.invalid_shape`.
|
|
2539
|
+
rev: { type: integer, minimum: 0 }
|
|
2540
|
+
grammar: { type: integer, minimum: 0 }
|
|
2525
2541
|
expectedRev: { type: integer, minimum: 0, description: CAS — reject if the stored rev differs. }
|
|
2526
2542
|
responses:
|
|
2527
2543
|
'200':
|
|
@@ -2537,18 +2553,22 @@ paths:
|
|
|
2537
2553
|
rules: { $ref: '#/components/schemas/StoredSessionRules' }
|
|
2538
2554
|
'400':
|
|
2539
2555
|
description: >-
|
|
2540
|
-
400 — the rules body is refused BEFORE anything is written (not one byte reaches the store). Branch on `errorCode`, never on the sentence.
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2556
|
+
400 — the rules body is refused BEFORE anything is written (not one byte reaches the store; the store's own refusal uses the same code and body). Branch on `errorCode`, never on the sentence.
|
|
2557
|
+
The `rules.*` family (the SDK lands both codes on `BadRequestError`); the judge is the ENGINE's write-door judge, one reader for the whole body (server >= 7.129.0, engine 7.51.0):
|
|
2558
|
+
`rules.inert_entry`: a rule entry in a bucket (`toolAllow` / `toolDeny`) that the engine reads as NOTHING — written down it would deny / allow nothing. Body keys (top level; the SDK reads them from `err.extra`):
|
|
2559
|
+
`field` (which bucket), `entry` (which entry, the original value), `reason` (the engine's `ALLOW_ENTRY_INERT_REASONS`, six words: `tool_name` no tool answers to the name · `wildcard_all` the bare `*` ·
|
|
2560
|
+
`namespace_wildcard` `mcp__*`-style wildcard naming no server · `content_unsupported` the tool has no rule form like that, e.g. `Monitor(touch:*)` · `grammar` the grammar fails, e.g. an empty string or `Bash(` ·
|
|
2561
|
+
`near_miss` a near-miss of a valid form), `verdict` (optional; only on `tool_name`: `retired` an engine-retired name / `unprefixed_namespace` a `__` name without a protocol prefix / `unmintable_segment` a protocol
|
|
2562
|
+
name whose peer segment or tool segment the engine cannot mint, or whose tool segment is EMPTY — `mcp__<server>__`, `a2a__<peer>__`), `currentName` (optional; present when there is a 1:1 successor, e.g. `bash` => `Bash`), `guidance` (optional; the engine judge's wording
|
|
2563
|
+
of the fix, byte for byte — carried by `retired` and `unmintable_segment`). The sentence is the engine's own: match on the code. Rewrite that entry and write again.
|
|
2564
|
+
`rules.invalid_shape`: the rules body itself has the wrong shape — not an object (`rules` absent, `null`, an array, a scalar), a key that is not a bucket (including the RETIRED `commandAllow` / `commandDeny`:
|
|
2565
|
+
never silently dropped, a dropped deny is enforced by nobody), a bucket that is not a list of strings, an `allowDirs` member that is empty / blank. Body keys: `field` (which key; the empty string when the
|
|
2566
|
+
body as a whole is not an object), `got` (the TYPE of that value, e.g. `a string` / `not a rule bucket` — the value is never echoed); `error` is the engine's sentence.
|
|
2567
|
+
The command rule's new spelling is an entry: `commandDeny: ["rm"]` => `toolDeny: ["Bash(rm:*)"]` (denies the command name `rm`; it does not deny a path-spelled `/bin/rm ...` nor a command line that does not read as one command
|
|
2568
|
+
name — the old bucket denied both; to deny those too deny the whole shell, `toolDeny: ["Bash"]`); `commandAllow` has no counterpart.
|
|
2569
|
+
🔴 BREAKING (server 7.129.0): server 7.105.0–7.128.x answered this family with `rules.legacy_tool_name` (entry in `name`, no `reason`) — that code is retired with no alias; a caller that branched on it branches on the two codes
|
|
2570
|
+
above (the `rules.` prefix test still holds). On server <= 7.128.x the shape problems answered `400 request.field_invalid` (`rules` not an object: `request.body_shape`) and a body with a command bucket was accepted.
|
|
2571
|
+
Also `request.id_invalid` (malformed session id).
|
|
2552
2572
|
content:
|
|
2553
2573
|
application/json:
|
|
2554
2574
|
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
@@ -3261,15 +3281,12 @@ paths:
|
|
|
3261
3281
|
schema: { $ref: '#/components/schemas/ImportStaged' }
|
|
3262
3282
|
'400':
|
|
3263
3283
|
description: >-
|
|
3264
|
-
400 — malformed input (the `policy` face is judged by the same write-door
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
SEGMENT (`mcp__<server>__`, `a2a__<peer>__`) here (`verdict: "unmintable_segment"`); server 7.116.0–7.122.x answered `400 request.field_invalid` for it.
|
|
3271
|
-
`request.field_invalid`: a bucket that is not a list of non-empty strings and — behavior change in server >= 7.123.0 — an EMPTY or BLANK member, which earlier
|
|
3272
|
-
servers took in (the sentence carries the `policy: [i].rules: ` position prefix and then the engine's own sentence; match on the code).
|
|
3284
|
+
400 — malformed input (the `policy` face is judged by the same engine write-door judge as `PUT /v1/sessions/{sessionId}/policy`, BEFORE any staging is created, so a refused push leaves nothing behind).
|
|
3285
|
+
Branch on `errorCode`, never on the sentence. `rules.inert_entry` / `rules.invalid_shape` (server >= 7.129.0, engine 7.51.0; the `rules.*` family, same code and same body as the policy door — read the bucket list, the `reason` words
|
|
3286
|
+
(`tool_name` / `wildcard_all` / `namespace_wildcard` / `content_unsupported` / `grammar` / `near_miss`) and the body keys `field` / `entry` / `reason` / `verdict` / `currentName` / `guidance` / `got` there):
|
|
3287
|
+
a record's rule entry the engine reads as nothing (e.g. a protocol name with an EMPTY TOOL SEGMENT — `mcp__<server>__`, `a2a__<peer>__` — has `reason` `tool_name` and `verdict` `unmintable_segment`), or a rules body of the wrong shape (including the retired `commandAllow` / `commandDeny` buckets). The sentence carries the record position prefix `policy: [i].rules: ` and then the
|
|
3288
|
+
engine's own sentence; match on the code. `rules.legacy_tool_name` is retired with no alias (server 7.105.0–7.128.x sent it) — it is replaced by `rules.inert_entry` (its `name` key is now `entry`).
|
|
3289
|
+
On server <= 7.128.x the shape problems answered `400 request.field_invalid` and a record with a command bucket was staged.
|
|
3273
3290
|
content:
|
|
3274
3291
|
application/json:
|
|
3275
3292
|
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
@@ -3646,12 +3663,13 @@ paths:
|
|
|
3646
3663
|
render it as "this card can only be denied / wait for a server upgrade". Not a regression: 7.31.x children
|
|
3647
3664
|
never had those tools.
|
|
3648
3665
|
🔴 THE TWO CAUSES ARE TOLD APART BY `cause` (server S-818, riding core #1161; ADDITIVE, optional): the body may
|
|
3649
|
-
carry `cause` — core's CAUSE WORD for this startup failure, relayed
|
|
3650
|
-
(no
|
|
3651
|
-
#1161). The closed set is core's (#1161 exports it as
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
`
|
|
3666
|
+
carry `cause` — core's CAUSE WORD for this startup failure, relayed from the engine receipt's `details`
|
|
3667
|
+
(no default; absent when the engine did not say, i.e. every server before S-818 and every core before
|
|
3668
|
+
#1161). The closed set is core's (#1161; core 7.51.0 exports it as `RESUME_STARTUP_CAUSES`); since server >= 7.129.0
|
|
3669
|
+
the key is one of those five words (`ResumeStartupCause`: `environment` / `session_gone` / `tool_unavailable` / `configuration` / `decision_rejected`)
|
|
3670
|
+
— the server screens it, a word outside the set is dropped; servers <= 7.128.x relay the word unscreened. Absent ⇒ "cannot tell": offer both
|
|
3671
|
+
ways out. SDK → `ParkedResumeStartupError.startupCause` (23.0.0: `ResumeStartupCause`; `undefined` when absent, not a string, or outside the five words — the raw value stays at `err.extra.cause`;
|
|
3672
|
+
the wire key stays `cause` — the SDK does not put it on the standard `Error.cause`, which means a chained error).
|
|
3655
3673
|
content:
|
|
3656
3674
|
application/json:
|
|
3657
3675
|
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
@@ -7144,7 +7162,7 @@ components:
|
|
|
7144
7162
|
name: { type: string, description: 'the `subagent_type` the model passes to Task. Non-empty, ≤64 chars, UNIQUE per request.' }
|
|
7145
7163
|
whenToUse: { type: string, description: 'when the model should delegate to this agent (shown in the Task tool''s agent list). ≤4096 chars.' }
|
|
7146
7164
|
whenToUseLean: { type: string, description: 'compact variant of `whenToUse` for lean tool listings. ≤4096 chars.' }
|
|
7147
|
-
allowTools: { type: array, items: { type: string }, description: 'allowlist of tool names. Omit ⇒ inherit ALL tools. server >= 7.126.0: the names are judged by the ENGINE, one selector shared with the root run''s `allowTools` / `excludeTools`; a name no tool answers to => the run is rejected with the terminal code `config.agent.unknown_tool` (not an HTTP code).' }
|
|
7165
|
+
allowTools: { type: array, items: { type: string }, description: 'allowlist of tool names. Omit ⇒ inherit ALL tools. server >= 7.126.0: the names are judged by the ENGINE, one selector shared with the root run''s `allowTools` / `excludeTools`; a name no tool answers to => the run is rejected with the terminal code `config.agent.unknown_tool` (not an HTTP code) on server 7.126.0–7.128.x, and RUNS AS WRITTEN on server >= 7.129.0 (the entry is kept, an operator row `runner_config_advisory` announces it once; `denyTools: ["bash"]` does not deny `Bash`).' }
|
|
7148
7166
|
denyTools: { type: array, items: { type: string }, description: 'denylist (SUBTRACTIVE — applied after `allowTools`). server >= 7.126.0: same engine-judged selector as `allowTools`; `Tool(content)` => the whole tool.' }
|
|
7149
7167
|
skills:
|
|
7150
7168
|
type: array
|
|
@@ -7667,7 +7685,7 @@ components:
|
|
|
7667
7685
|
🔴 server >= 7.118.0: the NAME layer of the allow side moved to the engine — a retired name, an unprefixed short name or an unmintable protocol name is ACCEPTED here (no 422) and the run opens with an
|
|
7668
7686
|
`engine_notice` `permission.allowed_tool_not_granted` (`entries[].reason: tool_name`); the `deny` / `ask` buckets still refuse that family with a whole-request 422. 🔴 server >= 7.123.0 (core 7.48.0) also accepts a
|
|
7669
7687
|
protocol name with an EMPTY TOOL SEGMENT (`mcp__<server>__`, `a2a__<peer>__`) in this bucket (server 7.118.0–7.122.x refused it on every table). One name, three outcomes:
|
|
7670
|
-
allow side accepts + notice, deny / ask side refuses, session-rule write doors answer `400 rules.
|
|
7688
|
+
allow side accepts + notice, deny / ask side refuses, session-rule write doors answer `400 rules.inert_entry` (server >= 7.129.0; `reason: tool_name`, `verdict: unmintable_segment`; 7.123.0–7.128.x answered a retired code, named in the 400 of `PUT /v1/sessions/{sessionId}/policy`).
|
|
7671
7689
|
🔴 THE 422 SENTENCE (server >= 7.117.0): `error` starts with `request settings cannot be applied as written: ` (<= 7.116.x: a different sentence that said tighten-only — all three
|
|
7672
7690
|
buckets share this exit, and the old head misled a caller writing a tightening rule and lied about the allow bucket). Do NOT dispatch on the sentence: it carries no code of its own, so
|
|
7673
7691
|
`errorCode` is the status-derived coarse code `request.unprocessable`.
|
|
@@ -7805,8 +7823,9 @@ components:
|
|
|
7805
7823
|
items: { type: string }
|
|
7806
7824
|
description: >
|
|
7807
7825
|
Tool names to withhold from this task's roster. server >= 7.126.0 (engine 7.49.0, contract G.9e): read with the SAME grammar as `allowTools`; a name that
|
|
7808
|
-
NO tool answers to rejects the run before it starts with `config.tool_face_control` (sync leg 400, no token burned; streamed / background legs: a `failed` terminal with
|
|
7809
|
-
the same code)
|
|
7826
|
+
NO tool answers to rejects the run before it starts with `config.tool_face_control` on server 7.126.0–7.128.x (sync leg 400, no token burned; streamed / background legs: a `failed` terminal with
|
|
7827
|
+
the same code); server >= 7.129.0 RUNS it as written (the entry is kept and takes effect if a tool of that name is mounted later; an operator row `runner_config_advisory` announces it once, with the spelling to write —
|
|
7828
|
+
names are read verbatim, so `excludeTools: ["bash"]` does not withhold `Bash`). `"*"` is rejected on this list too (unmount-everything is only `excludeAllTools`).
|
|
7810
7829
|
allowTools:
|
|
7811
7830
|
type: array
|
|
7812
7831
|
items: { type: string }
|
|
@@ -9586,6 +9605,13 @@ components:
|
|
|
9586
9605
|
additionalProperties: true
|
|
9587
9606
|
properties:
|
|
9588
9607
|
sideQuery: { type: boolean, description: 'POST /v1/side-query one-shot brain-routed Q&A face is wired (probe before use, no trial-by-501).' }
|
|
9608
|
+
inherit_env.own_credentials_withheld:
|
|
9609
|
+
type: boolean
|
|
9610
|
+
description: >-
|
|
9611
|
+
ENGINE CAPABILITY WORD (core `ENGINE_CAPABILITIES`, core 7.51.1; the key IS the word, dotted, value always `true` when present; sdk 23.0.0 `ENGINE_CAPABILITY_WORDS` derives the typed key from the engine's list):
|
|
9612
|
+
a child process spawned on this host NEVER inherits a name the deployment declared as its own credential — every `inheritEnv` policy (`"all"` and `"scrub"` alike) subtracts that list first, and an allow list naming one is
|
|
9613
|
+
refused. It speaks of INHERITANCE only (the engine process's own environment stays readable to same-user processes on the host; a credential that never enters it remains the root fix).
|
|
9614
|
+
ABSENT = an old server (<= 7.128.x) or a word this deployment does not wire: read it as "not promised", not as "absent capability". A shell gates on the word, never on a version number.
|
|
9589
9615
|
asyncRuns: { type: boolean }
|
|
9590
9616
|
artifacts: { type: boolean }
|
|
9591
9617
|
approvals: { type: boolean }
|
|
@@ -10089,7 +10115,7 @@ components:
|
|
|
10089
10115
|
name / unprefixed short name / unmintable protocol server segment => 422, at most 1000 entries => 400) — never the content.
|
|
10090
10116
|
🔴 server >= 7.118.0: the allow side's NAME layer moved to the engine (retired / short / unmintable names are accepted there and announced with `engine_notice` `permission.allowed_tool_not_granted`; `deny` / `ask` still 422);
|
|
10091
10117
|
server >= 7.123.0 also accepts a protocol name with an EMPTY TOOL SEGMENT (`mcp__<server>__`) on the allow side (server 7.118.0–7.122.x refused it on every table):
|
|
10092
|
-
allow side accepts + notice, deny / ask side refuses, session-rule write doors answer `400 rules.
|
|
10118
|
+
allow side accepts + notice, deny / ask side refuses, session-rule write doors answer `400 rules.inert_entry` (server >= 7.129.0; 7.123.0–7.128.x answered a retired code, named in the 400 of `PUT /v1/sessions/{sessionId}/policy`).
|
|
10093
10119
|
🔴 ABSENT `allowLayer` with `allowGrant` present = a 7.115.0–7.116.x server: allow is a STATEMENT (it does not clear the engine's own write gate / shell checks; a content form
|
|
10094
10120
|
is a whole-request 422). ABSENT KEY altogether (no `settings`) = an older server (<= 7.114.x), which read `allow` as a WHITELIST (everything off the list refused) — do not put
|
|
10095
10121
|
anything in the allow bucket there. NOT the same bit as `taskSettings.permissions` (unchanged, still a boolean: "this server accepts the `settings.permissions` face"). The
|
|
@@ -10749,7 +10775,7 @@ components:
|
|
|
10749
10775
|
`agents[].skills[]` judge identically). Probe `capabilities.skillToolLists`.
|
|
10750
10776
|
🔴 The name-layer 400s above hold for server 7.116.x–7.117.x only. server >= 7.118.0: the allow side's name layer moved to the engine — a retired name / unprefixed short name / unmintable protocol name is ACCEPTED here and the run opens with an
|
|
10751
10777
|
`engine_notice` `permission.allowed_tool_not_granted` (`entries[].reason: tool_name`); server >= 7.123.0 also accepts a protocol name with an EMPTY TOOL SEGMENT (`mcp__<server>__`, `a2a__<peer>__`) here (server 7.118.0–7.122.x refused it).
|
|
10752
|
-
One name, three outcomes: allow side accepts + notice, deny / ask side refuses, session-rule write doors answer `400 rules.
|
|
10778
|
+
One name, three outcomes: allow side accepts + notice, deny / ask side refuses, session-rule write doors answer `400 rules.inert_entry` (server >= 7.129.0; 7.123.0–7.128.x answered a retired code, named in the 400 of `PUT /v1/sessions/{sessionId}/policy`; `disallowedTools` below is a deny side).
|
|
10753
10779
|
disallowedTools:
|
|
10754
10780
|
type: array
|
|
10755
10781
|
items: { type: string }
|
|
@@ -10765,7 +10791,7 @@ components:
|
|
|
10765
10791
|
409 `resume_blocked_by_policy` (`blockedBy: "request.field_invalid"`, same sentence) BEFORE the claim — row and card untouched; a terminal run's `POST /v1/runs/{taskId}/steer`
|
|
10766
10792
|
answers it before minting the wake card. Why: engine 7.41 trimmed whitespace (`"Bash "` was a working deny), engine 7.42 reads verbatim (reads as nothing). The allow side does not refuse.
|
|
10767
10793
|
🔴 A protocol name with an EMPTY TOOL SEGMENT (`mcp__<server>__`, `a2a__<peer>__`) is refused here (server >= 7.123.0: `error` is the engine's entry-reading sentence, `... reads as nothing (tool_name) ...`), while `allowedTools` accepts it from 7.123.0 —
|
|
10768
|
-
one name, three outcomes: allow side accepts + notice, deny / ask side refuses, session-rule write doors answer `400 rules.
|
|
10794
|
+
one name, three outcomes: allow side accepts + notice, deny / ask side refuses, session-rule write doors answer `400 rules.inert_entry` (server >= 7.129.0; 7.123.0–7.128.x answered a retired code, named in the 400 of `PUT /v1/sessions/{sessionId}/policy`).
|
|
10769
10795
|
|
|
10770
10796
|
McpServerSpec:
|
|
10771
10797
|
type: object
|
|
@@ -11735,22 +11761,35 @@ components:
|
|
|
11735
11761
|
# 把 `AskOrigin` 改真开集的同一条判据)。core 的 `routePairingStatus` 把 `verdict.posture` **直接内插**
|
|
11736
11762
|
# 进 `ok:${…}`,server 的铸点与 core 的这只函数**全路径没有一处运行期成员判定** —— 一只自带
|
|
11737
11763
|
# `adjudicateRoute` 的 brain 供什么词就上什么词。写一条会执法的 enum = 假闭集,会把一台合法 worker
|
|
11738
|
-
#
|
|
11764
|
+
# 的真响应判违约。已知六词写在 description 里(消费端 switch 认已知词 + 一条 default 臂)。
|
|
11739
11765
|
type: string
|
|
11740
11766
|
x-open-enum: true
|
|
11741
11767
|
description: >-
|
|
11742
11768
|
A model row's key<->URL pairing posture (core `RoutePairingStatus`; server >= 7.45). OPEN on read —
|
|
11743
|
-
the
|
|
11769
|
+
the six words below are today's set, branch on the known ones and keep a `default` arm. NON-SECRET — no
|
|
11744
11770
|
URL, no credential.
|
|
11745
11771
|
`ok:per-model` this model has its own credential · `ok:paired` deployment credential, and the model's URL
|
|
11746
|
-
is on the deployment's declared root · `ok:
|
|
11747
|
-
root, so the pairing is UNVERIFIABLE · `ok:keyless` no credential anywhere (a local unauthenticated
|
|
11772
|
+
is on the deployment's declared root · `ok:keyless` no credential anywhere (a local unauthenticated
|
|
11748
11773
|
gateway) · `broken:credential_mismatch` / `broken:credential_missing` a real request WILL be refused by
|
|
11749
11774
|
the engine (flag it red) · `unknown` could not be adjudicated — render "unknown", NEVER "good" or "bad".
|
|
11775
|
+
🔴 sdk 23.0.0 BREAKING (server >= 7.129.0 / engine 7.51.0): `ok:unpinned` (a deployment credential on a deployment that declared no root, so the pairing was unverifiable) is RETIRED with no alias —
|
|
11776
|
+
on such a deployment the engine now judges the deployment credential `broken:credential_mismatch`. A server <= 7.128.x still sends `ok:unpinned`: it falls in the open tail, render it as
|
|
11777
|
+
"pairing not verified", never as good.
|
|
11750
11778
|
The verdict's frame of reference is the gateway root as of BRAIN CONSTRUCTION, so a live re-pointing of
|
|
11751
11779
|
the deployment root only shows up after a restart (both this reading and the real request judge from the
|
|
11752
11780
|
same snapshot).
|
|
11753
11781
|
|
|
11782
|
+
ResumeStartupCause:
|
|
11783
|
+
type: string
|
|
11784
|
+
enum: [environment, session_gone, tool_unavailable, configuration, decision_rejected]
|
|
11785
|
+
description: >-
|
|
11786
|
+
WHY a parked resume could not start its approved action — the engine's closed `RESUME_STARTUP_CAUSES` (core 7.51.0), word for word and in order. The body key of the 422 `parked_resume.startup_failed`
|
|
11787
|
+
(`ErrorResponse.cause`; SDK `ParkedResumeStartupError.startupCause`). Server >= 7.129.0 screens the engine's word against this set before it goes out (a word the engine did not say, or one outside the set => the key is absent).
|
|
11788
|
+
`environment` the leg's infrastructure failed (workspace restore, an abort) — the decision is untouched, retrying replays it · `session_gone` the resumed agent's transcript session is missing ·
|
|
11789
|
+
`tool_unavailable` the approved tool is not available to the resumed leg (gone, or replaced by a different contract) — this approval cannot run, decide afresh · `configuration` the deployment's
|
|
11790
|
+
configuration refuses the resumed leg; a retry under the same configuration fails the same way · `decision_rejected` the decision does not fit the checkpoint (another gate's answer, an input the constraints
|
|
11791
|
+
refuse) — correct it and resubmit. The shared `ErrorResponse.cause` stays an open string in this document because the key is shared by other codes and by servers <= 7.128.x (which relay the engine's word unscreened).
|
|
11792
|
+
|
|
11754
11793
|
ElicitResponse:
|
|
11755
11794
|
type: object
|
|
11756
11795
|
description: >
|
|
@@ -12716,6 +12755,7 @@ components:
|
|
|
12716
12755
|
seq: { type: integer, description: 'Stop-cycle counter (revive second cycle = higher seq); absent on older engines.' }
|
|
12717
12756
|
status: { type: string, enum: [completed, failed, killed] }
|
|
12718
12757
|
summary: { type: string }
|
|
12758
|
+
result: { type: string, description: "server >= 7.129.0 (K-86), the notification frame ONLY (row frames never carry it): the background child's report, the same string the parent model reads in the completion frame (the engine redacts it and truncates it once at the frame's cap; the server redacts again at its own wire boundary, like `summary`). ABSENT = no report. Additive / tolerate-absent." }
|
|
12719
12759
|
scope: { type: string, enum: [task, session], description: 'Background LIFETIME scope (design/129), NOT the tenant scope.' }
|
|
12720
12760
|
parentTaskId: { type: string }
|
|
12721
12761
|
stoppedBy: { type: string, description: 'Verbatim open enum (who/what stopped a killed child).' }
|
|
@@ -13264,7 +13304,10 @@ components:
|
|
|
13264
13304
|
description: 'server >= 7.126.0 (core 7.49.0 #1414): which card this row renders on — the engine-minted `gate.approvalCard` (closed eight-word set; the park-row twin seat reads the same word core''s `summarizeCheckpoint` projects onto `CheckpointSummary.approvalCard`; an out-of-set word reads as absent and is counted), the same word as the live frame / `livePending` row. It REPLACES `contentKind` (deleted at 7.126.0, no alias). ABSENT = the engine minted none: a row parked before engine 7.49.0 (core never derives a card from the tool name: no card = not a question, so an old question row carries no card word and is swept by the SLA at its deadline), a non-approval gate, or an older engine. Display / triage only; NEVER part of resume / gate / CAS; the inbox row does not carry it.'
|
|
13265
13305
|
ruleOffersAbsence:
|
|
13266
13306
|
allOf: [{ $ref: '#/components/schemas/RuleOffersAbsence' }]
|
|
13267
|
-
description: 'server >= 7.100.0 (S-700): WHY the park row offers no rule — the same narrow read and closed
|
|
13307
|
+
description: 'server >= 7.100.0 (S-700): WHY the park row offers no rule — the same narrow read and closed set (four words from server >= 7.129.0) as the live card frame and `card_json`.'
|
|
13308
|
+
boundGrantAbsence:
|
|
13309
|
+
allOf: [{ $ref: '#/components/schemas/BoundGrantAbsence' }]
|
|
13310
|
+
description: 'server >= 7.129.0 (core 7.51.0 #1688; park-row fact eight): why this parked card has no grant-binding digest — the park-row twin seat `PendingAction.tool_approval.boundGrantAbsence`, the same narrow read and the same name / value as the live frame and the inbox row. ABSENT on this face = a skill card (it carries the digest; the digest itself is not on this face) / a row parked before engine 7.51.0 (says neither — the inbox face projects `unreadable` for the same row) / a word outside the set / an old server — NOT `not_bindable`.'
|
|
13268
13311
|
askClass:
|
|
13269
13312
|
allOf: [{ $ref: '#/components/schemas/AskClass' }]
|
|
13270
13313
|
description: 'server >= 7.109.0 (S-934 ② / core 7.37.0 #1214): the park-row twin seat `PendingAction.tool_approval.askClass`, read through `readAskClassPair` — the SAME narrow read as the live frame and the card. Present together with `classes` (`classes[0] === askClass`); the basis a durable approver answers by. Absent = an old row (parked before core 7.37.0; resume does not backfill) or an unreadable pair — NOT "no class". The inbox row (InboxRow) does not carry it.'
|
|
@@ -13411,6 +13454,9 @@ components:
|
|
|
13411
13454
|
boundGrants:
|
|
13412
13455
|
allOf: [{ $ref: '#/components/schemas/BoundSkillGrants' }]
|
|
13413
13456
|
description: 'server >= 7.118.0 — the value `boundGrantDigest` hashes, published ONLY when it hashes to it (the engine judges; a pair that disagrees reads as absent). DEPLOYMENT TEXT: render with contextual escaping.'
|
|
13457
|
+
boundGrantAbsence:
|
|
13458
|
+
allOf: [{ $ref: '#/components/schemas/BoundGrantAbsence' }]
|
|
13459
|
+
description: 'server >= 7.129.0 (core 7.51.0 #1688) — why this card has no `boundGrantDigest` (exactly one of the two; the engine''s summary is total over tool-approval rows — a row parked before engine 7.51.0, which says neither, reads `unreadable` here). Same name and value on the live frame and the operator queue row.'
|
|
13414
13460
|
classifierNoRuling:
|
|
13415
13461
|
allOf: [{ $ref: '#/components/schemas/AutoModeFallbackCause' }]
|
|
13416
13462
|
description: 'server >= 7.118.0 — only on a parked `classifier_no_ruling` card: why the auto-mode classifier did not rule.'
|
|
@@ -13893,14 +13939,18 @@ components:
|
|
|
13893
13939
|
type: object
|
|
13894
13940
|
description: >
|
|
13895
13941
|
E6 (shell-host permissionMode; core SessionPermissionRules session-policy-store.ts) — operator-tightened
|
|
13896
|
-
per-session tool-permission rules (core reads them at prepare-time, SUBTRACT-only).
|
|
13897
|
-
|
|
13898
|
-
|
|
13899
|
-
|
|
13900
|
-
|
|
13942
|
+
per-session tool-permission rules (core reads them at prepare-time, SUBTRACT-only). THREE optional `string[]`
|
|
13943
|
+
buckets. tighten-only invariant is core-enforced (a loosen needs an operator).
|
|
13944
|
+
🔴 server >= 7.129.0 (engine 7.51.0): ONE grammar. `toolAllow` / `toolDeny` hold rule ENTRIES — a tool name, `mcp__<server>`,
|
|
13945
|
+
or a rule in the tool's own grammar such as `Bash(git push:*)` / `Edit(src/**)`; `toolDeny: ["Bash(rm:*)"]` denies the command name `rm` in any segment of a connected command line.
|
|
13946
|
+
The command buckets `commandAllow` / `commandDeny` are RETIRED (sdk 23.0.0 deletes them, no alias): writing one answers `400 rules.invalid_shape` (never silently ignored — an ignored
|
|
13947
|
+
deny is a deny nobody enforces); `commandDeny: ["rm"]` becomes `toolDeny: ["Bash(rm:*)"]` (which does not deny a path-spelled `/bin/rm ...` nor a command line that does not read as one command name — deny the whole
|
|
13948
|
+
shell with `toolDeny: ["Bash"]` for those); `commandAllow` has no counterpart. A stored SQL row (written by server <= 7.128.x) that still holds a command bucket reads back as `500 corrupt`
|
|
13949
|
+
on this deployment (the operator rewrites that row to the new grammar or deletes the session). An entry the engine reads as nothing answers `400 rules.inert_entry`.
|
|
13950
|
+
properties:
|
|
13951
|
+
toolAllow: { type: array, items: { type: string }, description: 'Narrowing allowlist (only these entries allowed) — rule entries, see the schema description.' }
|
|
13952
|
+
toolDeny: { type: array, items: { type: string }, description: 'Always-denied entries (deny wins) — a tool name, `mcp__<server>`, or a rule such as `Bash(rm:*)`.' }
|
|
13901
13953
|
allowDirs: { type: array, items: { type: string }, description: Write tools confined to these dirs; bash denied while set. }
|
|
13902
|
-
commandAllow: { type: array, items: { type: string }, description: "Only these bash command names (argv[0]) allowed." }
|
|
13903
|
-
commandDeny: { type: array, items: { type: string }, description: "Always-denied bash command names (argv[0])." }
|
|
13904
13954
|
|
|
13905
13955
|
StoredSessionRules:
|
|
13906
13956
|
description: '{@link SessionPermissionRules} + the monotonic `rev` the store stamps (the OCC key).'
|
|
@@ -13917,22 +13967,32 @@ components:
|
|
|
13917
13967
|
server >= 7.92.0 (engine 7.25.0): the session's rule LINEAGE generation. An erase
|
|
13918
13968
|
(`deleteBySession`) ends the lineage — the next write carries gen+1 — so a reader treats a
|
|
13919
13969
|
different gen as a new lineage, never a continuation, regardless of rev. Absent on older servers.
|
|
13970
|
+
grammar:
|
|
13971
|
+
type: integer
|
|
13972
|
+
minimum: 0
|
|
13973
|
+
description: >
|
|
13974
|
+
server >= 7.129.0 (engine 7.51.2): the rule GRAMMAR the row's entries are read in, stamped by the
|
|
13975
|
+
engine's write judge on every row written through it (every row this server writes carries `1`).
|
|
13976
|
+
Absent = a row stored before the stamp (grammar 0: an allow entry spelled as a rule, `Bash(rm:*)`,
|
|
13977
|
+
named no tool and admitted nothing — the engine keeps reading such a row that way and announces it
|
|
13978
|
+
once; the one-time migration rewrites stored rows and stamps them). Echoing a stored body back into
|
|
13979
|
+
PUT with its `rev` but no `grammar` is read as grammar 0 (`400 rules.inert_entry`, reason
|
|
13980
|
+
`earlier_grammar`); a `grammar` newer than the engine's answers `400 rules.invalid_shape` (field `grammar`).
|
|
13920
13981
|
# 🔴 封闭 + 本地镜像(census 批2 第二段,2026-07-30)。同 Event_reasoning 处那条长注的病:
|
|
13921
13982
|
# `additionalProperties` 只结算**同一个 subschema** 的 `properties`,`allOf` 分支($ref
|
|
13922
13983
|
# SessionPermissionRules + 内联的 `rev`)声明的键不在它作用域内 ⇒ 只写 `false` 会把合法的
|
|
13923
13984
|
# `toolAllow`/`rev` 判成违约。镜像的一致性由 spec.test.ts 的通用「封闭 allOf 臂必须镜像被引
|
|
13924
13985
|
# schema 全部键」门看守(以后往 SessionPermissionRules 加键,忘了同步镜像就当场红)。
|
|
13925
|
-
# 为什么值得封闭:
|
|
13926
|
-
#
|
|
13986
|
+
# 为什么值得封闭:GET 腿回的是 store 原样 —— 「store 多回一个键」正是本门要抓的那类漂(消费方按 spec 生成的类型会漏掉它)。
|
|
13987
|
+
# server >= 7.129.0 规则体只剩三只桶(命令桶退役,带命令桶的 SQL 存量行读回 500,不会出现在 200 体上)。
|
|
13927
13988
|
additionalProperties: false
|
|
13928
13989
|
properties:
|
|
13929
13990
|
toolAllow: { type: array, items: { type: string } }
|
|
13930
13991
|
toolDeny: { type: array, items: { type: string } }
|
|
13931
13992
|
allowDirs: { type: array, items: { type: string } }
|
|
13932
|
-
commandAllow: { type: array, items: { type: string } }
|
|
13933
|
-
commandDeny: { type: array, items: { type: string } }
|
|
13934
13993
|
rev: { type: integer }
|
|
13935
13994
|
gen: { type: integer }
|
|
13995
|
+
grammar: { type: integer }
|
|
13936
13996
|
|
|
13937
13997
|
McpServerStatus:
|
|
13938
13998
|
type: object
|
|
@@ -14616,10 +14676,10 @@ components:
|
|
|
14616
14676
|
type: string
|
|
14617
14677
|
description: >-
|
|
14618
14678
|
server S-818 (riding core #1161), on `parked_resume.startup_failed` (422) ONLY: core's CAUSE WORD for the parked
|
|
14619
|
-
resume's startup failure, relayed
|
|
14620
|
-
|
|
14621
|
-
|
|
14622
|
-
SDK reads it as `ParkedResumeStartupError.startupCause
|
|
14679
|
+
resume's startup failure, relayed (never defaulted; absent when the engine did not say). From server >= 7.129.0 it is
|
|
14680
|
+
one of the engine's closed `RESUME_STARTUP_CAUSES` (core 7.51.0; schema `ResumeStartupCause` — the server screens it, a word outside the set is dropped);
|
|
14681
|
+
server <= 7.128.x relays the engine's word unscreened (an OPEN string — hence the plain `string` here). Absent ⇒ cannot tell which cause (see the
|
|
14682
|
+
`/v1/approvals/{sessionId}/decide` 422 text). SDK reads it as `ParkedResumeStartupError.startupCause` (23.0.0: the five-word `ResumeStartupCause`; a word outside it reads as `undefined`, the raw value stays at `err.extra.cause`).
|
|
14623
14683
|
remaining:
|
|
14624
14684
|
type: array
|
|
14625
14685
|
items: { type: string }
|
|
@@ -14636,7 +14696,7 @@ components:
|
|
|
14636
14696
|
unreadable) / `unreachable` (unreachable / 5xx / timeout / bad body / any status other than 200 / 409 / 400 / 401 / 403 —
|
|
14637
14697
|
center 404 / 429 / 3xx included). On 400 `request.field_invalid` for a `settings.permissions` bucket (server >= 7.106.0 /
|
|
14638
14698
|
S-904): `over_cap` (more than `cap` entries; beside `count` + `cap`) / `bad_entry` (a non-string or empty item; beside
|
|
14639
|
-
`index`). Out-of-vocabulary word ⇒ treat as unknown. (Other codes carry their own `reason` words
|
|
14699
|
+
`index`). 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`). Out-of-vocabulary word ⇒ treat as unknown. (Other codes carry their own `reason` words.)
|
|
14640
14700
|
retryAfterSec:
|
|
14641
14701
|
type: number
|
|
14642
14702
|
description: >-
|
|
@@ -14648,8 +14708,8 @@ components:
|
|
|
14648
14708
|
type: string
|
|
14649
14709
|
description: >-
|
|
14650
14710
|
On 400 `request.field_invalid` for a `settings.permissions` bucket (server >= 7.106.0 / S-904): which bucket —
|
|
14651
|
-
`settings.permissions.allow` | `settings.permissions.deny` | `settings.permissions.ask`. (`rules.*`
|
|
14652
|
-
carry their own `field`.) Resume legs carry the same group on 409 `resume_blocked_by_policy`
|
|
14711
|
+
`settings.permissions.allow` | `settings.permissions.deny` | `settings.permissions.ask`. (`rules.*` refusals
|
|
14712
|
+
carry their own `field`: the bucket on `rules.inert_entry`, the offending key on `rules.invalid_shape`.) Resume legs carry the same group on 409 `resume_blocked_by_policy`
|
|
14653
14713
|
(`blockedBy: "request.field_invalid"`). Also on 409 `checkpoint.config_refused` (server >= 7.113.0): which part of the
|
|
14654
14714
|
row's configuration was refused — `taskConfig` (change the configuration, then decide the SAME card again) or
|
|
14655
14715
|
`inheritedGate` (the row's recorded mode seed is bad — the run can only be cancelled). Also on 400 `request.field_invalid` for a
|
|
@@ -14671,7 +14731,7 @@ components:
|
|
|
14671
14731
|
are unchanged): an OBJECT `{ event, alias, canonical }` — `event` = the tool event of the refused group (the first refused one when
|
|
14672
14732
|
several are), `alias` / `canonical` = the engine catalog's alias and canonical name (closed strings, never the caller's raw bytes),
|
|
14673
14733
|
beside `field: "settings.hooks"`. On 400 `request.field_invalid` for the two `POST /v1/adoption` principal sentences (adoption):
|
|
14674
|
-
a human-readable STRING. Other codes (`rules.*`
|
|
14734
|
+
a human-readable STRING. Other codes (core `checkpoint.*` mirrors; `rules.*` refusals carry no `detail` — their keys are `field` / `entry` / `reason` / `verdict` / `currentName` / `guidance` / `got` at the top level) carry their
|
|
14675
14735
|
own `detail`; read it by `errorCode`. SDK: `err.extra.detail` (verbatim, unnamed).
|
|
14676
14736
|
oneOf:
|
|
14677
14737
|
- type: object
|
|
@@ -15053,9 +15113,8 @@ components:
|
|
|
15053
15113
|
persisted rules). It is the personal-store sibling of `org`: same VETO family (it can overturn an approval
|
|
15054
15114
|
a person already gave, and it re-judges an edited command), which is why it is neither `policy` nor
|
|
15055
15115
|
`ask_resolution` · `classifier` = the auto-mode classifier denied directly ·
|
|
15056
|
-
⚰️ `plan_mode`
|
|
15057
|
-
|
|
15058
|
-
support floor still send it (delete when the floor passes 7.106.0) · `mode` (server >= 7.106.0 / core 7.34.0) = the
|
|
15116
|
+
(⚰️ `plan_mode` — server <= 7.105.0 / core <= 7.33.x: plan mode's read-only block on a write tool, folded into `mode` by core 7.34.0 design/1111 — is NOT in this set since sdk 23.0.0: it is an
|
|
15117
|
+
ARCHIVE word of the gate record, see `GateRecordDeniedBy`) · `mode` (server >= 7.106.0 / core 7.34.0) = the
|
|
15059
15118
|
permission MODE refused a surviving ask on its own account — `dontAsk` turns an ask nobody can answer into a refusal,
|
|
15060
15119
|
`bypassPermissions` refuses a write whose protection could not be judged; the deny arm then carries `mode` / `askClass` /
|
|
15061
15120
|
`classes` (core invariant I8) · `compliance` = the compliance call-time
|
|
@@ -15064,11 +15123,48 @@ components:
|
|
|
15064
15123
|
`read_boundary` = the shell READ STATION refused (core 7.25.0 #945): a deny-listed read before any approval,
|
|
15065
15124
|
or an approved rewrite re-judged onto a deny row (beside `settlement.kind: "human_allowed"`); may veto, carries no `cause`.
|
|
15066
15125
|
🔴 CLOSED ON THIS WIRE, and deliberately not marked open: the engine's invariant screen
|
|
15067
|
-
(`screenGateOutcome`) treats
|
|
15068
|
-
record WHOLE (report + withhold).
|
|
15069
|
-
|
|
15070
|
-
`GateOutcome.origin`.
|
|
15071
|
-
enum: [policy, hook, org, persisted_rule, classifier,
|
|
15126
|
+
(`screenGateOutcome`) treats a word that NO engine generation ever minted as a DEFECT, and the service withholds a defective
|
|
15127
|
+
record WHOLE (report + withhold). Since server >= 7.129.0 (engine 7.51.0) the screen reads the gate record as an ARCHIVE:
|
|
15128
|
+
today's words PLUS the engine's retired words (`RETIRED_MEMBERS`) — `GateRecordDeniedBy` is that archive set; this schema is today's set
|
|
15129
|
+
(the request / card faces). Same rule for `Settlement.kind` and for `GateOutcome.origin`.
|
|
15130
|
+
enum: [policy, hook, org, persisted_rule, classifier, compliance, write_protection, ask_resolution, read_boundary, mode]
|
|
15131
|
+
|
|
15132
|
+
|
|
15133
|
+
# ── sdk 23.0.0 · server >= 7.129.0 (engine 7.51.0 `RETIRED_MEMBERS`): the GATE RECORD's archive sets ──────────────────────────────────────────
|
|
15134
|
+
# 门记录({@link GateOutcome}:`tool_end.gate`、turns / trace 回放、`GET /v1/runs/:id/events` 的账本裸回放)只过引擎的 `screenGateOutcome` 一次,缺省按**档案**读:
|
|
15135
|
+
# 每只闭集 = 引擎今天的词 ∪ 引擎退役词表里那一集。这五张 enum 与引擎 `RETIRED_MEMBERS` 逐集逐词同(由 `server-7129-wire` 对拍);卡面 / 队列行 / 请求面的同名闭集只取今天的词。
|
|
15136
|
+
GateRecordAskClass:
|
|
15137
|
+
type: string
|
|
15138
|
+
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]
|
|
15139
|
+
description: >-
|
|
15140
|
+
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:
|
|
15141
|
+
today's `AskClass` set (twenty-five words) PLUS the words the engine retired (`write_protection_unresolved`, retired in core 7.49.0 — renamed `target_unresolved`; `governance_unavailable`,
|
|
15142
|
+
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`);
|
|
15143
|
+
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`.
|
|
15144
|
+
Render a known archive word normally and an unknown one generically; never drop the record because the word is not in today's set.
|
|
15145
|
+
GateRecordDeniedBy:
|
|
15146
|
+
type: string
|
|
15147
|
+
enum: [policy, hook, org, persisted_rule, classifier, compliance, write_protection, ask_resolution, read_boundary, mode, plan_mode]
|
|
15148
|
+
description: >-
|
|
15149
|
+
`deniedBy` on a GATE RECORD (archive read, server >= 7.129.0): today's `DeniedBy` set PLUS the retired `plan_mode` (core 7.34.0 folded it into `mode`). Same reading as `GateRecordAskClass`.
|
|
15150
|
+
GateRecordModeReleaseReason:
|
|
15151
|
+
type: string
|
|
15152
|
+
enum: [target_unresolved, write_protection_unresolved, rule_unreadable]
|
|
15153
|
+
description: >-
|
|
15154
|
+
`released.reason` on a GATE RECORD (archive read, server >= 7.129.0): today's one word `target_unresolved` (core `MODE_RELEASE_REASONS`) PLUS the retired `write_protection_unresolved`
|
|
15155
|
+
(server 7.122.0–7.125.x spelling, retired in core 7.49.0) and `rule_unreadable` (server <= 7.126.x: the release went over an unreadable rule; retired in core 7.50.0). Server 7.127.0–7.128.x
|
|
15156
|
+
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`.
|
|
15157
|
+
GateRecordPersistedRuleMandate:
|
|
15158
|
+
type: string
|
|
15159
|
+
enum: [operator_always, tool_marks, probe_mandate, probe_unanswered, write_protection, target_unresolved, write_protection_unresolved]
|
|
15160
|
+
description: >-
|
|
15161
|
+
`released.mandate` on a GATE RECORD (archive read, server >= 7.129.0): today's six `PersistedRuleMandate` words PLUS the retired `write_protection_unresolved` (core 7.49.0 rename). Same reading as `GateRecordAskClass`.
|
|
15162
|
+
GateRecordAskOrigin:
|
|
15163
|
+
type: string
|
|
15164
|
+
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]
|
|
15165
|
+
description: >-
|
|
15166
|
+
`origin` on a GATE RECORD (`GateOutcome.origin`, `ModeRelease.origin`; archive read, server >= 7.129.0): core's twelve `ASK_ORIGINS` PLUS the retired `unresolvable` (core 7.14.0 renamed it `ancestor_marked`).
|
|
15167
|
+
🔴 CLOSED on this face — the engine judges membership here — unlike the OPEN `AskOrigin` of the approval-card faces (the server only checks a non-empty string there). Same reading as `GateRecordAskClass`.
|
|
15072
15168
|
|
|
15073
15169
|
Settlement:
|
|
15074
15170
|
description: >
|
|
@@ -15247,7 +15343,7 @@ components:
|
|
|
15247
15343
|
required: [kind, deniedBy]
|
|
15248
15344
|
properties:
|
|
15249
15345
|
kind: { const: denied }
|
|
15250
|
-
deniedBy: { $ref: '#/components/schemas/
|
|
15346
|
+
deniedBy: { $ref: '#/components/schemas/GateRecordDeniedBy' }
|
|
15251
15347
|
classifier:
|
|
15252
15348
|
allOf: [{ $ref: '#/components/schemas/GateClassifierAttribution' }]
|
|
15253
15349
|
description: >
|
|
@@ -15275,12 +15371,12 @@ components:
|
|
|
15275
15371
|
that refused. `dontAsk` turns an ask nobody can answer into a refusal; `bypassPermissions` refuses a write whose
|
|
15276
15372
|
protection could not be judged. `plan` refuses nothing here (its questions ask).
|
|
15277
15373
|
askClass:
|
|
15278
|
-
allOf: [{ $ref: '#/components/schemas/
|
|
15279
|
-
description: 'Beside `mode`: the refused ask''s disclosed class word.'
|
|
15374
|
+
allOf: [{ $ref: '#/components/schemas/GateRecordAskClass' }]
|
|
15375
|
+
description: 'Beside `mode`: the refused ask''s disclosed class word (gate-record archive read — a 7.50.x mode refusal can carry the retired `governance_unavailable`).'
|
|
15280
15376
|
classes:
|
|
15281
15377
|
type: array
|
|
15282
|
-
items: { $ref: '#/components/schemas/
|
|
15283
|
-
description: 'Beside `mode`: the whole folded class set.'
|
|
15378
|
+
items: { $ref: '#/components/schemas/GateRecordAskClass' }
|
|
15379
|
+
description: 'Beside `mode`: the whole folded class set (archive read, see `GateRecordAskClass`).'
|
|
15284
15380
|
|
|
15285
15381
|
PermissionMode:
|
|
15286
15382
|
type: string
|
|
@@ -15293,9 +15389,9 @@ components:
|
|
|
15293
15389
|
`UNATTENDED_PERMISSION_MODE=dontAsk`).
|
|
15294
15390
|
AskClass:
|
|
15295
15391
|
type: string
|
|
15296
|
-
enum: [content_question, hook, ancestor, dangerous_delete, unverifiable_delete, unverifiable_shell_text, transcript_write, org_rule,
|
|
15392
|
+
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]
|
|
15297
15393
|
description: >
|
|
15298
|
-
core `ASK_CLASSES` (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
|
|
15394
|
+
core `ASK_CLASSES` (TWENTY-FIVE words 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
|
|
15299
15395
|
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
|
|
15300
15396
|
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
|
|
15301
15397
|
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
|
|
@@ -15307,6 +15403,7 @@ components:
|
|
|
15307
15403
|
it: no mode (`bypassPermissions` included), no allow (request `settings.permissions.allow`, operator `APPROVAL_GRANT`, a stored allow row, a
|
|
15308
15404
|
skill's tool list, a hook's allow) and no `auto` classifier does; the frame carries `requiresRealApproval: true`, nobody answering means the
|
|
15309
15405
|
call does not run (the unattended leg refuses), and it is bypass-IMMUNE (not a releasable class).
|
|
15406
|
+
`rule_unreadable` (core 7.51.0 / server >= 7.129.0 also covers the organization snapshot that could not be read — the former `governance_unavailable`) = a rule that may govern this call could not be READ (the organization's snapshot, a partition of the person's own rule store, or the rule this call was read against); bypass-immune.
|
|
15310
15407
|
`unverifiable_shell_text` (core 7.49.0 #1505, server >= 7.126.0) = the engine CANNOT READ the text this shell will execute — the shell is fed
|
|
15311
15408
|
text the command does not spell (a program's output, a substitution, a file's content, an argv a stream fills in: `bash -c "$CMD"`,
|
|
15312
15409
|
`eval "$(…)"`, `xargs sh -c …`; shell text that does not parse or is nested too deep lands here too). Its arbitration row is
|
|
@@ -15334,7 +15431,7 @@ components:
|
|
|
15334
15431
|
`shell_floor` = the operator's `shellGate: "always"` floor on a shell-running seat (the question the `operator_always` mandate
|
|
15335
15432
|
word discloses), a class of its own so a set that holds another forced question still holds it; like `operator_forced` it is
|
|
15336
15433
|
bypass-IMMUNE (not releasable).
|
|
15337
|
-
CLOSED
|
|
15434
|
+
CLOSED: the card / queue-row / request faces carry exactly today's twenty-five words; the GATE RECORD (`tool_end.gate` and the replay faces) carries today's words PLUS the engine's retired words (`GateRecordAskClass`). `bypassPermissions` releases only the RELEASABLE classes (core
|
|
15338
15435
|
`releasableAskClassesFor(face)`). Server >= 7.127.0 (core 7.50.0): nine on every face (`transcript_write` / `probe` / `write_protection` /
|
|
15339
15436
|
`tool_marks_mcp` / `tool_marks_a2a` / `artifact_shared_read` / `mode` / `unstated` / `ordinary`) plus a tenth on the SHELL face only,
|
|
15340
15437
|
`target_unresolved` (shell 10, other faces 9; on the file tools that class is refused, `deniedBy: mode`); `rule_unreadable` is no longer released
|
|
@@ -15353,30 +15450,29 @@ components:
|
|
|
15353
15450
|
`permission.ask_released_by_mode` per run.
|
|
15354
15451
|
properties:
|
|
15355
15452
|
mode: { $ref: '#/components/schemas/PermissionMode' }
|
|
15356
|
-
askClass: { $ref: '#/components/schemas/
|
|
15357
|
-
classes: { type: array, items: { $ref: '#/components/schemas/
|
|
15453
|
+
askClass: { $ref: '#/components/schemas/GateRecordAskClass' }
|
|
15454
|
+
classes: { type: array, items: { $ref: '#/components/schemas/GateRecordAskClass' } }
|
|
15358
15455
|
origin:
|
|
15359
|
-
|
|
15360
|
-
|
|
15361
|
-
|
|
15362
|
-
|
|
15456
|
+
allOf: [{ $ref: '#/components/schemas/GateRecordAskOrigin' }]
|
|
15457
|
+
description: 'The origin word the card would have shown (core `ASK_ORIGINS`, archive read — screened closed on this face, same set as `GateOutcome.origin`).'
|
|
15458
|
+
mandate:
|
|
15459
|
+
allOf: [{ $ref: '#/components/schemas/GateRecordPersistedRuleMandate' }]
|
|
15460
|
+
description: 'The mandate word the card would have shown (today''s six `PersistedRuleMandate` words plus the retired spelling, archive read).'
|
|
15363
15461
|
reason:
|
|
15364
|
-
|
|
15365
|
-
enum: [target_unresolved]
|
|
15462
|
+
allOf: [{ $ref: '#/components/schemas/GateRecordModeReleaseReason' }]
|
|
15366
15463
|
description: >
|
|
15367
15464
|
WHAT the release went over — one word: the FIRST member (in core `MODE_RELEASE_REASONS` array order) that the ask's class
|
|
15368
|
-
set holds; ABSENT when the set holds none.
|
|
15369
|
-
|
|
15465
|
+
set holds; ABSENT when the set holds none. Today's set has ONE word, `target_unresolved` (core `MODE_RELEASE_REASONS`); the GATE RECORD reads the ARCHIVE form
|
|
15466
|
+
(server >= 7.129.0, `GateRecordModeReleaseReason`), so the engine's two retired words can also arrive verbatim on the replay faces.
|
|
15370
15467
|
`target_unresolved` = the release went over a write whose landing place could not be determined (a link on the way could not be examined,
|
|
15371
15468
|
or the environment could not resolve the target), so whether it lands on a guarded path is unknown. It is released on the SHELL
|
|
15372
15469
|
face only: a caller-supplied tool whose wire name is exactly `Bash` or `Monitor` and which declares a write-path target,
|
|
15373
15470
|
under `bypassPermissions`; on the file tools the same unresolved write is still refused (`deniedBy: mode`). The engine's
|
|
15374
15471
|
own `Bash` / `Monitor` cannot mint this class and the server mounts no such tool — it comes from a caller-embedded tool of
|
|
15375
15472
|
those names.
|
|
15376
|
-
History (
|
|
15377
|
-
server 7.122.0–7.125.x
|
|
15378
|
-
|
|
15379
|
-
unreadable rule under no mode, so that member left (`rule_unreadable` stays an `AskClass`). Before server 7.122.0 the set was `rule_unreadable` alone.
|
|
15473
|
+
History (sdk 22.0.0 narrowed this to one word; sdk 23.0.0 reads the archive form because server >= 7.129.0 replays the engine's retired words verbatim):
|
|
15474
|
+
the two retired words are the server 7.122.0–7.125.x spelling of today's word and, on server <= 7.126.x, `rule_unreadable` (the release went over a rule this call
|
|
15475
|
+
could not be read against) — core 7.50.0 / server >= 7.127.0 releases an unreadable rule under no mode (`rule_unreadable` stays an `AskClass`). Before server 7.122.0 the set was `rule_unreadable` alone.
|
|
15380
15476
|
|
|
15381
15477
|
GateClassifierAttribution:
|
|
15382
15478
|
# sdk 9.5.0:具名 —— 同一份形挂在 `GateDisposition` 的两只臂上,内联会立刻是两份会各自漂的镜像。
|
|
@@ -15455,16 +15551,13 @@ components:
|
|
|
15455
15551
|
allOf: [{ $ref: '#/components/schemas/Settlement' }]
|
|
15456
15552
|
description: 'Present only when THIS pass settled an ask. Same-present/same-absent as `origin` (I1).'
|
|
15457
15553
|
origin:
|
|
15458
|
-
allOf:
|
|
15459
|
-
|
|
15460
|
-
|
|
15461
|
-
|
|
15462
|
-
# 所以 `AskOrigin` 本体保持真开(见该 schema 的长注)。这张 enum 是本 spec 里该词表的**唯一**
|
|
15463
|
-
# 执法点;core 加词时改这一处。
|
|
15464
|
-
- 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]
|
|
15554
|
+
allOf: [{ $ref: '#/components/schemas/GateRecordAskOrigin' }]
|
|
15555
|
+
# 🔴 闭集**执法**只在门记录这一面:引擎的 `screenGateOutcome` 对 `origin` 判成员(server >= 7.129.0 按**档案**读:今天的十二词 ∪ 退役词 `unresolvable`),
|
|
15556
|
+
# 任何一代引擎都没铸过的词即记录缺陷,服务对缺陷记录是整条不上帧。审批帧那一面不判成员,所以 `AskOrigin` 本体保持真开(见该 schema 的长注)。
|
|
15557
|
+
# 闭集执法点 = `GateRecordAskOrigin` 这一张 enum(门记录的两个引用点 `GateOutcome.origin` / `ModeRelease.origin` 共用);core 加词时改这一处。
|
|
15465
15558
|
description: >
|
|
15466
|
-
WHO ASKED. Same-present/same-absent as `settlement` (I1). CLOSED here (
|
|
15467
|
-
|
|
15559
|
+
WHO ASKED. Same-present/same-absent as `settlement` (I1). CLOSED here (archive read, `GateRecordAskOrigin`) — see the
|
|
15560
|
+
note above.
|
|
15468
15561
|
|
|
15469
15562
|
McpProbeFace:
|
|
15470
15563
|
type: object
|
|
@@ -16593,8 +16686,9 @@ components:
|
|
|
16593
16686
|
`toolCallId` joins the `tool_approval` card / `tool_end`; `sessionId` is conditional. Not a block; not counted by
|
|
16594
16687
|
`classifier.denial_limit`. · `config.permission_mode_unavailable` — the requested mode is NOT the mode this leg runs,
|
|
16595
16688
|
`{ requested, effective, by, sessionId, runId, childSessionId? }`, `requested` / `effective` = core's six mode words, `by` = core
|
|
16596
|
-
`PERMISSION_MODE_FOLD_REASONS` (`operator_deny` / `face_absent` / `inherited_ceiling`
|
|
16597
|
-
session's stream
|
|
16689
|
+
`PERMISSION_MODE_FOLD_REASONS` (`operator_deny` / `face_absent` / `inherited_ceiling`); from core 7.51.0 / server >= 7.129.0 a child leg's fold on ANY of the three arms is ALSO minted onto the PARENT
|
|
16690
|
+
session's stream (`detail.sessionId` = the parent, `childSessionId` = the child; dedupe by `childSessionId`) and `requested` reads as "the word that was folded" (a child that requested nothing: the word it inherited);
|
|
16691
|
+
the first two arms always have `effective: default`; the server-side
|
|
16598
16692
|
`settings.permissions.disableAutoMode` fold happens before the engine and mints no frame. · `permission.ask_released_by_mode`
|
|
16599
16693
|
— `bypassPermissions` released an ask nobody answered, `{ mode, askClass, origin?, mandate?, count, firstToolCallId, sessionId, runId }`,
|
|
16600
16694
|
first time per (mode, askClass) per run (`count` is 1); the per-call record is `tool_end.gate.disposition.released`. ·
|
|
@@ -16619,6 +16713,12 @@ components:
|
|
|
16619
16713
|
switch itself became the WORD `"disabled"` instead of `null` (a string is not nullish, so the ordinary
|
|
16620
16714
|
coalesce is the whole resolution rule and no reader has to remember a null test). A consumer still
|
|
16621
16715
|
matching `task_store_null` matches nothing.
|
|
16716
|
+
server 7.129.0 (the user-visible whitelist grows 37 -> 38 codes; contract appendix D.3): `permission.entry_mode_clamped` (SERVER-minted, audience `user`, at most one frame per submission) — the operator's
|
|
16717
|
+
entry floor `AUTONOMY=plan` folded the requested permission mode (top-level `permissionMode` or `settings.permissions.defaultMode`, whichever took effect) back to `plan`, so this run starts in a different mode
|
|
16718
|
+
(before, the fold was undisclosed). `detail` = `{ requested, effective, ceiling, sessionId }` — `requested` the folded word, `effective` the word the run starts in, `ceiling` the floor that folded it, all
|
|
16719
|
+
three from core's six `PERMISSION_MODES`, passed through verbatim; there is no `runId` (the fold happens at the admission door, before the engine mints a run identity — attribute the frame by the stream / ledger it lands on).
|
|
16720
|
+
Sent on the two fresh legs (`POST /v1/tasks/stream` / `POST /v1/runs`) at the open beat, before any model output; NOT sent when there is no entry floor, no word was declared, the requested word is not looser than the floor,
|
|
16721
|
+
on the resume family, or on the synchronous non-streaming `POST /v1/tasks` (that leg has no notice port). It is a different fact from `config.permission_mode_unavailable` (the engine's own fold), which cannot see this one.
|
|
16622
16722
|
server 7.126.0 (the user-visible whitelist grows 35 -> 37 codes; contract appendix D.3): `task.tool_turn_incomplete` gains an optional `detail.outcome` (core 7.49.1) —
|
|
16623
16723
|
`{cause, count, toolCallIds?, outcome?, sessionId, runId}`, `outcome` in {`terminal` (the dropped calls ended the run with `model_output_invalid`), `reissued` (the model was asked to reissue)}, present only when
|
|
16624
16724
|
the turn's own message can say it, ABSENT = not said; two NEW codes (core 7.49.0, audience `user`, zero body text): `route.finish_reason_unknown` `{finishReason, sessionId, runId}` — the provider ended the turn
|
|
@@ -16746,7 +16846,13 @@ components:
|
|
|
16746
16846
|
type: ['string', 'null']
|
|
16747
16847
|
description: >
|
|
16748
16848
|
server.ts —— 一条 resume 腿把 run 重新挂回 suspended 时,带上「为什么重开」的 errorCode
|
|
16749
|
-
(无则显式 null)。只出现在这条重开臂上;普通挂起帧不带此键。
|
|
16849
|
+
(无则显式 null)。只出现在这条重开臂上;普通挂起帧不带此键。server >= 7.129.0:重开与否只按店里那一行判(不再按失败码的三码名单),所以名单外的失败码也会出现在这里;
|
|
16850
|
+
`plan_review` 的重开落 `needs_review`(同形)。
|
|
16851
|
+
toolCallId:
|
|
16852
|
+
type: string
|
|
16853
|
+
description: >
|
|
16854
|
+
The tool call the run is parked on (a tool-approval gate; ABSENT for a tool-less park / when it could not be read — never `null`). Same value as the `tool_approval` card's `toolCallId`. The server's park ledger row has
|
|
16855
|
+
always carried it (sdk 23.0.0 declares it; this arm is closed, so a strict validator rejected a real replay row before).
|
|
16750
16856
|
ModelUsageDelta:
|
|
16751
16857
|
# 新增(2026-07-25 回填批)。SDK 的 facade 审计把这个形状命名了一次(此前是在两个使用点各写一遍的匿名内联形:
|
|
16752
16858
|
# `AgentEvent` 的 `model_usage` 臂 与 `done.result.stats.modelUsage` 的值类型,同源同形)。
|
|
@@ -17105,7 +17211,7 @@ components:
|
|
|
17105
17211
|
|
|
17106
17212
|
🔴 PRESENCE IS THE PROMISE (same as `ruleSuggestions`): present only where a permission-rule store
|
|
17107
17213
|
is really wired. The converse does not hold — a wired deployment still omits it for mandated /
|
|
17108
|
-
requiresRealApproval /
|
|
17214
|
+
requiresRealApproval / allow_outranked / ask_rule_matched / hook-produced / inherited-unresolved / ancestor-resolved /
|
|
17109
17215
|
anonymous asks. Absence reads as "THIS CARD has no such option", never as "this deployment has no
|
|
17110
17216
|
rule lane" (that is `capabilities.permissionRules`) and never as "your rule broke" (that is
|
|
17111
17217
|
`allowRuleShadowed`; `persistedRuleShadowed` on a server <= 7.116.x).
|
|
@@ -17358,6 +17464,9 @@ components:
|
|
|
17358
17464
|
boundGrants:
|
|
17359
17465
|
allOf: [{ $ref: '#/components/schemas/BoundSkillGrants' }]
|
|
17360
17466
|
description: 'server >= 7.118.0, "tool_approval" only — the value `boundGrantDigest` hashes; present iff it is. DEPLOYMENT TEXT: render with contextual escaping.'
|
|
17467
|
+
boundGrantAbsence:
|
|
17468
|
+
allOf: [{ $ref: '#/components/schemas/BoundGrantAbsence' }]
|
|
17469
|
+
description: 'server >= 7.129.0 (core 7.51.0 #1688), "tool_approval" only — why this card has no `boundGrantDigest` (exactly one of the two). Same name and value on the inbox row, the operator queue row and the `pending` stream frame; passed through verbatim.'
|
|
17361
17470
|
classifierNoRuling:
|
|
17362
17471
|
allOf: [{ $ref: '#/components/schemas/AutoModeFallbackCause' }]
|
|
17363
17472
|
description: >-
|
|
@@ -17743,21 +17852,38 @@ components:
|
|
|
17743
17852
|
|
|
17744
17853
|
RuleOffersAbsence:
|
|
17745
17854
|
type: string
|
|
17746
|
-
enum: [mandated,
|
|
17855
|
+
enum: [mandated, allow_outranked, ask_rule_matched, lane_cannot_speak]
|
|
17747
17856
|
description: >
|
|
17748
17857
|
WHY `ruleOffers` is absent while the rule lane IS present (`ToolApprovalFrame.ruleOffersAbsence` and
|
|
17749
17858
|
`ApprovalCard.ruleOffersAbsence`; server >= 7.55.0 / core #490 fix ②, S-15). The engine names which
|
|
17750
17859
|
door is shut: `mandated` = this ask must be confirmed every time (🔴 this arm must NOT point the user
|
|
17751
|
-
at writing a rule — a rule would not silence it, and saying so sends them into a loop); `
|
|
17752
|
-
|
|
17753
|
-
`
|
|
17860
|
+
at writing a rule — a rule would not silence it, and saying so sends them into a loop); `allow_outranked` = an
|
|
17861
|
+
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`; `persistedRuleShadowed` on a server <= 7.116.x);
|
|
17862
|
+
`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
|
|
17863
|
+
`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).
|
|
17864
|
+
🔴 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
|
|
17865
|
+
stored with `shadowed` is rewritten once by the server at boot (the key is dropped — an absent reason is legal).
|
|
17754
17866
|
|
|
17755
17867
|
Engine-side MUTUALLY EXCLUSIVE with `ruleOffers`. 🔴 ABSENCE IS NOT AN ASSERTION — it covers both
|
|
17756
|
-
"there are offers" and the
|
|
17868
|
+
"there are offers" and the structural doors, and one frame cannot tell them apart. ADVISORY
|
|
17757
17869
|
display metadata, NEVER adjudication input. The frame and the stored `card_json` go through the SAME
|
|
17758
|
-
server-side narrow read, so both faces carry identical values; a word outside the
|
|
17870
|
+
server-side narrow read, so both faces carry identical values; a word outside the four is treated as
|
|
17759
17871
|
malformed and the key is not minted at all (the server never passes an unknown word through).
|
|
17760
17872
|
|
|
17873
|
+
BoundGrantAbsence:
|
|
17874
|
+
type: string
|
|
17875
|
+
enum: [not_bindable, unreadable]
|
|
17876
|
+
description: >
|
|
17877
|
+
WHY a tool-approval card has NO grant-binding digest (`ToolApprovalFrame.boundGrantDigest`) — the engine's closed `BOUND_GRANT_ABSENCES` (core 7.51.0 #1688; server >= 7.129.0). The engine mints
|
|
17878
|
+
EXACTLY ONE of the digest and this word on every card. `not_bindable` = this ask is not an activation of the engine's `Skill` tool, so it binds no grant: approving it grants nothing else, and "allow for this
|
|
17879
|
+
session" can be remembered per tool. `unreadable` = the card binds a grant but what it binds could not be read (a stored row whose digest is not a string, or says neither — a row parked before engine 7.51.0
|
|
17880
|
+
reads as this on the inbox face); on a live card the engine always supplies the digest, so this word shows up only where STORED rows are read.
|
|
17881
|
+
Four carriers, same name and same value: the live `tool_approval` frame (and `livePending[].frame`), the inbox row, the operator queue row and the `/v1/approvals/stream` `pending` frame; `card_json` and the
|
|
17882
|
+
presented-card frame do not carry it.
|
|
17883
|
+
🔴 ABSENCE IS NOT `not_bindable`: a skill card carries the digest (exactly one of the two), and an old server (<= 7.128.x projects it on none of the faces), a row parked before engine 7.51.0 on the queue face (it says neither),
|
|
17884
|
+
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`.
|
|
17885
|
+
Display metadata, never an adjudication input. CLOSED: the server narrows with the engine's members, anything else reads as absent.
|
|
17886
|
+
|
|
17761
17887
|
PersistedRuleMandate:
|
|
17762
17888
|
type: string
|
|
17763
17889
|
enum: [operator_always, tool_marks, probe_mandate, probe_unanswered, write_protection, target_unresolved]
|
|
@@ -18892,7 +19018,7 @@ components:
|
|
|
18892
19018
|
type: object
|
|
18893
19019
|
description: >
|
|
18894
19020
|
DURABLE only (design/80 D-B, server http/server.ts) — a RESUMED plan_review leg got re-gated into
|
|
18895
|
-
another review pause. Same payload shape as `suspended`: the gate verbatim, or an explicit `null
|
|
19021
|
+
another review pause. Same payload shape as `suspended`: the gate verbatim, or an explicit `null`; plus `toolCallId?` / `reopened?` (sdk 23.0.0 declares both; the arm is closed).
|
|
18896
19022
|
NOTE `needs_review` is BOTH a `RunStatus` terminal and a `CheckpointGate.kind`; this arm is the third
|
|
18897
19023
|
use of the name — the stream frame announcing that pause.
|
|
18898
19024
|
additionalProperties: false
|
|
@@ -18904,6 +19030,14 @@ components:
|
|
|
18904
19030
|
oneOf:
|
|
18905
19031
|
- $ref: '#/components/schemas/CheckpointGate'
|
|
18906
19032
|
- type: 'null'
|
|
19033
|
+
reopened:
|
|
19034
|
+
type: ['string', 'null']
|
|
19035
|
+
description: >
|
|
19036
|
+
server >= 7.129.0 — same reading as `suspended.reopened`: a resume leg failed BEFORE the approved step started and the `plan_review` gate went back to `needs_review`; the errorCode of that failure (explicit null when it had none).
|
|
19037
|
+
(Before 7.129.0 a `plan_review` reopen of the three listed codes was recorded as `suspended`.) Ordinary frames do not carry the key.
|
|
19038
|
+
toolCallId:
|
|
19039
|
+
type: string
|
|
19040
|
+
description: 'Same as `suspended.toolCallId` (ABSENT when the park has no tool call; never `null`).'
|
|
18907
19041
|
Event_message_committed:
|
|
18908
19042
|
type: object
|
|
18909
19043
|
description: >
|
|
@@ -19199,6 +19333,9 @@ components:
|
|
|
19199
19333
|
ruleOffersAbsence:
|
|
19200
19334
|
allOf: [{ $ref: '#/components/schemas/RuleOffersAbsence' }]
|
|
19201
19335
|
description: '`pending` frames only (server >= 7.100.0 / S-700) — see PendingCheckpoint.ruleOffersAbsence.'
|
|
19336
|
+
boundGrantAbsence:
|
|
19337
|
+
allOf: [{ $ref: '#/components/schemas/BoundGrantAbsence' }]
|
|
19338
|
+
description: '`pending` frames only (server >= 7.129.0 / core 7.51.0 #1688) — see PendingCheckpoint.boundGrantAbsence. A `live_pending` frame carries it only inside its `frame`.'
|
|
19202
19339
|
askClass:
|
|
19203
19340
|
allOf: [{ $ref: '#/components/schemas/AskClass' }]
|
|
19204
19341
|
description: '`pending` frames only (server >= 7.109.0 / S-934 ②) — see PendingCheckpoint.askClass. A `live_pending` frame carries the pair only inside its `frame`.'
|
|
@@ -20437,12 +20574,12 @@ components:
|
|
|
20437
20574
|
remember for this session's category (fs-write tools share ONE category: approving Write covers
|
|
20438
20575
|
Edit/NotebookEdit; other tools are per-tool) — the CC "don't ask again this session" semantic.
|
|
20439
20576
|
|
|
20440
|
-
|
|
20577
|
+
ToolApprovalRespondApplied:
|
|
20441
20578
|
type: object
|
|
20442
20579
|
description: >
|
|
20443
|
-
The 200 ack of POST /v1/tool-approvals/{approvalId}/respond (server tool-approval.ts — exact
|
|
20444
|
-
shape, `decision` echoed). Owner-gated with a 404 and NO existence oracle: non-owner and
|
|
20445
|
-
unknown/settled/expired/wrong-replica ids are indistinguishable.
|
|
20580
|
+
The APPLIED arm of the 200 ack of POST /v1/tool-approvals/{approvalId}/respond (server tool-approval.ts — exact
|
|
20581
|
+
shape, `decision` echoed): the decision settled a live gate. Owner-gated with a 404 and NO existence oracle: non-owner and
|
|
20582
|
+
unknown/settled/expired/wrong-replica ids are indistinguishable. See ToolApprovalRespondAck for the replayed arm.
|
|
20446
20583
|
# 封闭(census 批2 第三段,2026-07-30):铸造点 `tool-approval.ts` 三恒在键 + 两条件键
|
|
20447
20584
|
# (rememberApplied/updatedInputForwarded 的条件展开就在同一行字面量里)。
|
|
20448
20585
|
# #154 车二(2026-08-10):`persistRuleAfterDecision` 的 `withFlag()` 在同一个体上再盖两个条件键
|
|
@@ -20544,6 +20681,33 @@ components:
|
|
|
20544
20681
|
server ignores the unknown key and still answers 200, so "this build does not know `note`" is only
|
|
20545
20682
|
distinguishable via `capabilities.approvalDecisionNote` — probe that bit before sending `note`.
|
|
20546
20683
|
|
|
20684
|
+
ToolApprovalRespondReplayed:
|
|
20685
|
+
type: object
|
|
20686
|
+
description: >
|
|
20687
|
+
The REPLAYED arm of the 200 ack of POST /v1/tool-approvals/{approvalId}/respond: the ask was already DECIDED
|
|
20688
|
+
(by anyone) when this answer arrived, so the server replays the FIRST decision — `decision` is the row's two-word
|
|
20689
|
+
value (`allow_session` was folded to `allow` when it landed), `decidedAtMs` the first decision's clock,
|
|
20690
|
+
`decisionNote` only when the row has one, `rememberApplied: false` only when THIS request asked `allow_session`.
|
|
20691
|
+
No `delivery` key: nothing was delivered. A reversed late answer is the same replay — not a 409, nothing re-judged.
|
|
20692
|
+
(server src/tool-approval.ts `replayDecidedAsk`; sdk 23.0.0 declares it, the shape predates it.)
|
|
20693
|
+
required: [approvalId, askId, idempotent, decision, decidedAtMs]
|
|
20694
|
+
additionalProperties: false
|
|
20695
|
+
properties:
|
|
20696
|
+
approvalId: { type: string }
|
|
20697
|
+
askId: { type: string }
|
|
20698
|
+
idempotent: { type: boolean, enum: [true] }
|
|
20699
|
+
decision: { type: string, enum: [allow, deny] }
|
|
20700
|
+
decidedAtMs: { type: integer }
|
|
20701
|
+
decisionNote: { type: string }
|
|
20702
|
+
rememberApplied: { type: boolean, enum: [false] }
|
|
20703
|
+
ToolApprovalRespondAck:
|
|
20704
|
+
description: >
|
|
20705
|
+
The 200 ack of POST /v1/tool-approvals/{approvalId}/respond — a discriminated union: the decision was APPLIED to a live
|
|
20706
|
+
gate (ToolApprovalRespondApplied, `delivery: "applied"`) or the ask was already decided and the first decision is
|
|
20707
|
+
REPLAYED (ToolApprovalRespondReplayed, `idempotent: true`). The discriminant is `idempotent`.
|
|
20708
|
+
oneOf:
|
|
20709
|
+
- $ref: '#/components/schemas/ToolApprovalRespondApplied'
|
|
20710
|
+
- $ref: '#/components/schemas/ToolApprovalRespondReplayed'
|
|
20547
20711
|
UsageMetric:
|
|
20548
20712
|
type: string
|
|
20549
20713
|
enum: [tasks, tokensIn, tokensOut, costUsd]
|