@sema-agent/sdk 16.3.0 → 17.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +138 -2
- package/dist/errors.d.ts +91 -14
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +104 -12
- package/dist/errors.js.map +1 -1
- package/dist/events.d.ts +8 -0
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js.map +1 -1
- package/dist/health.d.ts +10 -1
- package/dist/health.d.ts.map +1 -1
- package/dist/health.js.map +1 -1
- package/dist/index.d.ts +16 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -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/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 +12 -1
- package/dist/resources/sessions.d.ts.map +1 -1
- package/dist/resources/sessions.js +12 -1
- package/dist/resources/sessions.js.map +1 -1
- package/dist/resources/tasks.d.ts +12 -1
- package/dist/resources/tasks.d.ts.map +1 -1
- package/dist/resources/tasks.js +12 -1
- package/dist/resources/tasks.js.map +1 -1
- package/dist/resources/tool-approvals.d.ts +53 -90
- package/dist/resources/tool-approvals.d.ts.map +1 -1
- package/dist/resources/tool-approvals.js +24 -12
- package/dist/resources/tool-approvals.js.map +1 -1
- package/dist/settings.d.ts +7 -8
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +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/transport.d.ts +3 -1
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +4 -1
- package/dist/transport.js.map +1 -1
- package/dist/types.d.ts +114 -44
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +23 -0
- package/dist/types.js.map +1 -1
- package/openapi.yaml +283 -160
- package/package.json +1 -1
package/openapi.yaml
CHANGED
|
@@ -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.
|
|
@@ -1029,7 +1031,7 @@ paths:
|
|
|
1029
1031
|
Use POST /v1/runs/{taskId}/cancel for a run-level stop.
|
|
1030
1032
|
errorCode "steering.not_running" (reused verbatim) — terminal / not accepting, or the stream
|
|
1031
1033
|
settled in the race window ("run just finished on this replica — no in-flight turn to interrupt").
|
|
1032
|
-
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
|
|
1033
1035
|
content or at a DIFFERENT tier (core's replay identity includes the normalized tier: the same key
|
|
1034
1036
|
that once went through the steer door (default tier) then hits this door (now) is refused —
|
|
1035
1037
|
"idempotent success without the cut" would be a dispositive lie). Reissue with a fresh key.
|
|
@@ -2533,6 +2535,16 @@ paths:
|
|
|
2533
2535
|
'409': { $ref: '#/components/responses/Conflict' }
|
|
2534
2536
|
'422': { $ref: '#/components/responses/UnprocessableEntity' }
|
|
2535
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' }
|
|
2536
2548
|
|
|
2537
2549
|
/v1/sessions/{sessionId}/background:
|
|
2538
2550
|
parameters:
|
|
@@ -3322,8 +3334,11 @@ paths:
|
|
|
3322
3334
|
`approval_binding_mismatch` (`errorCode`, `field`: boundCallId|boundInputHash|answer — the third
|
|
3323
3335
|
member since core 5.7.0/RB-459: the content-ask `answer` payload was refused) — your binding does
|
|
3324
3336
|
NOT match the current pending action (TOCTOU/swap). The SDK splits these by `errorCode` into
|
|
3325
|
-
ConflictError vs ApprovalBindingMismatchError
|
|
3326
|
-
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
|
|
3327
3342
|
intercepted in steady state by the `approval_stale` second trigger source below — the server
|
|
3328
3343
|
compares the echoed boundCallId against the current pending pre-CAS and answers 409
|
|
3329
3344
|
`approval_stale` + `currentPending`; this code's boundCallId arm covers only the remaining race
|
|
@@ -3351,7 +3366,8 @@ paths:
|
|
|
3351
3366
|
decide either resumes the parked host or starts that new run (both land on the byte-identical
|
|
3352
3367
|
acceptance shape) — a host session already busy with another run instead answers the ordinary 409
|
|
3353
3368
|
`conflict.session_active_run`.
|
|
3354
|
-
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`
|
|
3355
3371
|
— the workflow run's record carries no readable `parks[]` list (the ENGINE's own park-ownership
|
|
3356
3372
|
ledger, and the single source this lane joins the caller's checkpoint against). A record written by
|
|
3357
3373
|
an engine older than 7.17.0 does not carry the key at all, or one entry is damaged and the whole list
|
|
@@ -3375,6 +3391,15 @@ paths:
|
|
|
3375
3391
|
there is no mapper branch, class, or type-union member to delete or keep for it — unlike
|
|
3376
3392
|
`decide.workflow_host_not_parked` above, an OPEN decide.* code this SDK never gave a name to degrades
|
|
3377
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`).
|
|
3378
3403
|
SIXTH group (A-075.10, TIME-BASED retry-later pair): `resume.usage_window_exhausted` (#449 G1,
|
|
3379
3404
|
core 5.60.1 — the deployment governance window on this run's ledger key is full; NOTHING was
|
|
3380
3405
|
consumed or unpinned, the SAME token with the SAME decision redeems once the window slides) and
|
|
@@ -4232,32 +4257,14 @@ paths:
|
|
|
4232
4257
|
substitution), the command bytes were unreadable, or this ask belongs to one of the seven
|
|
4233
4258
|
families that never get offers — so do NOT diagnose the deployment's rule lane from one
|
|
4234
4259
|
frame or from that one refusal.
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
enum: [policy]
|
|
4260
|
+
decidedBy:
|
|
4261
|
+
allOf: [{ $ref: '#/components/schemas/ApprovalDecidedBy' }]
|
|
4238
4262
|
description: >-
|
|
4239
|
-
server
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
(`POST /v1/approvals/{sessionId}/decide`, ApprovalDecision.settledBy) — see that schema for
|
|
4245
|
-
the full account. `decision:"deny"` ONLY: an allow-family decision carrying this key is a
|
|
4246
|
-
contradiction, refused BEFORE THE DECISION (400 `request.body_shape`, the same sentence as
|
|
4247
|
-
the durable leg) rather than after — the engine's refusal cannot travel back up this HTTP
|
|
4248
|
-
leg, and by then this door's own side effects (the session allow-all grant, a persisted
|
|
4249
|
-
rule, a DECIDED row that replays as a plain approval) have already landed. Because the
|
|
4250
|
-
refusal precedes the decision, the ask is NOT settled: the card is still open and the same
|
|
4251
|
-
`approvalId` can be answered again. The PARKED late-acceptance arm reads the same key.
|
|
4252
|
-
|
|
4253
|
-
SHAPE, not word: a non-string is a 400. A word core does NOT know travels UNTOUCHED — this
|
|
4254
|
-
door still answers 200 (its 200 only ever meant "your answer was delivered") and the ENGINE
|
|
4255
|
-
refuses it fail-closed, settling as `approver_contract`, NEVER as `policy_refused`: a
|
|
4256
|
-
misspelled word must not be rendered as "the deployment's policy refused".
|
|
4257
|
-
|
|
4258
|
-
No capability bit: an older server ignores unknown keys and answers 200, so the refusal is
|
|
4259
|
-
still read as a person's there. Judge by server version, or read back whether
|
|
4260
|
-
`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.
|
|
4261
4268
|
note:
|
|
4262
4269
|
type: string
|
|
4263
4270
|
maxLength: 2048
|
|
@@ -4349,7 +4356,7 @@ paths:
|
|
|
4349
4356
|
application/json:
|
|
4350
4357
|
schema: { $ref: '#/components/schemas/AskDecisionAck' }
|
|
4351
4358
|
'400':
|
|
4352
|
-
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`.'
|
|
4353
4360
|
content:
|
|
4354
4361
|
application/json:
|
|
4355
4362
|
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
@@ -6591,6 +6598,13 @@ components:
|
|
|
6591
6598
|
declared exceeds the roster bound (name 128, contract id / revision 64, `modelGate` 64); the caller cannot route
|
|
6592
6599
|
around it and a verbatim retry never changes it (DETERMINISTIC — the SDK does not auto-retry these codes; they land
|
|
6593
6600
|
on ServiceStateError via the 503 tail, with no wait hint); only the deployment fixing its declaration helps.
|
|
6601
|
+
Server >= 7.119.0 adds `state.scenario_assignment_unknown` to this slot — the center's scenario assignment for the principal names a
|
|
6602
|
+
scenario this deployment does not have; refused before any side effect or billing, zero run, body `detail: { scenario, source }`, NO
|
|
6603
|
+
`Retry-After` (resending before the assignment is re-pointed — or the scenario added back to the deployment — is pointless; then retry
|
|
6604
|
+
verbatim, the serving replica reads the new assignment after its own cache window, one minute by default). The caller did nothing wrong:
|
|
6605
|
+
an explicit unknown `scenario` in the body is still 400 `scenario_unknown`. DETERMINISTIC for the SDK's retry arm (see
|
|
6606
|
+
`ErrorResponse.errorCode`); lands on ServiceStateError. On a resume leg the same refusal folds into 409 `resume_blocked_by_policy`
|
|
6607
|
+
(`blockedBy: state.scenario_assignment_unknown`).
|
|
6594
6608
|
Which of them carry a wait hint is stated ONCE, under `headers.Retry-After` below (kept in one place so
|
|
6595
6609
|
the two statements cannot drift apart).
|
|
6596
6610
|
headers:
|
|
@@ -6849,16 +6863,6 @@ components:
|
|
|
6849
6863
|
name: { type: string }
|
|
6850
6864
|
description: { type: string }
|
|
6851
6865
|
content: { type: string }
|
|
6852
|
-
manifest:
|
|
6853
|
-
type: object
|
|
6854
|
-
additionalProperties: true
|
|
6855
|
-
deprecated: true
|
|
6856
|
-
description: >
|
|
6857
|
-
RETIRED — server >= 7.116.0 rejects the WHOLE skill when this key is present on a delegated agent's skill (unknown key:
|
|
6858
|
-
`agent.skills must be …`; a top-level skill answers 400 `request.field_invalid` "skill.manifest was retired in core 7.41.0 — …").
|
|
6859
|
-
core 7.41.0 retired the skill manifest (hard break, no alias). Rewrite `manifest.allowTools` to the skill's
|
|
6860
|
-
`allowedTools` (an allow-rule source, not a fence); `manifest.allowPaths` has no replacement (use the session rules'
|
|
6861
|
-
`allowDirs`). Older servers (<= 7.115.x) still accept it; the key stays declared here until the next major.
|
|
6862
6866
|
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).' }
|
|
6863
6867
|
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).' }
|
|
6864
6868
|
files:
|
|
@@ -7121,7 +7125,7 @@ components:
|
|
|
7121
7125
|
key on every submit of that session). ⚠️ A server < 7.104.0 answers 400 `request.body_shape` (unknown key).
|
|
7122
7126
|
permissionMode:
|
|
7123
7127
|
type: string
|
|
7124
|
-
enum: [default, plan, acceptEdits, bypassPermissions, auto]
|
|
7128
|
+
enum: [default, plan, acceptEdits, bypassPermissions, auto, dontAsk]
|
|
7125
7129
|
description: >
|
|
7126
7130
|
🔴 K-32 — server >= 7.106.0 (S-877 / core 7.34.0 design/1111 "one permission mode"), READ THIS FIRST; the long text
|
|
7127
7131
|
below describes <= 7.105.0. The effective mode passes through VERBATIM to the engine (`TaskSpec.permissionMode`); the
|
|
@@ -7134,13 +7138,16 @@ components:
|
|
|
7134
7138
|
`config.permission_mode_unavailable`. `bypassPermissions` RELEASES the ten releasable ask classes (server >= 7.115.0: the tenth is `unstated`, a server-tool call nobody stated)
|
|
7135
7139
|
and DISCLOSES each (`tool_end.gate.disposition.released` + one `engine_notice` `permission.ask_released_by_mode` per (mode, class) per run);
|
|
7136
7140
|
immune classes (deployment-authored, ask / org rules, hooks, ancestors, questions …) still ask; a write whose protection
|
|
7137
|
-
cannot be judged is refused (`deniedBy: mode`). Request vocabulary:
|
|
7138
|
-
|
|
7141
|
+
cannot be judged is refused (`deniedBy: mode`). Request vocabulary: SIX words (server >= 7.119.0) — `dontAsk`, the engine's
|
|
7142
|
+
"auto-refuse" mode, is accepted and passes through verbatim: the ENGINE refuses the calls that would ask (no card, no parked row,
|
|
7143
|
+
`tool_end.gate.disposition = { kind: denied, deniedBy: mode, mode: dontAsk, … }`), the run ends normally, and it is never looser
|
|
7144
|
+
than `default`. One exception: a PreToolUse hook's OWN ask, when no other class in that ask's set backs it, is still handed to a person
|
|
7145
|
+
(the engine's `arbitrateByMode`: the `hook` class on a row without `hookAskFloor`). Send it only when `capabilities.permissionModes` carries it: that bit absent = server <= 7.118.x, whose request
|
|
7146
|
+
vocabulary is FIVE words — `dontAsk` on a fresh submit is 400 `request.field_invalid` there. The three sentences below — "omitting both carriers means no mode-derived gate", "`auto`
|
|
7139
7147
|
is armed by the principal's entitlement independent of the mode word", "`bypassPermissions` adds no mode-derived gate" —
|
|
7140
7148
|
hold for <= 7.105.0 only.
|
|
7141
|
-
CC-parity permission-mode INTENT — the
|
|
7142
|
-
|
|
7143
|
-
`dontAsk`). The frontend (TUI shell / web portal) carries the RAW mode; the SERVICE interprets it TIGHTEN-ONLY vs the deployment baseline:
|
|
7149
|
+
CC-parity permission-mode INTENT — the values THIS wire's closed submit-gate accepts (the first five on server <= 7.118.x,
|
|
7150
|
+
plus `dontAsk` from 7.119.0 = the engine's whole mode vocabulary, `capabilities.permissionModes`). The frontend (TUI shell / web portal) carries the RAW mode; the SERVICE interprets it TIGHTEN-ONLY vs the deployment baseline:
|
|
7144
7151
|
`plan` ⇒ mount present_plan (core enablePlanMode) + read-only hands (CC EnterPlanMode research);
|
|
7145
7152
|
`default` adds the manual ask gate on non-exempt writes (scratchpad dirs / remembered session
|
|
7146
7153
|
exemptions still auto-allow — not literally every Write/Edit); the server emits the SAME gate-policy
|
|
@@ -7151,7 +7158,7 @@ components:
|
|
|
7151
7158
|
as plain `default`; `acceptEdits` adds the SAME gate narrowed to auto-allow inside the working
|
|
7152
7159
|
directory — but only when the service has a trusted working directory to scope it to; on a sandbox
|
|
7153
7160
|
execution lane with none wired, `acceptEdits` degrades to asking on every non-exempt write, same
|
|
7154
|
-
as `default`; `bypassPermissions` adds no mode-derived gate at all. None of
|
|
7161
|
+
as `default`; `bypassPermissions` adds no mode-derived gate at all. None of them can ever subtract
|
|
7155
7162
|
from the deployment/operator policy (deny-wins). Omitting this field is NOT itself "no mode-derived
|
|
7156
7163
|
gate" — this field wins over a `settings.permissions.defaultMode` bundle when both are sent, but when
|
|
7157
7164
|
THIS field is omitted the bundled mode (if any) still supplies one; only when neither carrier supplies
|
|
@@ -7422,6 +7429,14 @@ components:
|
|
|
7422
7429
|
items: { $ref: '#/components/schemas/TaskAgentDefinition' }
|
|
7423
7430
|
description: Per-task sub-agent definitions (roster additions scoped to this task only).
|
|
7424
7431
|
interactiveTools: { type: boolean, description: Mount interactive (prompting) tools. }
|
|
7432
|
+
approvalReach:
|
|
7433
|
+
allOf: [{ $ref: '#/components/schemas/ApprovalReach' }]
|
|
7434
|
+
description: >-
|
|
7435
|
+
server >= 7.118.0 (core 7.43.0 #1354) — declares that NOBODY on this run can answer approvals. Passed verbatim to the
|
|
7436
|
+
engine. The live approver callback is no longer this run's approver (`wiring_manifest` `ask.form: "declared_none"`); on a
|
|
7437
|
+
deployment with a parking facility the first gated call PARKS the run (`paused`) — for "refuse and keep going" use
|
|
7438
|
+
`permissionMode: "dontAsk"`. It never approves or releases anything. A value outside the closed set ⇒ 400
|
|
7439
|
+
`request.field_invalid`. Probe `capabilities.approvalReach` first.
|
|
7425
7440
|
oneShot:
|
|
7426
7441
|
type: boolean
|
|
7427
7442
|
description: >
|
|
@@ -8109,18 +8124,21 @@ components:
|
|
|
8109
8124
|
enum: [mysql, pg, local, memory]
|
|
8110
8125
|
description: >
|
|
8111
8126
|
The ASSEMBLED backend (same word table; nothing assembled = `memory`). `declared != backend` is legal:
|
|
8112
|
-
`DB_BACKEND=pg` + `SESSION_BACKEND=memory` (deliberately non-durable, no `degradedReason`) /
|
|
8113
|
-
|
|
8114
|
-
|
|
8127
|
+
`DB_BACKEND=pg` + `SESSION_BACKEND=memory` (deliberately non-durable, no `degradedReason`) / a default-derived local
|
|
8128
|
+
store that could not be built (`declared: local`, `backend: memory`, `local_unwritable`). Server >= 7.119.0: a configured
|
|
8129
|
+
SQL store that cannot open is retried inside the wait window and then the boot is REFUSED — it never degrades to memory
|
|
8130
|
+
(server <= 7.118.x degraded it silently under `SESSION_BACKEND=auto`, with the RETIRED word `degradedReason: db_unreachable`).
|
|
8115
8131
|
durable: { type: boolean, description: '`backend !== "memory"`.' }
|
|
8116
8132
|
degradedReason:
|
|
8117
8133
|
type: string
|
|
8118
8134
|
enum: [db_unreachable, local_unwritable]
|
|
8119
8135
|
description: >
|
|
8120
|
-
PRESENT-IFF the degrade arm ran;
|
|
8121
|
-
`db_unreachable` = the SQL family could not open under `SESSION_BACKEND=auto` (connect or DDL, not told apart);
|
|
8136
|
+
PRESENT-IFF the degrade arm ran; the server's own closed set (`STORE_DEGRADED_REASONS`) is ONE word since server >= 7.119.0:
|
|
8122
8137
|
`local_unwritable` = the default-derived local file store could not be built (mkdir / lock / permission / read-only
|
|
8123
|
-
mount / disk full).
|
|
8138
|
+
mount / disk full). ⚰️ RETIRED word `db_unreachable`: the SQL family could not open under `SESSION_BACKEND=auto` (connect or DDL, not told
|
|
8139
|
+
apart) — a server <= 7.118.x (inside the support floor) still sends it; a server >= 7.119.0 NEVER does (a configured SQL store that
|
|
8140
|
+
cannot open is retried, then the boot is refused — the SQL family has no degraded state). Kept in this enum so a valid response from an
|
|
8141
|
+
older server still validates; it is deleted once the support floor rises past 7.118.x. Absent = not degraded.
|
|
8124
8142
|
sql:
|
|
8125
8143
|
allOf: [{ $ref: '#/components/schemas/SqlEngineCapability' }]
|
|
8126
8144
|
description: >
|
|
@@ -8926,40 +8944,14 @@ components:
|
|
|
8926
8944
|
not inside the HMAC payload, so accepting it unsigned would be tamperable); 501 without an exemption
|
|
8927
8945
|
store. On the parked-background-agent leg the grant lands on the child's ROOT/HOST session (the whole
|
|
8928
8946
|
session tree shares it). The 200 body echoes `rememberApplied` when a store is wired.
|
|
8929
|
-
|
|
8930
|
-
|
|
8931
|
-
enum: [policy]
|
|
8947
|
+
decidedBy:
|
|
8948
|
+
allOf: [{ $ref: '#/components/schemas/ApprovalDecidedBy' }]
|
|
8932
8949
|
description: >-
|
|
8933
|
-
server
|
|
8934
|
-
|
|
8935
|
-
|
|
8936
|
-
|
|
8937
|
-
|
|
8938
|
-
WHY IT EXISTS: without the word, a policy refusal is byte-identical to "a person pressed No", and the
|
|
8939
|
-
model reads "the user doesn''t want to proceed; STOP and wait for the user to tell you how to proceed"
|
|
8940
|
-
— a sentence about a person, in a run where none is coming. With it the engine mints a POLICY sentence
|
|
8941
|
-
instead, this request''s `reason` travels as the POLICY''s words (not as the user''s guidance), and the
|
|
8942
|
-
settlement is recorded as `policy_refused` (`who.party: "none"`).
|
|
8943
|
-
|
|
8944
|
-
CONSTRAINTS: `decision:"deny"` only — only a person approves an action that EXECUTES, so
|
|
8945
|
-
`{decision:"approve", settledBy:"policy"}` is a contradiction, refused by the SERVER before the
|
|
8946
|
-
decision (400 `request.field_conflict`, the SAME SENTENCE as the live door) with the engine''s own
|
|
8947
|
-
pre-CAS refusal (`checkpoint.invalid_outcome`) behind it as depth; the server does NOT silently drop
|
|
8948
|
-
the key. It is therefore MUTUALLY EXCLUSIVE with `remember` (which is approve-only) — one body
|
|
8949
|
-
cannot carry both. The vocabulary''s owner is still core: the server accepts the KEY and relays the
|
|
8950
|
-
VALUE verbatim, so a word core does not know is refused by the engine, never by a server-side
|
|
8951
|
-
second judge of the vocabulary.
|
|
8952
|
-
REFUSED on a direct-door worker (400 `settled_by_not_in_proof` — not inside the HMAC payload, same
|
|
8953
|
-
rule as `remember` / `updatedInput` / `checkpointToken`); a non-direct-door deployment is unchanged.
|
|
8954
|
-
|
|
8955
|
-
SHAPE, not word: a non-string is 400 `request.field_invalid` (same family as `reason must be a
|
|
8956
|
-
string`). A word core does NOT know travels UNTOUCHED and is refused BY THE ENGINE pre-CAS:
|
|
8957
|
-
409 + `errorCode: "resume_outcome_invalid"` + `field: "hostDecision"` + `retriable: true`, and the
|
|
8958
|
-
card STAYS PENDING (re-decide the same coordinates with a corrected body). NOTE this family no
|
|
8959
|
-
longer answers `approval_binding_mismatch`: from server 7.93.3 the `policy_ask` gate splits
|
|
8960
|
-
`invalid_outcome` by `field`, and the non-binding seats (`hostDecision`, `approver`) answer
|
|
8961
|
-
`resume_outcome_invalid` — "you approved the wrong action" is a sentence about a different fault.
|
|
8962
|
-
The binding seats and the field-absent shape are byte-for-byte unchanged.
|
|
8950
|
+
server >= 7.118.0 — WHO made this decision (one key read the same way by all three decision doors; see
|
|
8951
|
+
ApprovalDecidedBy). ABSENT = `person`. This door's own answers: a `policy` allow on a person-required card is
|
|
8952
|
+
refused by the ENGINE before the CAS (409 `resume_outcome_invalid` + `field: "hostDecision"`, the card stays
|
|
8953
|
+
pending); outside the closed set ⇒ 400 `request.field_invalid`; any value on a direct-door worker ⇒ 400
|
|
8954
|
+
`decided_by_not_in_proof`. An older server (no `approvalDecidedBy` bit) refuses the key (400 `request.body_shape`).
|
|
8963
8955
|
|
|
8964
8956
|
ApprovalRow:
|
|
8965
8957
|
type: object
|
|
@@ -9364,6 +9356,29 @@ components:
|
|
|
9364
9356
|
`noteRecorded` (a live-card-only deployment has no row to write, and a flapping store cannot write
|
|
9365
9357
|
one; both answer `false` while the decision itself still stands). This bit only says "this build
|
|
9366
9358
|
knows the key" — same family as `permissionRulesRevoke`, deliberately gated on no facility.
|
|
9359
|
+
approvalDecidedBy:
|
|
9360
|
+
type: boolean
|
|
9361
|
+
description: >-
|
|
9362
|
+
server >= 7.118.0, hardcoded true (a version bit): the three approval decision doors read `decidedBy` (ApprovalDecidedBy).
|
|
9363
|
+
Send it only when this is present; absent = an older server (the durable doors and `plan_review` 400 it, the live
|
|
9364
|
+
`respond` drops it silently and answers 200).
|
|
9365
|
+
approvalReach:
|
|
9366
|
+
type: boolean
|
|
9367
|
+
description: >-
|
|
9368
|
+
server >= 7.118.0, hardcoded true (same word as the request key): TaskRequest.approvalReach is understood. Absent = an
|
|
9369
|
+
older server — do not send the key.
|
|
9370
|
+
permissionModes:
|
|
9371
|
+
type: array
|
|
9372
|
+
items: { type: string }
|
|
9373
|
+
description: >-
|
|
9374
|
+
server >= 7.119.0 — the permission-mode words this worker's REQUEST face accepts: the engine's `PERMISSION_MODES` whole set, engine order
|
|
9375
|
+
(today six: `default`, `acceptEdits`, `plan`, `auto`, `bypassPermissions`, `dontAsk`). The top-level `TaskRequest.permissionMode`,
|
|
9376
|
+
`settings.permissions.defaultMode` and `GET /v1/capabilities?permissionMode=` read ONE closed set: every word in the bit is accepted by
|
|
9377
|
+
all three, a word outside it is 400 `request.field_invalid` whose sentence lists the same words. Send `dontAsk` only when this bit
|
|
9378
|
+
carries it; ABSENT = server <= 7.118.x (five words, `dontAsk` is a 400). OPEN set: the bit forwards the engine's vocabulary
|
|
9379
|
+
verbatim, so a future engine word shows up here before this document knows it — decide by the bit, do not filter it through the
|
|
9380
|
+
`PermissionMode` schema. It says "this server accepts the word", not "the word takes effect on this submit" (`auto` still needs a
|
|
9381
|
+
classifier seat and an entitlement — see `permissionModeAuto`).
|
|
9367
9382
|
respondFreeFormRules:
|
|
9368
9383
|
type: boolean
|
|
9369
9384
|
description: >
|
|
@@ -9628,8 +9643,9 @@ components:
|
|
|
9628
9643
|
the same false, and a shell must do different things for those. `reason` is present ONLY when
|
|
9629
9644
|
`armed` is false; read its vocabulary as an OPEN set (server already grew it from four words to
|
|
9630
9645
|
six). `model` is omitted when the classifier route cannot be resolved (the server logs the
|
|
9631
|
-
engine's refusal rather than 500-ing the read face). `?permissionMode=<
|
|
9632
|
-
caller's INTENT into the verdict
|
|
9646
|
+
engine's refusal rather than 500-ing the read face). `?permissionMode=<mode word>` folds the
|
|
9647
|
+
caller's INTENT into the verdict (server >= 7.119.0 takes the six words of `capabilities.permissionModes` — `dontAsk` answers
|
|
9648
|
+
`armed: false` / `reason: mode_not_auto`; server <= 7.118.x takes five); anything outside the server's words is a 400
|
|
9633
9649
|
`request.field_invalid`.
|
|
9634
9650
|
# 🔴 required 只有**三位**:武装两键(armed / reason)是 server >= 7.57.0 (S-80)
|
|
9635
9651
|
# 才有的,而本包的支持地板是 3.0.0 => every 7.x <= 7.56.0 server is inside the promised face and
|
|
@@ -12529,6 +12545,13 @@ components:
|
|
|
12529
12545
|
the vocabulary's owner is still core and it has grown before, so read it the same way as everywhere
|
|
12530
12546
|
else: branch known words, keep a `default` arm, and treat an unknown one as "unknown origin", never
|
|
12531
12547
|
as "no origin". A row minted before the bit existed reads absent (unreported), not "no authority".
|
|
12548
|
+
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.' }
|
|
12549
|
+
boundGrants:
|
|
12550
|
+
allOf: [{ $ref: '#/components/schemas/BoundSkillGrants' }]
|
|
12551
|
+
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.'
|
|
12552
|
+
classifierNoRuling:
|
|
12553
|
+
allOf: [{ $ref: '#/components/schemas/AutoModeFallbackCause' }]
|
|
12554
|
+
description: 'server >= 7.118.0 — only on a parked `classifier_no_ruling` card: why the auto-mode classifier did not rule.'
|
|
12532
12555
|
objective: { type: ['string', 'null'], description: 'task objective, null if unavailable.' }
|
|
12533
12556
|
input:
|
|
12534
12557
|
description: >
|
|
@@ -12759,6 +12782,11 @@ components:
|
|
|
12759
12782
|
description: >
|
|
12760
12783
|
Session audit (`GET /v1/sessions/:id`) — true shape. `messages` = current model context
|
|
12761
12784
|
(older history folded into a summary message); can be large.
|
|
12785
|
+
Server >= 7.119.0 (wire VALUE change, shape unchanged): string leaves inside `messages` (assistant text / reasoning, tool arguments,
|
|
12786
|
+
tool results, shell output, compaction summaries) pass the SAME redaction as the live and ledger frames — a credential-shaped span reads
|
|
12787
|
+
`«redacted:…»`. Key set, count and segment order are verbatim; a history with no credential-shaped span is byte-identical; the archive itself is
|
|
12788
|
+
untouched (the redaction is read-side only), and `?blobCap=` truncation runs AFTER redaction. A caller that rebuilds a model context from this
|
|
12789
|
+
read gets the placeholder, not the original string.
|
|
12762
12790
|
# 🔴 **暂不封闭**(census 轴一裁定,2026-07-30):本体是 core `StoredSession.buildContext()` 的投影
|
|
12763
12791
|
# (server `audit.ts` auditFromStorage),再叠 server 自己的两条 additive 后处理(windowMessages /
|
|
12764
12792
|
# truncateMessageBlobs)与路由层追加的 owner —— 键集由「core 投影 × 查询参数」共同决定,不是一个
|
|
@@ -12790,7 +12818,11 @@ components:
|
|
|
12790
12818
|
them is not known" narrowed to this; it still does NOT say "never consumed"; see `engine_notice`
|
|
12791
12819
|
`task.user_steer_undrained{cause:"crashed"}`). Converged rows only, one row per (taskId, messageId),
|
|
12792
12820
|
newest 500. From server >=7.111.0 (S-966) the rows include SUBAGENT steers (recorded under the run on the
|
|
12793
|
-
receipt; same row shape
|
|
12821
|
+
receipt; same row shape; `messageId` = the subagent steer receipt's).
|
|
12822
|
+
`kind` = the engine LANE the input was queued on at acceptance (server >= 7.118.0 / core 7.44.0 #1362, closed two words):
|
|
12823
|
+
`steer` (priority `now` / `next` / absent) or `follow_up` (priority `later`, queued for the run's end); the crash notices
|
|
12824
|
+
split the same way (`task.user_steer_undrained` / `task.user_followup_undrained`, the latter with
|
|
12825
|
+
`detail: { followUp, cause: "crashed", taskId, sessionId }`). Read both words; branch on `cause`.
|
|
12794
12826
|
ABSENT = this read has nothing to report (zero rows, or the ledger could not be read —
|
|
12795
12827
|
the authoritative notice is the frame on the run stream); the `?message=` single-entry form never
|
|
12796
12828
|
carries it.
|
|
@@ -12800,7 +12832,7 @@ components:
|
|
|
12800
12832
|
additionalProperties: false
|
|
12801
12833
|
properties:
|
|
12802
12834
|
messageId: { type: string }
|
|
12803
|
-
kind: { type: string, enum: [steer] }
|
|
12835
|
+
kind: { type: string, enum: [steer, follow_up] }
|
|
12804
12836
|
taskId: { type: string, description: The run the input was accepted for (its 200/202 receipt). }
|
|
12805
12837
|
acceptedAt: { type: string, description: ISO timestamp. }
|
|
12806
12838
|
cause: { type: string, enum: [crashed] }
|
|
@@ -13573,8 +13605,10 @@ components:
|
|
|
13573
13605
|
7.104.0 / S-271 — a deployment-declared tool exceeds the roster bound), `config.tool_schema_bound` (503, server >=
|
|
13574
13606
|
7.106.0 / core 7.35.0 #765 — a tool's `parameters` schema exceeds the roster row's key bound; "same family, same tier";
|
|
13575
13607
|
<= 7.105.0 answered the synchronous leg 200 + a failed body), `corrupt` (500 — the session rule store
|
|
13576
|
-
cannot read its own bytes)
|
|
13577
|
-
fail-closed).
|
|
13608
|
+
cannot read its own bytes), `internal.memory_control_plane_corrupt` (500 — the memory control plane refuses
|
|
13609
|
+
fail-closed) and `state.scenario_assignment_unknown` (503, server >= 7.119.0 — the center's scenario assignment for the
|
|
13610
|
+
principal is dangling; only re-pointing the assignment, or adding the scenario back to the deployment, helps). Every other 5xx
|
|
13611
|
+
code keeps the status-based retry.
|
|
13578
13612
|
🔴 `request.host_posture_refused` (422) has TWO triggers (same code, same status — "you wrote it right, this
|
|
13579
13613
|
deployment will not grant that host posture"): (1) the write-containment face (`permissionMode: bypassPermissions` ⇒
|
|
13580
13614
|
`writeFace: open`; read `capabilities.taskWriteFaceOpen` first); (2) server >= 7.106.0 / S-895: `inheritEnv: "all"` on a
|
|
@@ -13600,6 +13634,20 @@ components:
|
|
|
13600
13634
|
🔴 NOT interchangeable with `taskId`: the workflow-child park lane mints no taskId at all (this
|
|
13601
13635
|
decision never landed on any run, so minting one would be a lie), and the other two decide lanes
|
|
13602
13636
|
mint no runId. Branch by `errorCode`, never fall back from one handle to the other.
|
|
13637
|
+
readableHolderRunId:
|
|
13638
|
+
type: string
|
|
13639
|
+
description: >-
|
|
13640
|
+
server >= 7.118.0, on `decide.workflow_parks_unreadable` (409) ONLY and only on its "the record cannot be read" cause: the
|
|
13641
|
+
readable record that holds the card at the highest generation (absent when there is none or a tie — never null). Where the
|
|
13642
|
+
work can be carried on — a CLUE, not a way to unblock this decision (the damaged record `runId` names keeps it undeliverable
|
|
13643
|
+
until it is repaired). SDK reads it as `DecideWorkflowParksUnreadableError.readableHolderRunId`.
|
|
13644
|
+
runIds:
|
|
13645
|
+
type: array
|
|
13646
|
+
items: { type: string }
|
|
13647
|
+
description: >-
|
|
13648
|
+
server >= 7.118.0, on `decide.workflow_park_forked` (409) ONLY: the resumed copies of one workflow run that all hold this card at
|
|
13649
|
+
the same generation (>= 2). Recovery = resume ONE of them, then decide again; retrying the decide never succeeds.
|
|
13650
|
+
SDK reads it as `DecideWorkflowParkForkedError.runIds`.
|
|
13603
13651
|
cause:
|
|
13604
13652
|
type: string
|
|
13605
13653
|
description: >-
|
|
@@ -13642,10 +13690,19 @@ components:
|
|
|
13642
13690
|
row's configuration was refused — `taskConfig` (change the configuration, then decide the SAME card again) or
|
|
13643
13691
|
`inheritedGate` (the row's recorded mode seed is bad — the run can only be cancelled). Also on 400 `request.field_invalid` for a
|
|
13644
13692
|
`settings.hooks` tool-event matcher that selects an engine alias but not its canonical name (server >= 7.115.0 / S-1025):
|
|
13645
|
-
`field` is always the literal `settings.hooks` (see `detail`).
|
|
13693
|
+
`field` is always the literal `settings.hooks` (see `detail`). Also on 400 `request.body_shape` (server >= 7.119.0), on the THREE
|
|
13694
|
+
tool-approval decision doors (`POST /v1/tool-approvals/{id}/respond`, `POST /v1/tasks/{taskId}/asks/{askId}/decision`,
|
|
13695
|
+
`POST /v1/approvals/{sessionId}/decide`) and ONLY there: the body carried the retired key `settledBy` (any value but `undefined` —
|
|
13696
|
+
`null` and `""` count), the refusal landed before any decision, and `field` is the literal `settledBy`; the three doors now answer
|
|
13697
|
+
the SAME sentence. Every such 400 carries it, even when another defect in the same body was hit first (the sentence then names that
|
|
13698
|
+
defect). Branch on the code plus `field`, never on the sentence; a bad body WITHOUT that key carries no `field`. SDK: `err.extra.field`.
|
|
13646
13699
|
detail:
|
|
13647
13700
|
description: >
|
|
13648
|
-
🔴
|
|
13701
|
+
🔴 SHAPES differ by code — branch on `field` (or the `errorCode`), never guess from the type of `detail`. On 503
|
|
13702
|
+
`state.scenario_assignment_unknown` (server >= 7.119.0): an OBJECT `{ scenario, source }` — `scenario` = the dangling scenario name,
|
|
13703
|
+
`source` ∈ `ruling` (the center's assignment for this principal names a scenario this deployment does not have) / `deployment`
|
|
13704
|
+
(the deployment default `DEFAULT_SCENARIO` is dangling — the server refuses to BOOT on that, so a request reaches it only on an
|
|
13705
|
+
embedder that assembles `resolveSpec` itself). On 400
|
|
13649
13706
|
`request.field_invalid` for a `settings.hooks` tool-event matcher (server >= 7.115.0 / S-1025; additive, the sentence and the code
|
|
13650
13707
|
are unchanged): an OBJECT `{ event, alias, canonical }` — `event` = the tool event of the refused group (the first refused one when
|
|
13651
13708
|
several are), `alias` / `canonical` = the engine catalog's alias and canonical name (closed strings, never the caller's raw bytes),
|
|
@@ -13660,6 +13717,12 @@ components:
|
|
|
13660
13717
|
event: { type: string }
|
|
13661
13718
|
alias: { type: string }
|
|
13662
13719
|
canonical: { type: string }
|
|
13720
|
+
- type: object
|
|
13721
|
+
required: [scenario, source]
|
|
13722
|
+
additionalProperties: false
|
|
13723
|
+
properties:
|
|
13724
|
+
scenario: { type: string }
|
|
13725
|
+
source: { type: string, enum: [ruling, deployment] }
|
|
13663
13726
|
- type: string
|
|
13664
13727
|
count:
|
|
13665
13728
|
type: integer
|
|
@@ -14251,16 +14314,27 @@ components:
|
|
|
14251
14314
|
enum: [default, acceptEdits, plan, auto, bypassPermissions, dontAsk]
|
|
14252
14315
|
description: >
|
|
14253
14316
|
core 7.34.0 `PERMISSION_MODES` (six words). CLOSED on the gate record (the server screens it with core `isPermissionMode`;
|
|
14254
|
-
an out-of-set word withholds the whole `gate`).
|
|
14255
|
-
7.
|
|
14256
|
-
|
|
14317
|
+
an out-of-set word withholds the whole `gate`). The REQUEST face (`TaskRequest.permissionMode`) takes the same six words from
|
|
14318
|
+
server 7.119.0 (`capabilities.permissionModes`); on server <= 7.118.x it takes FIVE — `dontAsk` on a fresh submit is 400
|
|
14319
|
+
`request.field_invalid` there — yet `dontAsk` can still appear here on those versions (e.g. an unattended leg run under
|
|
14320
|
+
`UNATTENDED_PERMISSION_MODE=dontAsk`).
|
|
14257
14321
|
AskClass:
|
|
14258
14322
|
type: string
|
|
14259
|
-
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]
|
|
14323
|
+
enum: [content_question, hook, ancestor, dangerous_delete, 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]
|
|
14260
14324
|
description: >
|
|
14261
|
-
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
|
|
14325
|
+
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 — 7.34.0 minted twenty; `shell_floor` joined in 7.39.0 #1260 and rides
|
|
14262
14326
|
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
|
|
14263
|
-
server >= 7.116.0
|
|
14327
|
+
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
|
|
14328
|
+
server >= 7.119.0) — which CLASS an ask falls in (the columns of the mode × class arbitration table).
|
|
14329
|
+
`dangerous_delete` = a DELETE OF A PROTECTED PLACE: an `rm` / `rmdir` whose target is the filesystem root, a top-level directory, the home
|
|
14330
|
+
directory, the task root / working directory / an added working directory or an ancestor of one (including `rm -rf *` at the task root and
|
|
14331
|
+
`rm -rf ~/*`), or a shell handed a script that is only assembled at run time (`bash -c "$CMD"`, `eval "$(…)"`). ONLY A PERSON'S ANSWER clears
|
|
14332
|
+
it: no mode (`bypassPermissions` included), no allow (request `settings.permissions.allow`, operator `APPROVAL_GRANT`, a stored allow row, a
|
|
14333
|
+
skill's tool list, a hook's allow) and no `auto` classifier does; the frame carries `requiresRealApproval: true`, nobody answering means the
|
|
14334
|
+
call does not run (the unattended leg refuses), and it is bypass-IMMUNE (not one of the ten releasable classes).
|
|
14335
|
+
`classifier_no_ruling` = the auto-mode classifier did not rule (it could not run — `error` / `timeout` — or replied with no verdict —
|
|
14336
|
+
`parse_error`), so a person decides: the denial-limit fallback's row (bypass-immune, no allow rule clears it, the classifier is not asked
|
|
14337
|
+
again); the live frame and the inbox row also carry the cause as `classifierNoRuling`.
|
|
14264
14338
|
`requires_user_interaction` = the MCP server declared, in the tool's advertisement, `_meta["anthropic/requiresUserInteraction"] === true`
|
|
14265
14339
|
(only the boolean `true`): EVERY call of that tool asks the human, in EVERY mode including `bypassPermissions` (it is not one of the ten
|
|
14266
14340
|
RELEASABLE classes), and only the human's answer clears it — `settings.permissions.allow`, operator `APPROVAL_GRANT`, a skill's tool list +
|
|
@@ -15963,18 +16037,12 @@ components:
|
|
|
15963
16037
|
offer on another replica is not a promise that it is still redeemable.
|
|
15964
16038
|
persistedRuleShadowed:
|
|
15965
16039
|
type: string
|
|
15966
|
-
|
|
15967
|
-
|
|
15968
|
-
|
|
15969
|
-
|
|
15970
|
-
|
|
15971
|
-
|
|
15972
|
-
For those older servers (server >= 7.13.0 / core 5.25.0 #144, ADDITIVE, "tool_approval" only): this ask MATCHED one of the
|
|
15973
|
-
caller's persisted allow rules and that rule COULD NOT silence it. The value is the shadowed rule's
|
|
15974
|
-
VERBATIM text (secret-redacted + length-bounded). UNTRUSTED for display, same treatment as `message`.
|
|
15975
|
-
🔴 ABSENT != "you have no rule": present only when a rule matched AND could not clear the ask. 🔴 Deliberately SEPARATE
|
|
15976
|
-
from `governanceForced` and not derivable from it: that key answers "who imposed the gate", this one answers "what
|
|
15977
|
-
happened to YOUR rule". The durable dual on those servers is `gate.riskDescriptor.shadowedRule` on the parked row.
|
|
16040
|
+
description: >-
|
|
16041
|
+
server >= 7.13.0 (core 5.25.0 #144), ADDITIVE, "tool_approval" only — this ask MATCHED one of the caller's persisted allow
|
|
16042
|
+
rules and that rule COULD NOT silence it; the value is the rule's VERBATIM text (secret-redacted + length-bounded; UNTRUSTED
|
|
16043
|
+
for display, like `message`). Absent != "you have no rule"; deliberately separate from `governanceForced`.
|
|
16044
|
+
RETIRED: server >= 7.117.0 (core 7.42.0, BREAKING rename, no alias) never mints this key on any face; its successor is
|
|
16045
|
+
`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).
|
|
15978
16046
|
allowRuleShadowed:
|
|
15979
16047
|
type: array
|
|
15980
16048
|
items: { $ref: '#/components/schemas/AllowRuleShadow' }
|
|
@@ -16192,6 +16260,24 @@ components:
|
|
|
16192
16260
|
`ASK_CLASSES` order, non-empty, `classes[0] === askClass`); one ask can belong to several, so read the whole
|
|
16193
16261
|
set, not just `askClass`. Present together with `askClass`; passed through verbatim in order (the server
|
|
16194
16262
|
neither sorts nor dedupes).
|
|
16263
|
+
boundGrantDigest:
|
|
16264
|
+
type: string
|
|
16265
|
+
description: >-
|
|
16266
|
+
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
|
|
16267
|
+
approving it ALSO grants (the skill name + its two lists, canonical spelling); absent on every other card. Same value as the
|
|
16268
|
+
parked row's / the inbox row's twin (the live card and its parked twin reconcile by `(toolCallId, boundGrantDigest)`); passed
|
|
16269
|
+
through verbatim. A live "allow for this session" is remembered under "this tool + this digest" and answers only an ask with the
|
|
16270
|
+
same digest (same skill, same lists); a different skill or a changed list asks again. The durable `remember: "session"` never
|
|
16271
|
+
remembers a skill card.
|
|
16272
|
+
boundGrants:
|
|
16273
|
+
allOf: [{ $ref: '#/components/schemas/BoundSkillGrants' }]
|
|
16274
|
+
description: 'server >= 7.118.0, "tool_approval" only — the value `boundGrantDigest` hashes; present iff it is. DEPLOYMENT TEXT: render with contextual escaping.'
|
|
16275
|
+
classifierNoRuling:
|
|
16276
|
+
allOf: [{ $ref: '#/components/schemas/AutoModeFallbackCause' }]
|
|
16277
|
+
description: >-
|
|
16278
|
+
server >= 7.118.0 (core 7.43.0 #1353), "tool_approval" only, only on a `classifier_no_ruling` card — WHY the auto-mode classifier
|
|
16279
|
+
did not rule (`error` / `timeout` = it could not run; `parse_error` = it replied with no verdict), so a person decides. The server
|
|
16280
|
+
screens it with the engine's closed set (an out-of-set word is withheld). Display only.
|
|
16195
16281
|
denialLimitFallback: { $ref: '#/components/schemas/DenialLimitFallback' }
|
|
16196
16282
|
parked:
|
|
16197
16283
|
type: boolean
|
|
@@ -16388,13 +16474,11 @@ components:
|
|
|
16388
16474
|
promise and the per-leg index rules are stated verbatim on ToolApprovalFrame.ruleOffers.
|
|
16389
16475
|
persistedRuleShadowed:
|
|
16390
16476
|
type: string
|
|
16391
|
-
|
|
16392
|
-
|
|
16393
|
-
|
|
16394
|
-
|
|
16395
|
-
|
|
16396
|
-
(secret-redacted + length-bounded). UNTRUSTED for display. Semantics, the ABSENT != "you have no rule" clause and the deliberate separation
|
|
16397
|
-
from `governanceForced` are stated verbatim on ToolApprovalFrame.persistedRuleShadowed.
|
|
16477
|
+
description: >-
|
|
16478
|
+
server >= 7.13.0 (core 5.25.0 #144), ADDITIVE — the persisted rule this ask matched but which could not silence it; frame and
|
|
16479
|
+
card carry the SAME material (see ToolApprovalFrame.persistedRuleShadowed).
|
|
16480
|
+
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
|
|
16481
|
+
carrying it); its successor is `allowRuleShadowed`. Kept for servers in [7.13.0, 7.117.0) only (sdk 16.3.0).
|
|
16398
16482
|
allowRuleShadowed:
|
|
16399
16483
|
type: array
|
|
16400
16484
|
items: { $ref: '#/components/schemas/AllowRuleShadow' }
|
|
@@ -16633,21 +16717,25 @@ components:
|
|
|
16633
16717
|
RuleOfferMatch:
|
|
16634
16718
|
type: string
|
|
16635
16719
|
description: >
|
|
16636
|
-
The MATCH FORM of a persisted rule — the server's `PERSISTED_RULE_MATCHES`, verbatim (the vocabulary
|
|
16720
|
+
The MATCH FORM of a persisted rule — the server's `PERSISTED_RULE_MATCHES`, verbatim and in order (the vocabulary
|
|
16637
16721
|
is OWNED BY CORE; the server only transcribes it and pins set equality, so core adding a member turns
|
|
16638
|
-
the server red at compile time).
|
|
16722
|
+
the server red at compile time). ONE vocabulary for two faces: a card's rule offer (`RuleOffer.match`, the
|
|
16723
|
+
server's card schema validates it with this very enum) and a persisted rule row (`GET /v1/rules`).
|
|
16639
16724
|
|
|
16640
16725
|
`exact` = this one command only (`Bash(git status)`). `prefix` = the historical colon-star
|
|
16641
16726
|
word-boundary prefix (`Bash(git status:*)`) and the ONLY spelling for a compound prefix form; it stays
|
|
16642
16727
|
a permanent compatibility read. `wildcard` = the trailing space-star form (`Bash(npm run *)`), the
|
|
16643
|
-
same predicate as `prefix` on a single command body
|
|
16644
|
-
|
|
16645
|
-
|
|
16646
|
-
|
|
16647
|
-
|
|
16648
|
-
|
|
16649
|
-
|
|
16650
|
-
|
|
16728
|
+
same predicate as `prefix` on a single command body. `subpath` = the `Read(//abs-dir/**)` directory form,
|
|
16729
|
+
which never holds a command. `path` (server >= 7.67.0) = the path-family pattern grammar for deny/ask/allow
|
|
16730
|
+
rules (`//abs`, `~/`, root-relative, cwd-relative; `*` within a segment, `**` across segments). `name` /
|
|
16731
|
+
`name_prefix` (server >= 7.118.0 / core 7.43.0 #1318) = the `Skill` name grammar: `Skill(<name>)` exact,
|
|
16732
|
+
`Skill(<prefix>:*)` a name prefix with NO word boundary; a skill card's "don't ask again" offer carries
|
|
16733
|
+
`match: "name"`. A replica rolled back to 7.117.x reads a `name` / `name_prefix` row as a damaged row.
|
|
16734
|
+
|
|
16735
|
+
🔴 SEVEN MEMBERS, closed: the server validates the member on both faces. A consumer type narrower than the
|
|
16736
|
+
mint point is a lie — it lets a `switch` claim exhaustiveness the wire does not honour (SDK <= 16 declared
|
|
16737
|
+
four here, so a `path` offer and every 7.118.0 skill-card offer were dropped as malformed).
|
|
16738
|
+
enum: [exact, prefix, wildcard, subpath, path, name, name_prefix]
|
|
16651
16739
|
|
|
16652
16740
|
RuleOfferUncoveredReason:
|
|
16653
16741
|
type: string
|
|
@@ -17223,25 +17311,66 @@ components:
|
|
|
17223
17311
|
additionalProperties: false
|
|
17224
17312
|
properties:
|
|
17225
17313
|
label: { type: string, maxLength: 256 }
|
|
17226
|
-
|
|
17227
|
-
|
|
17228
|
-
enum: [policy]
|
|
17314
|
+
decidedBy:
|
|
17315
|
+
allOf: [{ $ref: '#/components/schemas/ApprovalDecidedBy' }]
|
|
17229
17316
|
description: >-
|
|
17230
|
-
server
|
|
17231
|
-
|
|
17232
|
-
|
|
17233
|
-
|
|
17234
|
-
`
|
|
17235
|
-
|
|
17236
|
-
|
|
17237
|
-
|
|
17238
|
-
|
|
17239
|
-
|
|
17240
|
-
|
|
17241
|
-
|
|
17242
|
-
|
|
17243
|
-
|
|
17244
|
-
|
|
17317
|
+
server >= 7.118.0 — WHO made this decision (one key read the same way by all three decision doors; see
|
|
17318
|
+
ApprovalDecidedBy). ABSENT = `person`. This door's own answers (both refused before the decision, the card does
|
|
17319
|
+
not move): a `policy` allow on a person-required card ⇒ 400 `request.field_conflict`; outside the closed set ⇒
|
|
17320
|
+
400 `request.field_invalid`; any value on a direct-door worker ⇒ 400 `decided_by_not_in_proof`. An older server
|
|
17321
|
+
(no `approvalDecidedBy` bit) has a strict body here ⇒ 400 `request.body_shape`, nothing decided.
|
|
17322
|
+
|
|
17323
|
+
ApprovalDecidedBy:
|
|
17324
|
+
type: string
|
|
17325
|
+
enum: [person, policy]
|
|
17326
|
+
description: >
|
|
17327
|
+
server >= 7.118.0 (`APPROVAL_DECIDED_BY`, verbatim and in order) — WHO made an approval decision, one key on all three decision
|
|
17328
|
+
doors (live `POST /v1/tool-approvals/{approvalId}/respond`, durable `POST /v1/tasks/{taskId}/asks/{askId}/decision`, durable
|
|
17329
|
+
`POST /v1/approvals/{sessionId}/decide`). `person` = a person decided through the approval channel (ABSENT means this);
|
|
17330
|
+
`policy` = this deployment's own policy decided (a rule / posture answering on someone's behalf, a headless lane) — BOTH
|
|
17331
|
+
directions: the engine records `tool_end.gate.settlement.kind` = `policy_allowed` / `policy_refused` (`who.party: "policy"`).
|
|
17332
|
+
The only verb constraint is the engine's own: a person-required card (`requiresRealApproval`) cannot be ALLOWED by a policy.
|
|
17333
|
+
Send it only when `GET /v1/capabilities` carries `approvalDecidedBy: true`. Outside the closed set (including `sla_timeout` —
|
|
17334
|
+
"the wait ran out with nobody answering" is said only by the server's own deadline sweep — and the old `human` / `timeout`
|
|
17335
|
+
words) ⇒ 400 before the decision. It REPLACES the 7.93.3–7.117.x key `settledBy` (deny-only, the same fact), deleted
|
|
17336
|
+
by server 7.118.0 with no alias: sending `settledBy` ⇒ 400 `request.body_shape` on all three doors.
|
|
17337
|
+
`plan_review` does not take it (400 `request.body_shape`).
|
|
17338
|
+
|
|
17339
|
+
ApprovalReach:
|
|
17340
|
+
type: string
|
|
17341
|
+
enum: [none]
|
|
17342
|
+
description: >
|
|
17343
|
+
The closed set of the request key `approvalReach` (core 7.43.0 #1354 `APPROVAL_REACH`, verbatim): `none` — nobody on this run can be
|
|
17344
|
+
asked to approve. There is no positive word ("a person is reachable" is the `interactionPosture: "interactive"` promise).
|
|
17345
|
+
|
|
17346
|
+
AutoModeFallbackCause:
|
|
17347
|
+
type: string
|
|
17348
|
+
enum: [error, timeout, parse_error]
|
|
17349
|
+
description: >
|
|
17350
|
+
core 7.43.0 `AUTO_MODE_FALLBACK_CAUSES`, verbatim and in order — why the auto-mode classifier did not rule: `error` / `timeout` (it could
|
|
17351
|
+
not run), `parse_error` (it replied with no verdict). Carried as `classifierNoRuling` on a `classifier_no_ruling` card (server >= 7.118.0).
|
|
17352
|
+
|
|
17353
|
+
BoundSkillGrants:
|
|
17354
|
+
type: object
|
|
17355
|
+
description: >
|
|
17356
|
+
core 7.43.0 `BoundSkillGrants` (three keys, verbatim) — what approving an engine `Skill` card ALSO grants: `skill` = the loaded skill's
|
|
17357
|
+
name (`null` = the call names no loaded skill; it grants nothing), `allowedTools` = what activation lets be used without asking,
|
|
17358
|
+
`disallowedTools` = what activation refuses for the rest of the run (canonical spelling: de-duplicated, sorted). DEPLOYMENT TEXT.
|
|
17359
|
+
required: [skill, allowedTools, disallowedTools]
|
|
17360
|
+
additionalProperties: false
|
|
17361
|
+
properties:
|
|
17362
|
+
skill: { type: ['string', 'null'] }
|
|
17363
|
+
allowedTools: { type: array, items: { type: string } }
|
|
17364
|
+
disallowedTools: { type: array, items: { type: string } }
|
|
17365
|
+
|
|
17366
|
+
SteerReplayFacet:
|
|
17367
|
+
type: string
|
|
17368
|
+
enum: [text, trusted, priority, actor]
|
|
17369
|
+
description: >
|
|
17370
|
+
core 7.44.0 #1374 `STEER_REPLAY_FACETS`, verbatim and in order — the facets on which a replayed steering input id differs from the
|
|
17371
|
+
accepted one. Carried as `detail.differs` (a non-empty subset, the engine's order) on 409 `steering.duplicate_input_id` (server >= 7.118.0):
|
|
17372
|
+
contains `text` ⇒ a different instruction, reissue with a fresh key; no `text` ⇒ the SAME instruction — do NOT change the key (it
|
|
17373
|
+
would queue twice), replay with the original values.
|
|
17245
17374
|
|
|
17246
17375
|
AskDecisionAck:
|
|
17247
17376
|
type: object
|
|
@@ -18381,7 +18510,7 @@ components:
|
|
|
18381
18510
|
type: object
|
|
18382
18511
|
description: 'Approval seam: wired form, which seat it came from, and (effective half) what it adjudicates to.'
|
|
18383
18512
|
properties:
|
|
18384
|
-
form: { type: string, enum: [callback, allow, deny, absent] }
|
|
18513
|
+
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.' }
|
|
18385
18514
|
provenance: { type: string, enum: [spec, deps] }
|
|
18386
18515
|
effective: { type: string, enum: [human_reachable, auto_allow, auto_deny, park_only, unresolved] }
|
|
18387
18516
|
question:
|
|
@@ -19759,17 +19888,11 @@ components:
|
|
|
19759
19888
|
engine's catalogue, not on this wire. READ IT; do not switch over a closed list. A name the grammar
|
|
19760
19889
|
does not know is still a 400 on the input faces.
|
|
19761
19890
|
match:
|
|
19762
|
-
|
|
19763
|
-
|
|
19764
|
-
|
|
19765
|
-
|
|
19766
|
-
`
|
|
19767
|
-
compound prefix) · `wildcard` = the space-star form, the SAME predicate as `prefix` on a single
|
|
19768
|
-
command body · `subpath` = the directory (read-containment) form, which never admits a command ·
|
|
19769
|
-
`path` = NEW in server >= 7.67.0, the path-family pattern grammar for deny/ask/allow rules (`//abs`,
|
|
19770
|
-
`~/`, root-relative, cwd-relative; `*` within a segment, `**` across segments).
|
|
19771
|
-
SDK 8.7.0 and earlier declared only the first two — a consumer rendering this cell off a closed list
|
|
19772
|
-
must add the other three.
|
|
19891
|
+
allOf: [{ $ref: '#/components/schemas/RuleOfferMatch' }]
|
|
19892
|
+
description: >
|
|
19893
|
+
The match form — the same seven-member vocabulary as a card's rule offer (`RuleOfferMatch`, the server's
|
|
19894
|
+
`PERSISTED_RULE_MATCHES`): `exact` / `prefix` / `wildcard` / `subpath` / `path`, and from server >= 7.118.0
|
|
19895
|
+
`name` / `name_prefix` (a `Skill(<name>)` / `Skill(<prefix>:*)` rule).
|
|
19773
19896
|
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`.' }
|
|
19774
19897
|
adds:
|
|
19775
19898
|
type: array
|