@sema-agent/sdk 8.8.0 → 9.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -0
- package/dist/errors.d.ts +34 -4
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +52 -13
- package/dist/errors.js.map +1 -1
- package/dist/events.d.ts +48 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/resources/approvals.d.ts +24 -1
- package/dist/resources/approvals.d.ts.map +1 -1
- package/dist/resources/approvals.js +24 -1
- package/dist/resources/approvals.js.map +1 -1
- package/dist/resources/runs.d.ts +35 -1
- package/dist/resources/runs.d.ts.map +1 -1
- package/dist/resources/runs.js +39 -0
- package/dist/resources/runs.js.map +1 -1
- package/dist/resources/tool-approvals.d.ts +24 -2
- package/dist/resources/tool-approvals.d.ts.map +1 -1
- package/dist/resources/tool-approvals.js +4 -0
- package/dist/resources/tool-approvals.js.map +1 -1
- package/dist/resources/workflows.d.ts +10 -0
- package/dist/resources/workflows.d.ts.map +1 -1
- package/dist/resources/workflows.js.map +1 -1
- package/dist/types.d.ts +279 -20
- package/dist/types.d.ts.map +1 -1
- package/openapi.yaml +682 -39
- package/package.json +1 -1
package/openapi.yaml
CHANGED
|
@@ -918,6 +918,104 @@ paths:
|
|
|
918
918
|
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
919
919
|
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
920
920
|
|
|
921
|
+
/v1/runs/{taskId}/memory/capture-optout:
|
|
922
|
+
parameters:
|
|
923
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
924
|
+
- $ref: '#/components/parameters/TaskIdPath'
|
|
925
|
+
post:
|
|
926
|
+
tags: [runs]
|
|
927
|
+
operationId: runsMemoryCaptureOptOut
|
|
928
|
+
x-status: live # server >= 7.70.0 — the mid-run flip verb for the session memory-capture opt-out.
|
|
929
|
+
summary: Opt this SESSION out of long-term memory capture, mid-run.
|
|
930
|
+
description: >
|
|
931
|
+
"We got into something that should not be remembered." From this moment the session commits NOTHING to
|
|
932
|
+
long-term memory, already-committed contributions leave the consolidation pool, and write-root residue
|
|
933
|
+
is swept. ONE-WAY: there is no reverse verb, repeat calls are idempotent (`outcome:"existed"`), and
|
|
934
|
+
getting capture back needs a NEW session.
|
|
935
|
+
🔴 The SAME record as the `memoryCapture:"off"` submit-time declaration, reached through a second
|
|
936
|
+
ingress — so "declare it on the next submit" is the fallback for MOST refusals. It is not the answer to
|
|
937
|
+
all of them: read the per-errorCode recovery notes on the 409/503 arms below (one 409 is a PARTIAL
|
|
938
|
+
SUCCESS whose retry lane is this very verb, one names another replica, and one is a deployment-shape
|
|
939
|
+
fact the declaration hits too).
|
|
940
|
+
Owner-gated on the VERIFIED principal (a bare sessionId relay would let anyone switch off anyone's
|
|
941
|
+
memory); a non-owner gets the SAME 404 string as an unknown run (no existence oracle). The engine
|
|
942
|
+
re-adjudicates the per-principal entitlement at flip time — the service does not replicate that ruling.
|
|
943
|
+
Runs no model (not billable, no drain/roster gate); rate-limited as a mutating verb. Gate the UI entry
|
|
944
|
+
on `capabilities.runMemoryCaptureOptOut`.
|
|
945
|
+
requestBody:
|
|
946
|
+
required: false
|
|
947
|
+
content:
|
|
948
|
+
application/json:
|
|
949
|
+
schema:
|
|
950
|
+
type: object
|
|
951
|
+
additionalProperties: false # STRICT server-side — a mistyped key is a 400, not "no reason given".
|
|
952
|
+
properties:
|
|
953
|
+
reason: { type: string, minLength: 1, maxLength: 500, description: 'Optional one-line audit note. Does not affect the ruling.' }
|
|
954
|
+
responses:
|
|
955
|
+
'200':
|
|
956
|
+
description: Flipped — `created` on the first call, `existed` on an idempotent repeat.
|
|
957
|
+
content:
|
|
958
|
+
application/json:
|
|
959
|
+
schema: { $ref: '#/components/schemas/MemoryCaptureOptOutResult' }
|
|
960
|
+
'400':
|
|
961
|
+
description: >-
|
|
962
|
+
errorCode "request.body_shape" — the body must be `{}` or `{ reason }` (non-empty, at most 500
|
|
963
|
+
chars); this verb takes no other keys.
|
|
964
|
+
content:
|
|
965
|
+
application/json:
|
|
966
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
967
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
968
|
+
'403':
|
|
969
|
+
description: >-
|
|
970
|
+
errorCode "memory.capture_optout_denied" — this principal's entitlement does not permit the opt-out.
|
|
971
|
+
TERMINAL: the verdict is a deployment/center fact, so retrying changes nothing.
|
|
972
|
+
content:
|
|
973
|
+
application/json:
|
|
974
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
975
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
976
|
+
'409':
|
|
977
|
+
description: >-
|
|
978
|
+
Four DIFFERENT situations share this status; the recovery action is per errorCode, not per status.
|
|
979
|
+
(1) "steering.not_running" — this verb needs a LIVE turn and there is none. THREE of its four causes
|
|
980
|
+
(terminal / parked / no steerable face on this replica) are fixed the same way: declare
|
|
981
|
+
`memoryCapture: "off"` on this session's next NEW submit. ⚠️ For a PARKED run that matters twice
|
|
982
|
+
over — a resumed continuation rebuilds its spec from the PERSISTED body and cannot take a new
|
|
983
|
+
declaration, so content captured until that next submit stays captured. The FOURTH cause is
|
|
984
|
+
different: when the run is live on ANOTHER replica the service says so explicitly, and the recovery
|
|
985
|
+
is to RESEND this verb to the owning replica (the next-submit declaration also works).
|
|
986
|
+
(2) "memory.capture_optout_sweep_failed" — 🔴 PARTIAL SUCCESS, not a rejection: the one-way opt-out
|
|
987
|
+
record STANDS (capture is already off) and only the residue sweep failed on named paths. A repeat
|
|
988
|
+
call IS the retry lane; leaving it un-retried leaves that named residue on the authoritative plane.
|
|
989
|
+
(3) "memory.capture_optout_unavailable" — this run mounted no memory session, so there is nothing to
|
|
990
|
+
opt out of; no action is owed.
|
|
991
|
+
(4) "config.memory_capture_unsupported" — a DEPLOYMENT-SHAPE fact (no capture record store on this
|
|
992
|
+
worker, so the record would not survive to a resume replica). 🔴 The submit-time declaration hits the
|
|
993
|
+
SAME wall on this shape — do not advertise it as the workaround; the fix is on the deployment.
|
|
994
|
+
content:
|
|
995
|
+
application/json:
|
|
996
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
997
|
+
'422':
|
|
998
|
+
description: 'errorCode "steering.invalid_content" — the engine refused the reason payload.'
|
|
999
|
+
content:
|
|
1000
|
+
application/json:
|
|
1001
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
1002
|
+
'429':
|
|
1003
|
+
description: >-
|
|
1004
|
+
errorCode "limit.rate_exceeded" — mutating verb, rate-limited only (no quota/lease gate; it runs no
|
|
1005
|
+
model). `Retry-After` (seconds) set.
|
|
1006
|
+
content:
|
|
1007
|
+
application/json:
|
|
1008
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
1009
|
+
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
1010
|
+
'503':
|
|
1011
|
+
description: >-
|
|
1012
|
+
errorCode "memory.capture_optout_unpersisted" — the record could not be durably written (a store-mark
|
|
1013
|
+
failure, or a failing consolidation-epoch bump). The engine REFUSES rather than holding the flip
|
|
1014
|
+
in-process, so capture is still ON. RETRYABLE (the verb is idempotent).
|
|
1015
|
+
content:
|
|
1016
|
+
application/json:
|
|
1017
|
+
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
1018
|
+
|
|
921
1019
|
/v1/runs/{taskId}/subagents/{target}/output:
|
|
922
1020
|
parameters:
|
|
923
1021
|
- $ref: '#/components/parameters/PrincipalHeader'
|
|
@@ -2511,6 +2609,13 @@ paths:
|
|
|
2511
2609
|
approve carried no `answer` (the ONLY pre-claim rejection left on that leg; the retired codes
|
|
2512
2610
|
`decide.parked_answer_unsupported` / `decide.parked_question_unsupported` are gone — an answer HAS
|
|
2513
2611
|
a hookpoint now and a parked question CAN be approved, with an answer). SDK → DecideUnsupportedError.
|
|
2612
|
+
PARKED **WORKFLOW-CHILD** variant (server >= 7.69.0, S-185):
|
|
2613
|
+
`decide.workflow_remember_unsupported` — `remember:"session"` is REFUSED on that lane, fail-closed,
|
|
2614
|
+
BEFORE anything is delivered (zero side effects; re-issue without `remember`). The repo-wide rule for
|
|
2615
|
+
granting an exemption is "the engine COMMITTED this decision", and that lane's 200 does not carry the
|
|
2616
|
+
proof (the child's binding / updatedInput / answer checks run later, inside the workflow's own
|
|
2617
|
+
resume), so an exemption granted here could outlive a decision the engine then rejects. The body
|
|
2618
|
+
carries `runId` (the WORKFLOW run) and NO `taskId`. SDK → DecideUnsupportedError, `.runId`.
|
|
2514
2619
|
content:
|
|
2515
2620
|
application/json:
|
|
2516
2621
|
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
@@ -2545,6 +2650,18 @@ paths:
|
|
|
2545
2650
|
`gate_not_plan_review` (§4c) → SDK `GateNotToolApprovalError`. Client action: route by the row's
|
|
2546
2651
|
`gateKind` — `plan_review` → `POST /v1/assistant/tasks/{taskId}/plan_review`, `resource_limit` →
|
|
2547
2652
|
`POST /v1/assistant/tasks/{taskId}/resume`. Retrying the same body never succeeds.
|
|
2653
|
+
SEVENTH group (server >= 7.69.0 / S-185, the PARKED WORKFLOW-CHILD lane): `decide.workflow_host_unknown`
|
|
2654
|
+
and `decide.workflow_host_not_parked`. The pending belongs to a workflow child that parked on an
|
|
2655
|
+
approval gate, and the deployment could not hand the decision to that workflow's HOST session (the
|
|
2656
|
+
engine's only deployment-facing channel for a parked workflow ordinal is a resume of the host, which
|
|
2657
|
+
then re-invokes `Workflow({resumeFromRunId})`). Both bodies carry `runId` (the WORKFLOW run) and NO
|
|
2658
|
+
`taskId`. NOTHING was consumed: the child's checkpoint stays PENDING and the card is still listable
|
|
2659
|
+
and re-decidable. `_host_not_parked` = the host is not currently parked awaiting this run (it may
|
|
2660
|
+
have been interrupted, taking an already-delivered decision with it — the decision is not persisted
|
|
2661
|
+
anywhere) → retry once it parks again, or resume that `runId` yourself. `_host_unknown` = the run
|
|
2662
|
+
carries no originating session at all (a directly started `runWorkflow`, not a Workflow tool call) →
|
|
2663
|
+
NO retry value, a person resumes that `runId`. SDK → DecideWorkflowHostError (`.errorCode` splits the
|
|
2664
|
+
two recovery verbs, `.runId` is the handle).
|
|
2548
2665
|
SIXTH group (A-075.10, TIME-BASED retry-later pair): `resume.usage_window_exhausted` (#449 G1,
|
|
2549
2666
|
core 5.60.1 — the deployment governance window on this run's ledger key is full; NOTHING was
|
|
2550
2667
|
consumed or unpinned, the SAME token with the SAME decision redeems once the window slides) and
|
|
@@ -5213,7 +5330,9 @@ components:
|
|
|
5213
5330
|
type: object
|
|
5214
5331
|
description: >
|
|
5215
5332
|
Common request body. `[additionalProperties]` mirrors the service passthrough — scenario-specific fields
|
|
5216
|
-
(code-review's `repo`/`
|
|
5333
|
+
(code-review's `repo`/`lenses`/`rounds`, etc.) ride here. NOTE (sdk 9.1.0): `council`/`debate` used to be
|
|
5334
|
+
named here as passthrough examples, but the service declares them as TOP-LEVEL typed keys and reads them
|
|
5335
|
+
on two seams outside the scenario builder, so they are declared properties below.
|
|
5217
5336
|
required: [objective]
|
|
5218
5337
|
additionalProperties: true
|
|
5219
5338
|
properties:
|
|
@@ -5299,6 +5418,40 @@ components:
|
|
|
5299
5418
|
(mcpInjectionHonored = requirePrincipal!==true); multi-tenant ignores it (fail-closed). Merge:
|
|
5300
5419
|
deployment/center baseline wins on name collision (caller can ADD, never SHADOW).
|
|
5301
5420
|
items: { $ref: '#/components/schemas/McpServerSpec' }
|
|
5421
|
+
a2aPeers:
|
|
5422
|
+
type: array
|
|
5423
|
+
maxItems: 32
|
|
5424
|
+
description: >
|
|
5425
|
+
Per-request A2A peers — remote agents this task may call. HONORED only on a SINGLE-USER deployment
|
|
5426
|
+
that has neither locked the `a2a` key nor had `a2a_peers` denied by its compliance posture (the same
|
|
5427
|
+
three-veto predicate `capabilities.a2aInjection` advertises); multi-tenant ignores the field, and a
|
|
5428
|
+
LOCKED deployment refuses the whole request with 400 `config.locked_key`.
|
|
5429
|
+
🔴 Declaring a peer declares an OUTBOUND WRITE channel: every skill it advertises mounts with
|
|
5430
|
+
`egress:true` + `effect:"write"` (core's ruling); `toolAxes` is the caller's explicit override face.
|
|
5431
|
+
🔴 A malformed entry drops the WHOLE peer (named in a warn), never "drop the bad key and keep the
|
|
5432
|
+
peer": `allowSkills` is a NARROWING field whose absence means "no restriction", so a half-applied
|
|
5433
|
+
entry would turn a narrowing intent into a widening. The request itself is not rejected.
|
|
5434
|
+
The center/config baseline wins on a name clash (the peer name IS the tool namespace segment). Over
|
|
5435
|
+
the 32-entry cap the surplus entries are DROPPED and named too — same disposition as a malformed
|
|
5436
|
+
entry, not a 400.
|
|
5437
|
+
items: { $ref: '#/components/schemas/A2aServerSpec' }
|
|
5438
|
+
council:
|
|
5439
|
+
type: boolean
|
|
5440
|
+
description: >
|
|
5441
|
+
The `code-review` scenario's EXPENSIVE tier — fan the review out to N parallel lenses plus an
|
|
5442
|
+
arbiter (`run_council`) instead of the lead reviewing in-line. Read on TWO seams and both matter:
|
|
5443
|
+
the scenario builder mounts the council tool, and spec resolution treats it as an EXPLICIT team
|
|
5444
|
+
declaration (widening the task's wall-clock tenancy budget and suppressing the value router's
|
|
5445
|
+
auto-escalation). Only a literal `true` counts on both seams. On other scenarios the first seam is
|
|
5446
|
+
inert while the second still applies.
|
|
5447
|
+
debate:
|
|
5448
|
+
type: boolean
|
|
5449
|
+
description: >
|
|
5450
|
+
Run the L2 peer-debate rounds on top of `council`. 🔴 The two seams are ASYMMETRIC — this is not a
|
|
5451
|
+
standalone switch: the scenario builder reads it only INSIDE the `council === true` branch (sending
|
|
5452
|
+
`debate:true` alone produces no debate rounds), while the explicit-team predicate is
|
|
5453
|
+
`council || debate` (sending it alone still widens the budget and suppresses auto-escalation).
|
|
5454
|
+
Literal `true` only, same as `council`.
|
|
5302
5455
|
resumeAt:
|
|
5303
5456
|
type: string
|
|
5304
5457
|
description: >
|
|
@@ -5484,6 +5637,22 @@ components:
|
|
|
5484
5637
|
items: { type: string }
|
|
5485
5638
|
description: Capability names the caller requires; the worker refuses up front rather than 501-ing mid-run.
|
|
5486
5639
|
memoryWrite: { type: boolean, description: Allow this task to WRITE user memory (read is governed separately). }
|
|
5640
|
+
memoryCapture:
|
|
5641
|
+
type: string
|
|
5642
|
+
enum: [off]
|
|
5643
|
+
description: >
|
|
5644
|
+
server >= 7.70.0 — the SESSION-level memory-capture OPT-OUT declaration: "off" means "this session
|
|
5645
|
+
does not enter long-term memory". A ONE-MEMBER closed set: there is NO "on" spelling, absence is the
|
|
5646
|
+
only way to say "capture as usual".
|
|
5647
|
+
🔴 A DIFFERENT AXIS from `memoryWrite`: that one pauses writes for THIS run and can be flipped back
|
|
5648
|
+
per run; this one is a ONE-WAY, one-shot session record (later resumes never capture, repeat
|
|
5649
|
+
declarations are idempotent, and getting capture back needs a NEW session).
|
|
5650
|
+
🔴 The spelling gate is loud on BOTH legs (no resume downgrade): "OFF" / a boolean / anything else
|
|
5651
|
+
is a 400 `request.field_invalid` — silently dropping a privacy request as a typo is the banned
|
|
5652
|
+
direction. A deployment policy that refuses this principal's opt-out answers 403
|
|
5653
|
+
`memory.capture_optout_denied` (terminal). A worker older than 7.70.0 IGNORES the key silently, so
|
|
5654
|
+
it is not a capability probe. Rides the persisted body onto resume legs.
|
|
5655
|
+
Mid-run flip: POST /v1/runs/{taskId}/memory/capture-optout (the same record, a second ingress).
|
|
5487
5656
|
settings:
|
|
5488
5657
|
type: object
|
|
5489
5658
|
description: >
|
|
@@ -7264,6 +7433,71 @@ components:
|
|
|
7264
7433
|
face drops the whole server.
|
|
7265
7434
|
additionalProperties: { $ref: '#/components/schemas/McpToolFace' }
|
|
7266
7435
|
|
|
7436
|
+
A2aServerSpec:
|
|
7437
|
+
type: object
|
|
7438
|
+
description: >
|
|
7439
|
+
One per-request A2A peer (`TaskRequest.a2aPeers[]`). These six keys are exactly what the service's
|
|
7440
|
+
normalizer copies through — anything else on the entry never reaches the engine. `name`/`url` are
|
|
7441
|
+
required; if ANY present key is malformed the WHOLE peer is dropped (and named in a warn), because a
|
|
7442
|
+
half-applied entry can turn a narrowing intent into a widening.
|
|
7443
|
+
required: [name, url]
|
|
7444
|
+
additionalProperties: false
|
|
7445
|
+
properties:
|
|
7446
|
+
name:
|
|
7447
|
+
type: string
|
|
7448
|
+
minLength: 1
|
|
7449
|
+
maxLength: 128
|
|
7450
|
+
description: 'Stable local name; also the tool-namespace segment (`a2a__<peer>__<skill>`).'
|
|
7451
|
+
url:
|
|
7452
|
+
type: string
|
|
7453
|
+
description: 'The peer service URL; doubles as the origin the well-known agent-card path resolves against. http(s) ONLY.'
|
|
7454
|
+
cardUrl:
|
|
7455
|
+
type: string
|
|
7456
|
+
description: >
|
|
7457
|
+
Explicit agent-card location. When set it is used ALONE (no well-known probing behind the operator's
|
|
7458
|
+
back), so a malformed value drops the peer rather than falling back.
|
|
7459
|
+
headers:
|
|
7460
|
+
type: object
|
|
7461
|
+
additionalProperties: { type: string }
|
|
7462
|
+
description: 'Static headers sent on every request to this peer.'
|
|
7463
|
+
principalHeader:
|
|
7464
|
+
type: string
|
|
7465
|
+
description: >
|
|
7466
|
+
Header name the Runner injects this run's AUTHENTICATED principal into (the model can neither read
|
|
7467
|
+
nor set it). Absent principal => the header is not sent; the peer must then default to deny/public.
|
|
7468
|
+
allowSkills:
|
|
7469
|
+
type: array
|
|
7470
|
+
items: { type: string }
|
|
7471
|
+
description: >
|
|
7472
|
+
Allowlist of remote skill ids to mount. ⚠️ ABSENCE means "no restriction" (core's semantic), so this
|
|
7473
|
+
is a NARROWING key — never express narrowing by omitting it.
|
|
7474
|
+
toolAxes:
|
|
7475
|
+
type: object
|
|
7476
|
+
description: >
|
|
7477
|
+
Per-skill safety-axis overrides keyed by the peer's REMOTE skill id. The caller is the trust root,
|
|
7478
|
+
so this may LOWER the fail-closed default (`egress:true` + `effect:"write"`) as well as raise it.
|
|
7479
|
+
⚠️ Lowering `effect` WITHOUT clearing `egress` is a combination the engine refuses by design.
|
|
7480
|
+
additionalProperties:
|
|
7481
|
+
type: object
|
|
7482
|
+
additionalProperties: false
|
|
7483
|
+
properties:
|
|
7484
|
+
effect: { type: string, enum: [read, write, idempotent] }
|
|
7485
|
+
egress: { type: boolean }
|
|
7486
|
+
irreversibility: { type: string, enum: [always, never] }
|
|
7487
|
+
|
|
7488
|
+
MemoryCaptureOptOutResult:
|
|
7489
|
+
type: object
|
|
7490
|
+
description: >
|
|
7491
|
+
200 body of POST /v1/runs/{taskId}/memory/capture-optout. `outcome` is a closed two-word set:
|
|
7492
|
+
`created` = this call wrote the one-way session record; `existed` = the session had already opted out
|
|
7493
|
+
(idempotent replay).
|
|
7494
|
+
required: [taskId, sessionId, outcome]
|
|
7495
|
+
additionalProperties: false
|
|
7496
|
+
properties:
|
|
7497
|
+
taskId: { type: string }
|
|
7498
|
+
sessionId: { type: string }
|
|
7499
|
+
outcome: { type: string, enum: [created, existed] }
|
|
7500
|
+
|
|
7267
7501
|
MemoryEntryFrontmatter:
|
|
7268
7502
|
type: object
|
|
7269
7503
|
description: >
|
|
@@ -7431,6 +7665,107 @@ components:
|
|
|
7431
7665
|
items: { type: string }
|
|
7432
7666
|
description: 'E7 (SHIPPED) — the /effort picker default set (minimal/low/medium/high); present ONLY for a reasoning model.'
|
|
7433
7667
|
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.' }
|
|
7668
|
+
routePairing:
|
|
7669
|
+
$ref: '#/components/schemas/RoutePairingStatus'
|
|
7670
|
+
description: >-
|
|
7671
|
+
#345 (server >=7.45, core 5.57.0 `routePairingStatus`) — this row's key<->URL pairing posture.
|
|
7672
|
+
🔴 ALWAYS PRESENT on a >=7.45 worker: when the verdict cannot be reached the value is the WORD
|
|
7673
|
+
`unknown`, not a missing key ("this worker does not adjudicate" and "this row could not be
|
|
7674
|
+
adjudicated" must stay distinguishable on the wire). Declared here 2026-09-10 alongside `compat`:
|
|
7675
|
+
this schema is CLOSED, and the key has been minted unconditionally since 7.45 — so every real
|
|
7676
|
+
/v1/models response has been structurally rejected by a strict validator for the whole time (same
|
|
7677
|
+
class as `hasBidiControls` on InboxRow). Optional here because the SDK's supported server floor is
|
|
7678
|
+
3.0.0; the discriminant is the PEER'S VERSION, not this key's presence.
|
|
7679
|
+
compat:
|
|
7680
|
+
$ref: '#/components/schemas/ModelCompat'
|
|
7681
|
+
description: >-
|
|
7682
|
+
S-188 (server >=7.69.0, settings-schema >=1.10.0) — this row's wire-compatibility declaration for the
|
|
7683
|
+
openai-completions lane. ABSENT = this model declared nothing; see ModelCompat for the two causes of
|
|
7684
|
+
absence and why absence must NOT be rendered as "the operator did not write one".
|
|
7685
|
+
|
|
7686
|
+
ModelCompat:
|
|
7687
|
+
# 新(8.9.0 / S-188):`ModelInfo.compat` 的形 = core `OpenAICompletionsCompat` 的逐字镜像。
|
|
7688
|
+
# 词表在服务端**已判过**(src/model-compat.ts 的 readModelCompat:整只判形,任一键不合即整只丢),
|
|
7689
|
+
# 所以这里写闭集 enum 是**如实**的,不是本仓自己加的一道门 —— 消费方不必再验一遍。
|
|
7690
|
+
type: object
|
|
7691
|
+
description: >
|
|
7692
|
+
One model row's wire-compatibility declaration for the **openai-completions** lane (core
|
|
7693
|
+
`OpenAICompletionsCompat`, mirrored key-for-key; server >= 7.69.0 / S-188). It says how THIS gateway takes
|
|
7694
|
+
the thinking parameter and what the max-tokens field is called — no URL, no credential.
|
|
7695
|
+
🔴 ABSENT has TWO indistinguishable causes: (a) the model genuinely declared nothing (the engine infers
|
|
7696
|
+
from baseUrl / model id), or (b) the operator wrote one and the server DROPPED IT WHOLE (shape / word /
|
|
7697
|
+
unknown key), logging a named warning and falling back to the engine's inference. So a panel must NOT
|
|
7698
|
+
render "no compat" as "the operator did not configure one".
|
|
7699
|
+
🔴 WHOLE-OR-NOTHING: the server refuses a partially valid declaration rather than keeping the good keys
|
|
7700
|
+
(half a face on the wire is worse than none), so on the wire this object is always complete-and-legal or
|
|
7701
|
+
entirely absent.
|
|
7702
|
+
🔴 An EMPTY object never reaches the wire: the server's single mint point folds "all keys empty" (an
|
|
7703
|
+
empty `reasoningEffortLevels` counts as unset) into "do not mint the key". `compat: {}` is therefore an
|
|
7704
|
+
unrecognisable byte, not "declared but says nothing" — hence minProperties: 1.
|
|
7705
|
+
additionalProperties: false
|
|
7706
|
+
minProperties: 1
|
|
7707
|
+
properties:
|
|
7708
|
+
supportsReasoningEffort: { type: boolean, description: 'Does this endpoint accept `reasoning_effort` at all. Absent => the engine auto-detects from the URL.' }
|
|
7709
|
+
reasoningEffortLevels:
|
|
7710
|
+
type: array
|
|
7711
|
+
minItems: 1
|
|
7712
|
+
items: { $ref: '#/components/schemas/ModelThinkingLevel' }
|
|
7713
|
+
description: >-
|
|
7714
|
+
The effort tiers THIS endpoint really accepts (a subset of the six-rung ladder). Absent => the
|
|
7715
|
+
engine's conservative default (minimal|low|medium|high), so a higher requested tier clamps DOWN
|
|
7716
|
+
instead of 422-ing. Never empty on the wire (an empty declaration is folded into absence server-side).
|
|
7717
|
+
maxTokensField:
|
|
7718
|
+
type: string
|
|
7719
|
+
enum: [max_tokens, max_completion_tokens]
|
|
7720
|
+
description: 'Which field carries max tokens on this lane. Absent => inferred from the model id.'
|
|
7721
|
+
requiresReasoningContentOnAssistantMessages: { type: boolean, description: 'Whether every replayed assistant message must carry an empty `reasoning_content` when reasoning is on.' }
|
|
7722
|
+
thinkingFormat:
|
|
7723
|
+
$ref: '#/components/schemas/ModelThinkingFormat'
|
|
7724
|
+
description: 'How this gateway takes the thinking on/off parameter. Absent => the engine infers (default `openai`, i.e. `reasoning_effort`).'
|
|
7725
|
+
|
|
7726
|
+
ModelThinkingLevel:
|
|
7727
|
+
# 六档思考梯(core ThinkingLevel)。CLOSED:server 铸 Model 那一刻逐词判成员,出集词让整只 compat 被丢。
|
|
7728
|
+
type: string
|
|
7729
|
+
enum: [minimal, low, medium, high, xhigh, max]
|
|
7730
|
+
description: >-
|
|
7731
|
+
One rung of the six-rung thinking ladder (core `ThinkingLevel`). CLOSED — the server judges membership
|
|
7732
|
+
when it mints the Model, and an out-of-set word makes it drop the WHOLE `compat` declaration, so a word
|
|
7733
|
+
outside this set cannot reach the wire. (`off` is NOT a rung: it means "no model-level default" and
|
|
7734
|
+
belongs to a different knob.)
|
|
7735
|
+
|
|
7736
|
+
ModelThinkingFormat:
|
|
7737
|
+
# thinkingFormat 七词闭集(core OpenAICompletionsCompat["thinkingFormat"])。同上,server 已判成员。
|
|
7738
|
+
type: string
|
|
7739
|
+
enum: [openai, openrouter, deepseek, together, zai, qwen, qwen-chat-template]
|
|
7740
|
+
description: >-
|
|
7741
|
+
The spelling this gateway takes the thinking parameter in (core
|
|
7742
|
+
`OpenAICompletionsCompat.thinkingFormat`). CLOSED, judged server-side before it reaches the wire.
|
|
7743
|
+
`openai` = `reasoning_effort` · `openrouter` = `reasoning:{effort}` · `deepseek` = `thinking:{type}` plus
|
|
7744
|
+
`reasoning_effort` · `together` = `reasoning:{enabled}` plus `reasoning_effort` · `zai` / `qwen` =
|
|
7745
|
+
top-level `enable_thinking` · `qwen-chat-template` = `chat_template_kwargs.enable_thinking` — the ONLY
|
|
7746
|
+
spelling that can turn thinking OFF on a think-by-default vLLM/Qwen gateway.
|
|
7747
|
+
|
|
7748
|
+
RoutePairingStatus:
|
|
7749
|
+
# #345 core RoutePairingStatus(`ok:${RoutePairingPosture}` | `broken:*` 两词 | unknown)。
|
|
7750
|
+
# 🔴 **真开集,刻意不写 enum**:开/闭按**产方是否真判成员**定(8.4.0 拆 `CheckpointGate` 顶层 enum /
|
|
7751
|
+
# 把 `AskOrigin` 改真开集的同一条判据)。core 的 `routePairingStatus` 把 `verdict.posture` **直接内插**
|
|
7752
|
+
# 进 `ok:${…}`,server 的铸点与 core 的这只函数**全路径没有一处运行期成员判定** —— 一只自带
|
|
7753
|
+
# `adjudicateRoute` 的 brain 供什么词就上什么词。写一条会执法的 enum = 假闭集,会把一台合法 worker
|
|
7754
|
+
# 的真响应判违约。已知七词写在 description 里(消费端 switch 认已知词 + 一条 default 臂)。
|
|
7755
|
+
type: string
|
|
7756
|
+
x-open-enum: true
|
|
7757
|
+
description: >-
|
|
7758
|
+
A model row's key<->URL pairing posture (core `RoutePairingStatus`; server >= 7.45). OPEN on read —
|
|
7759
|
+
the seven words below are today's set, branch on the known ones and keep a `default` arm. NON-SECRET — no
|
|
7760
|
+
URL, no credential.
|
|
7761
|
+
`ok:per-model` this model has its own credential · `ok:paired` deployment credential, and the model's URL
|
|
7762
|
+
is on the deployment's declared root · `ok:unpinned` deployment credential but the deployment declared no
|
|
7763
|
+
root, so the pairing is UNVERIFIABLE · `ok:keyless` no credential anywhere (a local unauthenticated
|
|
7764
|
+
gateway) · `broken:credential_mismatch` / `broken:credential_missing` a real request WILL be refused by
|
|
7765
|
+
the engine (flag it red) · `unknown` could not be adjudicated — render "unknown", NEVER "good" or "bad".
|
|
7766
|
+
The verdict's frame of reference is the gateway root as of BRAIN CONSTRUCTION, so a live re-pointing of
|
|
7767
|
+
the deployment root only shows up after a restart (both this reading and the real request judge from the
|
|
7768
|
+
same snapshot).
|
|
7434
7769
|
|
|
7435
7770
|
ElicitResponse:
|
|
7436
7771
|
type: object
|
|
@@ -7633,8 +7968,25 @@ components:
|
|
|
7633
7968
|
additionalProperties: false
|
|
7634
7969
|
properties:
|
|
7635
7970
|
label: { type: string, description: 'display label (redacted — LLM-authored).' }
|
|
7636
|
-
status:
|
|
7637
|
-
|
|
7971
|
+
status:
|
|
7972
|
+
type: string
|
|
7973
|
+
description: >-
|
|
7974
|
+
Lifecycle status — core `WorkflowItemStatus` passed through verbatim (running|completed|failed|parked;
|
|
7975
|
+
OPEN on read, the vocabulary's owner is core). 🔴 `parked` (server >= 7.69.0 / core 7.10.0 #642) is an
|
|
7976
|
+
AGENT-ROW word only: this ordinal durably paused at an approval gate and the whole run suspended on it
|
|
7977
|
+
(a phase or group never parks — a parked agent ends the run, so the RUN-level status vocabulary does
|
|
7978
|
+
not have this word). The row's redemption key (`parkedCheckpointToken`) is a resume CAPABILITY and is
|
|
7979
|
+
NEVER projected onto the wire — it appears nowhere in this document by design.
|
|
7980
|
+
displayStatus:
|
|
7981
|
+
type: string
|
|
7982
|
+
description: >-
|
|
7983
|
+
core `deriveAgentDisplayStatus`: running|queued|done|failed|interrupted|parked — the canonical glyph
|
|
7984
|
+
vocabulary, one source of truth shared with the shell. 🔴 `parked` is the SIXTH arm (core 7.10.0
|
|
7985
|
+
#642, server >= 7.69.0): a durable approval gate is holding this agent. Before it existed a parked
|
|
7986
|
+
agent fell through to another word, i.e. a card waiting on a human rendered as a glyph that asks for
|
|
7987
|
+
no action. ALWAYS PRESENT — the server derives it with that pure function on every row. Open on read
|
|
7988
|
+
(the vocabulary's owner is core), but the function has no default arm, so a word outside this set
|
|
7989
|
+
means core grew the table.
|
|
7638
7990
|
taskStatus: { type: string, description: 'the underlying task''s terminal TaskStatus (open vocabulary; core enum verbatim).' }
|
|
7639
7991
|
callKey: { type: string, description: 'the stable deterministic identity of this ctx.agent call (resume-journal key).' }
|
|
7640
7992
|
groupId: { type: string, description: 'the nesting ctx.workflow sub-group this agent ran under; absent = top-level.' }
|
|
@@ -7681,7 +8033,7 @@ components:
|
|
|
7681
8033
|
additionalProperties: false
|
|
7682
8034
|
properties:
|
|
7683
8035
|
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.' }
|
|
8036
|
+
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
8037
|
startedAt: { type: integer, description: 'adoption time for a pre-registered phase (0 while still pending).' }
|
|
7686
8038
|
endedAt: { type: integer }
|
|
7687
8039
|
durationMs: { type: integer, description: 'endedAt − startedAt; absent until the phase ends.' }
|
|
@@ -7699,7 +8051,7 @@ components:
|
|
|
7699
8051
|
properties:
|
|
7700
8052
|
groupId: { type: string }
|
|
7701
8053
|
parentGroupId: { type: string, description: 'absent = top-level (a child of the implicit root).' }
|
|
7702
|
-
status: { type: string, description: 'core WorkflowItemStatus (running
|
|
8054
|
+
status: { type: string, description: 'core WorkflowItemStatus (running|completed|failed; a group never parks — `parked` is an agent-row word only).' }
|
|
7703
8055
|
startedAt: { type: integer }
|
|
7704
8056
|
endedAt: { type: integer }
|
|
7705
8057
|
durationMs: { type: integer, description: 'endedAt − startedAt; absent until the group ends.' }
|
|
@@ -7925,13 +8277,31 @@ components:
|
|
|
7925
8277
|
resultChars: { type: integer, description: 'server >=7.9.0 (A-002.6): the ORIGINAL result length in characters; present only alongside resultTruncated.' }
|
|
7926
8278
|
tokens: { type: integer, description: 'present only when the result carried stats.' }
|
|
7927
8279
|
turns: { type: integer, description: 'present only when the result carried stats.' }
|
|
8280
|
+
parked:
|
|
8281
|
+
type: boolean
|
|
8282
|
+
enum: [true]
|
|
8283
|
+
description: >-
|
|
8284
|
+
server >= 7.69.0 / core 7.10.0 #642 — this ordinal is PARKED on a durable approval gate, waiting for a
|
|
8285
|
+
decision. 🔴 NEVER minted as `false`: the park arm and the result arm carry the SAME payload (a
|
|
8286
|
+
TaskResult whose terminal cause is `paused`), and the read face adds exactly this one bit, so an
|
|
8287
|
+
ordinary row is simply ABSENT. Branch on `parked === true`, never on `!parked`. `status` is NOT
|
|
8288
|
+
rewritten by this layer — it is that TaskResult's persisted plane status (`suspended` on a human
|
|
8289
|
+
gate). 🔴 The oversize-stub arm (WorkflowJournalEntryTruncated) structurally CANNOT carry this key —
|
|
8290
|
+
see its note; absence there is not an assertion.
|
|
7928
8291
|
|
|
7929
8292
|
WorkflowJournalEntryTruncated:
|
|
7930
8293
|
# 新(census 批2 四段):the truncated arm (workflows.ts) — an honest stub for a row whose
|
|
7931
8294
|
# stored TaskResult exceeded the per-row 64KiB gate (SQL stores) or byte length (file/in-memory
|
|
7932
8295
|
# fallback): the server never pulls the oversized payload into process memory.
|
|
7933
8296
|
type: object
|
|
7934
|
-
description:
|
|
8297
|
+
description: >-
|
|
8298
|
+
A journal row too large to project — an honest "too big to show" stub, never a silent 2000-char
|
|
8299
|
+
truncation of the real result.
|
|
8300
|
+
🔴 DELIBERATELY carries NO `parked` (server 7.69.0, one mint point shared by all three arms): a stub by
|
|
8301
|
+
definition carries no payload-derived key — the SQL paging leg never pulled the payload back when the row
|
|
8302
|
+
is over the bound, so it structurally cannot answer that bit, and minting it only on the file/in-memory
|
|
8303
|
+
leg would fork the wire shape by backend. To decide "is this ordinal parked", read the `parked` of a
|
|
8304
|
+
NON-stub row, or `agents[].status === "parked"` on GET /v1/workflows/:id. Absence here is NOT an assertion.
|
|
7935
8305
|
required: [callKey, ordinal, truncated, resultBytes]
|
|
7936
8306
|
additionalProperties: false
|
|
7937
8307
|
properties:
|
|
@@ -8431,6 +8801,21 @@ components:
|
|
|
8431
8801
|
is guaranteed by all four shapes; discriminate: `idempotent` -> replay; `decision` without `sessionId` ->
|
|
8432
8802
|
parked receipt; `sessionId` + `bindingEnforced` (+ `status`) -> task-level acceptance / terminal.
|
|
8433
8803
|
`taskId` rides `getActiveTaskId` on the terminal leg (omitted when there is no active row).
|
|
8804
|
+
🔴 A FIFTH PROVENANCE, not a fifth key set (server >= 7.69.0 / S-185 — the PARKED WORKFLOW-CHILD lane):
|
|
8805
|
+
when the pending belongs to a workflow child, the success body is byte-identically the TASK-LEVEL
|
|
8806
|
+
ACCEPTANCE shape, but it is a wake of the workflow's **HOST session** — `taskId` is the host's FRESHLY
|
|
8807
|
+
MINTED run id, not the child's. Two readings a consumer MUST get right:
|
|
8808
|
+
(1) 🔴 the 200 is a **DELIVERY acceptance**, NOT "the gate is resolved". The child's checkpoint stays
|
|
8809
|
+
PENDING until the workflow engine itself resolves it, so the card can still be on `/v1/approvals` right
|
|
8810
|
+
after the 200 — NEVER remove the card from a UI on the strength of this 200; poll the HOST run
|
|
8811
|
+
(`GET /v1/runs/{taskId}`) for progress instead. The background-redemption lane's 200 and the task-level
|
|
8812
|
+
lane's 200 DO mean the engine committed the decision — one sentence ("approved and in effect") cannot be
|
|
8813
|
+
used for both lanes.
|
|
8814
|
+
(2) delivery can still be LOST: if the host run is interrupted before it re-invokes `Workflow`, the
|
|
8815
|
+
decision goes with that invocation (it is not persisted anywhere) and a retry answers 409
|
|
8816
|
+
`decide.workflow_host_not_parked`. Because the card was pending throughout, nothing is forged and the
|
|
8817
|
+
approval can be re-decided once the host parks again.
|
|
8818
|
+
The durable approval-queue projection is UNCHANGED — no key marks the workflow origin on any read face.
|
|
8434
8819
|
required: []
|
|
8435
8820
|
additionalProperties: false
|
|
8436
8821
|
properties:
|
|
@@ -8697,6 +9082,12 @@ components:
|
|
|
8697
9082
|
this spec over-declared decide bindings that never appeared on inbox wire at all. Four client repos were
|
|
8698
9083
|
structurally zero-consumers, so server 3.4.0 narrowed the wire and this schema follows. For the tool face
|
|
8699
9084
|
and decide bindings use /v1/approvals (PendingCheckpoint); for task attribution use /v1/assistant/tasks.
|
|
9085
|
+
🔴 STILL CLOSED (sdk 8.9.0). Four keys the server had been minting onto this row went undeclared until
|
|
9086
|
+
now — `requiresRealApproval` / `denialLimitFallback` / `origin` (server >= 7.57.0) and
|
|
9087
|
+
`classifierUnavailable` (server >= 7.69.0) — so a strict validator rejected every row that carried one
|
|
9088
|
+
and a generated client could not read them at all. They are declared below rather than tolerated: this
|
|
9089
|
+
row stays closed, which is the whole point of the shape (an undeclared key is drift, and drift should be
|
|
9090
|
+
loud).
|
|
8700
9091
|
type: object
|
|
8701
9092
|
required: [sessionId, scope, objective, input]
|
|
8702
9093
|
additionalProperties: false
|
|
@@ -8723,11 +9114,47 @@ components:
|
|
|
8723
9114
|
the /v1/approvals row's — the reading of absence does not). Absence = "not detected", never
|
|
8724
9115
|
"confirmed clean". Declared here because this schema is CLOSED — without the key a strict validator
|
|
8725
9116
|
rejects every >=7.53 inbox row that carries it.
|
|
9117
|
+
requiresRealApproval:
|
|
9118
|
+
type: boolean
|
|
9119
|
+
enum: [true]
|
|
9120
|
+
description: >-
|
|
9121
|
+
server >= 7.57.0 (core 7.4.0 #557), ADDITIVE, present ONLY when true: this parked ask demanded REAL
|
|
9122
|
+
HUMAN judgment — an inbox can say "only a person can clear this" without decoding the gate kind.
|
|
9123
|
+
🔴 NEVER minted as `false` (the OMIT contract this row's other flags follow); absence is "not marked",
|
|
9124
|
+
never "confirmed clearable without a person". The value comes WHOLE from core (`summarizeCheckpoint`
|
|
9125
|
+
projects the park row's own bit) — the server neither recomputes nor redacts it.
|
|
9126
|
+
denialLimitFallback:
|
|
9127
|
+
$ref: '#/components/schemas/DenialLimitFallback'
|
|
9128
|
+
description: >-
|
|
9129
|
+
server >= 7.57.0 (core 7.4.0 #557), ADDITIVE — this parked ask IS the auto-mode classifier's
|
|
9130
|
+
DENIAL-LIMIT fallback, carrying the counts that tripped the bound. 🔴 On a PARKED row
|
|
9131
|
+
`autoDenyAfterMs` is ALWAYS `0` and a consumer MUST NOT start a countdown from it: the window is a
|
|
9132
|
+
fact of the ask's ROUTE, armed only at a hand-out to a LIVE approver, and nothing counts down on the
|
|
9133
|
+
parked lane (a park's expiry is the row's own `deadline`/ttl). Render the counts and the limit; there
|
|
9134
|
+
is no countdown to render here. Core screens the four members before writing the row, so a
|
|
9135
|
+
half-shaped value reads as ABSENT rather than reaching a consumer as a malformed card.
|
|
9136
|
+
origin:
|
|
9137
|
+
$ref: '#/components/schemas/AskOrigin'
|
|
9138
|
+
description: >-
|
|
9139
|
+
server >= 7.57.0 (core 7.5.0), ADDITIVE — WHICH AUTHORITY raised this parked ask, the same word the
|
|
9140
|
+
synchronous card renders by (engine-stamped at the gate, never a policy's claim). Core writes it onto
|
|
9141
|
+
the row ONLY when it is a member of the set, so an out-of-set word is not expected on THIS face — but
|
|
9142
|
+
the vocabulary's owner is still core and it has grown before, so read it the same way as everywhere
|
|
9143
|
+
else: branch known words, keep a `default` arm, and treat an unknown one as "unknown origin", never
|
|
9144
|
+
as "no origin". A row minted before the bit existed reads absent (unreported), not "no authority".
|
|
8726
9145
|
objective: { type: ['string', 'null'], description: 'task objective, null if unavailable.' }
|
|
8727
9146
|
input:
|
|
8728
9147
|
description: >
|
|
8729
9148
|
REDACTED tool-args preview for display (null on gates without one, e.g. plan_review). Untyped by
|
|
8730
9149
|
design (per-tool shape). The raw `toolInput` is deliberately NOT on this row.
|
|
9150
|
+
classifierUnavailable:
|
|
9151
|
+
$ref: '#/components/schemas/ClassifierUnavailable'
|
|
9152
|
+
description: >-
|
|
9153
|
+
server >= 7.69.0 (core 7.10.0 #616), ADDITIVE — WHY this PARKED ask reached a human. The inbox is the
|
|
9154
|
+
TRIAGE face, so `breaker_open` matters most here: it forecasts an approval storm rather than a
|
|
9155
|
+
one-off question. The value comes WHOLE from core (`summarizeCheckpoint` echoes it only when the
|
|
9156
|
+
cause is a member of the engine's set), so the server neither recomputes nor re-screens it. See
|
|
9157
|
+
ClassifierUnavailable for the open-`cause` rule and the three shapes absence covers.
|
|
8731
9158
|
|
|
8732
9159
|
InboxList:
|
|
8733
9160
|
type: object
|
|
@@ -9389,6 +9816,14 @@ components:
|
|
|
9389
9816
|
activeTaskId:
|
|
9390
9817
|
type: string
|
|
9391
9818
|
description: On a 409 — the task currently holding the session lock (see the Conflict response note).
|
|
9819
|
+
runId:
|
|
9820
|
+
type: string
|
|
9821
|
+
description: >-
|
|
9822
|
+
server >= 7.69.0 (S-185) — on the three `decide.workflow_*` codes ONLY: the WORKFLOW run's id. It is
|
|
9823
|
+
the RECOVERY HANDLE (wait for the host session to park and re-decide, or resume that run directly).
|
|
9824
|
+
🔴 NOT interchangeable with `taskId`: the workflow-child park lane mints no taskId at all (this
|
|
9825
|
+
decision never landed on any run, so minting one would be a lie), and the other two decide lanes
|
|
9826
|
+
mint no runId. Branch by `errorCode`, never fall back from one handle to the other.
|
|
9392
9827
|
|
|
9393
9828
|
# ── Streaming event taxonomy (the UX 命脉). Each SSE `data:` line is one JSON-serialized AgentEvent. ──
|
|
9394
9829
|
# TWO vocabularies (the pinned wire contract Drift 3). ⚠️ The two lines below are the SHAPE-DEFINING
|
|
@@ -9461,6 +9896,13 @@ components:
|
|
|
9461
9896
|
- $ref: '#/components/schemas/Event_tool_approval_complete'
|
|
9462
9897
|
- $ref: '#/components/schemas/Event_approval_request'
|
|
9463
9898
|
- $ref: '#/components/schemas/Event_approval_revoke'
|
|
9899
|
+
# sdk 9.1.0: two LIVE-leg arms the service has been emitting while neither this union nor the
|
|
9900
|
+
# SDK's declared them — `text_end` since server 7.66.0 (segment boundary; retires the idle-flush
|
|
9901
|
+
# heuristic) and `tool_roster_delta` since server 7.70.0 (run-time roster change). A generated client
|
|
9902
|
+
# had no arm to switch on and dropped both. Found by the mechanical frame-arm reconciliation gate
|
|
9903
|
+
# (server `routes/tasks.ts` mint points x this union), which now runs on every build.
|
|
9904
|
+
- $ref: '#/components/schemas/Event_text_end'
|
|
9905
|
+
- $ref: '#/components/schemas/Event_tool_roster_delta'
|
|
9464
9906
|
discriminator:
|
|
9465
9907
|
propertyName: type
|
|
9466
9908
|
mapping:
|
|
@@ -9504,6 +9946,8 @@ components:
|
|
|
9504
9946
|
tool_approval_complete: '#/components/schemas/Event_tool_approval_complete'
|
|
9505
9947
|
approval_request: '#/components/schemas/Event_approval_request'
|
|
9506
9948
|
approval_revoke: '#/components/schemas/Event_approval_revoke'
|
|
9949
|
+
text_end: '#/components/schemas/Event_text_end'
|
|
9950
|
+
tool_roster_delta: '#/components/schemas/Event_tool_roster_delta'
|
|
9507
9951
|
|
|
9508
9952
|
# design/99 §E2 (SHIPPED, core TaskEventIdentity) — additive per-event identity on every CONTENT event (NOT on
|
|
9509
9953
|
# the lifecycle arms turn_end/compacted/suspended/done/failed). eventId = uuidv7 dedup/resume handle (the durable
|
|
@@ -9593,6 +10037,27 @@ components:
|
|
|
9593
10037
|
parentToolCallId: { type: string }
|
|
9594
10038
|
sourceTaskId: { type: string }
|
|
9595
10039
|
bgAgentId: { type: string }
|
|
10040
|
+
Event_text_end:
|
|
10041
|
+
type: object
|
|
10042
|
+
description: >
|
|
10043
|
+
LIVE stream only (server >= 7.66.0, core 5.62.0) — the END-OF-SEGMENT signal for one assistant text
|
|
10044
|
+
segment. `content` is the segment's AUTHORITATIVE full text (same trust class as `text_delta`, same
|
|
10045
|
+
zero content transformation). It retires the "no delta for N ms => the segment must be over" idle-flush
|
|
10046
|
+
heuristic: the boundary is now stated by the engine, not guessed by a timer.
|
|
10047
|
+
🔴 A TRUNCATED segment gets NO such frame (core deliberately withholds it) — "no text_end" does NOT
|
|
10048
|
+
mean "the segment is still going". 🔴 The durable leg does not append it (the batched `text` event
|
|
10049
|
+
carries the same final prose), so GET /v1/runs/{id}/events never replays it. Both mint points are in
|
|
10050
|
+
`routes/tasks.ts`: the top stream (all four identity keys) and the forwarded sub-agent leg (first two).
|
|
10051
|
+
allOf: [{ $ref: '#/components/schemas/EventIdentity' }]
|
|
10052
|
+
additionalProperties: false # 封闭 + identity 本地镜像:见 Event_reasoning 处的长注
|
|
10053
|
+
required: [type, content]
|
|
10054
|
+
properties:
|
|
10055
|
+
type: { const: text_end }
|
|
10056
|
+
content: { type: string }
|
|
10057
|
+
eventId: { type: string }
|
|
10058
|
+
parentToolCallId: { type: string }
|
|
10059
|
+
sourceTaskId: { type: string }
|
|
10060
|
+
bgAgentId: { type: string }
|
|
9596
10061
|
Event_tool_start:
|
|
9597
10062
|
type: object
|
|
9598
10063
|
allOf: [{ $ref: '#/components/schemas/EventIdentity' }]
|
|
@@ -10341,33 +10806,7 @@ components:
|
|
|
10341
10806
|
🔴 The engine''s `error` free text is DELIBERATELY NOT PROJECTED (it is remote-author text, and core
|
|
10342
10807
|
already owns the single redaction mint point for it). The actionable cause is `errorCode`.
|
|
10343
10808
|
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.
|
|
10809
|
+
items: { $ref: '#/components/schemas/WiringManifestMcpEntry' }
|
|
10371
10810
|
governance:
|
|
10372
10811
|
type: object
|
|
10373
10812
|
additionalProperties: false
|
|
@@ -10391,6 +10830,56 @@ components:
|
|
|
10391
10830
|
parentToolCallId: { type: string }
|
|
10392
10831
|
sourceTaskId: { type: string }
|
|
10393
10832
|
bgAgentId: { type: string }
|
|
10833
|
+
Event_tool_roster_delta:
|
|
10834
|
+
type: object
|
|
10835
|
+
description: >
|
|
10836
|
+
LIVE stream only (server >= 7.70.0, core 7.8.0) — a RUN-TIME roster change.
|
|
10837
|
+
`wiring_manifest.tools` is the snapshot taken when this leg started; this frame is the increment after
|
|
10838
|
+
it: one refresh that actually changed the roster emits EXACTLY one frame (ordered after the manifest and
|
|
10839
|
+
before that refresh's own `tool_end`).
|
|
10840
|
+
🔴 A `delta.fromDigest` that does not match what the consumer holds is NOT a rejection: `delta.roster`
|
|
10841
|
+
is the new state regardless — record a skew and re-sync from the whole roster (only `delta.summary`
|
|
10842
|
+
becomes unusable). 🔴 Half a roster never reaches the wire: the service drops the WHOLE frame when
|
|
10843
|
+
core's own typebox check rejects the payload. 🔴 Neither durable leg appends it, so it never replays on
|
|
10844
|
+
GET /v1/runs/{id}/events — after a reconnect re-sync from the `wiring_manifest.tools` snapshot instead
|
|
10845
|
+
of waiting for a replacement delta.
|
|
10846
|
+
allOf: [{ $ref: '#/components/schemas/EventIdentity' }]
|
|
10847
|
+
additionalProperties: false # 封闭 + identity 本地镜像:见 Event_reasoning 处的长注
|
|
10848
|
+
required: [type, delta]
|
|
10849
|
+
properties:
|
|
10850
|
+
type: { const: tool_roster_delta }
|
|
10851
|
+
delta: { $ref: '#/components/schemas/ToolRosterDelta' }
|
|
10852
|
+
eventId: { type: string }
|
|
10853
|
+
parentToolCallId: { type: string }
|
|
10854
|
+
sourceTaskId: { type: string }
|
|
10855
|
+
bgAgentId: { type: string }
|
|
10856
|
+
ToolRosterDelta:
|
|
10857
|
+
type: object
|
|
10858
|
+
description: >
|
|
10859
|
+
core `ToolRosterDelta` (core 7.8.0) — the payload of `tool_roster_delta`. `roster` is the WHOLE
|
|
10860
|
+
post-change roster and positions come from IT, never from `summary`; the three `summary` lists are
|
|
10861
|
+
pairwise disjoint and a `changed` name's new row lives in `roster`.
|
|
10862
|
+
🔴 OPEN on purpose (`additionalProperties: true`), unlike the frame that carries it. The rule: a schema is
|
|
10863
|
+
CLOSED where the SERVICE builds the object key by key (every `Event_*` arm), and OPEN where the service
|
|
10864
|
+
forwards a CORE-owned payload VERBATIM and keeps no key table of its own. This payload is the latter (the
|
|
10865
|
+
service passes `delta` through after core's own typebox check), so closing it would turn a future core key
|
|
10866
|
+
into "every legitimate frame is a violation". `ToolRoster` is closed despite the same provenance because
|
|
10867
|
+
the service DOES keep a compile-time key table for it.
|
|
10868
|
+
required: [fromDigest, roster, summary]
|
|
10869
|
+
additionalProperties: true
|
|
10870
|
+
properties:
|
|
10871
|
+
fromDigest:
|
|
10872
|
+
type: string
|
|
10873
|
+
description: 'The digest the consumer is expected to hold. A mismatch is a SKEW (re-sync from `roster`), never a rejection.'
|
|
10874
|
+
roster: { $ref: '#/components/schemas/ToolRoster' }
|
|
10875
|
+
summary:
|
|
10876
|
+
type: object
|
|
10877
|
+
required: [added, removed, changed]
|
|
10878
|
+
additionalProperties: true # same rule as the parent — core owns this payload's key table.
|
|
10879
|
+
properties:
|
|
10880
|
+
added: { type: array, items: { type: string } }
|
|
10881
|
+
removed: { type: array, items: { type: string } }
|
|
10882
|
+
changed: { type: array, items: { type: string } }
|
|
10394
10883
|
ToolRoster:
|
|
10395
10884
|
type: object
|
|
10396
10885
|
description: >
|
|
@@ -10431,7 +10920,13 @@ components:
|
|
|
10431
10920
|
capabilityId: { type: string }
|
|
10432
10921
|
effect: { type: string, enum: [read, write, idempotent] }
|
|
10433
10922
|
egress: { type: boolean }
|
|
10434
|
-
irreversibility:
|
|
10923
|
+
irreversibility:
|
|
10924
|
+
type: string
|
|
10925
|
+
enum: [always, maybe, never]
|
|
10926
|
+
description: >
|
|
10927
|
+
core's THREE-word set (`never` / `maybe` / `always`). `maybe` is a real emitted value (e.g. a shell
|
|
10928
|
+
tool under a classifying gate), so a two-word mirror rejects an entire legitimate roster and leaves
|
|
10929
|
+
the consumer on stale state.
|
|
10435
10930
|
contentOrigin: { type: string, enum: [local, execution, external] }
|
|
10436
10931
|
family: { type: string, enum: [shell, file-read, file-write, search, web, delegate, plan, scaffold, memory, protocol, other] }
|
|
10437
10932
|
pathTarget: { $ref: '#/components/schemas/McpToolPathTarget' }
|
|
@@ -10502,6 +10997,28 @@ components:
|
|
|
10502
10997
|
message varies with the memoryProvenance mode — matching on message text WILL break). `code` is an OPEN
|
|
10503
10998
|
set (the server whitelist grows with core's code register): render a known code specially, fall back to
|
|
10504
10999
|
`message` for an unknown one — never drop the frame.
|
|
11000
|
+
🔴 This list is NOT the full register and is not meant to become one (server 7.69.0's whitelist holds 21
|
|
11001
|
+
codes): `code` is OPEN, and only the codes with an EXTRA rendering rule are written out here.
|
|
11002
|
+
`mcp.injection_dropped` (server >= 7.68.0) — one entry of the CALLER'S OWN request-leg MCP injection was
|
|
11003
|
+
not mounted; `{sessionId, server, reason, field?}`, `reason` a four-word set (`malformed_entry` /
|
|
11004
|
+
`name_reserved_by_deployment` / `gate_closed` / `over_cap`). The recovery verb is on the USER's side
|
|
11005
|
+
(rename it, fix that key, drop a few servers) — without this notice all they see is "my .mcp.json seems
|
|
11006
|
+
to do nothing". Minted at ASSEMBLY time, so `detail` structurally has NO runId; the join key is the
|
|
11007
|
+
`server` name. ⚠️ The DELIVERY surface changed at server 7.69.0: 7.68.0 delivered it on the two FRESH
|
|
11008
|
+
legs only, and 7.69.0 delivers it on the RESUME family too (all four resume legs share one
|
|
11009
|
+
spec-resolver, which now passes the notice seat). Consequence for consumers that assert on ledger
|
|
11010
|
+
CONTENTS: a resume leg's ledger MAY now carry one extra `engine_notice` row it did not before — assert
|
|
11011
|
+
"may appear", never an exact row count or order (and consume idempotently: reconnect replay shows it again).
|
|
11012
|
+
`delegation.ask_unresolvable` (server >= 7.69.0 / core 7.10.0 #648) — an `ask` reached a FINAL DENY with
|
|
11013
|
+
NOBODY having ruled on it: the approver consulted for the call answered `unavailable` and no durable park
|
|
11014
|
+
caught it afterwards. The deny itself is unchanged (`tool_end.gate.settlement.kind:"approver_unavailable"`
|
|
11015
|
+
— that sentence IS the tool result); this code is the half the person watching notices could not see.
|
|
11016
|
+
`{sessionId, toolName, toolCallId, settlementKind, parkLaneExisted}`; `toolCallId` joins to the
|
|
11017
|
+
`tool_approval` card frame, `settlementKind` is passed through verbatim (the vocabulary's owner is core).
|
|
11018
|
+
Deduplicated ONCE PER TOOL CALL. 🔴 `parkLaneExisted` is a DISCRIMINANT, not a count: `true` = a park lane
|
|
11019
|
+
was armed but declined/failed (check the store, check authorisation), `false` = there was no lane at all
|
|
11020
|
+
(wire one up). The two recovery verbs differ, so a consumer MUST render them apart — NEVER fold them into
|
|
11021
|
+
one "nobody approved it".
|
|
10505
11022
|
Starter whitelist (server 7.36): `memory.session_polluted` `{reason, sessionId?}` ·
|
|
10506
11023
|
`memory.harvest_quarantined` `{count, moved, escalated, reason?}` — `moved` and `escalated` MUST NOT be
|
|
10507
11024
|
subtracted from each other (an in-place tombstone counts as both) · `memory.delegation_static_mark_waived`
|
|
@@ -11178,6 +11695,12 @@ components:
|
|
|
11178
11695
|
# 分两段:server ≤7.4.0 默认 OFF,≥7.5.0 默认 ON(BREAKING,见 server CHANGELOG)。──
|
|
11179
11696
|
# 与上面的 ToolApprovalFrame 是**并行加帧,不替换**:开关打开时同一只 ask 出两帧(先 tool_approval,
|
|
11180
11697
|
# 后 approval_request),两帧同带 `approvalId` ⇒ 消费端按它去重、优先渲染新帧。
|
|
11698
|
+
classifierUnavailable:
|
|
11699
|
+
$ref: '#/components/schemas/ClassifierUnavailable'
|
|
11700
|
+
description: >-
|
|
11701
|
+
server >= 7.69.0 (core 7.10.0 #616), ADDITIVE, present only when the classifier was consulted and
|
|
11702
|
+
could not run — WHY this ask reached a human. See ClassifierUnavailable for the open-`cause` rule and
|
|
11703
|
+
the three shapes absence covers.
|
|
11181
11704
|
ApprovalRequestFrame:
|
|
11182
11705
|
type: object
|
|
11183
11706
|
description: >
|
|
@@ -11310,6 +11833,12 @@ components:
|
|
|
11310
11833
|
# 但本 schema 此前只在帧上公示过。长 description 的单一真源在 ToolApprovalFrame 的同名键。
|
|
11311
11834
|
ruleOffersAbsence: { $ref: '#/components/schemas/RuleOffersAbsence' }
|
|
11312
11835
|
denialLimitFallback: { $ref: '#/components/schemas/DenialLimitFallback' }
|
|
11836
|
+
classifierUnavailable:
|
|
11837
|
+
$ref: '#/components/schemas/ClassifierUnavailable'
|
|
11838
|
+
description: >-
|
|
11839
|
+
server >= 7.69.0 (core 7.10.0 #616), ADDITIVE — WHY this ask reached a human. Frame and card carry
|
|
11840
|
+
the SAME value (one narrow-read function server-side). See ClassifierUnavailable for the open-`cause`
|
|
11841
|
+
rule and the three shapes absence covers.
|
|
11313
11842
|
|
|
11314
11843
|
RuleSuggestion:
|
|
11315
11844
|
type: object
|
|
@@ -11333,6 +11862,30 @@ components:
|
|
|
11333
11862
|
command: { type: string, maxLength: 512, description: 'The command pattern inside the rule, for a shell that would rather not re-parse `rule`.' }
|
|
11334
11863
|
|
|
11335
11864
|
# ─── S-139(sdk 8.3.0):对 server 7.60.0 整体重对账带进来的四张型面 ───────────────────────
|
|
11865
|
+
ClassifierUnavailable:
|
|
11866
|
+
# 8.9.0:三面(tool_approval 帧 / ApprovalCard / InboxRow)同形同值 ⇒ 具名单源,内联三份会各自漂。
|
|
11867
|
+
type: object
|
|
11868
|
+
description: >-
|
|
11869
|
+
WHY this ask reached a human (server >= 7.69.0, core 7.10.0 #616): the auto-mode classifier was consulted
|
|
11870
|
+
and could not run. Carried on all THREE faces with the same value — the live `tool_approval` frame, the
|
|
11871
|
+
`ApprovalCard` (`card_json` / `approval_request`), and the `GET /v1/assistant/inbox` row. Under the auto
|
|
11872
|
+
mode most calls are answered by the classifier, so the ones that DO reach a person are often exactly the
|
|
11873
|
+
ones it could not answer — and the causes want different handling (the gateway is broken / it is too slow
|
|
11874
|
+
/ the breaker is OPEN, i.e. every following ask will arrive too). The inbox is the TRIAGE face, which is
|
|
11875
|
+
where `breaker_open` is worth the most: it forecasts an approval storm, not a one-off question.
|
|
11876
|
+
🔴 `cause` is an OPEN string and is deliberately NOT enumerated here. The vocabulary's single owner is the
|
|
11877
|
+
engine (today: `error` / `timeout` / `breaker_open`); mirroring it would swallow a newly minted word as a
|
|
11878
|
+
violation — the same rule as `AskOrigin` and the MCP `errorCode`. Switch on the known words and KEEP A
|
|
11879
|
+
DEFAULT ARM.
|
|
11880
|
+
🔴 ABSENCE IS NOT AN ASSERTION. It covers three shapes at once: the classifier ANSWERED (this ask is one
|
|
11881
|
+
it ruled should go to a person) · this ask was not ELIGIBLE for the classifier · this deployment wired no
|
|
11882
|
+
classifier at all. Never read absence as any claim about classifier health, and never as "the classifier
|
|
11883
|
+
is fine". (`parse_error` does not set this bit, per the engine's contract.)
|
|
11884
|
+
additionalProperties: false
|
|
11885
|
+
required: [cause]
|
|
11886
|
+
properties:
|
|
11887
|
+
cause: { type: string, description: 'The engine''s cause word. OPEN — branch known words, default arm for the rest.' }
|
|
11888
|
+
|
|
11336
11889
|
AskOrigin:
|
|
11337
11890
|
type: string
|
|
11338
11891
|
x-open-enum: true
|
|
@@ -11410,8 +11963,13 @@ components:
|
|
|
11410
11963
|
waits for a person) is for RENDERING THE COUNTDOWN ONLY. Never start a second timer from it: the window
|
|
11411
11964
|
is executed by the ENGINE, and two overlapping windows are worse than the original defect and silent.
|
|
11412
11965
|
🔴 ABSENCE IS NOT AN ASSERTION — the vast majority of asks are not fallback cards.
|
|
11413
|
-
⚠️
|
|
11414
|
-
a
|
|
11966
|
+
⚠️ TWO LEGS CARRY IT, and they differ in ONE member. The live/card leg (`ToolApprovalFrame` /
|
|
11967
|
+
`ApprovalCard` / `card_json`) can carry a NON-ZERO `autoDenyAfterMs`; the DURABLE leg — the parked row,
|
|
11968
|
+
projected onto `InboxRow` since server >= 7.57.0 (core's park twin of the same member) — always carries
|
|
11969
|
+
`0` there, because nothing counts down on a parked lane (see InboxRow.denialLimitFallback).
|
|
11970
|
+
(Corrected 2026-09-10 against core's checkpoint-summary type and the server's inbox projection: this
|
|
11971
|
+
sentence used to read "the durable (parked) leg does NOT carry this key today", which stopped being
|
|
11972
|
+
true when the park twin landed.)
|
|
11415
11973
|
properties:
|
|
11416
11974
|
consecutive: { type: integer, description: 'Consecutive refusals at the moment the limit tripped.' }
|
|
11417
11975
|
total: { type: integer, description: 'Cumulative refusals at the moment the limit tripped.' }
|
|
@@ -12997,12 +13555,18 @@ components:
|
|
|
12997
13555
|
core's assembly self-evidence manifest (core >=5.14.0 `WiringManifest`). TWO HALVES, ONE shape (a
|
|
12998
13556
|
consumer must line the two faces up, so they are not split into separate types) — but which keys
|
|
12999
13557
|
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
|
|
13001
|
-
|
|
13002
|
-
|
|
13558
|
+
are EFFECTIVE-half only (the live `wiring_manifest` event) — as are the four per-leg sections
|
|
13559
|
+
`modelGate`, `autoMode`, `mcp` and `tools`, which describe what THIS leg did and have no static
|
|
13560
|
+
counterpart — and the STATIC half
|
|
13003
13561
|
(GET /v1/diagnostics/wiring) NEVER mints them; every other section, plus `governance`, is always
|
|
13004
13562
|
present on the static half. Fields stay optional because core may add or drop sections and that
|
|
13005
13563
|
must not hard-break a client — read by half, and never wait on a key the half never sends.
|
|
13564
|
+
🔴 EVERY section is declared HERE, each saying which half mints it (sdk 8.9.0). Before that, `tools`
|
|
13565
|
+
was declared while its three effective-half siblings were not, and `permissionRules` — which the
|
|
13566
|
+
engine mints UNCONDITIONALLY, i.e. on BOTH halves — was missing altogether, so a generated client
|
|
13567
|
+
reading GET /v1/diagnostics/wiring could not see it at all. "Two halves, one shape" is the stated
|
|
13568
|
+
doctrine of this schema; omitting a section because one half does not mint it contradicts it and
|
|
13569
|
+
splits consumers into two groups reading two different manifests.
|
|
13006
13570
|
🔴 `governance` and `configFingerprint` appear ONLY on an operator-scoped face. A tenant stream
|
|
13007
13571
|
gets neither — and NOT just the section: core hashes the WHOLE manifest unsalted and governance is
|
|
13008
13572
|
four booleans, so the fingerprint alone would let a tenant brute-force sixteen combinations against
|
|
@@ -13073,6 +13637,85 @@ components:
|
|
|
13073
13637
|
description: >
|
|
13074
13638
|
server >= 7.66.0 (design/388 B-4) — the leg's whole tool roster. EFFECTIVE half only (the diagnostics
|
|
13075
13639
|
endpoint's static half never carries it); absent as a whole when core's typebox check rejects it.
|
|
13640
|
+
permissionRules:
|
|
13641
|
+
type: object
|
|
13642
|
+
description: >
|
|
13643
|
+
core >= 5.18.0 (design/179) / >= 5.23.0 (design/182 §7/§9), server >= 7.6.0 — is a PERSISTED
|
|
13644
|
+
PERMISSION RULE store wired, how far does a "don't ask again" travel, and is an org governance layer
|
|
13645
|
+
armed over that lane. 🔴 BOTH HALVES: the engine mints this section unconditionally, so it rides the
|
|
13646
|
+
static `GET /v1/diagnostics/wiring` face too — read it before offering a "don't ask again"
|
|
13647
|
+
affordance. `false` is a REAL reading ("no rule lane on this worker"), never "too old to know"; only
|
|
13648
|
+
the whole key being absent means "never sent it". TENANT-visible (deliberately outside the
|
|
13649
|
+
operator-only `governance` section). Full per-key contract: see Event_wiring_manifest.permissionRules.
|
|
13650
|
+
properties:
|
|
13651
|
+
storeWired: { type: boolean }
|
|
13652
|
+
syncWired: { type: boolean }
|
|
13653
|
+
orgGoverned: { type: boolean }
|
|
13654
|
+
mcp:
|
|
13655
|
+
type: array
|
|
13656
|
+
description: >
|
|
13657
|
+
core >= 7.5.0 (#562) / >= 7.6.0 (S6-B), server >= 7.60 — one row per MCP server THIS leg declared.
|
|
13658
|
+
EFFECTIVE half only. 🔴 An empty array is NOT absence (`[]` = "this leg declared no servers").
|
|
13659
|
+
Full per-key contract: see Event_wiring_manifest.mcp.
|
|
13660
|
+
items: { $ref: '#/components/schemas/WiringManifestMcpEntry' }
|
|
13661
|
+
modelGate:
|
|
13662
|
+
type: object
|
|
13663
|
+
description: >
|
|
13664
|
+
core >= 7.3.x (design/385 片2b), server >= 7.58 — which tools the tool-MODEL gate removed from THIS
|
|
13665
|
+
session and how to get them back. EFFECTIVE half only; ALL-OR-NOTHING (absent = nothing was gated,
|
|
13666
|
+
never an empty section). Full contract: see Event_wiring_manifest.modelGate.
|
|
13667
|
+
required: [class, removed, restore]
|
|
13668
|
+
properties:
|
|
13669
|
+
class: { type: string }
|
|
13670
|
+
removed: { type: array, items: { type: string } }
|
|
13671
|
+
restore: { type: string }
|
|
13672
|
+
autoMode:
|
|
13673
|
+
type: object
|
|
13674
|
+
description: >
|
|
13675
|
+
core >= 7.3.1 (#529), server >= 7.59 — did AUTO mode arm on THIS leg, and if not which arm it stopped
|
|
13676
|
+
at. EFFECTIVE half only. 🔴 `reason` is core's closed word list passed through VERBATIM (branch known
|
|
13677
|
+
words, keep a default arm); ABSENCE IS NOT "not applicable" — never fold it to `armed: false`. The
|
|
13678
|
+
server projects exactly these two keys (core's `breaker` sub-fact is NOT projected onto the wire).
|
|
13679
|
+
Full contract: see Event_wiring_manifest.autoMode.
|
|
13680
|
+
required: [armed, reason]
|
|
13681
|
+
properties:
|
|
13682
|
+
armed: { type: boolean }
|
|
13683
|
+
reason: { type: string }
|
|
13684
|
+
WiringManifestMcpEntry:
|
|
13685
|
+
# 8.9.0:从 `Event_wiring_manifest.mcp.items` 的内联形**提取**为具名 schema —— 同一份行形现在被
|
|
13686
|
+
# 两处引用(live 帧与 `WiringManifest` 的静态/两半形),内联会立刻变成两份会各自漂的镜像。
|
|
13687
|
+
type: object
|
|
13688
|
+
description: >
|
|
13689
|
+
One row of a leg's MCP wiring manifest: did that declared server connect, if not which failure class,
|
|
13690
|
+
and how many tools it mounted. A row missing `name` or `status` is dropped INDIVIDUALLY — the other
|
|
13691
|
+
servers' rows still ship. 🔴 The engine's `error` free text is DELIBERATELY NOT PROJECTED (remote-author
|
|
13692
|
+
text; core owns the single redaction mint point). The actionable cause is `errorCode`.
|
|
13693
|
+
additionalProperties: false
|
|
13694
|
+
required: [name, status]
|
|
13695
|
+
properties:
|
|
13696
|
+
name: { type: string, description: 'The declared server name.' }
|
|
13697
|
+
status: { type: string, description: 'Three-word engine set (connected / failed / …). Open on read.' }
|
|
13698
|
+
source: { type: string, description: 'The declarer''s own layer label, echoed verbatim by core (single-line, length-capped).' }
|
|
13699
|
+
toolCount: { type: integer, description: 'Tools mounted. ABSENT rather than 0 — a minted 0 would read as "connected with no tools".' }
|
|
13700
|
+
errorCode:
|
|
13701
|
+
type: string
|
|
13702
|
+
description: >
|
|
13703
|
+
core >= 7.6.0 — the `McpFailureKind` word (`connect_refused` / `connection_failed` /
|
|
13704
|
+
`connection_closed` / `http_status` / `not_mcp_response` / `spawn_failed` / `timeout` /
|
|
13705
|
+
`protocol` / `invalid_config` / `unknown`). The retired 7.5.0 spellings — `network` and the
|
|
13706
|
+
`http_<status>` encoding — are GONE; an HTTP status now rides `httpStatus`.
|
|
13707
|
+
Deliberately NOT enumerated here: the vocabulary's single owner is the engine, and mirroring
|
|
13708
|
+
it would swallow a newly minted word as a violation. Switch with a `default` arm.
|
|
13709
|
+
delivered:
|
|
13710
|
+
allOf: [{ $ref: '#/components/schemas/McpDelivered' }]
|
|
13711
|
+
description: 'core >= 7.6.0 (S6-B) — did the request reach that server. Read WITH `errorCode`.'
|
|
13712
|
+
httpStatus:
|
|
13713
|
+
type: integer
|
|
13714
|
+
description: >
|
|
13715
|
+
core >= 7.6.0 (S6-B) — the status the endpoint answered with; rides ONLY with
|
|
13716
|
+
`errorCode === "http_status"` (401/403 = re-authorize, 5xx = that end is down). ABSENT rather
|
|
13717
|
+
than 0.
|
|
13718
|
+
|
|
13076
13719
|
ServerWiringGates:
|
|
13077
13720
|
type: object
|
|
13078
13721
|
description: >
|