@sema-agent/sdk 16.3.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.
Files changed (48) hide show
  1. package/README.md +69 -2
  2. package/dist/errors.d.ts +78 -12
  3. package/dist/errors.d.ts.map +1 -1
  4. package/dist/errors.js +91 -10
  5. package/dist/errors.js.map +1 -1
  6. package/dist/events.d.ts +8 -0
  7. package/dist/events.d.ts.map +1 -1
  8. package/dist/events.js.map +1 -1
  9. package/dist/index.d.ts +16 -4
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +10 -2
  12. package/dist/index.js.map +1 -1
  13. package/dist/resources/approvals.d.ts +34 -30
  14. package/dist/resources/approvals.d.ts.map +1 -1
  15. package/dist/resources/approvals.js +30 -0
  16. package/dist/resources/approvals.js.map +1 -1
  17. package/dist/resources/memory.d.ts +4 -0
  18. package/dist/resources/memory.d.ts.map +1 -1
  19. package/dist/resources/memory.js +4 -0
  20. package/dist/resources/memory.js.map +1 -1
  21. package/dist/resources/rules.d.ts +4 -7
  22. package/dist/resources/rules.d.ts.map +1 -1
  23. package/dist/resources/rules.js.map +1 -1
  24. package/dist/resources/session-sync.d.ts.map +1 -1
  25. package/dist/resources/session-sync.js +2 -3
  26. package/dist/resources/session-sync.js.map +1 -1
  27. package/dist/resources/sessions.d.ts +8 -0
  28. package/dist/resources/sessions.d.ts.map +1 -1
  29. package/dist/resources/sessions.js +8 -0
  30. package/dist/resources/sessions.js.map +1 -1
  31. package/dist/resources/tasks.d.ts +12 -1
  32. package/dist/resources/tasks.d.ts.map +1 -1
  33. package/dist/resources/tasks.js +12 -1
  34. package/dist/resources/tasks.js.map +1 -1
  35. package/dist/resources/tool-approvals.d.ts +53 -90
  36. package/dist/resources/tool-approvals.d.ts.map +1 -1
  37. package/dist/resources/tool-approvals.js +24 -12
  38. package/dist/resources/tool-approvals.js.map +1 -1
  39. package/dist/sync.d.ts +7 -7
  40. package/dist/sync.d.ts.map +1 -1
  41. package/dist/sync.js +32 -17
  42. package/dist/sync.js.map +1 -1
  43. package/dist/types.d.ts +65 -21
  44. package/dist/types.d.ts.map +1 -1
  45. package/dist/types.js +23 -0
  46. package/dist/types.js.map +1 -1
  47. package/openapi.yaml +205 -138
  48. 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) — the Idempotency-Key you
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. binding_mismatch = SAFETY signal: refetch + re-present
3326
- to the human, NEVER auto-retry. (S-57, server >= 7.54.0: the `field:"boundCallId"` shape is
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): `decide.workflow_parks_unreadable`
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
- settledBy:
4236
- type: string
4237
- enum: [policy]
4260
+ decidedBy:
4261
+ allOf: [{ $ref: '#/components/schemas/ApprovalDecidedBy' }]
4238
4262
  description: >-
4239
- server #1016 (>= 7.93.3, core 7.26.2), ADDITIVE — the SETTLEMENT SOURCE of this answer.
4240
- `"policy"` is a MACHINE REFUSAL, NEVER THE USER'S WORDS: the deployment's own policy answered
4241
- this card, not a person. ABSENT = today's shape (read as a person's answer).
4242
-
4243
- Same word, same source and the same three-question rationale as the durable leg's key
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.104.0 (S-843) adds the `settledBy` refusals shared with the durable `/decide` leg: `request.field_conflict` (with a non-deny decision), `settled_by_not_in_proof` (direct-door worker), `request.field_invalid` (non-string, or outside the 1..16-character column) — see AskDecisionBody.settledBy.'
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' }
@@ -6849,16 +6856,6 @@ components:
6849
6856
  name: { type: string }
6850
6857
  description: { type: string }
6851
6858
  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
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).' }
6863
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).' }
6864
6861
  files:
@@ -7422,6 +7419,14 @@ components:
7422
7419
  items: { $ref: '#/components/schemas/TaskAgentDefinition' }
7423
7420
  description: Per-task sub-agent definitions (roster additions scoped to this task only).
7424
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.
7425
7430
  oneShot:
7426
7431
  type: boolean
7427
7432
  description: >
@@ -8926,40 +8931,14 @@ components:
8926
8931
  not inside the HMAC payload, so accepting it unsigned would be tamperable); 501 without an exemption
8927
8932
  store. On the parked-background-agent leg the grant lands on the child's ROOT/HOST session (the whole
8928
8933
  session tree shares it). The 200 body echoes `rememberApplied` when a store is wired.
8929
- settledBy:
8930
- type: string
8931
- enum: [policy]
8934
+ decidedBy:
8935
+ allOf: [{ $ref: '#/components/schemas/ApprovalDecidedBy' }]
8932
8936
  description: >-
8933
- server #1016 (>= 7.93.3, core 7.26.2), ADDITIVE — WHO made this decision. `"policy"` is a MACHINE
8934
- REFUSAL, NEVER THE USER''S WORDS: the deployment''s own policy answered this card (a headless lane —
8935
- CI, a background batch, a shell refusing by a standing deployment posture). ABSENT = today''s shape
8936
- (`person`), so an unrevised caller is byte-for-byte unchanged.
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.
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`).
8963
8942
 
8964
8943
  ApprovalRow:
8965
8944
  type: object
@@ -9364,6 +9343,17 @@ components:
9364
9343
  `noteRecorded` (a live-card-only deployment has no row to write, and a flapping store cannot write
9365
9344
  one; both answer `false` while the decision itself still stands). This bit only says "this build
9366
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.
9367
9357
  respondFreeFormRules:
9368
9358
  type: boolean
9369
9359
  description: >
@@ -12529,6 +12519,13 @@ components:
12529
12519
  the vocabulary's owner is still core and it has grown before, so read it the same way as everywhere
12530
12520
  else: branch known words, keep a `default` arm, and treat an unknown one as "unknown origin", never
12531
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.'
12532
12529
  objective: { type: ['string', 'null'], description: 'task objective, null if unavailable.' }
12533
12530
  input:
12534
12531
  description: >
@@ -12790,7 +12787,11 @@ components:
12790
12787
  them is not known" narrowed to this; it still does NOT say "never consumed"; see `engine_notice`
12791
12788
  `task.user_steer_undrained{cause:"crashed"}`). Converged rows only, one row per (taskId, messageId),
12792
12789
  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, `kind` is still only `steer`; `messageId` = the subagent steer receipt's).
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`.
12794
12795
  ABSENT = this read has nothing to report (zero rows, or the ledger could not be read —
12795
12796
  the authoritative notice is the frame on the run stream); the `?message=` single-entry form never
12796
12797
  carries it.
@@ -12800,7 +12801,7 @@ components:
12800
12801
  additionalProperties: false
12801
12802
  properties:
12802
12803
  messageId: { type: string }
12803
- kind: { type: string, enum: [steer] }
12804
+ kind: { type: string, enum: [steer, follow_up] }
12804
12805
  taskId: { type: string, description: The run the input was accepted for (its 200/202 receipt). }
12805
12806
  acceptedAt: { type: string, description: ISO timestamp. }
12806
12807
  cause: { type: string, enum: [crashed] }
@@ -13600,6 +13601,20 @@ components:
13600
13601
  🔴 NOT interchangeable with `taskId`: the workflow-child park lane mints no taskId at all (this
13601
13602
  decision never landed on any run, so minting one would be a lie), and the other two decide lanes
13602
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`.
13603
13618
  cause:
13604
13619
  type: string
13605
13620
  description: >-
@@ -14256,11 +14271,14 @@ components:
14256
14271
  unattended leg run under `UNATTENDED_PERMISSION_MODE=dontAsk`).
14257
14272
  AskClass:
14258
14273
  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]
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]
14260
14275
  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
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
14262
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
14263
- 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`.
14264
14282
  `requires_user_interaction` = the MCP server declared, in the tool's advertisement, `_meta["anthropic/requiresUserInteraction"] === true`
14265
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
14266
14284
  RELEASABLE classes), and only the human's answer clears it — `settings.permissions.allow`, operator `APPROVAL_GRANT`, a skill's tool list +
@@ -15963,18 +15981,12 @@ components:
15963
15981
  offer on another replica is not a promise that it is still redeemable.
15964
15982
  persistedRuleShadowed:
15965
15983
  type: string
15966
- deprecated: true
15967
- description: >
15968
- 🔴 RETIRED by server 7.117.0 (core 7.42.0) — replaced by `allowRuleShadowed`, no alias. A server >= 7.117.0 never mints this key
15969
- (its strict card schema even refuses a stored card carrying it); only a server <= 7.116.x still does, and the SDK keeps the declaration
15970
- for those servers (the supported floor is far below 7.117.0). Slated for deletion in the next major.
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.
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).
15978
15990
  allowRuleShadowed:
15979
15991
  type: array
15980
15992
  items: { $ref: '#/components/schemas/AllowRuleShadow' }
@@ -16192,6 +16204,24 @@ components:
16192
16204
  `ASK_CLASSES` order, non-empty, `classes[0] === askClass`); one ask can belong to several, so read the whole
16193
16205
  set, not just `askClass`. Present together with `askClass`; passed through verbatim in order (the server
16194
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.
16195
16225
  denialLimitFallback: { $ref: '#/components/schemas/DenialLimitFallback' }
16196
16226
  parked:
16197
16227
  type: boolean
@@ -16388,13 +16418,11 @@ components:
16388
16418
  promise and the per-leg index rules are stated verbatim on ToolApprovalFrame.ruleOffers.
16389
16419
  persistedRuleShadowed:
16390
16420
  type: string
16391
- deprecated: true
16392
- description: >
16393
- 🔴 RETIRED by server 7.117.0 (core 7.42.0) — replaced by `allowRuleShadowed`, no alias (a server >= 7.117.0 never mints it; the strict card
16394
- schema refuses a stored card carrying it). Only a server <= 7.116.x still does; slated for deletion in the next major. For those servers
16395
- (ADDITIVE, server >= 7.13.0 / core 5.25.0 #144): the VERBATIM text of the persisted rule this ask matched but which could not silence it
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.
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).
16398
16426
  allowRuleShadowed:
16399
16427
  type: array
16400
16428
  items: { $ref: '#/components/schemas/AllowRuleShadow' }
@@ -16633,21 +16661,25 @@ components:
16633
16661
  RuleOfferMatch:
16634
16662
  type: string
16635
16663
  description: >
16636
- 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
16637
16665
  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).
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`).
16639
16668
 
16640
16669
  `exact` = this one command only (`Bash(git status)`). `prefix` = the historical colon-star
16641
16670
  word-boundary prefix (`Bash(git status:*)`) and the ONLY spelling for a compound prefix form; it stays
16642
16671
  a permanent compatibility read. `wildcard` = the trailing space-star form (`Bash(npm run *)`), the
16643
- same predicate as `prefix` on a single command body, minted by core's suggestion face since
16644
- design/382 / #510. `subpath` = the `Read(//abs-dir/**)` directory form, which never holds a command.
16645
-
16646
- 🔴 FOUR MEMBERS, not "the three today's miner emits": this type describes what a consumer may RECEIVE,
16647
- and the server's wire validator accepts all four. A consumer type narrower than the mint point is a
16648
- lie — it lets a `switch` claim exhaustiveness the wire does not honour. (The retired `RuleSuggestion`
16649
- declared only two, which is exactly why a legal `wildcard` candidate used to be dropped as malformed.)
16650
- enum: [exact, prefix, wildcard, subpath]
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]
16651
16683
 
16652
16684
  RuleOfferUncoveredReason:
16653
16685
  type: string
@@ -17223,25 +17255,66 @@ components:
17223
17255
  additionalProperties: false
17224
17256
  properties:
17225
17257
  label: { type: string, maxLength: 256 }
17226
- settledBy:
17227
- type: string
17228
- enum: [policy]
17258
+ decidedBy:
17259
+ allOf: [{ $ref: '#/components/schemas/ApprovalDecidedBy' }]
17229
17260
  description: >-
17230
- server S-843 (>= 7.104.0), ADDITIVE — the SETTLEMENT SOURCE of this decision. Same word, same source, same
17231
- sentence as the durable decide leg's key (`POST /v1/approvals/{sessionId}/decide`, ApprovalDecision.settledBy —
17232
- see that schema for the full account; the server judges this door and `/decide` with one function, and the live
17233
- respond leg reads the same two refusal sentences under its own `request.body_shape` family):
17234
- `"policy"` = the deployment's own policy answered this card, not a person; ABSENT = today's shape.
17235
- `decision:"deny"` only (an approve carrying it ⇒ 400 `request.field_conflict`); REFUSED on a direct-door worker
17236
- (400 `settled_by_not_in_proof`); a non-string ⇒ 400 `request.field_invalid`. 🔴 An UNKNOWN word is the OPPOSITE of
17237
- `/decide` here: this door does not judge the word, so it answers 200 and the word is stored with the decision (the
17238
- card is settled as `deny`; its source cannot be corrected afterwards, and an idempotent replay replays it), and the
17239
- engine refuses the word fail-closed at settlement — NOT `/decide`'s 409 `resume_outcome_invalid` with the card left
17240
- pending for a corrected re-decide. THIS leg only: the value lands in a fixed-width
17241
- `settled_by` column — outside 1..16 characters ⇒ 400 `request.field_invalid` before the CAS; none of the three
17242
- response bodies echoes it. OLDER SERVERS (< 7.104.0): this body schema is strict, so the key is an unknown key ⇒
17243
- 400 `request.body_shape` and nothing is decided — a loud refusal, not a silent drop. No capability bit; gate on
17244
- the server version.
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.
17245
17318
 
17246
17319
  AskDecisionAck:
17247
17320
  type: object
@@ -18381,7 +18454,7 @@ components:
18381
18454
  type: object
18382
18455
  description: 'Approval seam: wired form, which seat it came from, and (effective half) what it adjudicates to.'
18383
18456
  properties:
18384
- 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.' }
18385
18458
  provenance: { type: string, enum: [spec, deps] }
18386
18459
  effective: { type: string, enum: [human_reachable, auto_allow, auto_deny, park_only, unresolved] }
18387
18460
  question:
@@ -19759,17 +19832,11 @@ components:
19759
19832
  engine's catalogue, not on this wire. READ IT; do not switch over a closed list. A name the grammar
19760
19833
  does not know is still a 400 on the input faces.
19761
19834
  match:
19762
- type: string
19763
- enum: [exact, prefix, wildcard, subpath, path]
19764
- description: >
19765
- The match form (core `PersistedRuleMatch`, five members). `exact` = this one command only ·
19766
- `prefix` = word-boundary prefix (the historical colon-star spelling, and the only spelling for a
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.
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).
19773
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`.' }
19774
19841
  adds:
19775
19842
  type: array
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/sdk",
3
- "version": "16.3.0",
3
+ "version": "17.0.0",
4
4
  "description": "Typed, zero-runtime-dependency SDK for the Sema agent fleet usage plane. The shared substrate for all doors (CC/Codex MCP façade + web). Server-side only — tokens never enter the browser.",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",