@sema-agent/sdk 16.2.0 → 17.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 +116 -0
- package/dist/errors.d.ts +83 -13
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +96 -11
- package/dist/errors.js.map +1 -1
- package/dist/events.d.ts +16 -0
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js.map +1 -1
- package/dist/index.d.ts +20 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -2
- package/dist/index.js.map +1 -1
- package/dist/resources/approvals.d.ts +34 -30
- package/dist/resources/approvals.d.ts.map +1 -1
- package/dist/resources/approvals.js +30 -0
- package/dist/resources/approvals.js.map +1 -1
- package/dist/resources/memory.d.ts +4 -0
- package/dist/resources/memory.d.ts.map +1 -1
- package/dist/resources/memory.js +4 -0
- package/dist/resources/memory.js.map +1 -1
- package/dist/resources/rules.d.ts +4 -7
- package/dist/resources/rules.d.ts.map +1 -1
- package/dist/resources/rules.js.map +1 -1
- package/dist/resources/runs.d.ts +6 -0
- package/dist/resources/runs.d.ts.map +1 -1
- package/dist/resources/runs.js +6 -0
- package/dist/resources/runs.js.map +1 -1
- package/dist/resources/session-sync.d.ts.map +1 -1
- package/dist/resources/session-sync.js +2 -3
- package/dist/resources/session-sync.js.map +1 -1
- package/dist/resources/sessions.d.ts +8 -0
- package/dist/resources/sessions.d.ts.map +1 -1
- package/dist/resources/sessions.js +8 -0
- package/dist/resources/sessions.js.map +1 -1
- package/dist/resources/tasks.d.ts +18 -1
- package/dist/resources/tasks.d.ts.map +1 -1
- package/dist/resources/tasks.js +18 -1
- package/dist/resources/tasks.js.map +1 -1
- package/dist/resources/tool-approvals.d.ts +104 -84
- package/dist/resources/tool-approvals.d.ts.map +1 -1
- package/dist/resources/tool-approvals.js +55 -11
- package/dist/resources/tool-approvals.js.map +1 -1
- package/dist/settings.d.ts +20 -10
- package/dist/settings.d.ts.map +1 -1
- package/dist/sync.d.ts +7 -7
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +32 -17
- package/dist/sync.js.map +1 -1
- package/dist/types.d.ts +105 -33
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +23 -0
- package/dist/types.js.map +1 -1
- package/openapi.yaml +347 -172
- package/package.json +1 -1
package/openapi.yaml
CHANGED
|
@@ -417,7 +417,7 @@ paths:
|
|
|
417
417
|
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,
|
|
418
418
|
`rewind.store_unconfigured` / `rewind.capture_disabled` / `rewind.child_scope_unsupported` / `rewind_snapshot.unresolvable`, the
|
|
419
419
|
registered `config.*` codes incl. the three roster-bound ones and — server >= 7.116.0 — `config.invalid_checkpoint_store` (400) and
|
|
420
|
-
`config.user_invoked_skill_unknown` (400: top-level `userInvokedSkills` named a skill this run does not carry), `corrupt_session`; codes outside the table, e.g. `rewind.invalid_spec`, stay
|
|
420
|
+
`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: `), `corrupt_session`; codes outside the table, e.g. `rewind.invalid_spec`, stay
|
|
421
421
|
200 with a failed body) answers its 4xx / 5xx ERROR BODY only when the whole submission did ZERO WORK (cumulative
|
|
422
422
|
`stats.turns === 0` AND `stats.tokens === 0` — the common case: nothing billed, not idempotency-cached). When a
|
|
423
423
|
`verify` / `cascade` run's earlier tier already ran turns and only the last tier is refused, the SAME code arrives
|
|
@@ -869,7 +869,9 @@ paths:
|
|
|
869
869
|
evicts an already-accepted instruction to make room. Opposite handling from not_running: the
|
|
870
870
|
text is still valid — let the run resume (which drains the queue) or reduce concurrent steers,
|
|
871
871
|
then resend.
|
|
872
|
-
errorCode "steering.duplicate_input_id" (server >=7.3.0 / core 5.14.0
|
|
872
|
+
errorCode "steering.duplicate_input_id" (server >=7.3.0 / core 5.14.0; server >= 7.118.0 adds `detail.differs`, a
|
|
873
|
+
non-empty subset of SteerReplayFacet — contains `text` ⇒ fresh key; otherwise the same instruction, replay with the
|
|
874
|
+
original values and do NOT change the key) — the Idempotency-Key you
|
|
873
875
|
sent is already parked with DIFFERENT steering content (same key + same content is an idempotent
|
|
874
876
|
no-op and 202s). The engine refuses rather than silently picking a side. Reissue with a fresh
|
|
875
877
|
key — this is a new instruction, not a retry.
|
|
@@ -886,6 +888,13 @@ paths:
|
|
|
886
888
|
⚠️ Against a server 7.19.0–7.22.0 the OLD caveat applies verbatim: only the parked legs dedupe —
|
|
887
889
|
a lost 200 retried with the same key injects TWICE with byte-identical receipts; treat a lost 200
|
|
888
890
|
there as "outcome unknown", never "safe to retry".
|
|
891
|
+
🔴 server >= 7.117.0 — TWO MORE 409 answers on a TERMINAL run (the `parked_for_wake` leg), refused BEFORE the wake card is minted
|
|
892
|
+
(no card is minted; the row is untouched; on 7.116.x this was a 202 `parked_for_wake` followed by a refusal at `/wake`, i.e. a card that could
|
|
893
|
+
never be redeemed): when the stored submit body of this session is one this version no longer replays — errorCode
|
|
894
|
+
`checkpoint.unsupported_version` (`retriable: true` = the card is still pending / cancellable, NOT "a retry will succeed"; the stored body carries
|
|
895
|
+
`settings.permissions.allow` under an earlier reading — list or statement era) or errorCode `resume_blocked_by_policy` (`blockedBy: "request.field_invalid"`;
|
|
896
|
+
the stored skill `disallowedTools` carries an entry the engine now reads as nothing). Both answers are word-for-word the ones `POST /v1/sessions/{sessionId}/wake`
|
|
897
|
+
gives for the same body. Way out: cancel the task and resubmit as a new one (with the entry fixed). The SDK does not retry them (409 is not in the retry set).
|
|
889
898
|
content:
|
|
890
899
|
application/json:
|
|
891
900
|
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
@@ -1022,7 +1031,7 @@ paths:
|
|
|
1022
1031
|
Use POST /v1/runs/{taskId}/cancel for a run-level stop.
|
|
1023
1032
|
errorCode "steering.not_running" (reused verbatim) — terminal / not accepting, or the stream
|
|
1024
1033
|
settled in the race window ("run just finished on this replica — no in-flight turn to interrupt").
|
|
1025
|
-
errorCode "steering.duplicate_input_id" — this Idempotency-Key was already accepted with DIFFERENT
|
|
1034
|
+
errorCode "steering.duplicate_input_id" (server >= 7.118.0: read `detail.differs` first, see SteerReplayFacet) — this Idempotency-Key was already accepted with DIFFERENT
|
|
1026
1035
|
content or at a DIFFERENT tier (core's replay identity includes the normalized tier: the same key
|
|
1027
1036
|
that once went through the steer door (default tier) then hits this door (now) is refused —
|
|
1028
1037
|
"idempotent success without the cut" would be a dispositive lie). Reissue with a fresh key.
|
|
@@ -2526,6 +2535,16 @@ paths:
|
|
|
2526
2535
|
'409': { $ref: '#/components/responses/Conflict' }
|
|
2527
2536
|
'422': { $ref: '#/components/responses/UnprocessableEntity' }
|
|
2528
2537
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
2538
|
+
'503':
|
|
2539
|
+
description: >-
|
|
2540
|
+
server >= 7.118.0 — `state.mcp_reconnect_unsettled`: the time this endpoint waits for the re-dial to settle ran out (the same
|
|
2541
|
+
constant as the `/mcp` panel, 10 s). Read it as "this endpoint did not wait for a result", NOT "failed": the re-dial is NOT
|
|
2542
|
+
cancelled and still settles (success writes the roster onto this run, failure removes it). The body is `error` + `errorCode`
|
|
2543
|
+
only (no `outcome`) and carries NO `Retry-After`; this is a close-then-dial transaction — confirm the server state before
|
|
2544
|
+
re-sending, and do not use a re-send as a probe. SDK → ServiceStateError (never auto-retried: a non-submit POST).
|
|
2545
|
+
content:
|
|
2546
|
+
application/json:
|
|
2547
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
2529
2548
|
|
|
2530
2549
|
/v1/sessions/{sessionId}/background:
|
|
2531
2550
|
parameters:
|
|
@@ -3315,8 +3334,11 @@ paths:
|
|
|
3315
3334
|
`approval_binding_mismatch` (`errorCode`, `field`: boundCallId|boundInputHash|answer — the third
|
|
3316
3335
|
member since core 5.7.0/RB-459: the content-ask `answer` payload was refused) — your binding does
|
|
3317
3336
|
NOT match the current pending action (TOCTOU/swap). The SDK splits these by `errorCode` into
|
|
3318
|
-
ConflictError vs ApprovalBindingMismatchError
|
|
3319
|
-
to
|
|
3337
|
+
ConflictError vs ApprovalBindingMismatchError (the engine's own code for the same refusal, `checkpoint.invalid_outcome`,
|
|
3338
|
+
maps to that class too). binding_mismatch = SAFETY signal: refetch + re-present
|
|
3339
|
+
to the human, NEVER auto-retry. A `decidedBy: "policy"` allow on a person-required card is the OTHER `invalid_outcome`
|
|
3340
|
+
shape — the non-binding seat `hostDecision` — and answers `resume_outcome_invalid` (`field: "hostDecision"`, card pending;
|
|
3341
|
+
see ApprovalDecidedBy). (S-57, server >= 7.54.0: the `field:"boundCallId"` shape is
|
|
3320
3342
|
intercepted in steady state by the `approval_stale` second trigger source below — the server
|
|
3321
3343
|
compares the echoed boundCallId against the current pending pre-CAS and answers 409
|
|
3322
3344
|
`approval_stale` + `currentPending`; this code's boundCallId arm covers only the remaining race
|
|
@@ -3344,7 +3366,8 @@ paths:
|
|
|
3344
3366
|
decide either resumes the parked host or starts that new run (both land on the byte-identical
|
|
3345
3367
|
acceptance shape) — a host session already busy with another run instead answers the ordinary 409
|
|
3346
3368
|
`conflict.session_active_run`.
|
|
3347
|
-
EIGHTH group (server >= 7.75.0 / S-252, same PARKED WORKFLOW-CHILD lane
|
|
3369
|
+
EIGHTH group (server >= 7.75.0 / S-252, same PARKED WORKFLOW-CHILD lane; the 7.75.0–7.117.x reading — server >= 7.118.0
|
|
3370
|
+
narrows the triggers, see the note after this group): `decide.workflow_parks_unreadable`
|
|
3348
3371
|
— the workflow run's record carries no readable `parks[]` list (the ENGINE's own park-ownership
|
|
3349
3372
|
ledger, and the single source this lane joins the caller's checkpoint against). A record written by
|
|
3350
3373
|
an engine older than 7.17.0 does not carry the key at all, or one entry is damaged and the whole list
|
|
@@ -3368,6 +3391,15 @@ paths:
|
|
|
3368
3391
|
there is no mapper branch, class, or type-union member to delete or keep for it — unlike
|
|
3369
3392
|
`decide.workflow_host_not_parked` above, an OPEN decide.* code this SDK never gave a name to degrades
|
|
3370
3393
|
through the base `DecideError` exactly as it always did, on any server version.
|
|
3394
|
+
🔴 server >= 7.118.0 (core 7.44.0 #1319): routing and redemption read ONE source — the holder seat `parkedBy` the engine writes on
|
|
3395
|
+
a `parks[]` entry (the record naming the card at the highest generation, exactly one ⇒ it takes the decision). The EIGHTH group
|
|
3396
|
+
widens and now has two causes under one code (the body carries no cause word; the sentence differs): an entry written by an older
|
|
3397
|
+
engine (cards still parked at the upgrade) ⇒ resume `runId` and decide again; a record that cannot be read (a malformed entry,
|
|
3398
|
+
or `parks` present but not a list) ⇒ resuming does NOT clear it — an operator must repair that record; the body may add
|
|
3399
|
+
`readableHolderRunId` (a clue). A record with NO `parks` key holds nothing: a card only it names answers 409
|
|
3400
|
+
`conflict.resume_context_unavailable` (no `runId`). NINTH group: `decide.workflow_park_forked` (409, body `runIds`) — the same
|
|
3401
|
+
run was resumed more than once and the copies all hold this card; nothing delivered, the card stays PENDING; resume one of
|
|
3402
|
+
`runIds` and decide again (SDK → `DecideWorkflowParkForkedError`).
|
|
3371
3403
|
SIXTH group (A-075.10, TIME-BASED retry-later pair): `resume.usage_window_exhausted` (#449 G1,
|
|
3372
3404
|
core 5.60.1 — the deployment governance window on this run's ledger key is full; NOTHING was
|
|
3373
3405
|
consumed or unpinned, the SAME token with the SAME decision redeems once the window slides) and
|
|
@@ -4225,32 +4257,14 @@ paths:
|
|
|
4225
4257
|
substitution), the command bytes were unreadable, or this ask belongs to one of the seven
|
|
4226
4258
|
families that never get offers — so do NOT diagnose the deployment's rule lane from one
|
|
4227
4259
|
frame or from that one refusal.
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
enum: [policy]
|
|
4260
|
+
decidedBy:
|
|
4261
|
+
allOf: [{ $ref: '#/components/schemas/ApprovalDecidedBy' }]
|
|
4231
4262
|
description: >-
|
|
4232
|
-
server
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
(`POST /v1/approvals/{sessionId}/decide`, ApprovalDecision.settledBy) — see that schema for
|
|
4238
|
-
the full account. `decision:"deny"` ONLY: an allow-family decision carrying this key is a
|
|
4239
|
-
contradiction, refused BEFORE THE DECISION (400 `request.body_shape`, the same sentence as
|
|
4240
|
-
the durable leg) rather than after — the engine's refusal cannot travel back up this HTTP
|
|
4241
|
-
leg, and by then this door's own side effects (the session allow-all grant, a persisted
|
|
4242
|
-
rule, a DECIDED row that replays as a plain approval) have already landed. Because the
|
|
4243
|
-
refusal precedes the decision, the ask is NOT settled: the card is still open and the same
|
|
4244
|
-
`approvalId` can be answered again. The PARKED late-acceptance arm reads the same key.
|
|
4245
|
-
|
|
4246
|
-
SHAPE, not word: a non-string is a 400. A word core does NOT know travels UNTOUCHED — this
|
|
4247
|
-
door still answers 200 (its 200 only ever meant "your answer was delivered") and the ENGINE
|
|
4248
|
-
refuses it fail-closed, settling as `approver_contract`, NEVER as `policy_refused`: a
|
|
4249
|
-
misspelled word must not be rendered as "the deployment's policy refused".
|
|
4250
|
-
|
|
4251
|
-
No capability bit: an older server ignores unknown keys and answers 200, so the refusal is
|
|
4252
|
-
still read as a person's there. Judge by server version, or read back whether
|
|
4253
|
-
`tool_end.gate.settlement.kind` ever shows `policy_refused`.
|
|
4263
|
+
server >= 7.118.0 — WHO made this decision (one key read the same way by all three decision doors; see
|
|
4264
|
+
ApprovalDecidedBy). ABSENT = `person`. This door's own answers: a `policy` allow on a person-required card
|
|
4265
|
+
and a word outside the closed set are both 400 `request.body_shape`, refused BEFORE the decision (the card is
|
|
4266
|
+
still open; answer it again with the same `approvalId`). 🔴 An older server (no `approvalDecidedBy`
|
|
4267
|
+
capability bit) DROPS this key silently and answers 200 — probe the bit first.
|
|
4254
4268
|
note:
|
|
4255
4269
|
type: string
|
|
4256
4270
|
maxLength: 2048
|
|
@@ -4342,7 +4356,7 @@ paths:
|
|
|
4342
4356
|
application/json:
|
|
4343
4357
|
schema: { $ref: '#/components/schemas/AskDecisionAck' }
|
|
4344
4358
|
'400':
|
|
4345
|
-
description: 'Malformed body — `request.invalid_json` (unparseable) or `request.body_shape` (schema reject, e.g. an `actor` carrying anything but `label`, or — on a server < 7.104.0 — a `settledBy` key). Server >= 7.
|
|
4359
|
+
description: 'Malformed body — `request.invalid_json` (unparseable) or `request.body_shape` (schema reject, e.g. an `actor` carrying anything but `label`, or — on a server < 7.104.0 — a `settledBy` key). Server >= 7.118.0 reads `decidedBy` (see AskDecisionBody.decidedBy): `request.field_conflict` (a `policy` allow on a person-required card), `decided_by_not_in_proof` (direct-door worker), `request.field_invalid` (outside the closed set); the retired `settledBy` key is a `request.body_shape`.'
|
|
4346
4360
|
content:
|
|
4347
4361
|
application/json:
|
|
4348
4362
|
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
@@ -6842,16 +6856,6 @@ components:
|
|
|
6842
6856
|
name: { type: string }
|
|
6843
6857
|
description: { type: string }
|
|
6844
6858
|
content: { type: string }
|
|
6845
|
-
manifest:
|
|
6846
|
-
type: object
|
|
6847
|
-
additionalProperties: true
|
|
6848
|
-
deprecated: true
|
|
6849
|
-
description: >
|
|
6850
|
-
RETIRED — server >= 7.116.0 rejects the WHOLE skill when this key is present on a delegated agent's skill (unknown key:
|
|
6851
|
-
`agent.skills must be …`; a top-level skill answers 400 `request.field_invalid` "skill.manifest was retired in core 7.41.0 — …").
|
|
6852
|
-
core 7.41.0 retired the skill manifest (hard break, no alias). Rewrite `manifest.allowTools` to the skill's
|
|
6853
|
-
`allowedTools` (an allow-rule source, not a fence); `manifest.allowPaths` has no replacement (use the session rules'
|
|
6854
|
-
`allowDirs`). Older servers (<= 7.115.x) still accept it; the key stays declared here until the next major.
|
|
6855
6859
|
allowedTools: { type: array, items: { type: string }, description: 'server >= 7.116.0 — the skill''s allow-rule source (see `SkillSpec.allowedTools`); <= 7.115.x rejects the key (unknown key).' }
|
|
6856
6860
|
disallowedTools: { type: array, items: { type: string }, description: 'server >= 7.116.0 — the skill''s deny-rule source (see `SkillSpec.disallowedTools`); <= 7.115.x rejects the key (unknown key).' }
|
|
6857
6861
|
files:
|
|
@@ -7187,6 +7191,13 @@ components:
|
|
|
7187
7191
|
description: 'core 5.11.0: extra READ-ONLY absolute host paths — read fence only (classify read-boundary auto-allow + read_file/grep containment); writes to the same dirs are still refused. Same shape/400 rules as additionalDirectories; servers <6.6.0 silently ignore (harmless additive).'
|
|
7188
7192
|
images:
|
|
7189
7193
|
type: array
|
|
7194
|
+
description: >
|
|
7195
|
+
Image input: `{ data, mimeType }` (inline base64) or `{ url }`. 🔴 INLINE-ARM ADMISSION (server >= 7.117.0 / core 7.42.0): `mimeType` must be one of `image/png` /
|
|
7196
|
+
`image/jpeg` / `image/gif` / `image/webp` (the API's type word, not a file extension), the decoded bytes must carry the DECLARED type's file signature, and the decoded size must
|
|
7197
|
+
not exceed 5 MiB — otherwise the engine refuses BEFORE the first model call (after the UserPromptSubmit hook; not one model token spent) with 400 `config.image_invalid`, whose
|
|
7198
|
+
`error` starts with `TaskSpec.images: `. Before 7.117.0 such an image was sent as-is to the model provider and the whole first request failed there (no code to branch on).
|
|
7199
|
+
A base64 string longer than 5 242 880 characters (the model API's 5 MB image cap; the server reads the engine's exported constant) is NOT this code: the submit gate refuses it first
|
|
7200
|
+
with 413 `request.payload_too_large` (zero model calls, no submit hook, no run row). The `{ url }` arm is unchanged. The model must accept image input (`ModelInfo.vision`; absent = unknown, send anyway).
|
|
7190
7201
|
items:
|
|
7191
7202
|
oneOf:
|
|
7192
7203
|
- type: object
|
|
@@ -7330,9 +7341,20 @@ components:
|
|
|
7330
7341
|
the delivery lane, so raising it only helps a model whose declared window is below 200 000 tokens.
|
|
7331
7342
|
The engine's own twin code `config.skill_listing_budget_invalid` (400) is NOT reachable through this
|
|
7332
7343
|
submit leg today — it belongs to callers that build a `TaskSpec` directly.
|
|
7333
|
-
🔴 `permissions.allow`
|
|
7334
|
-
|
|
7335
|
-
|
|
7344
|
+
🔴 `permissions.allow` — THE READING DEPENDS ON THE SERVER GENERATION (probe `capabilities.settings.permissions`; an absent bit = the older generation).
|
|
7345
|
+
server >= 7.117.0 (`allowLayer === true`, core 7.42.0): the WHOLE list goes to the engine's allow layer and a listed call is NOT ASKED — the engine's ordinary and
|
|
7346
|
+
"nobody stated it" questions are cleared, built-in write gates and shell checks included (like the reference implementation's `permissions.allow`); content forms
|
|
7347
|
+
(`Bash(git:*)`, `Read(//dir/**)`) are accepted; entries the engine reads as nothing (`*`, write-path forms such as `Edit(src/**)`, domain forms, names with whitespace) are NOT
|
|
7348
|
+
refused — the run opens with an `engine_notice` `permission.allowed_tool_not_granted`. Any layer's deny / ask rule, operator `APPROVAL_REQUIRE` / `APPROVAL_NEVER_AUTO` /
|
|
7349
|
+
`MANUAL_MODE_SHELL_GATE=always`, write protection, governance, a hook's ask and plan-mode asks still ask (a matching `request` member then shows as `allowRuleShadowed` on the
|
|
7350
|
+
card). The submit gate judges only the shape (array of strings, at most 1000 entries => 400) and the NAMES (the same judge and the same sentence as `deny` / `ask`: a retired name,
|
|
7351
|
+
an unprefixed short name, or a protocol name whose server segment cannot be minted / is missing / is a wildcard (`mcp__*`) is a whole-request 422) — never the content.
|
|
7352
|
+
🔴 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
|
|
7353
|
+
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
|
|
7354
|
+
`errorCode` is the status-derived coarse code `request.unprocessable`.
|
|
7355
|
+
The skill lists (`skills[].allowedTools`) read exactly like this bucket from 7.117.0 on.
|
|
7356
|
+
Server 7.115.0–7.116.x (`allowGrant` without `allowLayer`): `permissions.allow` IS A STATEMENT, NOT A WHITELIST — an absent `allowGrant` is a server <= 7.114.x that reads the bucket
|
|
7357
|
+
as a list, so put nothing in it there. The bucket is compiled by the SAME rule compiler as `deny` / `ask`: a hit is a
|
|
7336
7358
|
reasoned allow = "somebody stated it" (it answers an MCP / A2A server-tool call nobody stated; without it an
|
|
7337
7359
|
ask-class mode raises a card, and with no approval surface the call is refused); a miss states nothing and the call is
|
|
7338
7360
|
judged as usual — a tool off the list is NOT refused any more (<= 7.114.x refused it). It does NOT skip the question
|
|
@@ -7397,6 +7419,14 @@ components:
|
|
|
7397
7419
|
items: { $ref: '#/components/schemas/TaskAgentDefinition' }
|
|
7398
7420
|
description: Per-task sub-agent definitions (roster additions scoped to this task only).
|
|
7399
7421
|
interactiveTools: { type: boolean, description: Mount interactive (prompting) tools. }
|
|
7422
|
+
approvalReach:
|
|
7423
|
+
allOf: [{ $ref: '#/components/schemas/ApprovalReach' }]
|
|
7424
|
+
description: >-
|
|
7425
|
+
server >= 7.118.0 (core 7.43.0 #1354) — declares that NOBODY on this run can answer approvals. Passed verbatim to the
|
|
7426
|
+
engine. The live approver callback is no longer this run's approver (`wiring_manifest` `ask.form: "declared_none"`); on a
|
|
7427
|
+
deployment with a parking facility the first gated call PARKS the run (`paused`) — for "refuse and keep going" use
|
|
7428
|
+
`permissionMode: "dontAsk"`. It never approves or releases anything. A value outside the closed set ⇒ 400
|
|
7429
|
+
`request.field_invalid`. Probe `capabilities.approvalReach` first.
|
|
7400
7430
|
oneShot:
|
|
7401
7431
|
type: boolean
|
|
7402
7432
|
description: >
|
|
@@ -8901,40 +8931,14 @@ components:
|
|
|
8901
8931
|
not inside the HMAC payload, so accepting it unsigned would be tamperable); 501 without an exemption
|
|
8902
8932
|
store. On the parked-background-agent leg the grant lands on the child's ROOT/HOST session (the whole
|
|
8903
8933
|
session tree shares it). The 200 body echoes `rememberApplied` when a store is wired.
|
|
8904
|
-
|
|
8905
|
-
|
|
8906
|
-
enum: [policy]
|
|
8934
|
+
decidedBy:
|
|
8935
|
+
allOf: [{ $ref: '#/components/schemas/ApprovalDecidedBy' }]
|
|
8907
8936
|
description: >-
|
|
8908
|
-
server
|
|
8909
|
-
|
|
8910
|
-
|
|
8911
|
-
|
|
8912
|
-
|
|
8913
|
-
WHY IT EXISTS: without the word, a policy refusal is byte-identical to "a person pressed No", and the
|
|
8914
|
-
model reads "the user doesn''t want to proceed; STOP and wait for the user to tell you how to proceed"
|
|
8915
|
-
— a sentence about a person, in a run where none is coming. With it the engine mints a POLICY sentence
|
|
8916
|
-
instead, this request''s `reason` travels as the POLICY''s words (not as the user''s guidance), and the
|
|
8917
|
-
settlement is recorded as `policy_refused` (`who.party: "none"`).
|
|
8918
|
-
|
|
8919
|
-
CONSTRAINTS: `decision:"deny"` only — only a person approves an action that EXECUTES, so
|
|
8920
|
-
`{decision:"approve", settledBy:"policy"}` is a contradiction, refused by the SERVER before the
|
|
8921
|
-
decision (400 `request.field_conflict`, the SAME SENTENCE as the live door) with the engine''s own
|
|
8922
|
-
pre-CAS refusal (`checkpoint.invalid_outcome`) behind it as depth; the server does NOT silently drop
|
|
8923
|
-
the key. It is therefore MUTUALLY EXCLUSIVE with `remember` (which is approve-only) — one body
|
|
8924
|
-
cannot carry both. The vocabulary''s owner is still core: the server accepts the KEY and relays the
|
|
8925
|
-
VALUE verbatim, so a word core does not know is refused by the engine, never by a server-side
|
|
8926
|
-
second judge of the vocabulary.
|
|
8927
|
-
REFUSED on a direct-door worker (400 `settled_by_not_in_proof` — not inside the HMAC payload, same
|
|
8928
|
-
rule as `remember` / `updatedInput` / `checkpointToken`); a non-direct-door deployment is unchanged.
|
|
8929
|
-
|
|
8930
|
-
SHAPE, not word: a non-string is 400 `request.field_invalid` (same family as `reason must be a
|
|
8931
|
-
string`). A word core does NOT know travels UNTOUCHED and is refused BY THE ENGINE pre-CAS:
|
|
8932
|
-
409 + `errorCode: "resume_outcome_invalid"` + `field: "hostDecision"` + `retriable: true`, and the
|
|
8933
|
-
card STAYS PENDING (re-decide the same coordinates with a corrected body). NOTE this family no
|
|
8934
|
-
longer answers `approval_binding_mismatch`: from server 7.93.3 the `policy_ask` gate splits
|
|
8935
|
-
`invalid_outcome` by `field`, and the non-binding seats (`hostDecision`, `approver`) answer
|
|
8936
|
-
`resume_outcome_invalid` — "you approved the wrong action" is a sentence about a different fault.
|
|
8937
|
-
The binding seats and the field-absent shape are byte-for-byte unchanged.
|
|
8937
|
+
server >= 7.118.0 — WHO made this decision (one key read the same way by all three decision doors; see
|
|
8938
|
+
ApprovalDecidedBy). ABSENT = `person`. This door's own answers: a `policy` allow on a person-required card is
|
|
8939
|
+
refused by the ENGINE before the CAS (409 `resume_outcome_invalid` + `field: "hostDecision"`, the card stays
|
|
8940
|
+
pending); outside the closed set ⇒ 400 `request.field_invalid`; any value on a direct-door worker ⇒ 400
|
|
8941
|
+
`decided_by_not_in_proof`. An older server (no `approvalDecidedBy` bit) refuses the key (400 `request.body_shape`).
|
|
8938
8942
|
|
|
8939
8943
|
ApprovalRow:
|
|
8940
8944
|
type: object
|
|
@@ -9339,6 +9343,17 @@ components:
|
|
|
9339
9343
|
`noteRecorded` (a live-card-only deployment has no row to write, and a flapping store cannot write
|
|
9340
9344
|
one; both answer `false` while the decision itself still stands). This bit only says "this build
|
|
9341
9345
|
knows the key" — same family as `permissionRulesRevoke`, deliberately gated on no facility.
|
|
9346
|
+
approvalDecidedBy:
|
|
9347
|
+
type: boolean
|
|
9348
|
+
description: >-
|
|
9349
|
+
server >= 7.118.0, hardcoded true (a version bit): the three approval decision doors read `decidedBy` (ApprovalDecidedBy).
|
|
9350
|
+
Send it only when this is present; absent = an older server (the durable doors and `plan_review` 400 it, the live
|
|
9351
|
+
`respond` drops it silently and answers 200).
|
|
9352
|
+
approvalReach:
|
|
9353
|
+
type: boolean
|
|
9354
|
+
description: >-
|
|
9355
|
+
server >= 7.118.0, hardcoded true (same word as the request key): TaskRequest.approvalReach is understood. Absent = an
|
|
9356
|
+
older server — do not send the key.
|
|
9342
9357
|
respondFreeFormRules:
|
|
9343
9358
|
type: boolean
|
|
9344
9359
|
description: >
|
|
@@ -9414,22 +9429,28 @@ components:
|
|
|
9414
9429
|
settings:
|
|
9415
9430
|
type: object
|
|
9416
9431
|
description: >
|
|
9417
|
-
🔴 server >= 7.115.0
|
|
9418
|
-
server give `settings.permissions.allow` on a task submit"
|
|
9419
|
-
|
|
9420
|
-
|
|
9421
|
-
|
|
9422
|
-
|
|
9423
|
-
|
|
9424
|
-
`
|
|
9425
|
-
(
|
|
9432
|
+
🔴 server >= 7.115.0, ALWAYS present: `{ permissions: { allowGrant: true } }`; server >= 7.117.0 adds `allowLayer: true` to `permissions` — the VERSION bits for
|
|
9433
|
+
"which reading does this server give `settings.permissions.allow` on a task submit" (both constant `true`, never vary with the deployment: the single-user
|
|
9434
|
+
and `REQUIRE_PRINCIPAL=true` forms carry the same value). Read three levels: `settings` -> `permissions` -> <bit>; an absent / non-boolean bit = a server without it.
|
|
9435
|
+
`permissions.allowGrant === true` (>= 7.115.0) = the `allow` bucket is read as allow RULES, not as a list (a tool off the list is no longer refused).
|
|
9436
|
+
`permissions.allowLayer === true` (>= 7.117.0, core 7.42.0) = the server hands the WHOLE `allow` list to the engine's allow layer: a listed call is NOT ASKED (the engine's
|
|
9437
|
+
ordinary and "nobody stated it" questions are cleared — built-in write gates / shell checks included, like the reference implementation's `permissions.allow`); content forms
|
|
9438
|
+
(`Bash(git:*)`, `Read(//dir/**)`) are accepted; entries the engine reads as nothing (`*`, write-path / domain forms, names with whitespace) are NOT refused — the run opens with an
|
|
9439
|
+
`engine_notice` `permission.allowed_tool_not_granted`; any layer's deny / ask rule, operator `APPROVAL_REQUIRE` / `APPROVAL_NEVER_AUTO` / shell floor / write protection /
|
|
9440
|
+
governance, a hook's ask and plan-mode asks still ask (a matching member then shows as `allowRuleShadowed` on the card). The submit gate judges only the shape and the NAMES (retired
|
|
9441
|
+
name / unprefixed short name / unmintable protocol server segment => 422, at most 1000 entries => 400) — never the content.
|
|
9442
|
+
🔴 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
|
|
9443
|
+
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
|
|
9444
|
+
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
|
|
9445
|
+
deployment-level "who stated what" (operator env `APPROVAL_GRANT`) is on the operator diagnostics face (`serverGates.approvalGrant`), not here.
|
|
9426
9446
|
additionalProperties: true
|
|
9427
9447
|
properties:
|
|
9428
9448
|
permissions:
|
|
9429
9449
|
type: object
|
|
9430
9450
|
additionalProperties: true
|
|
9431
9451
|
properties:
|
|
9432
|
-
allowGrant: { type: boolean, description: 'Constant `true` on server >= 7.115.0: `settings.permissions.allow` is read as allow RULES
|
|
9452
|
+
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.' }
|
|
9453
|
+
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).' }
|
|
9433
9454
|
memoryWrite:
|
|
9434
9455
|
type: boolean
|
|
9435
9456
|
description: >
|
|
@@ -10014,8 +10035,9 @@ components:
|
|
|
10014
10035
|
🔴 server >= 7.116.0 (core 7.41.0): a skill carries two TOOL LISTS, `allowedTools` / `disallowedTools` — the
|
|
10015
10036
|
run-scoped ALLOW-RULE and DENY-RULE source of the skill once it is activated. NOT a fence (<= 7.115.x the engine read
|
|
10016
10037
|
the skill's tool list as "only these"): unlisted tools are judged as usual, listed ones are not asked about for the rest
|
|
10017
|
-
of the run
|
|
10018
|
-
release the engine's own write gate / shell ordinary asks on built-in tools).
|
|
10038
|
+
of the run. On server 7.116.x that made it WIDER than `settings.permissions.allow` (then a statement that did not
|
|
10039
|
+
release the engine's own write gate / shell ordinary asks on built-in tools); server >= 7.117.0 reads the two request-level
|
|
10040
|
+
allow sources the SAME way (see `capabilities.settings.permissions.allowLayer`). Probe `capabilities.skillToolLists`
|
|
10019
10041
|
(absent = an old server that accepts the keys on a top-level skill and silently ignores them). The skill's `manifest`
|
|
10020
10042
|
key is RETIRED: any value on a top-level skill is a 400 `request.field_invalid` ("skill.manifest was retired in core
|
|
10021
10043
|
7.41.0 — …"; rewrite `manifest.allowTools` to `allowedTools`; `allowPaths` has no replacement), and on a delegated
|
|
@@ -10032,21 +10054,28 @@ components:
|
|
|
10032
10054
|
server >= 7.116.0. The skill's run-scoped ALLOW-RULE source (permission-rule spellings: a tool name,
|
|
10033
10055
|
`mcp__<server>` / `mcp__<server>__*`; `*`, write paths and domain forms grant nothing). Once the skill is activated
|
|
10034
10056
|
(named in top-level `userInvokedSkills` — no `Skill` card — or activated by the model and the card approved) the
|
|
10035
|
-
listed tools are not asked about for the rest of the run: it clears the engine's ordinary and "nobody stated it" asks
|
|
10036
|
-
|
|
10037
|
-
|
|
10038
|
-
|
|
10039
|
-
|
|
10057
|
+
listed tools are not asked about for the rest of the run: it clears the engine's ordinary and "nobody stated it" asks;
|
|
10058
|
+
deny / ask rules, `APPROVAL_REQUIRE`, `APPROVAL_NEVER_AUTO`, the shell floor, write protection and plan-mode asks are not cleared.
|
|
10059
|
+
NOT a fence: unlisted tools are judged as usual. Versus `settings.permissions.allow`: on server 7.116.x this list was WIDER (that bucket
|
|
10060
|
+
was then only a statement); server >= 7.117.0 reads the two the SAME way, entry for entry (entries the engine reads as nothing get an
|
|
10061
|
+
`engine_notice` `permission.allowed_tool_not_granted`). A non-string-array is a 400 `request.field_invalid`; a protocol name whose server
|
|
10062
|
+
segment cannot be minted / is missing / is a wildcard (`mcp__*`) is a 400 too, and from 7.117.0 so is a retired name / unprefixed short
|
|
10063
|
+
name (the name layer is judged by the same judge as the allow bucket) and a list of more than 1000 entries (top-level `skills[]` and
|
|
10064
|
+
`agents[].skills[]` judge identically). Probe `capabilities.skillToolLists`.
|
|
10040
10065
|
disallowedTools:
|
|
10041
10066
|
type: array
|
|
10042
10067
|
items: { type: string }
|
|
10043
10068
|
description: >
|
|
10044
10069
|
server >= 7.116.0. The skill's run-scoped DENY-RULE source (same grammar / gates / probe as `allowedTools`): once the skill
|
|
10045
10070
|
is activated these tools are refused for the run (no execution, no card; `tool_end.gate.disposition` is
|
|
10046
|
-
`{ kind: "denied", deniedBy: "policy" }`)
|
|
10047
|
-
|
|
10048
|
-
|
|
10049
|
-
|
|
10071
|
+
`{ kind: "denied", deniedBy: "policy" }`); write-path forms and command rules compile into deny rows too.
|
|
10072
|
+
🔴 server >= 7.117.0: an entry the engine reads as nothing (`*`, a domain form `WebFetch(domain:…)`, a protocol name with a wildcard, a name with
|
|
10073
|
+
whitespace or malformed) is refused AT SUBMIT — 400 `request.field_invalid`, `error` = `skill.disallowedTools entry "<entry>" reads as nothing (<reason>): <how to write it>`
|
|
10074
|
+
(delegated level: prefix `agents[<i>]: skills[<j>]: `), top-level `skills[]` and `agents[].skills[]` alike — because an entry that looks like a refusal but refuses nothing is
|
|
10075
|
+
worse than none (<= 7.116.x accepted it with only an operator-audience notice that was not sent to the session stream; that code is now `permission.allowed_tool_not_granted`,
|
|
10076
|
+
user audience, on the session stream). 🔴 The resume legs judge the stored body the same way: a parked task whose stored skill `disallowedTools` carries such an entry answers
|
|
10077
|
+
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`
|
|
10078
|
+
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.
|
|
10050
10079
|
|
|
10051
10080
|
McpServerSpec:
|
|
10052
10081
|
type: object
|
|
@@ -10788,16 +10817,17 @@ components:
|
|
|
10788
10817
|
baseUrl/apiKey/headers (the service strips them).
|
|
10789
10818
|
# 🔴 封闭 + required 补铸造点(census 批2 第三段,2026-07-30):铸造点 `routes/capabilities.ts`
|
|
10790
10819
|
# 是逐键字面量投影(白名单,不是透传——「open set」旧注不成立,新键要先过这道 spec)。core Model 类型
|
|
10791
|
-
# 里 id/provider/reasoning
|
|
10792
|
-
#
|
|
10793
|
-
|
|
10820
|
+
# 里 id/provider/reasoning 都是必填 ⇒ 四键无条件;contextWindow/maxOutputTokens 0/缺省即省略,
|
|
10821
|
+
# supportedEffortLevels 仅 reasoning 模型。🔴 server 7.117.0 起 `vision` 是三态(收 / 不收 / 没声明 `input`),
|
|
10822
|
+
# 没声明 ⇒ 键缺席 ⇒ 不再是必有键(此前由 `input` 恒算成布尔,对没声明的条目报 false)。
|
|
10823
|
+
required: [name, id, provider, reasoning]
|
|
10794
10824
|
additionalProperties: false
|
|
10795
10825
|
properties:
|
|
10796
10826
|
name: { type: string, description: 'the @mention handle (user-pickable model name).' }
|
|
10797
10827
|
id: { type: string, description: 'underlying model id (display, e.g. deepseek-v4-pro).' }
|
|
10798
10828
|
provider: { type: string }
|
|
10799
10829
|
reasoning: { type: boolean, description: 'a reasoning-tier model (picker can badge it).' }
|
|
10800
|
-
vision: { type: boolean, description: 'accepts image input.' }
|
|
10830
|
+
vision: { type: boolean, description: 'Whether this catalog row accepts image input — THREE states (server >= 7.117.0; before it a row that declared no `input` reported `false`): `true` = the row declares `input` and it contains "image"; `false` = declares `input` without it; ABSENT = the row declared no `input` (a hand-written catalog entry; the env lane and the config-center lane always declare it). ABSENT means UNKNOWN, not false: treat it as "send the image anyway and tell the user", do not read it as `false` and block the image locally — the server hands the image to the model for that same row at run time. Only an explicit `false` justifies "this model does not take images".' }
|
|
10801
10831
|
contextWindow: { type: integer, description: 'E4 (SHIPPED) — context-window token DENOMINATOR for the %-used gauge; omitted when unknown.' }
|
|
10802
10832
|
maxOutputTokens: { type: integer, description: 'E4 (SHIPPED) — single-call max output tokens; omitted when unknown.' }
|
|
10803
10833
|
supportedEffortLevels:
|
|
@@ -12260,6 +12290,16 @@ components:
|
|
|
12260
12290
|
toolName: { type: string }
|
|
12261
12291
|
summary: { type: string }
|
|
12262
12292
|
touchedPaths: {}
|
|
12293
|
+
allowRuleShadowed:
|
|
12294
|
+
type: array
|
|
12295
|
+
items: { $ref: '#/components/schemas/AllowRuleShadow' }
|
|
12296
|
+
description: >
|
|
12297
|
+
server >= 7.117.0 (core 7.42.0): the DURABLE dual of `ToolApprovalFrame.allowRuleShadowed` — the allow-layer members that matched this call
|
|
12298
|
+
and could not clear its ask (same shape and semantics; entries are secret-redacted again at the server's read boundary). Passed through whole
|
|
12299
|
+
on `GET /v1/approvals` and `/v1/approvals/stream`. Absent != "you have no rule". 🔴 The old key `shadowedRule` (a string, server <= 7.116.x)
|
|
12300
|
+
is RETIRED: the SQL backend rewrote its parked rows once at the 7.117.0 boot to `[{ member: "persisted", entries: [<old value>] }]`, but the
|
|
12301
|
+
file backend's pre-upgrade parked rows and run results already on disk (`checkpointGate.riskDescriptor`) keep the old key verbatim
|
|
12302
|
+
(text still redacted, no structured disclosure) — a consumer reading those must still tolerate the old string.
|
|
12263
12303
|
# server 7.16.0 [3684]② — durable 队列行归因两键(与活卡 tool_approval 帧的同名键语义同族、判据不同源;
|
|
12264
12304
|
# 两条腿对同一只 ask 的在场性可以不一致,两侧都只认在场、都禁把缺席读成否定)。
|
|
12265
12305
|
ruleSuggestions:
|
|
@@ -12479,6 +12519,13 @@ components:
|
|
|
12479
12519
|
the vocabulary's owner is still core and it has grown before, so read it the same way as everywhere
|
|
12480
12520
|
else: branch known words, keep a `default` arm, and treat an unknown one as "unknown origin", never
|
|
12481
12521
|
as "no origin". A row minted before the bit existed reads absent (unreported), not "no authority".
|
|
12522
|
+
boundGrantDigest: { type: string, description: 'server >= 7.118.0 — a parked `Skill` card''s grant-binding digest; same value as the live frame''s ToolApprovalFrame.boundGrantDigest.' }
|
|
12523
|
+
boundGrants:
|
|
12524
|
+
allOf: [{ $ref: '#/components/schemas/BoundSkillGrants' }]
|
|
12525
|
+
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.'
|
|
12526
|
+
classifierNoRuling:
|
|
12527
|
+
allOf: [{ $ref: '#/components/schemas/AutoModeFallbackCause' }]
|
|
12528
|
+
description: 'server >= 7.118.0 — only on a parked `classifier_no_ruling` card: why the auto-mode classifier did not rule.'
|
|
12482
12529
|
objective: { type: ['string', 'null'], description: 'task objective, null if unavailable.' }
|
|
12483
12530
|
input:
|
|
12484
12531
|
description: >
|
|
@@ -12740,7 +12787,11 @@ components:
|
|
|
12740
12787
|
them is not known" narrowed to this; it still does NOT say "never consumed"; see `engine_notice`
|
|
12741
12788
|
`task.user_steer_undrained{cause:"crashed"}`). Converged rows only, one row per (taskId, messageId),
|
|
12742
12789
|
newest 500. From server >=7.111.0 (S-966) the rows include SUBAGENT steers (recorded under the run on the
|
|
12743
|
-
receipt; same row shape
|
|
12790
|
+
receipt; same row shape; `messageId` = the subagent steer receipt's).
|
|
12791
|
+
`kind` = the engine LANE the input was queued on at acceptance (server >= 7.118.0 / core 7.44.0 #1362, closed two words):
|
|
12792
|
+
`steer` (priority `now` / `next` / absent) or `follow_up` (priority `later`, queued for the run's end); the crash notices
|
|
12793
|
+
split the same way (`task.user_steer_undrained` / `task.user_followup_undrained`, the latter with
|
|
12794
|
+
`detail: { followUp, cause: "crashed", taskId, sessionId }`). Read both words; branch on `cause`.
|
|
12744
12795
|
ABSENT = this read has nothing to report (zero rows, or the ledger could not be read —
|
|
12745
12796
|
the authoritative notice is the frame on the run stream); the `?message=` single-entry form never
|
|
12746
12797
|
carries it.
|
|
@@ -12750,7 +12801,7 @@ components:
|
|
|
12750
12801
|
additionalProperties: false
|
|
12751
12802
|
properties:
|
|
12752
12803
|
messageId: { type: string }
|
|
12753
|
-
kind: { type: string, enum: [steer] }
|
|
12804
|
+
kind: { type: string, enum: [steer, follow_up] }
|
|
12754
12805
|
taskId: { type: string, description: The run the input was accepted for (its 200/202 receipt). }
|
|
12755
12806
|
acceptedAt: { type: string, description: ISO timestamp. }
|
|
12756
12807
|
cause: { type: string, enum: [crashed] }
|
|
@@ -13550,6 +13601,20 @@ components:
|
|
|
13550
13601
|
🔴 NOT interchangeable with `taskId`: the workflow-child park lane mints no taskId at all (this
|
|
13551
13602
|
decision never landed on any run, so minting one would be a lie), and the other two decide lanes
|
|
13552
13603
|
mint no runId. Branch by `errorCode`, never fall back from one handle to the other.
|
|
13604
|
+
readableHolderRunId:
|
|
13605
|
+
type: string
|
|
13606
|
+
description: >-
|
|
13607
|
+
server >= 7.118.0, on `decide.workflow_parks_unreadable` (409) ONLY and only on its "the record cannot be read" cause: the
|
|
13608
|
+
readable record that holds the card at the highest generation (absent when there is none or a tie — never null). Where the
|
|
13609
|
+
work can be carried on — a CLUE, not a way to unblock this decision (the damaged record `runId` names keeps it undeliverable
|
|
13610
|
+
until it is repaired). SDK reads it as `DecideWorkflowParksUnreadableError.readableHolderRunId`.
|
|
13611
|
+
runIds:
|
|
13612
|
+
type: array
|
|
13613
|
+
items: { type: string }
|
|
13614
|
+
description: >-
|
|
13615
|
+
server >= 7.118.0, on `decide.workflow_park_forked` (409) ONLY: the resumed copies of one workflow run that all hold this card at
|
|
13616
|
+
the same generation (>= 2). Recovery = resume ONE of them, then decide again; retrying the decide never succeeds.
|
|
13617
|
+
SDK reads it as `DecideWorkflowParkForkedError.runIds`.
|
|
13553
13618
|
cause:
|
|
13554
13619
|
type: string
|
|
13555
13620
|
description: >-
|
|
@@ -13628,6 +13693,8 @@ components:
|
|
|
13628
13693
|
`not_found.resource` / `request.unprocessable` / `capability.unavailable` …). ALWAYS PRESENT on server >= 7.104.0
|
|
13629
13694
|
(S-607); before that it was absent when the original refusal carried no code. SDK:
|
|
13630
13695
|
`ResumeBlockedByPolicyError.blockedBy` (`undefined` when the key is absent or not a string — never defaulted).
|
|
13696
|
+
server >= 7.117.0 adds a third fold source: a stored skill `disallowedTools` entry the engine now reads as nothing (the submit leg's 400 `request.field_invalid` sentence)
|
|
13697
|
+
⇒ `blockedBy: "request.field_invalid"`, refused before the claim; the terminal-run steer leg answers it before minting the wake card.
|
|
13631
13698
|
|
|
13632
13699
|
ResumeLegConflictError:
|
|
13633
13700
|
description: >
|
|
@@ -14204,11 +14271,14 @@ components:
|
|
|
14204
14271
|
unattended leg run under `UNATTENDED_PERMISSION_MODE=dontAsk`).
|
|
14205
14272
|
AskClass:
|
|
14206
14273
|
type: string
|
|
14207
|
-
enum: [content_question, hook, ancestor, unverifiable_delete, transcript_write, org_rule, governance_unavailable, rule_unreadable, ask_rule, denial_limit_fallback, requires_user_interaction, operator_forced, shell_floor, write_protection_unresolved, probe, write_protection, tool_marks_mcp, tool_marks_a2a, policy_rra, artifact_shared_read, mode, unstated, ordinary]
|
|
14274
|
+
enum: [content_question, hook, ancestor, unverifiable_delete, transcript_write, org_rule, governance_unavailable, rule_unreadable, ask_rule, denial_limit_fallback, classifier_no_ruling, requires_user_interaction, operator_forced, shell_floor, write_protection_unresolved, probe, write_protection, tool_marks_mcp, tool_marks_a2a, policy_rra, artifact_shared_read, mode, unstated, ordinary]
|
|
14208
14275
|
description: >
|
|
14209
|
-
core `ASK_CLASSES` (twenty-two words as of core 7.40.0, twenty-three as of core 7.41.0 — 7.34.0 minted twenty; `shell_floor` joined in 7.39.0 #1260 and rides
|
|
14276
|
+
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 — 7.34.0 minted twenty; `shell_floor` joined in 7.39.0 #1260 and rides
|
|
14210
14277
|
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
|
|
14211
|
-
server >= 7.116.0) — which CLASS an ask falls in (the columns of the mode × class arbitration table).
|
|
14278
|
+
server >= 7.116.0; `classifier_no_ruling` joined in 7.43.0 #1353 and rides server >= 7.118.0) — which CLASS an ask falls in (the columns of the mode × class arbitration table).
|
|
14279
|
+
`classifier_no_ruling` = the auto-mode classifier did not rule (it could not run — `error` / `timeout` — or replied with no verdict —
|
|
14280
|
+
`parse_error`), so a person decides: the denial-limit fallback's row (bypass-immune, no allow rule clears it, the classifier is not asked
|
|
14281
|
+
again); the live frame and the inbox row also carry the cause as `classifierNoRuling`.
|
|
14212
14282
|
`requires_user_interaction` = the MCP server declared, in the tool's advertisement, `_meta["anthropic/requiresUserInteraction"] === true`
|
|
14213
14283
|
(only the boolean `true`): EVERY call of that tool asks the human, in EVERY mode including `bypassPermissions` (it is not one of the ten
|
|
14214
14284
|
RELEASABLE classes), and only the human's answer clears it — `settings.permissions.allow`, operator `APPROVAL_GRANT`, a skill's tool list +
|
|
@@ -15905,29 +15975,39 @@ components:
|
|
|
15905
15975
|
requiresRealApproval / shadowed / hook-produced / inherited-unresolved / ancestor-resolved /
|
|
15906
15976
|
anonymous asks. Absence reads as "THIS CARD has no such option", never as "this deployment has no
|
|
15907
15977
|
rule lane" (that is `capabilities.permissionRules`) and never as "your rule broke" (that is
|
|
15908
|
-
`persistedRuleShadowed`).
|
|
15978
|
+
`allowRuleShadowed`; `persistedRuleShadowed` on a server <= 7.116.x).
|
|
15909
15979
|
|
|
15910
15980
|
⚠️ REDEMPTION SCOPE is the live respond leg keyed by this frame's `approvalId` only; a replayed
|
|
15911
15981
|
offer on another replica is not a promise that it is still redeemable.
|
|
15912
15982
|
persistedRuleShadowed:
|
|
15913
15983
|
type: string
|
|
15914
|
-
description:
|
|
15915
|
-
server >= 7.13.0 (core 5.25.0 #144), ADDITIVE, "tool_approval" only
|
|
15916
|
-
|
|
15917
|
-
|
|
15918
|
-
|
|
15919
|
-
|
|
15920
|
-
|
|
15921
|
-
|
|
15922
|
-
|
|
15923
|
-
|
|
15924
|
-
|
|
15925
|
-
|
|
15926
|
-
|
|
15927
|
-
|
|
15928
|
-
|
|
15929
|
-
|
|
15930
|
-
The
|
|
15984
|
+
description: >-
|
|
15985
|
+
server >= 7.13.0 (core 5.25.0 #144), ADDITIVE, "tool_approval" only — this ask MATCHED one of the caller's persisted allow
|
|
15986
|
+
rules and that rule COULD NOT silence it; the value is the rule's VERBATIM text (secret-redacted + length-bounded; UNTRUSTED
|
|
15987
|
+
for display, like `message`). Absent != "you have no rule"; deliberately separate from `governanceForced`.
|
|
15988
|
+
RETIRED: server >= 7.117.0 (core 7.42.0, BREAKING rename, no alias) never mints this key on any face; its successor is
|
|
15989
|
+
`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).
|
|
15990
|
+
allowRuleShadowed:
|
|
15991
|
+
type: array
|
|
15992
|
+
items: { $ref: '#/components/schemas/AllowRuleShadow' }
|
|
15993
|
+
description: >
|
|
15994
|
+
server >= 7.117.0 (core 7.42.0), ADDITIVE, "tool_approval" only — BREAKING RENAME, replacing the retired string `persistedRuleShadowed`
|
|
15995
|
+
(no alias): the allow-layer members that MATCHED this call and could NOT clear this ask, one `{ member, entries, omitted? }` record per
|
|
15996
|
+
matching member (see AllowRuleShadow). `member` is one of `hook` / `deployment` / `persisted` / `skill` / `request`; `entries` are the
|
|
15997
|
+
entries that member matched, as their source spells them (UNTRUSTED for display like `message`, secret-redacted, <= 512 chars each,
|
|
15998
|
+
<= 32 listed; a `hook` record's `entries` is always the empty list); `omitted` counts the entries not listed.
|
|
15999
|
+
|
|
16000
|
+
The explaining sentence ("... matches this call but does not clear the ask — <reason>") is IN `message` (the engine writes it where it builds
|
|
16001
|
+
the card); this key is the structured twin of the same fact. Render a line from it ("your rule matched, but this call must be confirmed each
|
|
16002
|
+
time"): without it the only conclusion a user can draw is "my rule broke".
|
|
16003
|
+
|
|
16004
|
+
🔴 ABSENT != "you have no rule": present only when a member matched AND could not clear the ask; a match that cleared never becomes an ask.
|
|
16005
|
+
🔴 `member: "hook"` does not say whose hook (a deployment PreToolUse hook and, on the single-user lane, the request's own `settings.hooks`
|
|
16006
|
+
share one member). 🔴 Deliberately SEPARATE from `governanceForced` (that one: "who imposed the gate"; this one: "what happened to YOUR
|
|
16007
|
+
rule"). What the allow layer can NOT clear: any layer's deny / ask rule, operator `APPROVAL_REQUIRE` / `APPROVAL_NEVER_AUTO` /
|
|
16008
|
+
`MANUAL_MODE_SHELL_GATE=always`, write protection, sensitive-path writes, governance, a hook's ask, plan-mode asks. The durable dual is
|
|
16009
|
+
`gate.riskDescriptor.allowRuleShadowed` on the parked row (PendingCheckpoint.riskDescriptor). Rolling-upgrade caveat: a card stored by a
|
|
16010
|
+
<= 7.116.x replica (`card_json` carrying `persistedRuleShadowed`) cannot be read by a >= 7.117.0 replica — the replay skips it.
|
|
15931
16011
|
probeCause:
|
|
15932
16012
|
type: object
|
|
15933
16013
|
description: >
|
|
@@ -16124,6 +16204,24 @@ components:
|
|
|
16124
16204
|
`ASK_CLASSES` order, non-empty, `classes[0] === askClass`); one ask can belong to several, so read the whole
|
|
16125
16205
|
set, not just `askClass`. Present together with `askClass`; passed through verbatim in order (the server
|
|
16126
16206
|
neither sorts nor dedupes).
|
|
16207
|
+
boundGrantDigest:
|
|
16208
|
+
type: string
|
|
16209
|
+
description: >-
|
|
16210
|
+
server >= 7.118.0 (core 7.43.0 #1348 / #1357), "tool_approval" only — on a call of the engine's `Skill` tool: the hash of what
|
|
16211
|
+
approving it ALSO grants (the skill name + its two lists, canonical spelling); absent on every other card. Same value as the
|
|
16212
|
+
parked row's / the inbox row's twin (the live card and its parked twin reconcile by `(toolCallId, boundGrantDigest)`); passed
|
|
16213
|
+
through verbatim. A live "allow for this session" is remembered under "this tool + this digest" and answers only an ask with the
|
|
16214
|
+
same digest (same skill, same lists); a different skill or a changed list asks again. The durable `remember: "session"` never
|
|
16215
|
+
remembers a skill card.
|
|
16216
|
+
boundGrants:
|
|
16217
|
+
allOf: [{ $ref: '#/components/schemas/BoundSkillGrants' }]
|
|
16218
|
+
description: 'server >= 7.118.0, "tool_approval" only — the value `boundGrantDigest` hashes; present iff it is. DEPLOYMENT TEXT: render with contextual escaping.'
|
|
16219
|
+
classifierNoRuling:
|
|
16220
|
+
allOf: [{ $ref: '#/components/schemas/AutoModeFallbackCause' }]
|
|
16221
|
+
description: >-
|
|
16222
|
+
server >= 7.118.0 (core 7.43.0 #1353), "tool_approval" only, only on a `classifier_no_ruling` card — WHY the auto-mode classifier
|
|
16223
|
+
did not rule (`error` / `timeout` = it could not run; `parse_error` = it replied with no verdict), so a person decides. The server
|
|
16224
|
+
screens it with the engine's closed set (an out-of-set word is withheld). Display only.
|
|
16127
16225
|
denialLimitFallback: { $ref: '#/components/schemas/DenialLimitFallback' }
|
|
16128
16226
|
parked:
|
|
16129
16227
|
type: boolean
|
|
@@ -16320,12 +16418,19 @@ components:
|
|
|
16320
16418
|
promise and the per-leg index rules are stated verbatim on ToolApprovalFrame.ruleOffers.
|
|
16321
16419
|
persistedRuleShadowed:
|
|
16322
16420
|
type: string
|
|
16421
|
+
description: >-
|
|
16422
|
+
server >= 7.13.0 (core 5.25.0 #144), ADDITIVE — the persisted rule this ask matched but which could not silence it; frame and
|
|
16423
|
+
card carry the SAME material (see ToolApprovalFrame.persistedRuleShadowed).
|
|
16424
|
+
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
|
|
16425
|
+
carrying it); its successor is `allowRuleShadowed`. Kept for servers in [7.13.0, 7.117.0) only (sdk 16.3.0).
|
|
16426
|
+
allowRuleShadowed:
|
|
16427
|
+
type: array
|
|
16428
|
+
items: { $ref: '#/components/schemas/AllowRuleShadow' }
|
|
16323
16429
|
description: >
|
|
16324
|
-
ADDITIVE (server >= 7.
|
|
16325
|
-
|
|
16326
|
-
|
|
16327
|
-
`
|
|
16328
|
-
the stored `card_json` and the replayed frame carry the SAME material.
|
|
16430
|
+
ADDITIVE (server >= 7.117.0 / core 7.42.0, BREAKING RENAME of the retired `persistedRuleShadowed`): the allow-layer members that matched this
|
|
16431
|
+
call and could not clear this ask. Semantics, the five members, the always-empty `hook` entries, the explaining sentence living in `message`,
|
|
16432
|
+
the ABSENT != "you have no rule" clause and the separation from `governanceForced` are stated verbatim on ToolApprovalFrame.allowRuleShadowed
|
|
16433
|
+
— the live frame, the stored `card_json`, the replayed frame and the `approval_request` card carry the SAME material.
|
|
16329
16434
|
fromSubagent: { type: boolean, enum: [true], description: 'Present ⇔ the ask comes from a DELEGATED child — the explicit discriminator (a worker cannot forge it).' }
|
|
16330
16435
|
sourceTaskId: { type: string, description: 'The child''s core session id — row-join value + fallback discriminator.' }
|
|
16331
16436
|
sourceAgentName: { type: string, description: 'The child agent''s display name, redacted. UNTRUSTED.' }
|
|
@@ -16450,6 +16555,37 @@ components:
|
|
|
16450
16555
|
`.strict()`). The eleven known words are exported at runtime as the SDK's `ASK_ORIGINS` (core 7.32.1
|
|
16451
16556
|
`ASK_ORIGINS`, verbatim and in order) — known words, NOT an enforcement table.
|
|
16452
16557
|
|
|
16558
|
+
AllowRuleMember:
|
|
16559
|
+
type: string
|
|
16560
|
+
enum: [hook, deployment, persisted, skill, request]
|
|
16561
|
+
description: >
|
|
16562
|
+
The closed five-word vocabulary of `AllowRuleShadow.member` (server >= 7.117.0 / core 7.42.0 `ALLOW_RULE_MEMBERS`, verbatim and in the order the engine reads the members). The SDK exports
|
|
16563
|
+
it at runtime as `ALLOW_RULE_MEMBERS`. Owner = core; read it as an open string with a default arm (core may add a member and the server follows).
|
|
16564
|
+
|
|
16565
|
+
AllowRuleShadow:
|
|
16566
|
+
type: object
|
|
16567
|
+
additionalProperties: false
|
|
16568
|
+
required: [member, entries]
|
|
16569
|
+
description: >
|
|
16570
|
+
One allow-layer DISCLOSURE record (`ToolApprovalFrame.allowRuleShadowed` / `ApprovalCard.allowRuleShadowed` / the durable
|
|
16571
|
+
`PendingCheckpoint.riskDescriptor.allowRuleShadowed`; server >= 7.117.0 / core 7.42.0 `AllowRuleShadow`): one member of the allow layer that
|
|
16572
|
+
MATCHED the call and could NOT clear its ask. At most one record per member, at most five per ask. The vocabulary owner is core; the server
|
|
16573
|
+
drops the whole key on a member outside the five words, so a >= 7.117.0 server only ever sends these five — still read `member` as an open
|
|
16574
|
+
string with a default arm (core may add members and the server follows).
|
|
16575
|
+
properties:
|
|
16576
|
+
member:
|
|
16577
|
+
$ref: '#/components/schemas/AllowRuleMember'
|
|
16578
|
+
description: >
|
|
16579
|
+
Which allow-layer member matched, in the order the engine reads them: `hook` = a PreToolUse hook's explicit `allow` (a deployment hook or,
|
|
16580
|
+
on the single-user lane, the request's own `settings.hooks` — the key cannot say which) · `deployment` = the operator's `APPROVAL_GRANT` ·
|
|
16581
|
+
`persisted` = a person's stored allow rules · `skill` = an activated skill's `allowedTools` · `request` = the request's `settings.permissions.allow`.
|
|
16582
|
+
entries:
|
|
16583
|
+
type: array
|
|
16584
|
+
maxItems: 32
|
|
16585
|
+
items: { type: string, maxLength: 512 }
|
|
16586
|
+
description: 'The entries that member matched, as their source spells them (UNTRUSTED for display, secret-redacted, <= 512 chars each, <= 32 listed). Always the empty list for `hook` — its word is about the call, not about an entry.'
|
|
16587
|
+
omitted: { type: integer, minimum: 1, description: 'How many matched entries were not listed (beyond the cap). Absent = none omitted.' }
|
|
16588
|
+
|
|
16453
16589
|
RuleOffersAbsence:
|
|
16454
16590
|
type: string
|
|
16455
16591
|
enum: [mandated, shadowed, lane_cannot_speak]
|
|
@@ -16458,7 +16594,7 @@ components:
|
|
|
16458
16594
|
`ApprovalCard.ruleOffersAbsence`; server >= 7.55.0 / core #490 fix ②, S-15). The engine names which
|
|
16459
16595
|
door is shut: `mandated` = this ask must be confirmed every time (🔴 this arm must NOT point the user
|
|
16460
16596
|
at writing a rule — a rule would not silence it, and saying so sends them into a loop); `shadowed` = a
|
|
16461
|
-
|
|
16597
|
+
an allow-layer member matched but could not clear it (the machine-readable twin of `allowRuleShadowed`; `persistedRuleShadowed` on a server <= 7.116.x);
|
|
16462
16598
|
`lane_cannot_speak` = the engine could mint no coverable form for this command.
|
|
16463
16599
|
|
|
16464
16600
|
Engine-side MUTUALLY EXCLUSIVE with `ruleOffers`. 🔴 ABSENCE IS NOT AN ASSERTION — it covers both
|
|
@@ -16525,21 +16661,25 @@ components:
|
|
|
16525
16661
|
RuleOfferMatch:
|
|
16526
16662
|
type: string
|
|
16527
16663
|
description: >
|
|
16528
|
-
The MATCH FORM of a persisted rule — the server's `PERSISTED_RULE_MATCHES`, verbatim (the vocabulary
|
|
16664
|
+
The MATCH FORM of a persisted rule — the server's `PERSISTED_RULE_MATCHES`, verbatim and in order (the vocabulary
|
|
16529
16665
|
is OWNED BY CORE; the server only transcribes it and pins set equality, so core adding a member turns
|
|
16530
|
-
the server red at compile time).
|
|
16666
|
+
the server red at compile time). ONE vocabulary for two faces: a card's rule offer (`RuleOffer.match`, the
|
|
16667
|
+
server's card schema validates it with this very enum) and a persisted rule row (`GET /v1/rules`).
|
|
16531
16668
|
|
|
16532
16669
|
`exact` = this one command only (`Bash(git status)`). `prefix` = the historical colon-star
|
|
16533
16670
|
word-boundary prefix (`Bash(git status:*)`) and the ONLY spelling for a compound prefix form; it stays
|
|
16534
16671
|
a permanent compatibility read. `wildcard` = the trailing space-star form (`Bash(npm run *)`), the
|
|
16535
|
-
same predicate as `prefix` on a single command body
|
|
16536
|
-
|
|
16537
|
-
|
|
16538
|
-
|
|
16539
|
-
|
|
16540
|
-
|
|
16541
|
-
|
|
16542
|
-
|
|
16672
|
+
same predicate as `prefix` on a single command body. `subpath` = the `Read(//abs-dir/**)` directory form,
|
|
16673
|
+
which never holds a command. `path` (server >= 7.67.0) = the path-family pattern grammar for deny/ask/allow
|
|
16674
|
+
rules (`//abs`, `~/`, root-relative, cwd-relative; `*` within a segment, `**` across segments). `name` /
|
|
16675
|
+
`name_prefix` (server >= 7.118.0 / core 7.43.0 #1318) = the `Skill` name grammar: `Skill(<name>)` exact,
|
|
16676
|
+
`Skill(<prefix>:*)` a name prefix with NO word boundary; a skill card's "don't ask again" offer carries
|
|
16677
|
+
`match: "name"`. A replica rolled back to 7.117.x reads a `name` / `name_prefix` row as a damaged row.
|
|
16678
|
+
|
|
16679
|
+
🔴 SEVEN MEMBERS, closed: the server validates the member on both faces. A consumer type narrower than the
|
|
16680
|
+
mint point is a lie — it lets a `switch` claim exhaustiveness the wire does not honour (SDK <= 16 declared
|
|
16681
|
+
four here, so a `path` offer and every 7.118.0 skill-card offer were dropped as malformed).
|
|
16682
|
+
enum: [exact, prefix, wildcard, subpath, path, name, name_prefix]
|
|
16543
16683
|
|
|
16544
16684
|
RuleOfferUncoveredReason:
|
|
16545
16685
|
type: string
|
|
@@ -17115,25 +17255,66 @@ components:
|
|
|
17115
17255
|
additionalProperties: false
|
|
17116
17256
|
properties:
|
|
17117
17257
|
label: { type: string, maxLength: 256 }
|
|
17118
|
-
|
|
17119
|
-
|
|
17120
|
-
enum: [policy]
|
|
17258
|
+
decidedBy:
|
|
17259
|
+
allOf: [{ $ref: '#/components/schemas/ApprovalDecidedBy' }]
|
|
17121
17260
|
description: >-
|
|
17122
|
-
server
|
|
17123
|
-
|
|
17124
|
-
|
|
17125
|
-
|
|
17126
|
-
`
|
|
17127
|
-
|
|
17128
|
-
|
|
17129
|
-
|
|
17130
|
-
|
|
17131
|
-
|
|
17132
|
-
|
|
17133
|
-
|
|
17134
|
-
|
|
17135
|
-
|
|
17136
|
-
|
|
17261
|
+
server >= 7.118.0 — WHO made this decision (one key read the same way by all three decision doors; see
|
|
17262
|
+
ApprovalDecidedBy). ABSENT = `person`. This door's own answers (both refused before the decision, the card does
|
|
17263
|
+
not move): a `policy` allow on a person-required card ⇒ 400 `request.field_conflict`; outside the closed set ⇒
|
|
17264
|
+
400 `request.field_invalid`; any value on a direct-door worker ⇒ 400 `decided_by_not_in_proof`. An older server
|
|
17265
|
+
(no `approvalDecidedBy` bit) has a strict body here ⇒ 400 `request.body_shape`, nothing decided.
|
|
17266
|
+
|
|
17267
|
+
ApprovalDecidedBy:
|
|
17268
|
+
type: string
|
|
17269
|
+
enum: [person, policy]
|
|
17270
|
+
description: >
|
|
17271
|
+
server >= 7.118.0 (`APPROVAL_DECIDED_BY`, verbatim and in order) — WHO made an approval decision, one key on all three decision
|
|
17272
|
+
doors (live `POST /v1/tool-approvals/{approvalId}/respond`, durable `POST /v1/tasks/{taskId}/asks/{askId}/decision`, durable
|
|
17273
|
+
`POST /v1/approvals/{sessionId}/decide`). `person` = a person decided through the approval channel (ABSENT means this);
|
|
17274
|
+
`policy` = this deployment's own policy decided (a rule / posture answering on someone's behalf, a headless lane) — BOTH
|
|
17275
|
+
directions: the engine records `tool_end.gate.settlement.kind` = `policy_allowed` / `policy_refused` (`who.party: "policy"`).
|
|
17276
|
+
The only verb constraint is the engine's own: a person-required card (`requiresRealApproval`) cannot be ALLOWED by a policy.
|
|
17277
|
+
Send it only when `GET /v1/capabilities` carries `approvalDecidedBy: true`. Outside the closed set (including `sla_timeout` —
|
|
17278
|
+
"the wait ran out with nobody answering" is said only by the server's own deadline sweep — and the old `human` / `timeout`
|
|
17279
|
+
words) ⇒ 400 before the decision. It REPLACES the 7.93.3–7.117.x key `settledBy` (deny-only, the same fact), deleted
|
|
17280
|
+
by server 7.118.0 with no alias: sending `settledBy` ⇒ 400 `request.body_shape` on all three doors.
|
|
17281
|
+
`plan_review` does not take it (400 `request.body_shape`).
|
|
17282
|
+
|
|
17283
|
+
ApprovalReach:
|
|
17284
|
+
type: string
|
|
17285
|
+
enum: [none]
|
|
17286
|
+
description: >
|
|
17287
|
+
The closed set of the request key `approvalReach` (core 7.43.0 #1354 `APPROVAL_REACH`, verbatim): `none` — nobody on this run can be
|
|
17288
|
+
asked to approve. There is no positive word ("a person is reachable" is the `interactionPosture: "interactive"` promise).
|
|
17289
|
+
|
|
17290
|
+
AutoModeFallbackCause:
|
|
17291
|
+
type: string
|
|
17292
|
+
enum: [error, timeout, parse_error]
|
|
17293
|
+
description: >
|
|
17294
|
+
core 7.43.0 `AUTO_MODE_FALLBACK_CAUSES`, verbatim and in order — why the auto-mode classifier did not rule: `error` / `timeout` (it could
|
|
17295
|
+
not run), `parse_error` (it replied with no verdict). Carried as `classifierNoRuling` on a `classifier_no_ruling` card (server >= 7.118.0).
|
|
17296
|
+
|
|
17297
|
+
BoundSkillGrants:
|
|
17298
|
+
type: object
|
|
17299
|
+
description: >
|
|
17300
|
+
core 7.43.0 `BoundSkillGrants` (three keys, verbatim) — what approving an engine `Skill` card ALSO grants: `skill` = the loaded skill's
|
|
17301
|
+
name (`null` = the call names no loaded skill; it grants nothing), `allowedTools` = what activation lets be used without asking,
|
|
17302
|
+
`disallowedTools` = what activation refuses for the rest of the run (canonical spelling: de-duplicated, sorted). DEPLOYMENT TEXT.
|
|
17303
|
+
required: [skill, allowedTools, disallowedTools]
|
|
17304
|
+
additionalProperties: false
|
|
17305
|
+
properties:
|
|
17306
|
+
skill: { type: ['string', 'null'] }
|
|
17307
|
+
allowedTools: { type: array, items: { type: string } }
|
|
17308
|
+
disallowedTools: { type: array, items: { type: string } }
|
|
17309
|
+
|
|
17310
|
+
SteerReplayFacet:
|
|
17311
|
+
type: string
|
|
17312
|
+
enum: [text, trusted, priority, actor]
|
|
17313
|
+
description: >
|
|
17314
|
+
core 7.44.0 #1374 `STEER_REPLAY_FACETS`, verbatim and in order — the facets on which a replayed steering input id differs from the
|
|
17315
|
+
accepted one. Carried as `detail.differs` (a non-empty subset, the engine's order) on 409 `steering.duplicate_input_id` (server >= 7.118.0):
|
|
17316
|
+
contains `text` ⇒ a different instruction, reissue with a fresh key; no `text` ⇒ the SAME instruction — do NOT change the key (it
|
|
17317
|
+
would queue twice), replay with the original values.
|
|
17137
17318
|
|
|
17138
17319
|
AskDecisionAck:
|
|
17139
17320
|
type: object
|
|
@@ -18273,7 +18454,7 @@ components:
|
|
|
18273
18454
|
type: object
|
|
18274
18455
|
description: 'Approval seam: wired form, which seat it came from, and (effective half) what it adjudicates to.'
|
|
18275
18456
|
properties:
|
|
18276
|
-
form: { type: string, enum: [callback, allow, deny, absent] }
|
|
18457
|
+
form: { type: string, enum: [callback, allow, deny, absent, declared_none], description: '`declared_none` (server >= 7.118.0 / core 7.43.0): the run declared nobody on it can be asked (`approvalReach: "none"`), so a callback beside the declaration is not this run''s approver.' }
|
|
18277
18458
|
provenance: { type: string, enum: [spec, deps] }
|
|
18278
18459
|
effective: { type: string, enum: [human_reachable, auto_allow, auto_deny, park_only, unresolved] }
|
|
18279
18460
|
question:
|
|
@@ -19651,17 +19832,11 @@ components:
|
|
|
19651
19832
|
engine's catalogue, not on this wire. READ IT; do not switch over a closed list. A name the grammar
|
|
19652
19833
|
does not know is still a 400 on the input faces.
|
|
19653
19834
|
match:
|
|
19654
|
-
|
|
19655
|
-
|
|
19656
|
-
|
|
19657
|
-
|
|
19658
|
-
`
|
|
19659
|
-
compound prefix) · `wildcard` = the space-star form, the SAME predicate as `prefix` on a single
|
|
19660
|
-
command body · `subpath` = the directory (read-containment) form, which never admits a command ·
|
|
19661
|
-
`path` = NEW in server >= 7.67.0, the path-family pattern grammar for deny/ask/allow rules (`//abs`,
|
|
19662
|
-
`~/`, root-relative, cwd-relative; `*` within a segment, `**` across segments).
|
|
19663
|
-
SDK 8.7.0 and earlier declared only the first two — a consumer rendering this cell off a closed list
|
|
19664
|
-
must add the other three.
|
|
19835
|
+
allOf: [{ $ref: '#/components/schemas/RuleOfferMatch' }]
|
|
19836
|
+
description: >
|
|
19837
|
+
The match form — the same seven-member vocabulary as a card's rule offer (`RuleOfferMatch`, the server's
|
|
19838
|
+
`PERSISTED_RULE_MATCHES`): `exact` / `prefix` / `wildcard` / `subpath` / `path`, and from server >= 7.118.0
|
|
19839
|
+
`name` / `name_prefix` (a `Skill(<name>)` / `Skill(<prefix>:*)` rule).
|
|
19665
19840
|
command: { type: string, description: 'What the matcher actually compares against, parsed out of `rule` at mint time and stored so no consumer re-parses. Trust this + `match`, not your own parse of `rule`.' }
|
|
19666
19841
|
adds:
|
|
19667
19842
|
type: array
|