@sema-agent/sdk 18.1.0 → 19.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +199 -0
- package/dist/control-types.d.ts +4 -3
- package/dist/control-types.d.ts.map +1 -1
- package/dist/control-types.js.map +1 -1
- package/dist/errors.d.ts +82 -30
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +85 -31
- package/dist/errors.js.map +1 -1
- package/dist/events.d.ts +11 -6
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js.map +1 -1
- package/dist/health.d.ts +41 -1
- package/dist/health.d.ts.map +1 -1
- package/dist/health.js.map +1 -1
- package/dist/index.d.ts +11 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/resources/admin.d.ts +4 -6
- package/dist/resources/admin.d.ts.map +1 -1
- package/dist/resources/admin.js.map +1 -1
- package/dist/resources/approvals.d.ts +12 -8
- package/dist/resources/approvals.d.ts.map +1 -1
- package/dist/resources/approvals.js +12 -7
- package/dist/resources/approvals.js.map +1 -1
- package/dist/resources/images.d.ts +2 -1
- package/dist/resources/images.d.ts.map +1 -1
- package/dist/resources/images.js +3 -2
- package/dist/resources/images.js.map +1 -1
- package/dist/resources/models.d.ts +5 -10
- package/dist/resources/models.d.ts.map +1 -1
- package/dist/resources/models.js +3 -1
- package/dist/resources/models.js.map +1 -1
- package/dist/resources/sessions.d.ts +4 -0
- package/dist/resources/sessions.d.ts.map +1 -1
- package/dist/resources/sessions.js +6 -2
- package/dist/resources/sessions.js.map +1 -1
- package/dist/resources/tasks.d.ts +9 -2
- package/dist/resources/tasks.d.ts.map +1 -1
- package/dist/resources/tasks.js +9 -2
- package/dist/resources/tasks.js.map +1 -1
- package/dist/resources/tool-approvals.d.ts +29 -1
- package/dist/resources/tool-approvals.d.ts.map +1 -1
- package/dist/resources/tool-approvals.js +5 -0
- package/dist/resources/tool-approvals.js.map +1 -1
- package/dist/resources/trace.d.ts.map +1 -1
- package/dist/resources/trace.js +3 -3
- package/dist/resources/trace.js.map +1 -1
- package/dist/settings.d.ts +4 -0
- package/dist/settings.d.ts.map +1 -1
- package/dist/sse.d.ts +30 -10
- package/dist/sse.d.ts.map +1 -1
- package/dist/sse.js +44 -11
- package/dist/sse.js.map +1 -1
- package/dist/types.d.ts +102 -16
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +14 -0
- package/dist/types.js.map +1 -1
- package/openapi.yaml +263 -66
- package/package.json +2 -2
package/openapi.yaml
CHANGED
|
@@ -2416,7 +2416,23 @@ paths:
|
|
|
2416
2416
|
required: [rules]
|
|
2417
2417
|
properties:
|
|
2418
2418
|
rules: { $ref: '#/components/schemas/StoredSessionRules' }
|
|
2419
|
-
'400':
|
|
2419
|
+
'400':
|
|
2420
|
+
description: >-
|
|
2421
|
+
400 — the rules body is refused BEFORE anything is written (not one byte reaches the store). Branch on `errorCode`, never on the sentence.
|
|
2422
|
+
`rules.legacy_tool_name` (server >= 7.105.0; the `rules.*` family, the SDK lands it on `BadRequestError`): a tool-name bucket (`toolAllow` / `toolDeny`) entry
|
|
2423
|
+
that would be written but could never run — an engine-retired name, a `__` name without a protocol prefix, or a protocol name whose peer segment the
|
|
2424
|
+
engine cannot mint. Body keys (top level; the SDK reads them from `err.extra`): `field` (which bucket), `name` (which entry), `verdict`
|
|
2425
|
+
(`retired` / `unprefixed_namespace`, server >= 7.116.0; `unmintable_segment`, server >= 7.118.0), `currentName` (server >= 7.116.0; present when
|
|
2426
|
+
there is a 1:1 successor), `guidance` (server >= 7.119.0; the engine judge's wording of the fix, byte for byte — carried by `retired` and
|
|
2427
|
+
`unmintable_segment`, absent for `unprefixed_namespace`). 🔴 server >= 7.123.0 also lands a protocol name with an EMPTY TOOL SEGMENT
|
|
2428
|
+
(`mcp__<server>__`, `a2a__<peer>__`) here, as `verdict: "unmintable_segment"`; server 7.116.0–7.122.x answered `400 request.field_invalid`
|
|
2429
|
+
for it — a caller that branched on that code for this shape now branches on `rules.legacy_tool_name` (the body's `verdict` tells the shapes apart).
|
|
2430
|
+
`request.field_invalid`: a bucket that is not a list of non-empty strings (a bare string, `null`, a non-string member) and — behavior change
|
|
2431
|
+
in server >= 7.123.0 — an EMPTY or BLANK (whitespace-only) member, which earlier servers took in (200); the sentence is the engine's own,
|
|
2432
|
+
match on the code. Also `request.id_invalid` (malformed session id) and a malformed body.
|
|
2433
|
+
content:
|
|
2434
|
+
application/json:
|
|
2435
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
2420
2436
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
2421
2437
|
'403':
|
|
2422
2438
|
description: 403 `loosen_forbidden` — a non-operator write tried to relax the rules (tighten-only).
|
|
@@ -3116,7 +3132,20 @@ paths:
|
|
|
3116
3132
|
content:
|
|
3117
3133
|
application/json:
|
|
3118
3134
|
schema: { $ref: '#/components/schemas/ImportStaged' }
|
|
3119
|
-
'400':
|
|
3135
|
+
'400':
|
|
3136
|
+
description: >-
|
|
3137
|
+
400 — malformed input (the `policy` face is judged by the same write-door reader as `PUT /v1/sessions/{sessionId}/policy`, BEFORE any staging
|
|
3138
|
+
is created, so a refused push leaves nothing behind). Branch on `errorCode`, never on the sentence. `rules.legacy_tool_name` (server >= 7.105.0):
|
|
3139
|
+
a record's tool-name bucket (`toolAllow` / `toolDeny`) holds a name that could never run — an engine-retired name, a `__` name without a protocol prefix,
|
|
3140
|
+
or a protocol name whose peer segment the engine cannot mint. Body keys (top level; the SDK reads them from `err.extra`): `field`, `name`, `verdict`
|
|
3141
|
+
(`retired` / `unprefixed_namespace`, server >= 7.116.0; `unmintable_segment`, server >= 7.118.0), `currentName` (server >= 7.116.0, when there is a 1:1 successor),
|
|
3142
|
+
`guidance` (server >= 7.119.0, the engine judge's wording; absent for `unprefixed_namespace`). 🔴 server >= 7.123.0 also lands a protocol name with an EMPTY TOOL
|
|
3143
|
+
SEGMENT (`mcp__<server>__`, `a2a__<peer>__`) here (`verdict: "unmintable_segment"`); server 7.116.0–7.122.x answered `400 request.field_invalid` for it.
|
|
3144
|
+
`request.field_invalid`: a bucket that is not a list of non-empty strings and — behavior change in server >= 7.123.0 — an EMPTY or BLANK member, which earlier
|
|
3145
|
+
servers took in (the sentence carries the `policy: [i].rules: ` position prefix and then the engine's own sentence; match on the code).
|
|
3146
|
+
content:
|
|
3147
|
+
application/json:
|
|
3148
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
3120
3149
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
3121
3150
|
'404': { $ref: '#/components/responses/NotFound' }
|
|
3122
3151
|
'409':
|
|
@@ -3233,10 +3262,11 @@ paths:
|
|
|
3233
3262
|
description: >
|
|
3234
3263
|
Frames: `meta` ({type,version,mode:"approvals-delta",pollMs}), then `synced` ({type,count}) once the
|
|
3235
3264
|
first full snapshot has been emitted, then `pending`/`resolved` deltas; plus `heartbeat` ({type}) on the
|
|
3236
|
-
keep-alive tick and
|
|
3237
|
-
fires — or, server >= 7.122.0, `error` with
|
|
3238
|
-
|
|
3239
|
-
|
|
3265
|
+
keep-alive tick and an `error` ({type,errorCode:"STREAM_MAX_DURATION",…}) when the 15-min cap
|
|
3266
|
+
fires (the server closes the stream; the SDK reader reconnects and tails on) — or, server >= 7.122.0, `error` with
|
|
3267
|
+
`errorCode:"draining"` at the moment the instance enters drain (same code and sentence as the billable-submit 503 `draining`;
|
|
3268
|
+
the server closes the stream right after the frame). After `draining` the SDK reader ENDS (shared terminal predicate): re-opening is
|
|
3269
|
+
the caller's call, on its own schedule and backoff, and lands on the replacement instance. (Corrected 2026-07-31: this list used to name only meta/pending/resolved — a consumer written to
|
|
3240
3270
|
it silently drops three frame kinds it really receives.) Each data payload mirrors its SSE event name in
|
|
3241
3271
|
`data.type` — proxy-safe dispatch. Cross-replica by
|
|
3242
3272
|
construction (polls the SHARED checkpoint table). 15-min cap + heartbeats; a DB blip retries, never
|
|
@@ -3337,9 +3367,13 @@ paths:
|
|
|
3337
3367
|
'409':
|
|
3338
3368
|
description: >
|
|
3339
3369
|
Either a session-CAS conflict (`ErrorResponse.activeTaskId`), OR a design/80 D-1
|
|
3340
|
-
`approval_binding_mismatch` (`errorCode`, `field`: boundCallId|boundInputHash
|
|
3341
|
-
|
|
3342
|
-
|
|
3370
|
+
`approval_binding_mismatch` (`errorCode`, `field`: boundCallId|boundInputHash, or absent — e.g. core's row-integrity
|
|
3371
|
+
refusal, whatever engine wrote the parked row) — your binding does NOT match the current pending action (TOCTOU/swap).
|
|
3372
|
+
🔴 server >= 7.124.0 ONE CODE, ONE CAUSE: this code only says "the binding changed". The content-ask `answer` cell (core 5.7.0's
|
|
3373
|
+
third `field` member) now answers `resume_outcome_invalid` with `field: "answer"` (fix the answer and decide again — not
|
|
3374
|
+
"refetch the coordinates"); before 7.124.0 that cell borrowed this code. The SDK reads `field` here as the closed pair of binding
|
|
3375
|
+
seats (an older server's borrowed `answer` reads as absent; the raw value stays in `err.extra.field`). `retriable: true` is unchanged
|
|
3376
|
+
(the card stays pending, the run row re-parks). The SDK splits these by `errorCode` into
|
|
3343
3377
|
ConflictError vs ApprovalBindingMismatchError (the engine's own code for the same refusal, `checkpoint.invalid_outcome`,
|
|
3344
3378
|
maps to that class too). binding_mismatch = SAFETY signal: refetch + re-present
|
|
3345
3379
|
to the human, NEVER auto-retry. A `decidedBy: "policy"` allow on a person-required card is the OTHER `invalid_outcome`
|
|
@@ -3419,7 +3453,12 @@ paths:
|
|
|
3419
3453
|
`checkpoint.reopen_revote` / `checkpoint.already_exists` / `checkpoint.resume_aborted` /
|
|
3420
3454
|
`checkpoint.unsupported_version` all arrive as 409 and fall on the SDK's `checkpoint.` PREFIX family
|
|
3421
3455
|
(→ ConflictError, `.errorCode` carried verbatim; an OPEN set, so a future core code degrades with
|
|
3422
|
-
information instead of collapsing anonymously).
|
|
3456
|
+
information instead of collapsing anonymously). 🔴 server >= 7.124.0: the `checkpoint.unsupported_version` body carries two
|
|
3457
|
+
additive keys `writerVersion` (the service version that parked the row, as recorded in the resume context) and `readerVersion`
|
|
3458
|
+
(this engine) — the same rule at EVERY mint point of that code (core's mirrored code and the server's two self-minted reading-marker
|
|
3459
|
+
refusals), present iff the resume context records a writer version (rows parked before 7.124.0 carry neither; equal values are still
|
|
3460
|
+
sent). `writerVersion` newer than `readerVersion` ⇒ decide on that version (or a newer one), not by retrying here. SDK type
|
|
3461
|
+
`CheckpointUnsupportedVersionExtra`, values at `err.extra`. The one non-409 member is `checkpoint.not_found`,
|
|
3423
3462
|
which the same mint point sends as a 404 (see below).
|
|
3424
3463
|
THIRD member (moved here from a documented-but-nonexistent 410, 2026-07-31): `approval_stale`
|
|
3425
3464
|
(`errorCode`; `terminal:"resolved"` when attributable, else the key is absent) — the checkpoint is
|
|
@@ -4123,8 +4162,9 @@ paths:
|
|
|
4123
4162
|
description: >
|
|
4124
4163
|
READ-ONLY catalog of `@mention`-pickable models. The user selects a model PER-TASK by typing `@<name>` in
|
|
4125
4164
|
the objective body (the service allowlist-parses it; the client never re-decides). Envelope
|
|
4126
|
-
`{ models, default, defaultModel }` (`default` = the deployment default model id,
|
|
4127
|
-
internal `default` alias is hidden; `defaultModel` = the same default as `{id, name?}` in one piece
|
|
4165
|
+
`{ models, default, defaultModel, droppedEntries? }` — see `ModelsResponse` (`default` = the deployment default model id,
|
|
4166
|
+
display-only — the internal `default` alias is hidden; `defaultModel` = the same default as `{id, name?}` in one piece;
|
|
4167
|
+
`droppedEntries` = server >= 7.123.0, the catalog entries the read leg dropped, in the same generation as `models`).
|
|
4128
4168
|
🔴 NON-SECRET: only name/id/provider/reasoning/vision + the E4/E7 capability fields — NEVER baseUrl/apiKey/
|
|
4129
4169
|
headers (the service strips them). Fail-safe: on error, render an empty picker (never block).
|
|
4130
4170
|
responses:
|
|
@@ -4132,26 +4172,7 @@ paths:
|
|
|
4132
4172
|
description: Model catalog (possibly empty).
|
|
4133
4173
|
content:
|
|
4134
4174
|
application/json:
|
|
4135
|
-
schema:
|
|
4136
|
-
type: object
|
|
4137
|
-
# 🔴 封闭 + `defaultModel` 补登记(census 批2 第三段,2026-07-30):铸造点
|
|
4138
|
-
# `routes/capabilities.ts` 三键字面量。`defaultModel`([865]③ id/name 撕裂消解,server
|
|
4139
|
-
# 已发多版)SDK `models.list()` 的返回类型早就带,**spec 是唯一没登记的一侧** —— 生成式
|
|
4140
|
-
# 消费方对它全瞎。`name` 条件(目录无命中即省略),`id` 恒在。
|
|
4141
|
-
additionalProperties: false
|
|
4142
|
-
required: [models, default, defaultModel]
|
|
4143
|
-
properties:
|
|
4144
|
-
models:
|
|
4145
|
-
type: array
|
|
4146
|
-
items: { $ref: '#/components/schemas/ModelInfo' }
|
|
4147
|
-
default: { type: string, description: "deployment default model id (display-only; legacy id-form, byte-stable)" }
|
|
4148
|
-
defaultModel:
|
|
4149
|
-
type: object
|
|
4150
|
-
additionalProperties: false
|
|
4151
|
-
required: [id]
|
|
4152
|
-
properties:
|
|
4153
|
-
id: { type: string }
|
|
4154
|
-
name: { type: string, description: 'the catalog @handle of the default model; omitted when the catalog has no match (env single-model lane).' }
|
|
4175
|
+
schema: { $ref: '#/components/schemas/ModelsResponse' }
|
|
4155
4176
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
4156
4177
|
|
|
4157
4178
|
/v1/elicitations/{id}/respond:
|
|
@@ -4322,7 +4343,16 @@ paths:
|
|
|
4322
4343
|
schema: { $ref: '#/components/schemas/ToolApprovalRespondAck' }
|
|
4323
4344
|
'400': { description: Malformed body (existence-independent). }
|
|
4324
4345
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
4325
|
-
'404':
|
|
4346
|
+
'404':
|
|
4347
|
+
description: >
|
|
4348
|
+
Unknown/settled/expired/foreign approval (no existence oracle) — `tool_approval.not_pending`. Past the owner gate the body may carry
|
|
4349
|
+
an additive `cause` (`ToolApprovalNotPendingCause`): a row-ending cause word (the card ended without a human decision — dismiss it)
|
|
4350
|
+
or `unknown_or_other_replica` (this replica does not know the id — retry on another endpoint / replica). 🔴 server >= 7.124.0 retired
|
|
4351
|
+
the old words `voided` / `denied` (replaced by the concrete cause word, no alias); a row with no recorded cause carries no key, and a
|
|
4352
|
+
deployment without a durable ask ledger never sends it. Not-yours is byte-identical to unknown.
|
|
4353
|
+
content:
|
|
4354
|
+
application/json:
|
|
4355
|
+
schema: { $ref: '#/components/schemas/ToolApprovalNotPendingBody' }
|
|
4326
4356
|
'501': { description: Live tool-approval HITL not enabled (TOOL_APPROVAL_ENABLED). }
|
|
4327
4357
|
|
|
4328
4358
|
/v1/tasks/{taskId}/asks/{askId}/decision:
|
|
@@ -4391,8 +4421,8 @@ paths:
|
|
|
4391
4421
|
'410':
|
|
4392
4422
|
description: >
|
|
4393
4423
|
The window is closed and the outcome is already cast: `gone.ask_parked` (gate coordinates in the
|
|
4394
|
-
body — route to the durable gate flow) or `gone.ask` (`{state,
|
|
4395
|
-
DISJOINT bodies.
|
|
4424
|
+
body — route to the durable gate flow) or `gone.ask` (`{state, cause?}`; server >= 7.124.0 renamed the key from `deniedReason`,
|
|
4425
|
+
no alias). The two legs carry DISJOINT bodies.
|
|
4396
4426
|
content:
|
|
4397
4427
|
application/json:
|
|
4398
4428
|
schema: { $ref: '#/components/schemas/AskGoneBody' }
|
|
@@ -5638,7 +5668,7 @@ paths:
|
|
|
5638
5668
|
apart, hard-stops — so the platform stop timeout (Docker `stop_grace_period`, systemd `TimeoutStopSec`, k8s
|
|
5639
5669
|
`terminationGracePeriodSeconds`) must be >= the requested deadline.
|
|
5640
5670
|
IDENTITY — the request names WHICH instance, compared field by field (no state change on a mismatch, 409 `conflict.fleet_identity`
|
|
5641
|
-
naming the field, never echoing the value): `worker` = the engine's worker name (`SEMA_REGISTRY_WORKER
|
|
5671
|
+
naming the field, never echoing the value): `worker` = the engine's worker name (`SEMA_WEB_ADMIN_WORKER`, `SEMA_REGISTRY_WORKER` on servers <= 7.123.x; the authenticated `GET /v1/capabilities` `centerWiring.worker` reads the same value; an engine without a center
|
|
5642
5672
|
lane has none, so every request mismatches); `expectedGeneration` = the fleet generation (`/health.generation`), ABSENT AGAINST ABSENT
|
|
5643
5673
|
(omitting it is the standalone form, not a wildcard — it mismatches a fleet member); `scope` = the tenant scope, both sides read as a
|
|
5644
5674
|
value (the engine's = env `FLEET_SCOPE`; unset on the engine and omitted in the request both read as the default scope `global`);
|
|
@@ -7469,6 +7499,10 @@ components:
|
|
|
7469
7499
|
`MANUAL_MODE_SHELL_GATE=always`, write protection, governance, a hook's ask and plan-mode asks still ask (a matching `request` member then shows as `allowRuleShadowed` on the
|
|
7470
7500
|
card). The submit gate judges only the shape (array of strings, at most 1000 entries => 400) and the NAMES (the same judge and the same sentence as `deny` / `ask`: a retired name,
|
|
7471
7501
|
an unprefixed short name, or a protocol name whose server segment cannot be minted / is missing / is a wildcard (`mcp__*`) is a whole-request 422) — never the content.
|
|
7502
|
+
🔴 server >= 7.118.0: the NAME layer of the allow side moved to the engine — a retired name, an unprefixed short name or an unmintable protocol name is ACCEPTED here (no 422) and the run opens with an
|
|
7503
|
+
`engine_notice` `permission.allowed_tool_not_granted` (`entries[].reason: tool_name`); the `deny` / `ask` buckets still refuse that family with a whole-request 422. 🔴 server >= 7.123.0 (core 7.48.0) also accepts a
|
|
7504
|
+
protocol name with an EMPTY TOOL SEGMENT (`mcp__<server>__`, `a2a__<peer>__`) in this bucket (server 7.118.0–7.122.x refused it on every table). One name, three outcomes:
|
|
7505
|
+
allow side accepts + notice, deny / ask side refuses, session-rule write doors answer `400 rules.legacy_tool_name` (`verdict: unmintable_segment`).
|
|
7472
7506
|
🔴 THE 422 SENTENCE (server >= 7.117.0): `error` starts with `request settings cannot be applied as written: ` (<= 7.116.x: a different sentence that said tighten-only — all three
|
|
7473
7507
|
buckets share this exit, and the old head misled a caller writing a tightening rule and lied about the allow bucket). Do NOT dispatch on the sentence: it carries no code of its own, so
|
|
7474
7508
|
`errorCode` is the status-derived coarse code `request.unprocessable`.
|
|
@@ -8298,7 +8332,16 @@ components:
|
|
|
8298
8332
|
enum: ['open', closed]
|
|
8299
8333
|
description: 'PRESENT-IFF `lease: configured`. The effective `FLEET_LEASE_FAIL_CLOSED` (default derived from `REQUIRE_PRINCIPAL`); `closed` ⇒ connection-level failures answer 503 `fleet.lease_unavailable` on every billing entry.'
|
|
8300
8334
|
usageReport: { type: boolean, description: 'The usage-report loop is mounted (the three center pieces suffice; `FLEET_ADVERTISE_ADDRESS` not required, S-866 ④).' }
|
|
8301
|
-
issuer: { type: string, enum: [explicit, fallback], description: 'Auth-bridge issuer: explicit `AUTH_BRIDGE_ISSUER` / fallback to `SEMA_REGISTRY_URL
|
|
8335
|
+
issuer: { type: string, enum: [explicit, fallback], description: 'Auth-bridge issuer: explicit `AUTH_BRIDGE_ISSUER` / fallback to the center base URL (`SEMA_WEB_ADMIN_URL`; `SEMA_REGISTRY_URL` on servers <= 7.123.x).' }
|
|
8336
|
+
worker:
|
|
8337
|
+
type: string
|
|
8338
|
+
minLength: 1
|
|
8339
|
+
description: >
|
|
8340
|
+
server >= 7.124.0 — the worker name this engine registers with the center (`SEMA_WEB_ADMIN_WORKER`, the same value the drain identity
|
|
8341
|
+
reads), verbatim. PRESENT-IFF a name is configured AND it is disclosable (the contract's identifier rule `isDisclosableIdentity`: not in
|
|
8342
|
+
identifier form / secret-shaped ⇒ treated as no name, key ABSENT, never masked). It lives only on this AUTHENTICATED face — the
|
|
8343
|
+
unauthenticated `/health` deliberately does not carry it. A shell uses it to build the web session link
|
|
8344
|
+
`<webBaseUrl>/chat?agent=<worker>&session=<id>` (paired with `/health.center.webBaseUrl`).
|
|
8302
8345
|
caller:
|
|
8303
8346
|
type: object
|
|
8304
8347
|
additionalProperties: false
|
|
@@ -8425,6 +8468,8 @@ components:
|
|
|
8425
8468
|
(log `one_time_migrations_applied` `holderDeathCodeRowsBackfilled`) and a new replica reads such a row as the code during the upgrade window,
|
|
8426
8469
|
but the migration has a window (30 days after the release, then the write-back and the read half are deleted) — so
|
|
8427
8470
|
"no `errorCode`" never proves "not a holder death": rows migrated late or after the window stay sentence-only.
|
|
8471
|
+
The three run-death codes as ONE runtime table: schema `RunDeathVerdictCode` (closed enum) = the SDK entry export
|
|
8472
|
+
`RUN_DEATH_VERDICT_CODES` — judge "this run was reaped / reclaimed" by that table; this column itself stays an open string.
|
|
8428
8473
|
error: { type: string }
|
|
8429
8474
|
jobId:
|
|
8430
8475
|
type: string
|
|
@@ -9601,6 +9646,9 @@ components:
|
|
|
9601
9646
|
`engine_notice` `permission.allowed_tool_not_granted`; any layer's deny / ask rule, operator `APPROVAL_REQUIRE` / `APPROVAL_NEVER_AUTO` / shell floor / write protection /
|
|
9602
9647
|
governance, a hook's ask and plan-mode asks still ask (a matching member then shows as `allowRuleShadowed` on the card). The submit gate judges only the shape and the NAMES (retired
|
|
9603
9648
|
name / unprefixed short name / unmintable protocol server segment => 422, at most 1000 entries => 400) — never the content.
|
|
9649
|
+
🔴 server >= 7.118.0: the allow side's NAME layer moved to the engine (retired / short / unmintable names are accepted there and announced with `engine_notice` `permission.allowed_tool_not_granted`; `deny` / `ask` still 422);
|
|
9650
|
+
server >= 7.123.0 also accepts a protocol name with an EMPTY TOOL SEGMENT (`mcp__<server>__`) on the allow side (server 7.118.0–7.122.x refused it on every table):
|
|
9651
|
+
allow side accepts + notice, deny / ask side refuses, session-rule write doors answer `400 rules.legacy_tool_name`.
|
|
9604
9652
|
🔴 ABSENT `allowLayer` with `allowGrant` present = a 7.115.0–7.116.x server: allow is a STATEMENT (it does not clear the engine's own write gate / shell checks; a content form
|
|
9605
9653
|
is a whole-request 422). ABSENT KEY altogether (no `settings`) = an older server (<= 7.114.x), which read `allow` as a WHITELIST (everything off the list refused) — do not put
|
|
9606
9654
|
anything in the allow bucket there. NOT the same bit as `taskSettings.permissions` (unchanged, still a boolean: "this server accepts the `settings.permissions` face"). The
|
|
@@ -9715,7 +9763,7 @@ components:
|
|
|
9715
9763
|
K-32 (server >= 7.106.0 / S-866 ②; mint = release bytes `dist/http/routes/capabilities.js`
|
|
9716
9764
|
`centerWiring: deps.centerWiring?.(principal)`, shape owner `dist/capabilities/center-wiring.d.ts` `CenterWiring`) —
|
|
9717
9765
|
"IS THIS ENGINE WIRED TO THE CENTER RIGHT NOW, AND IS YOUR QUOTA ENFORCED". 🔴 PRESENT-IFF wired to a center
|
|
9718
|
-
(`SEMA_REGISTRY_URL`) — like `sealedKey`, a registered present-iff exception (contract appendix B.1): absence is the
|
|
9766
|
+
(`SEMA_WEB_ADMIN_URL`; `SEMA_REGISTRY_URL` on servers <= 7.123.x) — like `sealedKey`, a registered present-iff exception (contract appendix B.1): absence is the
|
|
9719
9767
|
KEY BEING ABSENT (never `null`) and has two meanings — an older server (< 7.106.0) or an engine not wired to a center
|
|
9720
9768
|
(the same body's `version` tells them apart); under both read "this face cannot answer wiring / enforcement", NEVER
|
|
9721
9769
|
"quota is enforced". 🔴 PER CALLER (`caller` is the verified principal's lease reading): do not cache it across
|
|
@@ -9779,10 +9827,11 @@ components:
|
|
|
9779
9827
|
does not know auto" and "it knows auto but this box has no entitlement source" would collapse to
|
|
9780
9828
|
the same false, and a shell must do different things for those. `reason` is present ONLY when
|
|
9781
9829
|
`armed` is false; read its vocabulary as an OPEN set (server already grew it from four words to
|
|
9782
|
-
six). `model` is omitted when the classifier route cannot be resolved (the server logs the
|
|
9830
|
+
six, and to seven with `operator_floor` in server >= 7.123.0). `model` is omitted when the classifier route cannot be resolved (the server logs the
|
|
9783
9831
|
engine's refusal rather than 500-ing the read face). `?permissionMode=<mode word>` folds the
|
|
9784
9832
|
caller's INTENT into the verdict (server >= 7.119.0 takes the six words of `capabilities.permissionModes` — `dontAsk` answers
|
|
9785
|
-
`armed: false` / `reason: mode_not_auto
|
|
9833
|
+
`armed: false` / `reason: mode_not_auto`, except on a deployment whose operator entry floor rewrites the word — `AUTONOMY=plan`, where
|
|
9834
|
+
server >= 7.123.0 answers `operator_floor`, see `reason`; server <= 7.118.x takes five); anything outside the server's words is a 400
|
|
9786
9835
|
`request.field_invalid`.
|
|
9787
9836
|
# 🔴 required 只有**三位**:武装两键(armed / reason)是 server >= 7.57.0 (S-80)
|
|
9788
9837
|
# 才有的,而本包的支持地板是 3.0.0 => every 7.x <= 7.56.0 server is inside the promised face and
|
|
@@ -9803,7 +9852,17 @@ components:
|
|
|
9803
9852
|
fold that absence into false (it would render a 7.50 box that IS arming as "auto == default").
|
|
9804
9853
|
reason:
|
|
9805
9854
|
type: string
|
|
9806
|
-
enum: [mode_not_auto, deployment_incapable, org_denied, local_denied, settings_denied, resolver_fault]
|
|
9855
|
+
enum: [mode_not_auto, operator_floor, deployment_incapable, org_denied, local_denied, settings_denied, resolver_fault]
|
|
9856
|
+
description: >
|
|
9857
|
+
Why `armed` is false — the server's closed SEVEN words, in judgement order (read as an open set; keep a default arm).
|
|
9858
|
+
`mode_not_auto` = this request's intent (the `?permissionMode=` query word) is not `auto` and that word reaches the engine as it is.
|
|
9859
|
+
`operator_floor` (server >= 7.123.0) = this deployment's operator entry floor (`AUTONOMY=plan`) rewrote the intent into another word, and that
|
|
9860
|
+
word is not `auto`: the word that reaches the engine is the operator's, so the way out is the operator changing the level, NOT the word the
|
|
9861
|
+
request sends. On an `AUTONOMY=plan` deployment an absent intent (answered as the `auto` intent), `auto` and any word looser than `plan` read
|
|
9862
|
+
`operator_floor`; an intent that IS `plan` stays `mode_not_auto` (the word arrives as sent, not rewritten by the floor).
|
|
9863
|
+
Server <= 7.122.x answers `mode_not_auto` for both cases. The rest: `deployment_incapable` = no classifier seat mounted;
|
|
9864
|
+
`org_denied` = this principal's caps say auto is off and no local deny is set (the center's explicit deny, or its fail-closed refusal); `local_denied` = the local `PERMISSIONS_DISABLE_AUTO_MODE`;
|
|
9865
|
+
`settings_denied` = the settings layer folds auto (run-level, not produced by this read face); `resolver_fault` = the resolver threw.
|
|
9807
9866
|
model: { type: string }
|
|
9808
9867
|
readFace:
|
|
9809
9868
|
type: ["string", "null"]
|
|
@@ -10228,6 +10287,9 @@ components:
|
|
|
10228
10287
|
segment cannot be minted / is missing / is a wildcard (`mcp__*`) is a 400 too, and from 7.117.0 so is a retired name / unprefixed short
|
|
10229
10288
|
name (the name layer is judged by the same judge as the allow bucket) and a list of more than 1000 entries (top-level `skills[]` and
|
|
10230
10289
|
`agents[].skills[]` judge identically). Probe `capabilities.skillToolLists`.
|
|
10290
|
+
🔴 The name-layer 400s above hold for server 7.116.x–7.117.x only. server >= 7.118.0: the allow side's name layer moved to the engine — a retired name / unprefixed short name / unmintable protocol name is ACCEPTED here and the run opens with an
|
|
10291
|
+
`engine_notice` `permission.allowed_tool_not_granted` (`entries[].reason: tool_name`); server >= 7.123.0 also accepts a protocol name with an EMPTY TOOL SEGMENT (`mcp__<server>__`, `a2a__<peer>__`) here (server 7.118.0–7.122.x refused it).
|
|
10292
|
+
One name, three outcomes: allow side accepts + notice, deny / ask side refuses, session-rule write doors answer `400 rules.legacy_tool_name` (`disallowedTools` below is a deny side).
|
|
10231
10293
|
disallowedTools:
|
|
10232
10294
|
type: array
|
|
10233
10295
|
items: { type: string }
|
|
@@ -10242,6 +10304,8 @@ components:
|
|
|
10242
10304
|
user audience, on the session stream). 🔴 The resume legs judge the stored body the same way: a parked task whose stored skill `disallowedTools` carries such an entry answers
|
|
10243
10305
|
409 `resume_blocked_by_policy` (`blockedBy: "request.field_invalid"`, same sentence) BEFORE the claim — row and card untouched; a terminal run's `POST /v1/runs/{taskId}/steer`
|
|
10244
10306
|
answers it before minting the wake card. Why: engine 7.41 trimmed whitespace (`"Bash "` was a working deny), engine 7.42 reads verbatim (reads as nothing). The allow side does not refuse.
|
|
10307
|
+
🔴 A protocol name with an EMPTY TOOL SEGMENT (`mcp__<server>__`, `a2a__<peer>__`) is refused here (server >= 7.123.0: `error` is the engine's entry-reading sentence, `... reads as nothing (tool_name) ...`), while `allowedTools` accepts it from 7.123.0 —
|
|
10308
|
+
one name, three outcomes: allow side accepts + notice, deny / ask side refuses, session-rule write doors answer `400 rules.legacy_tool_name`.
|
|
10245
10309
|
|
|
10246
10310
|
McpServerSpec:
|
|
10247
10311
|
type: object
|
|
@@ -10975,6 +11039,92 @@ components:
|
|
|
10975
11039
|
`"other"`). This is the "where did those 180 404s actually land" cell. Empty object = zero failures;
|
|
10976
11040
|
key absent = an older server.
|
|
10977
11041
|
|
|
11042
|
+
ModelsResponse:
|
|
11043
|
+
type: object
|
|
11044
|
+
description: >
|
|
11045
|
+
The `GET /v1/models` 200 body (the envelope; service `src/http/routes/capabilities.ts` mints the keys as literals).
|
|
11046
|
+
`models` = the `@mention`-pickable catalog (`ModelInfo`); `default` = the deployment default model id (display-only, legacy id
|
|
11047
|
+
form, byte-stable); `defaultModel` = the same default as `{id, name?}`; `droppedEntries` = server >= 7.123.0, ALWAYS present
|
|
11048
|
+
there: the catalog entries THIS generation's read leg dropped one by one (an entry that does not pass the settings-schema
|
|
11049
|
+
`ModelEntry` shape; the rest of the catalog is served as usual). ABSENT = an earlier server (<= 7.122.x) — so the key is
|
|
11050
|
+
optional here (the SDK's supported server floor is 3.0.0); `[]` = the engine says nothing was dropped (also the env lane,
|
|
11051
|
+
where no catalog is read), which is NOT the same as absent (the engine cannot say).
|
|
11052
|
+
# 🔴 封闭 + `defaultModel` 补登记(census 批2 第三段,2026-07-30):铸造点
|
|
11053
|
+
# `routes/capabilities.ts` 三键字面量。`defaultModel`([865]③ id/name 撕裂消解,server
|
|
11054
|
+
# 已发多版)SDK `models.list()` 的返回类型早就带,**spec 是唯一没登记的一侧** —— 生成式
|
|
11055
|
+
# 消费方对它全瞎。`name` 条件(目录无命中即省略),`id` 恒在。
|
|
11056
|
+
# 18.2.0:信封升为具名 schema(此前内联在 `GET /v1/models` 的 200 上),新键 `droppedEntries`(server >= 7.123.0)挂在上面,**不进 required**。
|
|
11057
|
+
additionalProperties: false
|
|
11058
|
+
required: [models, default, defaultModel]
|
|
11059
|
+
properties:
|
|
11060
|
+
models:
|
|
11061
|
+
type: array
|
|
11062
|
+
items: { $ref: '#/components/schemas/ModelInfo' }
|
|
11063
|
+
default: { type: string, description: "deployment default model id (display-only; legacy id-form, byte-stable)" }
|
|
11064
|
+
defaultModel:
|
|
11065
|
+
type: object
|
|
11066
|
+
additionalProperties: false
|
|
11067
|
+
required: [id]
|
|
11068
|
+
properties:
|
|
11069
|
+
id: { type: string }
|
|
11070
|
+
name: { type: string, description: 'the catalog @handle of the default model; omitted when the catalog has no match (env single-model lane).' }
|
|
11071
|
+
droppedEntries:
|
|
11072
|
+
type: array
|
|
11073
|
+
items: { $ref: '#/components/schemas/DroppedEntry' }
|
|
11074
|
+
description: >
|
|
11075
|
+
server >= 7.123.0 (settings-schema >= 10.1.0), always present there; ABSENT = an earlier server. The catalog entries the read leg
|
|
11076
|
+
dropped from THIS generation (an entry that does not pass the `ModelEntry` shape) so a shell / admin panel can tell "which entry
|
|
11077
|
+
I wrote did the engine not take, and why" without re-judging the schema. Rows carry an index, a closed reason word and (when
|
|
11078
|
+
disclosable) a name / id — NEVER free text (the schema diagnostics, the entry value and any Record key stay in the server log).
|
|
11079
|
+
🔴 SAME GENERATION as `models`: both are the two faces of one catalog generation and commit together. When a center candidate's
|
|
11080
|
+
model plane is refused as a group (for example the default model is not in the catalog), or the candidate has no enabled entry at all
|
|
11081
|
+
(survivors and dropped rows both count by the `enabled` each row wrote; a dropped row whose `enabled` cannot be read counts as enabled),
|
|
11082
|
+
`models` and `droppedEntries` both stay on the previous generation — the key does NOT report the entries of the candidate that did not
|
|
11083
|
+
land (why it did not land: `/health.modelRefusals`). So a disabled dropped entry shows up only in a catalog that has an enabled entry and whose model
|
|
11084
|
+
plane landed. Different question from `/health.modelRefusals` (which names the models this box does NOT serve, with the engine's refusal code):
|
|
11085
|
+
this key lists what the read leg dropped, including nameless and disabled rows.
|
|
11086
|
+
Upgrade window (as it is): an on-disk LKG written by server <= 7.122.x only recorded the dropped entries that had a name AND were
|
|
11087
|
+
enabled; while serving from it with the center still unreachable this key lists only those rows (possibly `[]`) and the first pull after the
|
|
11088
|
+
center is reachable fills it in. An unknown `reasonCode` word reads as "dropped, reason unknown".
|
|
11089
|
+
|
|
11090
|
+
DroppedEntry:
|
|
11091
|
+
type: object
|
|
11092
|
+
description: >
|
|
11093
|
+
One row of `ModelsResponse.droppedEntries` (server >= 7.123.0): a catalog entry the read leg dropped. `index` = the entry's original
|
|
11094
|
+
position in the `models` array of the catalog document read this time (the local lane = its place in `config.d/models.json`; the
|
|
11095
|
+
center lane = the catalog the center sent this worker) — the same coordinate as the server's `models.<index>` log line.
|
|
11096
|
+
`identity` is optional: absent when none of the three keys is disclosable.
|
|
11097
|
+
additionalProperties: false
|
|
11098
|
+
required: [index, reasonCode]
|
|
11099
|
+
properties:
|
|
11100
|
+
index: { type: integer, minimum: 0 }
|
|
11101
|
+
reasonCode: { $ref: '#/components/schemas/DroppedEntryReasonCode' }
|
|
11102
|
+
identity: { $ref: '#/components/schemas/DroppedEntryIdentity' }
|
|
11103
|
+
|
|
11104
|
+
DroppedEntryReasonCode:
|
|
11105
|
+
type: string
|
|
11106
|
+
enum: [entry-schema-invalid, entry-key-invalid, entry-unreadable]
|
|
11107
|
+
description: >
|
|
11108
|
+
Why the read leg dropped a catalog entry (server >= 7.123.0). The words are settings-schema's closed `DROPPED_ENTRY_REASON_CODES`, passed through
|
|
11109
|
+
verbatim (the server neither hand-copies nor rewrites them; a new word in the package is a compile error there, forcing an explicit decision).
|
|
11110
|
+
`entry-schema-invalid` = the entry did not pass its own schema; `entry-key-invalid` = a Record-shaped collection's key did not pass the key schema
|
|
11111
|
+
(the catalog is an array, so this word does not appear on `droppedEntries` today — keep it in the switch anyway); `entry-unreadable` = the judging
|
|
11112
|
+
threw (a pathological accessor; the engine never carries what it threw). Read an unknown word as "dropped, reason unknown".
|
|
11113
|
+
|
|
11114
|
+
DroppedEntryIdentity:
|
|
11115
|
+
type: object
|
|
11116
|
+
description: >
|
|
11117
|
+
The disclosable identity of a dropped entry (settings-schema's entry-identity shape). `name` / `id` appear only when they are DISCLOSABLE identifiers
|
|
11118
|
+
(allow-listed characters, no secret shape, no URL userinfo — the server asks again at the exit); a non-disclosable one is simply absent, NOT masked (a masked
|
|
11119
|
+
string is no longer an identity). `enabled` is the boolean the entry wrote itself (a disabled dropped entry is listed with `enabled: false`). With none of the
|
|
11120
|
+
three keys the whole `identity` is absent — an empty object never reaches the wire, hence minProperties: 1.
|
|
11121
|
+
additionalProperties: false
|
|
11122
|
+
minProperties: 1
|
|
11123
|
+
properties:
|
|
11124
|
+
name: { type: string }
|
|
11125
|
+
id: { type: string }
|
|
11126
|
+
enabled: { type: boolean }
|
|
11127
|
+
|
|
10978
11128
|
ModelInfo:
|
|
10979
11129
|
type: object
|
|
10980
11130
|
description: >
|
|
@@ -16483,6 +16633,13 @@ components:
|
|
|
16483
16633
|
type: string
|
|
16484
16634
|
enum: [allowed, denied, expired]
|
|
16485
16635
|
description: '"tool_approval_complete" only. Cosmetic dismiss reason. `expired` = TTL/abort/disconnect; what the ENGINE received for it DEPENDS on the deployment: where the design/172 ask ledger is on (server >= 7.3.0 under STREAM_APPROVAL_ENABLED) the server''s expireAsk CAS re-adjudicates the outcome to "unavailable" and the run PARKS; without that ledger the historical fail-closed DENY stands. Never derive the run''s fate from this frame.'
|
|
16636
|
+
cause:
|
|
16637
|
+
allOf: [{ $ref: '#/components/schemas/ApprovalSettleCause' }]
|
|
16638
|
+
description: >
|
|
16639
|
+
server >= 7.124.0, "tool_approval_complete" ONLY, ADDITIVE: the CAUSE of `outcome: "expired"` — one of the fourteen non-human ending
|
|
16640
|
+
words (`ApprovalSettleCause`), the same word the audit settle row carries. Present ⇔ a non-human ending; a human answer
|
|
16641
|
+
(`allowed` / `denied`) has none. The only HTTP face that shows the four waiting words. ABSENT is not evidence (older servers never send it);
|
|
16642
|
+
the run's fate is still read from the run row / checkpoint face.
|
|
16486
16643
|
|
|
16487
16644
|
# ── design/172 流内审批协议(#151)—— 已随 server 7.3.0 上线;开关 STREAM_APPROVAL_ENABLED 的默认值
|
|
16488
16645
|
# 分两段:server ≤7.4.0 默认 OFF,≥7.5.0 默认 ON(BREAKING,见 server CHANGELOG)。──
|
|
@@ -17322,6 +17479,49 @@ components:
|
|
|
17322
17479
|
first-decision echo now carries `decisionNote` when the row has one.
|
|
17323
17480
|
Retrying the same decision can never succeed — single-winner CAS means the outcome is already cast.
|
|
17324
17481
|
|
|
17482
|
+
RunDeathVerdictCode:
|
|
17483
|
+
type: string
|
|
17484
|
+
enum: [run.stalled, run.orphaned, approval.expired]
|
|
17485
|
+
description: >
|
|
17486
|
+
server >= 7.116.0 — the run-death verdict codes on `RunRecord.errorCode` (contract appendix A.1 (6)): `run.stalled` (heartbeat-lost reap),
|
|
17487
|
+
`run.orphaned` (local-lane restart reclaim) and `approval.expired` (a run parked on an approval was reaped). None means the request was
|
|
17488
|
+
wrong; the run cannot be picked up again — submit the work as a new task. The server's own `isHolderDeathVerdict` counts only the first two
|
|
17489
|
+
(the holding process died before the leg finished). Branch on the code, never on the `error` sentence; absence proves nothing. SDK runtime
|
|
17490
|
+
table: `RUN_DEATH_VERDICT_CODES` (same order).
|
|
17491
|
+
AskRowEndingCause:
|
|
17492
|
+
type: string
|
|
17493
|
+
enum: [routing_failure_fail_closed, run_cancelled, run_exited, batch_aborted, batch_sibling_withdrawn, batch_bound_elsewhere, adhoc_leg_no_durable_domain, orphan_ttl_exceeded, crashed_before_park, shutdown_before_park]
|
|
17494
|
+
description: >
|
|
17495
|
+
server >= 7.124.0 — the ask-row subset of the approval ENDING-CAUSE table (one closed table of seventeen words; a card that ended
|
|
17496
|
+
without a human decision says WHY with one word, and every read face reads the same word). These ten are the ones that land on the
|
|
17497
|
+
ask row: `410 gone.ask` `cause` (grouped by `state`: `DENIED` ⇒ `routing_failure_fail_closed` only, `VOID` ⇒ the other nine) and the
|
|
17498
|
+
late-answer `404 tool_approval.not_pending` `cause` (plus its own `unknown_or_other_replica`). Present ⇔ a non-human ending; a human
|
|
17499
|
+
answer has no cause. The server mints from this table and filters reads through it — closed.
|
|
17500
|
+
ApprovalSettleCause:
|
|
17501
|
+
type: string
|
|
17502
|
+
enum: [ttl_expired, broadcast_lost, stream_disconnected, undeliverable, run_cancelled, run_exited, batch_aborted, batch_sibling_withdrawn, batch_bound_elsewhere, routing_failure_fail_closed, crashed_before_park, shutdown_before_park, orphan_ttl_exceeded, adhoc_leg_no_durable_domain]
|
|
17503
|
+
description: >
|
|
17504
|
+
server >= 7.124.0 — the NON-HUMAN ending causes (fourteen words) carried by the live close frame `tool_approval_complete` `cause` (and the
|
|
17505
|
+
audit ledger's settle rows): the ten ask-row words (`AskRowEndingCause`) plus four waiting words that never land on the ask row —
|
|
17506
|
+
`ttl_expired` (the wait window ran out), `broadcast_lost` (neither frame family reached any connection), `stream_disconnected` (the
|
|
17507
|
+
carrying stream dropped; forced park), `undeliverable` (persisted row unusable / identity indeterminate / hanging without a row). The three
|
|
17508
|
+
decision words (`binding_changed` / `answer_invalid` / `outcome_cell_invalid`) refuse one decision, never end a card, and are not here.
|
|
17509
|
+
ToolApprovalNotPendingCause:
|
|
17510
|
+
type: string
|
|
17511
|
+
enum: [routing_failure_fail_closed, run_cancelled, run_exited, batch_aborted, batch_sibling_withdrawn, batch_bound_elsewhere, adhoc_leg_no_durable_domain, orphan_ttl_exceeded, crashed_before_park, shutdown_before_park, unknown_or_other_replica]
|
|
17512
|
+
description: >
|
|
17513
|
+
The late-answer `404 tool_approval.not_pending` `cause`: the ask-row ending-cause words (the card ended without a human decision — dismiss
|
|
17514
|
+
it) plus the 404-only `unknown_or_other_replica` (this replica does not know the id — retry on another endpoint / replica). server >= 7.124.0
|
|
17515
|
+
retired the old words `voided` / `denied` (no alias).
|
|
17516
|
+
ToolApprovalNotPendingBody:
|
|
17517
|
+
allOf:
|
|
17518
|
+
- $ref: '#/components/schemas/ErrorResponse'
|
|
17519
|
+
- type: object
|
|
17520
|
+
properties:
|
|
17521
|
+
cause:
|
|
17522
|
+
allOf: [{ $ref: '#/components/schemas/ToolApprovalNotPendingCause' }]
|
|
17523
|
+
description: 'Optional, past the owner gate only. See `ToolApprovalNotPendingCause`.'
|
|
17524
|
+
description: '404 body of `POST /v1/tool-approvals/{id}/respond` (`tool_approval.not_pending`).'
|
|
17325
17525
|
AskGoneBody:
|
|
17326
17526
|
allOf:
|
|
17327
17527
|
- $ref: '#/components/schemas/ErrorResponse'
|
|
@@ -17330,25 +17530,21 @@ components:
|
|
|
17330
17530
|
sessionId: { type: string, description: 'gone.ask_parked ONLY — gate reconciliation coordinate 1; drive the existing durable-gate flow with it.' }
|
|
17331
17531
|
gateBoundCallId: { type: string, description: 'gone.ask_parked ONLY — gate reconciliation coordinate 2.' }
|
|
17332
17532
|
gateBoundInputHash: { type: string, description: 'gone.ask_parked ONLY — gate reconciliation coordinate 3.' }
|
|
17333
|
-
state: { type: string, enum: [DENIED, VOID], description: 'gone.ask ONLY — the terminal state. server >= 7.116.0 also mints `VOID` for "the process that held the ask is gone" (local lane restart; see `
|
|
17334
|
-
|
|
17335
|
-
|
|
17533
|
+
state: { type: string, enum: [DENIED, VOID], description: 'gone.ask ONLY — the terminal state. server >= 7.116.0 also mints `VOID` for "the process that held the ask is gone" (local lane restart; see `cause`).' }
|
|
17534
|
+
cause:
|
|
17535
|
+
allOf: [{ $ref: '#/components/schemas/AskRowEndingCause' }]
|
|
17336
17536
|
description: >
|
|
17337
|
-
gone.ask ONLY —
|
|
17338
|
-
|
|
17339
|
-
|
|
17340
|
-
`adhoc_leg_no_durable_domain`, `orphan_ttl_exceeded`
|
|
17341
|
-
`
|
|
17342
|
-
|
|
17343
|
-
The last two are the SAME word as `GET /v1/approvals` `crashConverged[].cause`; the other six were already on the wire
|
|
17344
|
-
(7.3.0 / 7.55.0 / 7.108.0) and are only registered in the server's self-minted word table by 7.116.0.
|
|
17345
|
-
ABSENT = the row carries no attribution. The two vocabularies never mix; the VOID one is open (read an unknown word as "the ask ended without
|
|
17346
|
-
a human decision, reason unknown").
|
|
17537
|
+
gone.ask ONLY — the row's ENDING CAUSE (server >= 7.124.0; the key was `deniedReason` before — renamed, no alias, the SDK reads only
|
|
17538
|
+
`cause`, as `AskGoneError.endingCause`). GROUPED BY `state`: `DENIED` carries only `routing_failure_fail_closed`; `VOID` carries the
|
|
17539
|
+
other nine words of the ask-row subset of the approval ending-cause table — `run_cancelled`, `run_exited`, `batch_aborted`,
|
|
17540
|
+
`batch_sibling_withdrawn`, `batch_bound_elsewhere`, `adhoc_leg_no_durable_domain`, `orphan_ttl_exceeded`, `crashed_before_park` /
|
|
17541
|
+
`shutdown_before_park` (the process that held the ask is gone; the SAME word as `GET /v1/approvals` `crashConverged[].cause`).
|
|
17542
|
+
Present ⇔ a non-human ending; ABSENT = the row carries no recorded cause (rows written before the upgrade are not back-filled).
|
|
17347
17543
|
description: >
|
|
17348
17544
|
410 body. THE TWO LEGS ARE DISJOINT: `gone.ask_parked` carries exactly the three gate coordinates,
|
|
17349
|
-
`gone.ask` carries exactly `{state,
|
|
17545
|
+
`gone.ask` carries exactly `{state, cause?}` (no `consumerState`; server >= 7.124.0 renamed `deniedReason` → `cause`, no alias).
|
|
17350
17546
|
🔴 server >= 7.116.0 (local lane, `DB_BACKEND=local`): after a crash / kill and a restart on the same data root, answering the PREVIOUS
|
|
17351
|
-
life's still-open card is now `410 gone.ask` with `state: "VOID"` and `deniedReason` `crashed_before_park` / `shutdown_before_park` —
|
|
17547
|
+
life's still-open card is now `410 gone.ask` with `state: "VOID"` and `cause` (`deniedReason` before 7.124.0) `crashed_before_park` / `shutdown_before_park` —
|
|
17352
17548
|
<= 7.115.x answered `200` + `consumerState: "absent"` (the run had long been reclaimed; nothing executed). An ask that was already decided
|
|
17353
17549
|
(decided, then the process died mid-execution) is unchanged: `409 conflict.ask_decided`. SQL backends are unchanged in 7.116.0. Routing off field presence across the legs is how a
|
|
17354
17550
|
consumer ends up driving the wrong durable gate.
|
|
@@ -17945,19 +18141,20 @@ components:
|
|
|
17945
18141
|
STREAM-CONTROL frame, NOT a run failure (server src/http/sse-log.ts; the trace leg emits the same
|
|
17946
18142
|
shape at routes/trace-usage.ts). The server writes it as a NAMED frame (`event: error`) whose payload
|
|
17947
18143
|
echoes `type`, so both an `event:`-dispatching client and a payload-dispatching one see it.
|
|
17948
|
-
🔴
|
|
17949
|
-
|
|
17950
|
-
|
|
17951
|
-
|
|
17952
|
-
replaced — each of the eight read-only subscription streams gets this frame at the moment drain begins and
|
|
17953
|
-
same code and sentence as the billable-submit 503 `draining
|
|
17954
|
-
|
|
17955
|
-
|
|
18144
|
+
🔴 NOT a run failure (neither known code) — rendering it as a run terminal would declare a live task dead (`done` / `failed` are the
|
|
18145
|
+
run's terminal arms). How a reader handles it is ONE terminal predicate (SDK `isTerminalFrame`, shared by every resumable reader):
|
|
18146
|
+
`STREAM_MAX_DURATION` (the 15-minute cap fired; the run is still alive) does NOT end the reader — it reconnects with `Last-Event-ID`
|
|
18147
|
+
and keeps reading (the durable leg stamps `id:` on every frame). `draining` (server >= 7.122.0: the instance entered drain / is being
|
|
18148
|
+
replaced — each of the eight read-only subscription streams gets this frame at the moment drain begins and at the hard-stop start, and is
|
|
18149
|
+
then closed by the server; same code and sentence as the billable-submit 503 `draining`) ENDS the reader's leg: the frame is delivered,
|
|
18150
|
+
then the reader stops without reconnecting — re-opening (with the last `id:`) is the caller's call, on its own schedule and backoff, and
|
|
18151
|
+
lands on the replacement instance; it is a connection signal that says nothing about the run's state. Any other code ENDS the leg too
|
|
18152
|
+
(open set — branch on the code, read an unknown code as "this leg ended, reason unknown").
|
|
17956
18153
|
additionalProperties: false
|
|
17957
18154
|
required: [type, errorCode]
|
|
17958
18155
|
properties:
|
|
17959
18156
|
type: { const: error }
|
|
17960
|
-
errorCode: { type: string, description: 'STREAM_MAX_DURATION | draining (server >= 7.122.0) (open set).' }
|
|
18157
|
+
errorCode: { type: string, description: 'STREAM_MAX_DURATION (reader resumes) | draining (server >= 7.122.0; reader leg ends) (open set; any other code ends the leg).' }
|
|
17961
18158
|
message: { type: string }
|
|
17962
18159
|
Event_question:
|
|
17963
18160
|
type: object
|
|
@@ -19785,7 +19982,7 @@ components:
|
|
|
19785
19982
|
draining by this deadline". See `POST /v1/admin/drain` for how the identity fields are compared.
|
|
19786
19983
|
properties:
|
|
19787
19984
|
actionId: { type: string, minLength: 1, description: 'Idempotency key: the same id again answers `already_draining` and changes nothing. Hand-issued actions pick their own (e.g. `manual:<uuid>`).' }
|
|
19788
|
-
worker: { type: string, minLength: 1, description: The engine's worker name (`SEMA_REGISTRY_WORKER`). }
|
|
19985
|
+
worker: { type: string, minLength: 1, description: "The engine's worker name (`SEMA_WEB_ADMIN_WORKER`; `SEMA_REGISTRY_WORKER` on servers <= 7.123.x)." }
|
|
19789
19986
|
deadlineAt: { type: integer, minimum: 0, description: Epoch milliseconds by which the engine stops on its own. }
|
|
19790
19987
|
scope: { type: string, minLength: 1, description: 'The tenant scope; omitted = the default scope `global` (compared against env `FLEET_SCOPE`, unset = `global`).' }
|
|
19791
19988
|
expectedGeneration: { type: string, pattern: '^(0|[1-9][0-9]*)$', description: 'The fleet generation as a canonical decimal string (`/health.generation`); absent against absent — omit it only for a non-member.' }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "19.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",
|
|
@@ -52,6 +52,6 @@
|
|
|
52
52
|
"prepublishOnly": "rm -f tsconfig.tsbuildinfo && npm run build && npm run typecheck --workspaces=false --prefix ../.. && npm test --workspaces=false --prefix ../.. && npm run prepack"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
-
"@sema-agent/settings-schema": "^
|
|
55
|
+
"@sema-agent/settings-schema": "^11.2.0"
|
|
56
56
|
}
|
|
57
57
|
}
|