@sema-agent/sdk 8.8.0 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/openapi.yaml CHANGED
@@ -2511,6 +2511,13 @@ paths:
2511
2511
  approve carried no `answer` (the ONLY pre-claim rejection left on that leg; the retired codes
2512
2512
  `decide.parked_answer_unsupported` / `decide.parked_question_unsupported` are gone — an answer HAS
2513
2513
  a hookpoint now and a parked question CAN be approved, with an answer). SDK → DecideUnsupportedError.
2514
+ PARKED **WORKFLOW-CHILD** variant (server >= 7.69.0, S-185):
2515
+ `decide.workflow_remember_unsupported` — `remember:"session"` is REFUSED on that lane, fail-closed,
2516
+ BEFORE anything is delivered (zero side effects; re-issue without `remember`). The repo-wide rule for
2517
+ granting an exemption is "the engine COMMITTED this decision", and that lane's 200 does not carry the
2518
+ proof (the child's binding / updatedInput / answer checks run later, inside the workflow's own
2519
+ resume), so an exemption granted here could outlive a decision the engine then rejects. The body
2520
+ carries `runId` (the WORKFLOW run) and NO `taskId`. SDK → DecideUnsupportedError, `.runId`.
2514
2521
  content:
2515
2522
  application/json:
2516
2523
  schema: { $ref: '#/components/schemas/ErrorResponse' }
@@ -2545,6 +2552,18 @@ paths:
2545
2552
  `gate_not_plan_review` (§4c) → SDK `GateNotToolApprovalError`. Client action: route by the row's
2546
2553
  `gateKind` — `plan_review` → `POST /v1/assistant/tasks/{taskId}/plan_review`, `resource_limit` →
2547
2554
  `POST /v1/assistant/tasks/{taskId}/resume`. Retrying the same body never succeeds.
2555
+ SEVENTH group (server >= 7.69.0 / S-185, the PARKED WORKFLOW-CHILD lane): `decide.workflow_host_unknown`
2556
+ and `decide.workflow_host_not_parked`. The pending belongs to a workflow child that parked on an
2557
+ approval gate, and the deployment could not hand the decision to that workflow's HOST session (the
2558
+ engine's only deployment-facing channel for a parked workflow ordinal is a resume of the host, which
2559
+ then re-invokes `Workflow({resumeFromRunId})`). Both bodies carry `runId` (the WORKFLOW run) and NO
2560
+ `taskId`. NOTHING was consumed: the child's checkpoint stays PENDING and the card is still listable
2561
+ and re-decidable. `_host_not_parked` = the host is not currently parked awaiting this run (it may
2562
+ have been interrupted, taking an already-delivered decision with it — the decision is not persisted
2563
+ anywhere) → retry once it parks again, or resume that `runId` yourself. `_host_unknown` = the run
2564
+ carries no originating session at all (a directly started `runWorkflow`, not a Workflow tool call) →
2565
+ NO retry value, a person resumes that `runId`. SDK → DecideWorkflowHostError (`.errorCode` splits the
2566
+ two recovery verbs, `.runId` is the handle).
2548
2567
  SIXTH group (A-075.10, TIME-BASED retry-later pair): `resume.usage_window_exhausted` (#449 G1,
2549
2568
  core 5.60.1 — the deployment governance window on this run's ledger key is full; NOTHING was
2550
2569
  consumed or unpinned, the SAME token with the SAME decision redeems once the window slides) and
@@ -7431,6 +7450,107 @@ components:
7431
7450
  items: { type: string }
7432
7451
  description: 'E7 (SHIPPED) — the /effort picker default set (minimal/low/medium/high); present ONLY for a reasoning model.'
7433
7452
  atMentionable: { type: boolean, description: '#233/A-002.8 (SHIPPED) — @model mention allowlist verdict; present ONLY when the deployment configured an at-mention allowlist. Absent = no allowlist verdict, NOT "not mentionable" — do not narrow absence to false.' }
7453
+ routePairing:
7454
+ $ref: '#/components/schemas/RoutePairingStatus'
7455
+ description: >-
7456
+ #345 (server >=7.45, core 5.57.0 `routePairingStatus`) — this row's key<->URL pairing posture.
7457
+ 🔴 ALWAYS PRESENT on a >=7.45 worker: when the verdict cannot be reached the value is the WORD
7458
+ `unknown`, not a missing key ("this worker does not adjudicate" and "this row could not be
7459
+ adjudicated" must stay distinguishable on the wire). Declared here 2026-09-10 alongside `compat`:
7460
+ this schema is CLOSED, and the key has been minted unconditionally since 7.45 — so every real
7461
+ /v1/models response has been structurally rejected by a strict validator for the whole time (same
7462
+ class as `hasBidiControls` on InboxRow). Optional here because the SDK's supported server floor is
7463
+ 3.0.0; the discriminant is the PEER'S VERSION, not this key's presence.
7464
+ compat:
7465
+ $ref: '#/components/schemas/ModelCompat'
7466
+ description: >-
7467
+ S-188 (server >=7.69.0, settings-schema >=1.10.0) — this row's wire-compatibility declaration for the
7468
+ openai-completions lane. ABSENT = this model declared nothing; see ModelCompat for the two causes of
7469
+ absence and why absence must NOT be rendered as "the operator did not write one".
7470
+
7471
+ ModelCompat:
7472
+ # 新(8.9.0 / S-188):`ModelInfo.compat` 的形 = core `OpenAICompletionsCompat` 的逐字镜像。
7473
+ # 词表在服务端**已判过**(src/model-compat.ts 的 readModelCompat:整只判形,任一键不合即整只丢),
7474
+ # 所以这里写闭集 enum 是**如实**的,不是本仓自己加的一道门 —— 消费方不必再验一遍。
7475
+ type: object
7476
+ description: >
7477
+ One model row's wire-compatibility declaration for the **openai-completions** lane (core
7478
+ `OpenAICompletionsCompat`, mirrored key-for-key; server >= 7.69.0 / S-188). It says how THIS gateway takes
7479
+ the thinking parameter and what the max-tokens field is called — no URL, no credential.
7480
+ 🔴 ABSENT has TWO indistinguishable causes: (a) the model genuinely declared nothing (the engine infers
7481
+ from baseUrl / model id), or (b) the operator wrote one and the server DROPPED IT WHOLE (shape / word /
7482
+ unknown key), logging a named warning and falling back to the engine's inference. So a panel must NOT
7483
+ render "no compat" as "the operator did not configure one".
7484
+ 🔴 WHOLE-OR-NOTHING: the server refuses a partially valid declaration rather than keeping the good keys
7485
+ (half a face on the wire is worse than none), so on the wire this object is always complete-and-legal or
7486
+ entirely absent.
7487
+ 🔴 An EMPTY object never reaches the wire: the server's single mint point folds "all keys empty" (an
7488
+ empty `reasoningEffortLevels` counts as unset) into "do not mint the key". `compat: {}` is therefore an
7489
+ unrecognisable byte, not "declared but says nothing" — hence minProperties: 1.
7490
+ additionalProperties: false
7491
+ minProperties: 1
7492
+ properties:
7493
+ supportsReasoningEffort: { type: boolean, description: 'Does this endpoint accept `reasoning_effort` at all. Absent => the engine auto-detects from the URL.' }
7494
+ reasoningEffortLevels:
7495
+ type: array
7496
+ minItems: 1
7497
+ items: { $ref: '#/components/schemas/ModelThinkingLevel' }
7498
+ description: >-
7499
+ The effort tiers THIS endpoint really accepts (a subset of the six-rung ladder). Absent => the
7500
+ engine's conservative default (minimal|low|medium|high), so a higher requested tier clamps DOWN
7501
+ instead of 422-ing. Never empty on the wire (an empty declaration is folded into absence server-side).
7502
+ maxTokensField:
7503
+ type: string
7504
+ enum: [max_tokens, max_completion_tokens]
7505
+ description: 'Which field carries max tokens on this lane. Absent => inferred from the model id.'
7506
+ requiresReasoningContentOnAssistantMessages: { type: boolean, description: 'Whether every replayed assistant message must carry an empty `reasoning_content` when reasoning is on.' }
7507
+ thinkingFormat:
7508
+ $ref: '#/components/schemas/ModelThinkingFormat'
7509
+ description: 'How this gateway takes the thinking on/off parameter. Absent => the engine infers (default `openai`, i.e. `reasoning_effort`).'
7510
+
7511
+ ModelThinkingLevel:
7512
+ # 六档思考梯(core ThinkingLevel)。CLOSED:server 铸 Model 那一刻逐词判成员,出集词让整只 compat 被丢。
7513
+ type: string
7514
+ enum: [minimal, low, medium, high, xhigh, max]
7515
+ description: >-
7516
+ One rung of the six-rung thinking ladder (core `ThinkingLevel`). CLOSED — the server judges membership
7517
+ when it mints the Model, and an out-of-set word makes it drop the WHOLE `compat` declaration, so a word
7518
+ outside this set cannot reach the wire. (`off` is NOT a rung: it means "no model-level default" and
7519
+ belongs to a different knob.)
7520
+
7521
+ ModelThinkingFormat:
7522
+ # thinkingFormat 七词闭集(core OpenAICompletionsCompat["thinkingFormat"])。同上,server 已判成员。
7523
+ type: string
7524
+ enum: [openai, openrouter, deepseek, together, zai, qwen, qwen-chat-template]
7525
+ description: >-
7526
+ The spelling this gateway takes the thinking parameter in (core
7527
+ `OpenAICompletionsCompat.thinkingFormat`). CLOSED, judged server-side before it reaches the wire.
7528
+ `openai` = `reasoning_effort` · `openrouter` = `reasoning:{effort}` · `deepseek` = `thinking:{type}` plus
7529
+ `reasoning_effort` · `together` = `reasoning:{enabled}` plus `reasoning_effort` · `zai` / `qwen` =
7530
+ top-level `enable_thinking` · `qwen-chat-template` = `chat_template_kwargs.enable_thinking` — the ONLY
7531
+ spelling that can turn thinking OFF on a think-by-default vLLM/Qwen gateway.
7532
+
7533
+ RoutePairingStatus:
7534
+ # #345 core RoutePairingStatus(`ok:${RoutePairingPosture}` | `broken:*` 两词 | unknown)。
7535
+ # 🔴 **真开集,刻意不写 enum**:开/闭按**产方是否真判成员**定(8.4.0 拆 `CheckpointGate` 顶层 enum /
7536
+ # 把 `AskOrigin` 改真开集的同一条判据)。core 的 `routePairingStatus` 把 `verdict.posture` **直接内插**
7537
+ # 进 `ok:${…}`,server 的铸点与 core 的这只函数**全路径没有一处运行期成员判定** —— 一只自带
7538
+ # `adjudicateRoute` 的 brain 供什么词就上什么词。写一条会执法的 enum = 假闭集,会把一台合法 worker
7539
+ # 的真响应判违约。已知七词写在 description 里(消费端 switch 认已知词 + 一条 default 臂)。
7540
+ type: string
7541
+ x-open-enum: true
7542
+ description: >-
7543
+ A model row's key<->URL pairing posture (core `RoutePairingStatus`; server >= 7.45). OPEN on read —
7544
+ the seven words below are today's set, branch on the known ones and keep a `default` arm. NON-SECRET — no
7545
+ URL, no credential.
7546
+ `ok:per-model` this model has its own credential · `ok:paired` deployment credential, and the model's URL
7547
+ is on the deployment's declared root · `ok:unpinned` deployment credential but the deployment declared no
7548
+ root, so the pairing is UNVERIFIABLE · `ok:keyless` no credential anywhere (a local unauthenticated
7549
+ gateway) · `broken:credential_mismatch` / `broken:credential_missing` a real request WILL be refused by
7550
+ the engine (flag it red) · `unknown` could not be adjudicated — render "unknown", NEVER "good" or "bad".
7551
+ The verdict's frame of reference is the gateway root as of BRAIN CONSTRUCTION, so a live re-pointing of
7552
+ the deployment root only shows up after a restart (both this reading and the real request judge from the
7553
+ same snapshot).
7434
7554
 
7435
7555
  ElicitResponse:
7436
7556
  type: object
@@ -7633,8 +7753,25 @@ components:
7633
7753
  additionalProperties: false
7634
7754
  properties:
7635
7755
  label: { type: string, description: 'display label (redacted — LLM-authored).' }
7636
- status: { type: string, description: 'lifecycle status (the input vocabulary the display derivation reads).' }
7637
- displayStatus: { type: string, description: 'core deriveAgentDisplayStatus: running|queued|done|failed|interrupted — the canonical glyph vocabulary, one source of truth shared with the shell.' }
7756
+ status:
7757
+ type: string
7758
+ description: >-
7759
+ Lifecycle status — core `WorkflowItemStatus` passed through verbatim (running|completed|failed|parked;
7760
+ OPEN on read, the vocabulary's owner is core). 🔴 `parked` (server >= 7.69.0 / core 7.10.0 #642) is an
7761
+ AGENT-ROW word only: this ordinal durably paused at an approval gate and the whole run suspended on it
7762
+ (a phase or group never parks — a parked agent ends the run, so the RUN-level status vocabulary does
7763
+ not have this word). The row's redemption key (`parkedCheckpointToken`) is a resume CAPABILITY and is
7764
+ NEVER projected onto the wire — it appears nowhere in this document by design.
7765
+ displayStatus:
7766
+ type: string
7767
+ description: >-
7768
+ core `deriveAgentDisplayStatus`: running|queued|done|failed|interrupted|parked — the canonical glyph
7769
+ vocabulary, one source of truth shared with the shell. 🔴 `parked` is the SIXTH arm (core 7.10.0
7770
+ #642, server >= 7.69.0): a durable approval gate is holding this agent. Before it existed a parked
7771
+ agent fell through to another word, i.e. a card waiting on a human rendered as a glyph that asks for
7772
+ no action. ALWAYS PRESENT — the server derives it with that pure function on every row. Open on read
7773
+ (the vocabulary's owner is core), but the function has no default arm, so a word outside this set
7774
+ means core grew the table.
7638
7775
  taskStatus: { type: string, description: 'the underlying task''s terminal TaskStatus (open vocabulary; core enum verbatim).' }
7639
7776
  callKey: { type: string, description: 'the stable deterministic identity of this ctx.agent call (resume-journal key).' }
7640
7777
  groupId: { type: string, description: 'the nesting ctx.workflow sub-group this agent ran under; absent = top-level.' }
@@ -7681,7 +7818,7 @@ components:
7681
7818
  additionalProperties: false
7682
7819
  properties:
7683
7820
  title: { type: string, description: 'phase title (redacted — LLM-authored).' }
7684
- status: { type: string, description: 'core WorkflowItemStatus, plus "pending" for a meta-preregistered phase not yet adopted.' }
7821
+ status: { type: string, description: 'core WorkflowItemStatus (running|completed|failed; `parked` is an agent-row word only), plus "pending" for a meta-preregistered phase not yet adopted.' }
7685
7822
  startedAt: { type: integer, description: 'adoption time for a pre-registered phase (0 while still pending).' }
7686
7823
  endedAt: { type: integer }
7687
7824
  durationMs: { type: integer, description: 'endedAt − startedAt; absent until the phase ends.' }
@@ -7699,7 +7836,7 @@ components:
7699
7836
  properties:
7700
7837
  groupId: { type: string }
7701
7838
  parentGroupId: { type: string, description: 'absent = top-level (a child of the implicit root).' }
7702
- status: { type: string, description: 'core WorkflowItemStatus (running/completed/failed).' }
7839
+ status: { type: string, description: 'core WorkflowItemStatus (running|completed|failed; a group never parks — `parked` is an agent-row word only).' }
7703
7840
  startedAt: { type: integer }
7704
7841
  endedAt: { type: integer }
7705
7842
  durationMs: { type: integer, description: 'endedAt − startedAt; absent until the group ends.' }
@@ -7925,13 +8062,31 @@ components:
7925
8062
  resultChars: { type: integer, description: 'server >=7.9.0 (A-002.6): the ORIGINAL result length in characters; present only alongside resultTruncated.' }
7926
8063
  tokens: { type: integer, description: 'present only when the result carried stats.' }
7927
8064
  turns: { type: integer, description: 'present only when the result carried stats.' }
8065
+ parked:
8066
+ type: boolean
8067
+ enum: [true]
8068
+ description: >-
8069
+ server >= 7.69.0 / core 7.10.0 #642 — this ordinal is PARKED on a durable approval gate, waiting for a
8070
+ decision. 🔴 NEVER minted as `false`: the park arm and the result arm carry the SAME payload (a
8071
+ TaskResult whose terminal cause is `paused`), and the read face adds exactly this one bit, so an
8072
+ ordinary row is simply ABSENT. Branch on `parked === true`, never on `!parked`. `status` is NOT
8073
+ rewritten by this layer — it is that TaskResult's persisted plane status (`suspended` on a human
8074
+ gate). 🔴 The oversize-stub arm (WorkflowJournalEntryTruncated) structurally CANNOT carry this key —
8075
+ see its note; absence there is not an assertion.
7928
8076
 
7929
8077
  WorkflowJournalEntryTruncated:
7930
8078
  # 新(census 批2 四段):the truncated arm (workflows.ts) — an honest stub for a row whose
7931
8079
  # stored TaskResult exceeded the per-row 64KiB gate (SQL stores) or byte length (file/in-memory
7932
8080
  # fallback): the server never pulls the oversized payload into process memory.
7933
8081
  type: object
7934
- description: A journal row too large to project — an honest "too big to show" stub, never a silent 2000-char truncation of the real result.
8082
+ description: >-
8083
+ A journal row too large to project — an honest "too big to show" stub, never a silent 2000-char
8084
+ truncation of the real result.
8085
+ 🔴 DELIBERATELY carries NO `parked` (server 7.69.0, one mint point shared by all three arms): a stub by
8086
+ definition carries no payload-derived key — the SQL paging leg never pulled the payload back when the row
8087
+ is over the bound, so it structurally cannot answer that bit, and minting it only on the file/in-memory
8088
+ leg would fork the wire shape by backend. To decide "is this ordinal parked", read the `parked` of a
8089
+ NON-stub row, or `agents[].status === "parked"` on GET /v1/workflows/:id. Absence here is NOT an assertion.
7935
8090
  required: [callKey, ordinal, truncated, resultBytes]
7936
8091
  additionalProperties: false
7937
8092
  properties:
@@ -8431,6 +8586,21 @@ components:
8431
8586
  is guaranteed by all four shapes; discriminate: `idempotent` -> replay; `decision` without `sessionId` ->
8432
8587
  parked receipt; `sessionId` + `bindingEnforced` (+ `status`) -> task-level acceptance / terminal.
8433
8588
  `taskId` rides `getActiveTaskId` on the terminal leg (omitted when there is no active row).
8589
+ 🔴 A FIFTH PROVENANCE, not a fifth key set (server >= 7.69.0 / S-185 — the PARKED WORKFLOW-CHILD lane):
8590
+ when the pending belongs to a workflow child, the success body is byte-identically the TASK-LEVEL
8591
+ ACCEPTANCE shape, but it is a wake of the workflow's **HOST session** — `taskId` is the host's FRESHLY
8592
+ MINTED run id, not the child's. Two readings a consumer MUST get right:
8593
+ (1) 🔴 the 200 is a **DELIVERY acceptance**, NOT "the gate is resolved". The child's checkpoint stays
8594
+ PENDING until the workflow engine itself resolves it, so the card can still be on `/v1/approvals` right
8595
+ after the 200 — NEVER remove the card from a UI on the strength of this 200; poll the HOST run
8596
+ (`GET /v1/runs/{taskId}`) for progress instead. The background-redemption lane's 200 and the task-level
8597
+ lane's 200 DO mean the engine committed the decision — one sentence ("approved and in effect") cannot be
8598
+ used for both lanes.
8599
+ (2) delivery can still be LOST: if the host run is interrupted before it re-invokes `Workflow`, the
8600
+ decision goes with that invocation (it is not persisted anywhere) and a retry answers 409
8601
+ `decide.workflow_host_not_parked`. Because the card was pending throughout, nothing is forged and the
8602
+ approval can be re-decided once the host parks again.
8603
+ The durable approval-queue projection is UNCHANGED — no key marks the workflow origin on any read face.
8434
8604
  required: []
8435
8605
  additionalProperties: false
8436
8606
  properties:
@@ -8697,6 +8867,12 @@ components:
8697
8867
  this spec over-declared decide bindings that never appeared on inbox wire at all. Four client repos were
8698
8868
  structurally zero-consumers, so server 3.4.0 narrowed the wire and this schema follows. For the tool face
8699
8869
  and decide bindings use /v1/approvals (PendingCheckpoint); for task attribution use /v1/assistant/tasks.
8870
+ 🔴 STILL CLOSED (sdk 8.9.0). Four keys the server had been minting onto this row went undeclared until
8871
+ now — `requiresRealApproval` / `denialLimitFallback` / `origin` (server >= 7.57.0) and
8872
+ `classifierUnavailable` (server >= 7.69.0) — so a strict validator rejected every row that carried one
8873
+ and a generated client could not read them at all. They are declared below rather than tolerated: this
8874
+ row stays closed, which is the whole point of the shape (an undeclared key is drift, and drift should be
8875
+ loud).
8700
8876
  type: object
8701
8877
  required: [sessionId, scope, objective, input]
8702
8878
  additionalProperties: false
@@ -8723,11 +8899,47 @@ components:
8723
8899
  the /v1/approvals row's — the reading of absence does not). Absence = "not detected", never
8724
8900
  "confirmed clean". Declared here because this schema is CLOSED — without the key a strict validator
8725
8901
  rejects every >=7.53 inbox row that carries it.
8902
+ requiresRealApproval:
8903
+ type: boolean
8904
+ enum: [true]
8905
+ description: >-
8906
+ server >= 7.57.0 (core 7.4.0 #557), ADDITIVE, present ONLY when true: this parked ask demanded REAL
8907
+ HUMAN judgment — an inbox can say "only a person can clear this" without decoding the gate kind.
8908
+ 🔴 NEVER minted as `false` (the OMIT contract this row's other flags follow); absence is "not marked",
8909
+ never "confirmed clearable without a person". The value comes WHOLE from core (`summarizeCheckpoint`
8910
+ projects the park row's own bit) — the server neither recomputes nor redacts it.
8911
+ denialLimitFallback:
8912
+ $ref: '#/components/schemas/DenialLimitFallback'
8913
+ description: >-
8914
+ server >= 7.57.0 (core 7.4.0 #557), ADDITIVE — this parked ask IS the auto-mode classifier's
8915
+ DENIAL-LIMIT fallback, carrying the counts that tripped the bound. 🔴 On a PARKED row
8916
+ `autoDenyAfterMs` is ALWAYS `0` and a consumer MUST NOT start a countdown from it: the window is a
8917
+ fact of the ask's ROUTE, armed only at a hand-out to a LIVE approver, and nothing counts down on the
8918
+ parked lane (a park's expiry is the row's own `deadline`/ttl). Render the counts and the limit; there
8919
+ is no countdown to render here. Core screens the four members before writing the row, so a
8920
+ half-shaped value reads as ABSENT rather than reaching a consumer as a malformed card.
8921
+ origin:
8922
+ $ref: '#/components/schemas/AskOrigin'
8923
+ description: >-
8924
+ server >= 7.57.0 (core 7.5.0), ADDITIVE — WHICH AUTHORITY raised this parked ask, the same word the
8925
+ synchronous card renders by (engine-stamped at the gate, never a policy's claim). Core writes it onto
8926
+ the row ONLY when it is a member of the set, so an out-of-set word is not expected on THIS face — but
8927
+ the vocabulary's owner is still core and it has grown before, so read it the same way as everywhere
8928
+ else: branch known words, keep a `default` arm, and treat an unknown one as "unknown origin", never
8929
+ as "no origin". A row minted before the bit existed reads absent (unreported), not "no authority".
8726
8930
  objective: { type: ['string', 'null'], description: 'task objective, null if unavailable.' }
8727
8931
  input:
8728
8932
  description: >
8729
8933
  REDACTED tool-args preview for display (null on gates without one, e.g. plan_review). Untyped by
8730
8934
  design (per-tool shape). The raw `toolInput` is deliberately NOT on this row.
8935
+ classifierUnavailable:
8936
+ $ref: '#/components/schemas/ClassifierUnavailable'
8937
+ description: >-
8938
+ server >= 7.69.0 (core 7.10.0 #616), ADDITIVE — WHY this PARKED ask reached a human. The inbox is the
8939
+ TRIAGE face, so `breaker_open` matters most here: it forecasts an approval storm rather than a
8940
+ one-off question. The value comes WHOLE from core (`summarizeCheckpoint` echoes it only when the
8941
+ cause is a member of the engine's set), so the server neither recomputes nor re-screens it. See
8942
+ ClassifierUnavailable for the open-`cause` rule and the three shapes absence covers.
8731
8943
 
8732
8944
  InboxList:
8733
8945
  type: object
@@ -9389,6 +9601,14 @@ components:
9389
9601
  activeTaskId:
9390
9602
  type: string
9391
9603
  description: On a 409 — the task currently holding the session lock (see the Conflict response note).
9604
+ runId:
9605
+ type: string
9606
+ description: >-
9607
+ server >= 7.69.0 (S-185) — on the three `decide.workflow_*` codes ONLY: the WORKFLOW run's id. It is
9608
+ the RECOVERY HANDLE (wait for the host session to park and re-decide, or resume that run directly).
9609
+ 🔴 NOT interchangeable with `taskId`: the workflow-child park lane mints no taskId at all (this
9610
+ decision never landed on any run, so minting one would be a lie), and the other two decide lanes
9611
+ mint no runId. Branch by `errorCode`, never fall back from one handle to the other.
9392
9612
 
9393
9613
  # ── Streaming event taxonomy (the UX 命脉). Each SSE `data:` line is one JSON-serialized AgentEvent. ──
9394
9614
  # TWO vocabularies (the pinned wire contract Drift 3). ⚠️ The two lines below are the SHAPE-DEFINING
@@ -10341,33 +10561,7 @@ components:
10341
10561
  🔴 The engine''s `error` free text is DELIBERATELY NOT PROJECTED (it is remote-author text, and core
10342
10562
  already owns the single redaction mint point for it). The actionable cause is `errorCode`.
10343
10563
  A row missing `name` or `status` is dropped individually — the other servers'' rows still ship.
10344
- items:
10345
- type: object
10346
- additionalProperties: false
10347
- required: [name, status]
10348
- properties:
10349
- name: { type: string, description: 'The declared server name.' }
10350
- status: { type: string, description: 'Three-word engine set (connected / failed / …). Open on read.' }
10351
- source: { type: string, description: 'The declarer''s own layer label, echoed verbatim by core (single-line, length-capped).' }
10352
- toolCount: { type: integer, description: 'Tools mounted. ABSENT rather than 0 — a minted 0 would read as "connected with no tools".' }
10353
- errorCode:
10354
- type: string
10355
- description: >
10356
- core >= 7.6.0 — the `McpFailureKind` word (`connect_refused` / `connection_failed` /
10357
- `connection_closed` / `http_status` / `not_mcp_response` / `spawn_failed` / `timeout` /
10358
- `protocol` / `invalid_config` / `unknown`). The retired 7.5.0 spellings — `network` and the
10359
- `http_<status>` encoding — are GONE; an HTTP status now rides `httpStatus`.
10360
- Deliberately NOT enumerated here: the vocabulary''s single owner is the engine, and mirroring
10361
- it would swallow a newly minted word as a violation. Switch with a `default` arm.
10362
- delivered:
10363
- allOf: [{ $ref: '#/components/schemas/McpDelivered' }]
10364
- description: 'core >= 7.6.0 (S6-B) — did the request reach that server. Read WITH `errorCode`.'
10365
- httpStatus:
10366
- type: integer
10367
- description: >
10368
- core >= 7.6.0 (S6-B) — the status the endpoint answered with; rides ONLY with
10369
- `errorCode === "http_status"` (401/403 = re-authorize, 5xx = that end is down). ABSENT rather
10370
- than 0.
10564
+ items: { $ref: '#/components/schemas/WiringManifestMcpEntry' }
10371
10565
  governance:
10372
10566
  type: object
10373
10567
  additionalProperties: false
@@ -10502,6 +10696,28 @@ components:
10502
10696
  message varies with the memoryProvenance mode — matching on message text WILL break). `code` is an OPEN
10503
10697
  set (the server whitelist grows with core's code register): render a known code specially, fall back to
10504
10698
  `message` for an unknown one — never drop the frame.
10699
+ 🔴 This list is NOT the full register and is not meant to become one (server 7.69.0's whitelist holds 21
10700
+ codes): `code` is OPEN, and only the codes with an EXTRA rendering rule are written out here.
10701
+ `mcp.injection_dropped` (server >= 7.68.0) — one entry of the CALLER'S OWN request-leg MCP injection was
10702
+ not mounted; `{sessionId, server, reason, field?}`, `reason` a four-word set (`malformed_entry` /
10703
+ `name_reserved_by_deployment` / `gate_closed` / `over_cap`). The recovery verb is on the USER's side
10704
+ (rename it, fix that key, drop a few servers) — without this notice all they see is "my .mcp.json seems
10705
+ to do nothing". Minted at ASSEMBLY time, so `detail` structurally has NO runId; the join key is the
10706
+ `server` name. ⚠️ The DELIVERY surface changed at server 7.69.0: 7.68.0 delivered it on the two FRESH
10707
+ legs only, and 7.69.0 delivers it on the RESUME family too (all four resume legs share one
10708
+ spec-resolver, which now passes the notice seat). Consequence for consumers that assert on ledger
10709
+ CONTENTS: a resume leg's ledger MAY now carry one extra `engine_notice` row it did not before — assert
10710
+ "may appear", never an exact row count or order (and consume idempotently: reconnect replay shows it again).
10711
+ `delegation.ask_unresolvable` (server >= 7.69.0 / core 7.10.0 #648) — an `ask` reached a FINAL DENY with
10712
+ NOBODY having ruled on it: the approver consulted for the call answered `unavailable` and no durable park
10713
+ caught it afterwards. The deny itself is unchanged (`tool_end.gate.settlement.kind:"approver_unavailable"`
10714
+ — that sentence IS the tool result); this code is the half the person watching notices could not see.
10715
+ `{sessionId, toolName, toolCallId, settlementKind, parkLaneExisted}`; `toolCallId` joins to the
10716
+ `tool_approval` card frame, `settlementKind` is passed through verbatim (the vocabulary's owner is core).
10717
+ Deduplicated ONCE PER TOOL CALL. 🔴 `parkLaneExisted` is a DISCRIMINANT, not a count: `true` = a park lane
10718
+ was armed but declined/failed (check the store, check authorisation), `false` = there was no lane at all
10719
+ (wire one up). The two recovery verbs differ, so a consumer MUST render them apart — NEVER fold them into
10720
+ one "nobody approved it".
10505
10721
  Starter whitelist (server 7.36): `memory.session_polluted` `{reason, sessionId?}` ·
10506
10722
  `memory.harvest_quarantined` `{count, moved, escalated, reason?}` — `moved` and `escalated` MUST NOT be
10507
10723
  subtracted from each other (an in-place tombstone counts as both) · `memory.delegation_static_mark_waived`
@@ -11178,6 +11394,12 @@ components:
11178
11394
  # 分两段:server ≤7.4.0 默认 OFF,≥7.5.0 默认 ON(BREAKING,见 server CHANGELOG)。──
11179
11395
  # 与上面的 ToolApprovalFrame 是**并行加帧,不替换**:开关打开时同一只 ask 出两帧(先 tool_approval,
11180
11396
  # 后 approval_request),两帧同带 `approvalId` ⇒ 消费端按它去重、优先渲染新帧。
11397
+ classifierUnavailable:
11398
+ $ref: '#/components/schemas/ClassifierUnavailable'
11399
+ description: >-
11400
+ server >= 7.69.0 (core 7.10.0 #616), ADDITIVE, present only when the classifier was consulted and
11401
+ could not run — WHY this ask reached a human. See ClassifierUnavailable for the open-`cause` rule and
11402
+ the three shapes absence covers.
11181
11403
  ApprovalRequestFrame:
11182
11404
  type: object
11183
11405
  description: >
@@ -11310,6 +11532,12 @@ components:
11310
11532
  # 但本 schema 此前只在帧上公示过。长 description 的单一真源在 ToolApprovalFrame 的同名键。
11311
11533
  ruleOffersAbsence: { $ref: '#/components/schemas/RuleOffersAbsence' }
11312
11534
  denialLimitFallback: { $ref: '#/components/schemas/DenialLimitFallback' }
11535
+ classifierUnavailable:
11536
+ $ref: '#/components/schemas/ClassifierUnavailable'
11537
+ description: >-
11538
+ server >= 7.69.0 (core 7.10.0 #616), ADDITIVE — WHY this ask reached a human. Frame and card carry
11539
+ the SAME value (one narrow-read function server-side). See ClassifierUnavailable for the open-`cause`
11540
+ rule and the three shapes absence covers.
11313
11541
 
11314
11542
  RuleSuggestion:
11315
11543
  type: object
@@ -11333,6 +11561,30 @@ components:
11333
11561
  command: { type: string, maxLength: 512, description: 'The command pattern inside the rule, for a shell that would rather not re-parse `rule`.' }
11334
11562
 
11335
11563
  # ─── S-139(sdk 8.3.0):对 server 7.60.0 整体重对账带进来的四张型面 ───────────────────────
11564
+ ClassifierUnavailable:
11565
+ # 8.9.0:三面(tool_approval 帧 / ApprovalCard / InboxRow)同形同值 ⇒ 具名单源,内联三份会各自漂。
11566
+ type: object
11567
+ description: >-
11568
+ WHY this ask reached a human (server >= 7.69.0, core 7.10.0 #616): the auto-mode classifier was consulted
11569
+ and could not run. Carried on all THREE faces with the same value — the live `tool_approval` frame, the
11570
+ `ApprovalCard` (`card_json` / `approval_request`), and the `GET /v1/assistant/inbox` row. Under the auto
11571
+ mode most calls are answered by the classifier, so the ones that DO reach a person are often exactly the
11572
+ ones it could not answer — and the causes want different handling (the gateway is broken / it is too slow
11573
+ / the breaker is OPEN, i.e. every following ask will arrive too). The inbox is the TRIAGE face, which is
11574
+ where `breaker_open` is worth the most: it forecasts an approval storm, not a one-off question.
11575
+ 🔴 `cause` is an OPEN string and is deliberately NOT enumerated here. The vocabulary's single owner is the
11576
+ engine (today: `error` / `timeout` / `breaker_open`); mirroring it would swallow a newly minted word as a
11577
+ violation — the same rule as `AskOrigin` and the MCP `errorCode`. Switch on the known words and KEEP A
11578
+ DEFAULT ARM.
11579
+ 🔴 ABSENCE IS NOT AN ASSERTION. It covers three shapes at once: the classifier ANSWERED (this ask is one
11580
+ it ruled should go to a person) · this ask was not ELIGIBLE for the classifier · this deployment wired no
11581
+ classifier at all. Never read absence as any claim about classifier health, and never as "the classifier
11582
+ is fine". (`parse_error` does not set this bit, per the engine's contract.)
11583
+ additionalProperties: false
11584
+ required: [cause]
11585
+ properties:
11586
+ cause: { type: string, description: 'The engine''s cause word. OPEN — branch known words, default arm for the rest.' }
11587
+
11336
11588
  AskOrigin:
11337
11589
  type: string
11338
11590
  x-open-enum: true
@@ -11410,8 +11662,13 @@ components:
11410
11662
  waits for a person) is for RENDERING THE COUNTDOWN ONLY. Never start a second timer from it: the window
11411
11663
  is executed by the ENGINE, and two overlapping windows are worse than the original defect and silent.
11412
11664
  🔴 ABSENCE IS NOT AN ASSERTION — the vast majority of asks are not fallback cards.
11413
- ⚠️ The durable (parked) leg does NOT carry this key today; the card's copy rides `card_json`, which is
11414
- a different leg from the parked row's `pendingAction`.
11665
+ ⚠️ TWO LEGS CARRY IT, and they differ in ONE member. The live/card leg (`ToolApprovalFrame` /
11666
+ `ApprovalCard` / `card_json`) can carry a NON-ZERO `autoDenyAfterMs`; the DURABLE leg — the parked row,
11667
+ projected onto `InboxRow` since server >= 7.57.0 (core's park twin of the same member) — always carries
11668
+ `0` there, because nothing counts down on a parked lane (see InboxRow.denialLimitFallback).
11669
+ (Corrected 2026-09-10 against core's checkpoint-summary type and the server's inbox projection: this
11670
+ sentence used to read "the durable (parked) leg does NOT carry this key today", which stopped being
11671
+ true when the park twin landed.)
11415
11672
  properties:
11416
11673
  consecutive: { type: integer, description: 'Consecutive refusals at the moment the limit tripped.' }
11417
11674
  total: { type: integer, description: 'Cumulative refusals at the moment the limit tripped.' }
@@ -12997,12 +13254,18 @@ components:
12997
13254
  core's assembly self-evidence manifest (core >=5.14.0 `WiringManifest`). TWO HALVES, ONE shape (a
12998
13255
  consumer must line the two faces up, so they are not split into separate types) — but which keys
12999
13256
  belong to which half is DETERMINATE, not "usually": `leg`, `ask.effective` and `configFingerprint`
13000
- are EFFECTIVE-half only (the live `wiring_manifest` event) — as are the three per-leg sections the
13001
- event schema declares (`modelGate`, `autoMode`, `mcp`), which describe what THIS leg did and have no
13002
- static counterpart — and the STATIC half
13257
+ are EFFECTIVE-half only (the live `wiring_manifest` event) — as are the four per-leg sections
13258
+ `modelGate`, `autoMode`, `mcp` and `tools`, which describe what THIS leg did and have no static
13259
+ counterpart — and the STATIC half
13003
13260
  (GET /v1/diagnostics/wiring) NEVER mints them; every other section, plus `governance`, is always
13004
13261
  present on the static half. Fields stay optional because core may add or drop sections and that
13005
13262
  must not hard-break a client — read by half, and never wait on a key the half never sends.
13263
+ 🔴 EVERY section is declared HERE, each saying which half mints it (sdk 8.9.0). Before that, `tools`
13264
+ was declared while its three effective-half siblings were not, and `permissionRules` — which the
13265
+ engine mints UNCONDITIONALLY, i.e. on BOTH halves — was missing altogether, so a generated client
13266
+ reading GET /v1/diagnostics/wiring could not see it at all. "Two halves, one shape" is the stated
13267
+ doctrine of this schema; omitting a section because one half does not mint it contradicts it and
13268
+ splits consumers into two groups reading two different manifests.
13006
13269
  🔴 `governance` and `configFingerprint` appear ONLY on an operator-scoped face. A tenant stream
13007
13270
  gets neither — and NOT just the section: core hashes the WHOLE manifest unsalted and governance is
13008
13271
  four booleans, so the fingerprint alone would let a tenant brute-force sixteen combinations against
@@ -13073,6 +13336,85 @@ components:
13073
13336
  description: >
13074
13337
  server >= 7.66.0 (design/388 B-4) — the leg's whole tool roster. EFFECTIVE half only (the diagnostics
13075
13338
  endpoint's static half never carries it); absent as a whole when core's typebox check rejects it.
13339
+ permissionRules:
13340
+ type: object
13341
+ description: >
13342
+ core >= 5.18.0 (design/179) / >= 5.23.0 (design/182 §7/§9), server >= 7.6.0 — is a PERSISTED
13343
+ PERMISSION RULE store wired, how far does a "don't ask again" travel, and is an org governance layer
13344
+ armed over that lane. 🔴 BOTH HALVES: the engine mints this section unconditionally, so it rides the
13345
+ static `GET /v1/diagnostics/wiring` face too — read it before offering a "don't ask again"
13346
+ affordance. `false` is a REAL reading ("no rule lane on this worker"), never "too old to know"; only
13347
+ the whole key being absent means "never sent it". TENANT-visible (deliberately outside the
13348
+ operator-only `governance` section). Full per-key contract: see Event_wiring_manifest.permissionRules.
13349
+ properties:
13350
+ storeWired: { type: boolean }
13351
+ syncWired: { type: boolean }
13352
+ orgGoverned: { type: boolean }
13353
+ mcp:
13354
+ type: array
13355
+ description: >
13356
+ core >= 7.5.0 (#562) / >= 7.6.0 (S6-B), server >= 7.60 — one row per MCP server THIS leg declared.
13357
+ EFFECTIVE half only. 🔴 An empty array is NOT absence (`[]` = "this leg declared no servers").
13358
+ Full per-key contract: see Event_wiring_manifest.mcp.
13359
+ items: { $ref: '#/components/schemas/WiringManifestMcpEntry' }
13360
+ modelGate:
13361
+ type: object
13362
+ description: >
13363
+ core >= 7.3.x (design/385 片2b), server >= 7.58 — which tools the tool-MODEL gate removed from THIS
13364
+ session and how to get them back. EFFECTIVE half only; ALL-OR-NOTHING (absent = nothing was gated,
13365
+ never an empty section). Full contract: see Event_wiring_manifest.modelGate.
13366
+ required: [class, removed, restore]
13367
+ properties:
13368
+ class: { type: string }
13369
+ removed: { type: array, items: { type: string } }
13370
+ restore: { type: string }
13371
+ autoMode:
13372
+ type: object
13373
+ description: >
13374
+ core >= 7.3.1 (#529), server >= 7.59 — did AUTO mode arm on THIS leg, and if not which arm it stopped
13375
+ at. EFFECTIVE half only. 🔴 `reason` is core's closed word list passed through VERBATIM (branch known
13376
+ words, keep a default arm); ABSENCE IS NOT "not applicable" — never fold it to `armed: false`. The
13377
+ server projects exactly these two keys (core's `breaker` sub-fact is NOT projected onto the wire).
13378
+ Full contract: see Event_wiring_manifest.autoMode.
13379
+ required: [armed, reason]
13380
+ properties:
13381
+ armed: { type: boolean }
13382
+ reason: { type: string }
13383
+ WiringManifestMcpEntry:
13384
+ # 8.9.0:从 `Event_wiring_manifest.mcp.items` 的内联形**提取**为具名 schema —— 同一份行形现在被
13385
+ # 两处引用(live 帧与 `WiringManifest` 的静态/两半形),内联会立刻变成两份会各自漂的镜像。
13386
+ type: object
13387
+ description: >
13388
+ One row of a leg's MCP wiring manifest: did that declared server connect, if not which failure class,
13389
+ and how many tools it mounted. A row missing `name` or `status` is dropped INDIVIDUALLY — the other
13390
+ servers' rows still ship. 🔴 The engine's `error` free text is DELIBERATELY NOT PROJECTED (remote-author
13391
+ text; core owns the single redaction mint point). The actionable cause is `errorCode`.
13392
+ additionalProperties: false
13393
+ required: [name, status]
13394
+ properties:
13395
+ name: { type: string, description: 'The declared server name.' }
13396
+ status: { type: string, description: 'Three-word engine set (connected / failed / …). Open on read.' }
13397
+ source: { type: string, description: 'The declarer''s own layer label, echoed verbatim by core (single-line, length-capped).' }
13398
+ toolCount: { type: integer, description: 'Tools mounted. ABSENT rather than 0 — a minted 0 would read as "connected with no tools".' }
13399
+ errorCode:
13400
+ type: string
13401
+ description: >
13402
+ core >= 7.6.0 — the `McpFailureKind` word (`connect_refused` / `connection_failed` /
13403
+ `connection_closed` / `http_status` / `not_mcp_response` / `spawn_failed` / `timeout` /
13404
+ `protocol` / `invalid_config` / `unknown`). The retired 7.5.0 spellings — `network` and the
13405
+ `http_<status>` encoding — are GONE; an HTTP status now rides `httpStatus`.
13406
+ Deliberately NOT enumerated here: the vocabulary's single owner is the engine, and mirroring
13407
+ it would swallow a newly minted word as a violation. Switch with a `default` arm.
13408
+ delivered:
13409
+ allOf: [{ $ref: '#/components/schemas/McpDelivered' }]
13410
+ description: 'core >= 7.6.0 (S6-B) — did the request reach that server. Read WITH `errorCode`.'
13411
+ httpStatus:
13412
+ type: integer
13413
+ description: >
13414
+ core >= 7.6.0 (S6-B) — the status the endpoint answered with; rides ONLY with
13415
+ `errorCode === "http_status"` (401/403 = re-authorize, 5xx = that end is down). ABSENT rather
13416
+ than 0.
13417
+
13076
13418
  ServerWiringGates:
13077
13419
  type: object
13078
13420
  description: >
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/sdk",
3
- "version": "8.8.0",
3
+ "version": "9.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",