@sema-agent/sdk 9.6.0 → 9.7.1
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 +43 -0
- package/dist/errors.d.ts +7 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +7 -1
- package/dist/errors.js.map +1 -1
- package/dist/events.d.ts +33 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/resources/approvals.d.ts +20 -2
- package/dist/resources/approvals.d.ts.map +1 -1
- package/dist/resources/approvals.js.map +1 -1
- package/dist/resources/assistant.d.ts +8 -1
- package/dist/resources/assistant.d.ts.map +1 -1
- package/dist/resources/assistant.js +8 -1
- package/dist/resources/assistant.js.map +1 -1
- package/dist/resources/fleet.d.ts +15 -1
- package/dist/resources/fleet.d.ts.map +1 -1
- package/dist/resources/fleet.js.map +1 -1
- package/dist/resources/ops.d.ts +29 -5
- package/dist/resources/ops.d.ts.map +1 -1
- package/dist/resources/ops.js +12 -1
- package/dist/resources/ops.js.map +1 -1
- package/dist/resources/sessions.d.ts +23 -1
- package/dist/resources/sessions.d.ts.map +1 -1
- package/dist/resources/sessions.js +27 -0
- package/dist/resources/sessions.js.map +1 -1
- package/dist/types.d.ts +434 -5
- package/dist/types.d.ts.map +1 -1
- package/openapi.yaml +670 -28
- package/package.json +1 -1
package/openapi.yaml
CHANGED
|
@@ -2079,6 +2079,77 @@ paths:
|
|
|
2079
2079
|
schema: { $ref: '#/components/schemas/McpStatusPanel' }
|
|
2080
2080
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
2081
2081
|
|
|
2082
|
+
/v1/sessions/{sessionId}/mcp/reconnect:
|
|
2083
|
+
parameters:
|
|
2084
|
+
- $ref: '#/components/parameters/PrincipalHeader'
|
|
2085
|
+
- in: path
|
|
2086
|
+
name: sessionId
|
|
2087
|
+
required: true
|
|
2088
|
+
schema: { type: string }
|
|
2089
|
+
post:
|
|
2090
|
+
tags: [sessions]
|
|
2091
|
+
operationId: sessionsMcpReconnect
|
|
2092
|
+
x-status: live # S-381 (server 7.85.0; core 7.22.0 #857); SDK sessions.mcpReconnect.
|
|
2093
|
+
summary: Re-dial ONE named MCP server inside a live session (a TRANSACTION, not a refresh button).
|
|
2094
|
+
description: >
|
|
2095
|
+
S-381 (server >= 7.85.0; core 7.22.0 #857) — re-dial the transport of ONE NAMED MCP server WITHOUT
|
|
2096
|
+
ending the task. Body is `{ server }`: required, non-empty, at most 190 characters, and the body gate
|
|
2097
|
+
is STRICT — a misspelled key is refused, never stripped-and-ignored, and there is NO all-servers form
|
|
2098
|
+
(re-dialing everything would CLOSE the healthy connections, and a stdio server that owns a device
|
|
2099
|
+
cannot be re-dialed while the old connection still holds it). A bad body ⇒ 400 `request.body_shape`.
|
|
2100
|
+
🔴 A TRANSACTION, NOT A REFRESH BUTTON (core's own words — this sentence belongs in the UI copy): the
|
|
2101
|
+
old connection is closed BEFORE the new dial, so a FAILED re-dial spends a working connection — the
|
|
2102
|
+
server stays disconnected, ITS TOOLS ARE WITHDRAWN from the model's roster, and in-flight calls fail.
|
|
2103
|
+
Offering "reconnect" on a HEALTHY server is offering a transaction.
|
|
2104
|
+
🔴 `outcome` IS THE ONLY DISCRIMINATOR AND ALL THREE STATES ARE HTTP 200 (the status answers only
|
|
2105
|
+
"did the verb reach the engine", the same law as steer's "branch on `delivery`, not the status"):
|
|
2106
|
+
`accepted` = core's `reconnected`; `refused` = core's `refused` OR `not_declared` (an unknown server
|
|
2107
|
+
name is a ROSTER fact, not an engine-capability fact); `unsupported` = THE SERVER'S OWN WORD (core has
|
|
2108
|
+
none) for "this run declared no MCP at all" (core throws `mcp.reconnect_unavailable`) — and that arm is
|
|
2109
|
+
an ALWAYS-FIVE-KEY body `{taskId, sessionId, server, outcome, reason}`: with no core result the server
|
|
2110
|
+
coins NO `prefix`/`toolCount`/`added`/`removed`/`toolNames`. Consumers MUST branch on `outcome` and
|
|
2111
|
+
must NOT read `body.added` unconditionally.
|
|
2112
|
+
🔴 `toolNames` is PRESENT-IFF core supplied `tools` (NOT keyed off `outcome`): `[]` (a failed dial) and
|
|
2113
|
+
ABSENT (an arm that never touched the connection) are two different sentences. It is the roster THIS
|
|
2114
|
+
re-dial asserted for its own `prefix` domain, NOT "what the model finally sees" — core filters again by
|
|
2115
|
+
the deployment's exclusion table before mounting, and a refused axis-fold mounts none, so this key is a
|
|
2116
|
+
SUPERSET in both shapes. For "what actually got mounted" read a roster face.
|
|
2117
|
+
Next action comes from `status` (machine-readable: `errorCode` is core's `McpFailureKind` word — do not
|
|
2118
|
+
narrow it, switch with a default arm; `httpStatus` 401 is a re-authorize door); `reason` is HUMAN TEXT
|
|
2119
|
+
(server-redacted, length-bounded) and must NOT be parsed.
|
|
2120
|
+
Gates, the same family as steer / capture-optout: 501 `capability.run_store_required` (no durable run
|
|
2121
|
+
ledger) · 501 `capability.session_ownership_required` (no `sessionStorage.ownerOf`) · 400
|
|
2122
|
+
`request.path_malformed` (bad percent-encoding in the id) · 404 `not_found.session` — unknown session
|
|
2123
|
+
AND someone else's session answer the SAME code and SAME message (deliberate: no existence oracle); an
|
|
2124
|
+
EXPLICIT operator may re-dial any tenant's session · 422 `steering.invalid_content` · 409
|
|
2125
|
+
`steering.not_running` with FOUR arms behind ONE code (parked awaiting a decision / already terminal /
|
|
2126
|
+
live on ANOTHER replica / no live steerable face here). A re-dial is LEG-LOCAL — a durable resume dials
|
|
2127
|
+
every declared server afresh — so "park a re-dial until resume" is structurally not a shape, and this
|
|
2128
|
+
face deliberately has no park leg. Probe with `capabilities.mcpReconnect`; absent ⇒ probe by 404/405.
|
|
2129
|
+
requestBody:
|
|
2130
|
+
required: true
|
|
2131
|
+
content:
|
|
2132
|
+
application/json:
|
|
2133
|
+
schema:
|
|
2134
|
+
type: object
|
|
2135
|
+
additionalProperties: false
|
|
2136
|
+
required: [server]
|
|
2137
|
+
properties:
|
|
2138
|
+
server: { type: string, minLength: 1, maxLength: 190, description: 'The ONE named MCP server to re-dial (the declared name). Required; there is no all-servers form. The cap is load-bearing: the string is relayed into an operator notice detail.' }
|
|
2139
|
+
responses:
|
|
2140
|
+
'200':
|
|
2141
|
+
description: >-
|
|
2142
|
+
The re-dial receipt. Branch on `outcome`; the `unsupported` arm is an always-five-key body.
|
|
2143
|
+
content:
|
|
2144
|
+
application/json:
|
|
2145
|
+
schema: { $ref: '#/components/schemas/SessionMcpReconnectResult' }
|
|
2146
|
+
'400': { $ref: '#/components/responses/BadRequest' }
|
|
2147
|
+
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
2148
|
+
'404': { $ref: '#/components/responses/NotFound' }
|
|
2149
|
+
'409': { $ref: '#/components/responses/Conflict' }
|
|
2150
|
+
'422': { $ref: '#/components/responses/UnprocessableEntity' }
|
|
2151
|
+
'501': { $ref: '#/components/responses/NotImplemented' }
|
|
2152
|
+
|
|
2082
2153
|
/v1/sessions/{sessionId}/memory-status:
|
|
2083
2154
|
parameters:
|
|
2084
2155
|
- $ref: '#/components/parameters/PrincipalHeader'
|
|
@@ -2578,9 +2649,24 @@ paths:
|
|
|
2578
2649
|
`data.type` — proxy-safe dispatch. Cross-replica by
|
|
2579
2650
|
construction (polls the SHARED checkpoint table). 15-min cap + heartbeats; a DB blip retries, never
|
|
2580
2651
|
kills the stream. Scope = the caller's principal (operator/trace token = fleet-wide).
|
|
2652
|
+
🔴 TWO MORE EVENTS (server >= 7.87.0 / S-454 P1; cli L-397): this leg now has TWO diff sources, not one.
|
|
2653
|
+
`live_pending` = a LIVE (in-stream) ask entered or its wire projection changed (UPSERT semantics;
|
|
2654
|
+
payload = a `LivePendingRow` spread plus `serverNowMs`, AND IT CARRIES `frame`, so a consumer can render
|
|
2655
|
+
the card straight from the push). `live_resolved` = a live ask left (settled / window expired / entry
|
|
2656
|
+
gone — ALL THREE ARE ONE FRAME: "why it left" is for the consumer to fetch, the push does not conclude
|
|
2657
|
+
it; payload = `{type, approvalId, serverNowMs}`). First-tick order: `pending…` → `live_pending…` →
|
|
2658
|
+
`synced` (and `synced.count` still counts ONLY the durable queue).
|
|
2659
|
+
🔴 THE TWO WORDS DELIBERATELY DO NOT REUSE `pending`/`resolved`: those are keyed by
|
|
2660
|
+
`(sessionId, toolCallId)` and decided at `POST /v1/approvals/{sessionId}/decide`, while a live row is
|
|
2661
|
+
keyed by `approvalId` and decided at `POST /v1/tool-approvals/{approvalId}/respond`. Sharing one word
|
|
2662
|
+
would eventually send a live card to the decide door — a 404 with no wire evidence of the mistake.
|
|
2663
|
+
⚠️ Both live words come from an IN-PROCESS window projection, so their cross-replica reach is exactly
|
|
2664
|
+
that of `GET /v1/approvals`'s `livePending` — narrower than the durable pair above. The deployment
|
|
2665
|
+
without a live approval face (coordinator unwired) emits neither, and the stream's bytes are identical
|
|
2666
|
+
to 7.86.0's (capability absent = zero change, not an empty event).
|
|
2581
2667
|
responses:
|
|
2582
2668
|
'200':
|
|
2583
|
-
description: text/event-stream of meta/pending/resolved frames.
|
|
2669
|
+
description: text/event-stream of meta/pending/resolved/live_pending/live_resolved frames.
|
|
2584
2670
|
content:
|
|
2585
2671
|
text/event-stream:
|
|
2586
2672
|
schema: { type: string }
|
|
@@ -2621,9 +2707,15 @@ paths:
|
|
|
2621
2707
|
string; `remember` not `"session"`; `checkpointToken`/`boundCallId`/`boundInputHash` not a string)
|
|
2622
2708
|
· "request.field_conflict" (`remember` without `decision:"approve"`; TC-5.4 answer coherence —
|
|
2623
2709
|
approving a pending AskUserQuestion REQUIRES `answer`, and `answer` is only valid on that approve)
|
|
2624
|
-
· "remember_not_in_proof" / "updated_input_not_in_proof"
|
|
2625
|
-
direct-door worker — not covered by the
|
|
2626
|
-
|
|
2710
|
+
· "remember_not_in_proof" / "updated_input_not_in_proof" / "checkpoint_token_not_in_proof"
|
|
2711
|
+
(`remember` / `updatedInput` / `checkpointToken` on a direct-door worker — not covered by the
|
|
2712
|
+
decision proof; the third one is server >= 7.87.0 / S-452, and its message points at the
|
|
2713
|
+
replacement: bind the decision with `boundCallId` + `boundInputHash`. Refusing it loses nothing —
|
|
2714
|
+
the token was only ever a staleness check, and on the direct door that check is already covered by
|
|
2715
|
+
the proof's binding. A compliant client never sends it: this SDK deleted the field at 1.0.0)
|
|
2716
|
+
· "request.field_invalid" ALSO covers an unrecognised key inside `answer` (server >= 7.87.0 /
|
|
2717
|
+
S-452: the closed answer shape — the message names the key paths and the body carries `unknownKeys`)
|
|
2718
|
+
· "remember_requires_binding" (`remember` without the D-1 binding echo). PARKED variant (server 5.12.0, core 5.7.0 RB-459):
|
|
2627
2719
|
`decide.parked_answer_required` — the parked child's pending action is an AskUserQuestion and the
|
|
2628
2720
|
approve carried no `answer` (the ONLY pre-claim rejection left on that leg; the retired codes
|
|
2629
2721
|
`decide.parked_answer_unsupported` / `decide.parked_question_unsupported` are gone — an answer HAS
|
|
@@ -3015,8 +3107,15 @@ paths:
|
|
|
3015
3107
|
schema: { $ref: '#/components/schemas/AssistantTaskStatus' }
|
|
3016
3108
|
'400':
|
|
3017
3109
|
description: >
|
|
3018
|
-
|
|
3019
|
-
|
|
3110
|
+
`ErrorResponse`-shaped. Codes: "request.body_shape" (bad/missing `decision`, OR an unrecognised
|
|
3111
|
+
top-level key — server >= 7.86.0 / S-438 key closure, the body carries `unknownKeys` /
|
|
3112
|
+
`unsupportedKeys`) · "request.field_conflict" (`edit` without a non-empty `editedPlan`;
|
|
3113
|
+
`editedPlan` present on approve/reject; `permissionModeAfter` sent with `edit`/`reject`;
|
|
3114
|
+
`permissionModeAfter` on a task that was not submitted with `permissionMode:"plan"`) ·
|
|
3115
|
+
"request.field_invalid" (non-string `reason`; `permissionModeAfter` outside the two-word closed
|
|
3116
|
+
set — zero widening arm) · "request.path_malformed" (a bad percent-encoded path segment —
|
|
3117
|
+
server >= 7.86.0 / S-430 turned that family from 500 `internal.error` into a 400 across 16 doors).
|
|
3118
|
+
413 `reason_too_large` rides its own status.
|
|
3020
3119
|
content:
|
|
3021
3120
|
application/json:
|
|
3022
3121
|
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
@@ -5033,11 +5132,16 @@ paths:
|
|
|
5033
5132
|
tags: [metrics]
|
|
5034
5133
|
operationId: metricsSummary
|
|
5035
5134
|
x-status: draft # ops surface; uses a metricsToken (NOT a principal). Shape loose.
|
|
5036
|
-
x-sdk: none # DELIBERATELY not in @sema-agent/sdk (2026-07-27 定性): ops/observability face, credential = metricsToken not a principal; README walks curl. Revocable — delete this key + the gate exemption to bring it in.
|
|
5037
5135
|
summary: Worker health/cost summary (ops).
|
|
5038
5136
|
description: >
|
|
5039
|
-
Uses `metricsToken` (operator), not `x-agent-principal`.
|
|
5040
|
-
|
|
5137
|
+
Uses `metricsToken` (operator) OR a full authToken, not `x-agent-principal`.
|
|
5138
|
+
⚠️ The `x-sdk: none` key that used to sit here ("deliberately not in @sema-agent/sdk", 2026-07-27) was
|
|
5139
|
+
STALE and is deleted at sdk 9.7.0: the SDK has wrapped this face since the 2026-07-17 coverage batch
|
|
5140
|
+
(`client.metrics.summary()`), and since 9.7.0 the two S-451 keys are named on the returned type
|
|
5141
|
+
(`MetricsSummaryOps`). The path stays on the spec-path gate's spec-only exemption list for a DIFFERENT
|
|
5142
|
+
reason, now written there: that gate's SDK-side extractor only collects `/v1/...` literals, so a
|
|
5143
|
+
non-`/v1` path can never match — the exemption compensates for the EXTRACTOR's scope, not for a
|
|
5144
|
+
missing verb.
|
|
5041
5145
|
security:
|
|
5042
5146
|
- metricsToken: []
|
|
5043
5147
|
responses:
|
|
@@ -5045,11 +5149,9 @@ paths:
|
|
|
5045
5149
|
description: Metrics summary (shape draft).
|
|
5046
5150
|
content:
|
|
5047
5151
|
application/json:
|
|
5048
|
-
# 🔴 census 批2
|
|
5049
|
-
#
|
|
5050
|
-
#
|
|
5051
|
-
# untouched (still deliberately outside the SDK's wrapped surface) — closing the SHAPE and
|
|
5052
|
-
# keeping it un-wrapped are independent axes.
|
|
5152
|
+
# 🔴 census 批2 五段把它按真铸点 CLOSED(`{ model, ...deps.metrics.summarize() }` 是定键集,
|
|
5153
|
+
# 不是 ops free-for-all)。sdk 9.7.0 **改开**并补上生产方早已多出来的三键 —— 理由与 receipts
|
|
5154
|
+
# 在 `MetricsSummaryOps` 的 description 里(投影型响应体开集;请求体仍封闭)。
|
|
5053
5155
|
schema: { $ref: '#/components/schemas/MetricsSummaryOps' }
|
|
5054
5156
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
5055
5157
|
|
|
@@ -5316,6 +5418,20 @@ components:
|
|
|
5316
5418
|
waitMs: { type: integer }
|
|
5317
5419
|
decision: { type: string }
|
|
5318
5420
|
toolName: { type: string }
|
|
5421
|
+
toolCallId:
|
|
5422
|
+
type: string
|
|
5423
|
+
description: >-
|
|
5424
|
+
core 7.23.0 (#897), server >= 7.87.0 — the engine's own call id, passed through verbatim.
|
|
5425
|
+
🔴 ABSENT MEANS "NOT JOINABLE", NOT "no call": three of the four mint sites (an inherited
|
|
5426
|
+
grant being reused, the durable-resume leg, the park self-check's synthetic row) may have
|
|
5427
|
+
no call identity to give. 🔴 JOIN BY THIS KEY — never by row count or position: a
|
|
5428
|
+
multi-leg aggregation (verify / cascade / repair-loop) pushes each leg's gates as a
|
|
5429
|
+
BLOCK, so the order is "leg completion order concatenated", and a consumer's own
|
|
5430
|
+
permission ledger legitimately has a DIFFERENT number of rows. A right-hand key existing
|
|
5431
|
+
is not a reason to mint a parallel ledger elsewhere: the engine's is authoritative.
|
|
5432
|
+
⚠️ Since server 7.84.0 (S-394) the model-authored `toolArg` here goes through the SAME
|
|
5433
|
+
redactor as `result`, so credential-shaped BYTES CHANGE (anything else is byte-identical)
|
|
5434
|
+
— a consumer hashing these fields for identity must move off them.
|
|
5319
5435
|
required: [turns, tokens]
|
|
5320
5436
|
# 🔴 OPEN: the engine may carry fields not named here (live-observed, e.g. extra costBreakdown axes) — the SDK
|
|
5321
5437
|
# passes them through rather than dropping, so consumers must not assume this list is exhaustive.
|
|
@@ -5705,6 +5821,33 @@ components:
|
|
|
5705
5821
|
`memory.capture_optout_denied` (terminal). A worker older than 7.70.0 IGNORES the key silently, so
|
|
5706
5822
|
it is not a capability probe. Rides the persisted body onto resume legs.
|
|
5707
5823
|
Mid-run flip: POST /v1/runs/{taskId}/memory/capture-optout (the same record, a second ingress).
|
|
5824
|
+
memory:
|
|
5825
|
+
type: string
|
|
5826
|
+
enum: [off]
|
|
5827
|
+
description: >
|
|
5828
|
+
server >= 7.84.0 (S-405 / core #863 W3; mint `src/http/wire-types.ts:309`) — the REQUEST-level
|
|
5829
|
+
"no memory face at all" declaration: "off" means THIS RUN mounts no memory face (no `# Memory`
|
|
5830
|
+
section, no index, none of the three memory tools, no write admission). A ONE-MEMBER closed set:
|
|
5831
|
+
there is NO "on" spelling, absence is the only way to say "memory as usual".
|
|
5832
|
+
🔴 THREE ORTHOGONAL AXES, do not mix them up: this key = "this RUN mounts no memory face";
|
|
5833
|
+
`memoryCapture:"off"` = "memory stays mounted, but THIS SESSION's content never enters long-term
|
|
5834
|
+
memory"; `memoryWrite:false` = "this run reads memory as usual, the harvest just does not commit".
|
|
5835
|
+
🔴 SENDING BOTH IS NOT BUYING BOTH (contract §12.7, verified against core `prepare-memory.js:48/:55`):
|
|
5836
|
+
core conjoins the capture leg with the engine mount, so on a turn carrying `memory:"off"` the
|
|
5837
|
+
capture opt-out's ONE-WAY session record DOES NOT LAND ⇒ the next turn without this key captures as
|
|
5838
|
+
usual. To make the session permanently uncaptured, do NOT send this key on that turn.
|
|
5839
|
+
⚠️ Honest scope: this is "no memory face", NOT a filesystem ban — if the memory root sits inside the
|
|
5840
|
+
workspace this run was granted, ordinary Read/Write still go through the ordinary fences.
|
|
5841
|
+
⚠️ The FACT IS KEPT, not erased: the server passes it through as `TaskSpec.memory.enabled:false`
|
|
5842
|
+
(it does not drop the spec) ⇒ the terminal observation face reads `disabled`, not `no-spec`.
|
|
5843
|
+
⚠️ Same name, different thing from the legacy `GET /v1/capabilities.memory` bit (always false — the
|
|
5844
|
+
RETIRED MemoryStore verb family). Do not read that bit to decide whether this key is available.
|
|
5845
|
+
🔴 NO CAPABILITY BIT: 7.78.1–7.83.x can trial-by-400 (the spelling gate refuses); older workers
|
|
5846
|
+
cannot be probed (they ignore it silently).
|
|
5847
|
+
🔴 The spelling gate is loud on BOTH legs: "OFF" / a boolean / anything else is a 400
|
|
5848
|
+
`request.field_invalid` (fresh admission and the resume replay share ONE predicate,
|
|
5849
|
+
`singleMemberOffFieldIssue`; on the resume leg it folds to 409 `resume_blocked_by_policy`).
|
|
5850
|
+
Rides the persisted body onto resume legs.
|
|
5708
5851
|
settings:
|
|
5709
5852
|
type: object
|
|
5710
5853
|
description: >
|
|
@@ -6661,9 +6804,106 @@ components:
|
|
|
6661
6804
|
properties:
|
|
6662
6805
|
id: { type: string }
|
|
6663
6806
|
status: { type: string, enum: [running, completed, failed, needs_human] }
|
|
6664
|
-
result:
|
|
6807
|
+
result:
|
|
6808
|
+
allOf: [{ $ref: '#/components/schemas/LeaderRunResult' }]
|
|
6809
|
+
description: >
|
|
6810
|
+
The leader run's terminal payload. sdk 9.7.0 declares the two segments a consumer actually renders
|
|
6811
|
+
(`conflict` / `salvaged`) and leaves the rest open — see `LeaderRunResult`. (The SDK's TS side keeps
|
|
6812
|
+
`LeaderRecord.result: unknown` for one more version: pointing it at the named type is a NARROWING,
|
|
6813
|
+
which RELEASE.md reserves for a major. Cast it: `record.result as LeaderRunResult`.)
|
|
6665
6814
|
error: { type: string }
|
|
6666
6815
|
|
|
6816
|
+
LeaderRunResult:
|
|
6817
|
+
type: object
|
|
6818
|
+
additionalProperties: true
|
|
6819
|
+
description: >
|
|
6820
|
+
The `result` of a leader run (server `leader/leader.ts` `LeaderResult`). Only the segments a consumer
|
|
6821
|
+
really renders are declared; the rest passes through (leader is a gated v2 lane and its result carries
|
|
6822
|
+
server-internal shapes — mirroring all of them here would just create a second copy that drifts).
|
|
6823
|
+
🔴 S-113 / clay C-R63 ④ (server >= 7.83.0): A MERGE THAT DOES NOT APPLY IS HANDED TO THE USER (the CC
|
|
6824
|
+
shape). The apply conflict no longer starts a conflict-resolver model: `ok:false` plus the `conflict`
|
|
6825
|
+
segment, and the run's status is the EXISTING third terminal `needs_human` (no new status word). The
|
|
6826
|
+
user takes `workers[].branch` + `files` and merges/cherry-picks. SAME BATCH, DELETED WITH ZERO ALIASES:
|
|
6827
|
+
`merge.conflictsResolved`, `conflictResolverCostUsd`, and the `merge-conflict` member of
|
|
6828
|
+
`replan.trigger` — a consumer that read them goes tsc-red, and that IS the notice.
|
|
6829
|
+
properties:
|
|
6830
|
+
ok: { type: boolean, description: 'Did the whole wave succeed (always false on a conflict terminal).' }
|
|
6831
|
+
conflict:
|
|
6832
|
+
type: object
|
|
6833
|
+
additionalProperties: true
|
|
6834
|
+
description: >
|
|
6835
|
+
The wave that would not merge (server >= 7.83.0; ONE mint — `conflictSegment` — shared by the main
|
|
6836
|
+
path and the collapse path, so "the same situation, two different terminals" is structurally gone).
|
|
6837
|
+
ABSENT = no conflict on this wave, OR an older server — the two look the same, so do not infer a
|
|
6838
|
+
version from absence.
|
|
6839
|
+
required: [baseSha, files, workers]
|
|
6840
|
+
properties:
|
|
6841
|
+
baseSha: { type: string, description: "The user's merge base = this wave's durable base." }
|
|
6842
|
+
files: { type: array, items: { type: string }, description: 'Paths carrying conflict markers / .rej (CONTENTS NOT INCLUDED).' }
|
|
6843
|
+
filesTruncated:
|
|
6844
|
+
type: boolean
|
|
6845
|
+
enum: [true]
|
|
6846
|
+
description: >-
|
|
6847
|
+
Present only when the list hit the CAP. 🔴 ABSENCE DOES NOT MEAN "this is all of it" (corrected
|
|
6848
|
+
at sdk 9.7.0 after a codex finding was verified against the server's own probe arm in
|
|
6849
|
+
`leader/merge.ts`): this flag reflects the CAP and nothing else. The list is assembled by two
|
|
6850
|
+
shell probes (`grep -rIl` for conflict markers + `find -name '*.rej'`), and when either leg
|
|
6851
|
+
fails the server honestly returns a possibly EMPTY or partial `files` while logging one line
|
|
6852
|
+
(`merge_conflict_files_probe_failed`) — there is NO discriminator for that shape on the wire.
|
|
6853
|
+
⇒ A `conflict` segment with an empty (or implausibly short) `files` must NOT be rendered as
|
|
6854
|
+
"no conflicting files"; render "the list may be incomplete" and point the user at `rejHead`
|
|
6855
|
+
and `workers[].branch`. A bit that separates "probe failed" from "there really are no more"
|
|
6856
|
+
would have to come from the server; this SDK does not mint a second judge for it.
|
|
6857
|
+
workers:
|
|
6858
|
+
type: array
|
|
6859
|
+
description: 'EVERY completed worker. `applied:false` = that worker''s patch is not in the tree (the conflicting one, or one queued behind it — apply is sequential and stops at the first failure). The branch comes from the provisioning registry, so it always has a value: the user checks it out.'
|
|
6860
|
+
items:
|
|
6861
|
+
type: object
|
|
6862
|
+
additionalProperties: true
|
|
6863
|
+
required: [workerId, branch, applied]
|
|
6864
|
+
properties:
|
|
6865
|
+
workerId: { type: string }
|
|
6866
|
+
branch: { type: string }
|
|
6867
|
+
applied: { type: boolean }
|
|
6868
|
+
rejHead: { type: string, description: 'Head of the .rej (through the server''s existing artifact redaction, <= 2 KiB) — FOR A HUMAN TO LOOK AT, not to parse.' }
|
|
6869
|
+
salvaged:
|
|
6870
|
+
type: array
|
|
6871
|
+
description: >
|
|
6872
|
+
Patches of the workers that did not merge (best-effort; present only when non-empty). 🔴 Since
|
|
6873
|
+
7.83.0 a CONFLICT terminal carries EVERY worker's patch (it used to carry only the unfinished ones):
|
|
6874
|
+
the integration sandbox is destroyed in a `finally`, so the branches are not guaranteed to still be
|
|
6875
|
+
on a remote — the patch itself is the deliverable, and the consumer should give the user a way to
|
|
6876
|
+
save it. ⚠️ `patch` is WORKER-AUTHORED CODE (untrusted text): render it, save it, never feed it back
|
|
6877
|
+
to a model.
|
|
6878
|
+
items:
|
|
6879
|
+
type: object
|
|
6880
|
+
additionalProperties: true
|
|
6881
|
+
required: [workerId, sessionId, patch]
|
|
6882
|
+
properties:
|
|
6883
|
+
workerId: { type: string }
|
|
6884
|
+
sessionId: { type: string }
|
|
6885
|
+
patch: { type: string }
|
|
6886
|
+
suspended:
|
|
6887
|
+
type: array
|
|
6888
|
+
description: 'Resume handles of suspended workers (C4) — their checkpoints and paused envs are intact.'
|
|
6889
|
+
items:
|
|
6890
|
+
type: object
|
|
6891
|
+
additionalProperties: true
|
|
6892
|
+
required: [workerId, sessionId]
|
|
6893
|
+
properties:
|
|
6894
|
+
workerId: { type: string }
|
|
6895
|
+
sessionId: { type: string }
|
|
6896
|
+
route: { type: string, description: 'single = a one-subtask plan; fanout = N.' }
|
|
6897
|
+
error: { type: string, description: 'Planning/provisioning failed before fan-out.' }
|
|
6898
|
+
replan:
|
|
6899
|
+
type: object
|
|
6900
|
+
additionalProperties: true
|
|
6901
|
+
description: 'Replan-lite: which trigger won arbitration and what was done. 🔴 The closed set LOST `merge-conflict` in 7.83.0 (S-113, hard breaking, zero aliases): a merge that does not apply is no longer replanned, it is handed to the user.'
|
|
6902
|
+
properties:
|
|
6903
|
+
trigger: { type: string, description: 'suspended | budget | verify-fail | worker-failed.' }
|
|
6904
|
+
redispatched: { type: array, items: { type: string } }
|
|
6905
|
+
collapsed: { type: string }
|
|
6906
|
+
|
|
6667
6907
|
ApprovalDecision:
|
|
6668
6908
|
type: object
|
|
6669
6909
|
description: HITL decision (`POST /v1/approvals/:sessionId/decide`).
|
|
@@ -6675,12 +6915,23 @@ components:
|
|
|
6675
6915
|
AskUserQuestion answer — TRUE WIRE SHAPE (worker-validated, 400 otherwise; found in prod
|
|
6676
6916
|
integration, service-pinned): {answers:[{header, selected:string[], note?}]} where header matches
|
|
6677
6917
|
the question's header. Omit for a plain F4 approve/deny.
|
|
6918
|
+
🔴 CLOSED SHAPE (server >= 7.87.0 / S-452): an unrecognised key ANYWHERE in this object (including
|
|
6919
|
+
inside `answers[]`) is REFUSED — 400 `request.field_invalid`, naming the KEY PATHS in the message
|
|
6920
|
+
and in the `unknownKeys` machine table. Refused rather than dropped, because a misspelled
|
|
6921
|
+
`selected` would silently drop part of the operator's own typed answer with a 200 on top (#157
|
|
6922
|
+
family). Accepted: `answers`, `answers[].header`, `answers[].selected`, `answers[].note`.
|
|
6923
|
+
⚠️ THE TWO LEGS DIFFER IN STRICTNESS and the live one is not the contract: on this DURABLE leg
|
|
6924
|
+
`answers[]` must be non-empty and each `selected` non-empty (an empty array is a 400), while the
|
|
6925
|
+
LIVE leg (`POST /v1/questions/{id}/respond`) only checks that it is an array (an empty one has a
|
|
6926
|
+
headless-default meaning there).
|
|
6678
6927
|
type: object
|
|
6928
|
+
additionalProperties: false
|
|
6679
6929
|
properties:
|
|
6680
6930
|
answers:
|
|
6681
6931
|
type: array
|
|
6682
6932
|
items:
|
|
6683
6933
|
type: object
|
|
6934
|
+
additionalProperties: false
|
|
6684
6935
|
required: [header, selected]
|
|
6685
6936
|
properties:
|
|
6686
6937
|
header: { type: string }
|
|
@@ -6976,6 +7227,28 @@ components:
|
|
|
6976
7227
|
mcpElicitation: { type: boolean, description: "MCP elicitation round-trips are supported." }
|
|
6977
7228
|
askUserQuestion: { type: boolean, description: "AskUserQuestion gate is available (durable when approvals are)." }
|
|
6978
7229
|
toolApproval: { type: boolean, description: "Tool-approval gating is active." }
|
|
7230
|
+
approvalsStreamLive:
|
|
7231
|
+
type: boolean
|
|
7232
|
+
description: >
|
|
7233
|
+
server >= 7.87.1 (S-455, the consumption half of S-454; mint `routes/capabilities.ts:667` =
|
|
7234
|
+
`projectApprovalsStreamLiveCapability(deps)`, predicate's sole owner `capabilities/approvals-push.ts`)
|
|
7235
|
+
— whether this deployment's approvals push face carries the LIVE event family. TRUE ⟺
|
|
7236
|
+
`GET /v1/approvals/stream` carries `live_pending` / `live_resolved` in addition to the durable
|
|
7237
|
+
park rows (same read function as `GET /v1/approvals`'s `livePending`). FACILITY DEPENDENCY, TWO
|
|
7238
|
+
conjuncts, each against its own leg's own presence predicate: ① the approvals foundation (same as
|
|
7239
|
+
the `approvals` bit; `checkpointStore` — absent ⇒ the whole `/v1/approvals/*` domain 404s, there
|
|
7240
|
+
is no stream at all) AND ② the live coordinator (same as the `toolApproval` bit;
|
|
7241
|
+
`TOOL_APPROVAL_ENABLED` — absent ⇒ the stream exists but that source is entirely absent, bytes
|
|
7242
|
+
identical to pre-S-454).
|
|
7243
|
+
🔴 DELIBERATELY NOT FOLDED into `approvals` or `toolApproval`: `approvals` true with this bit false
|
|
7244
|
+
is the COMMON case (tool-approval not enabled); `toolApproval` true with no checkpoint foundation
|
|
7245
|
+
means the stream DOES NOT EXIST AT ALL — reading either bit alone would lie to half the fleet.
|
|
7246
|
+
⚠️ True only promises the SOURCE IS PRESENT: not that any ask is pending right now, and not
|
|
7247
|
+
cross-replica visibility (the live half is an IN-PROCESS PROJECTION, the same limit as
|
|
7248
|
+
`livePending`; the durable park rows are the cross-replica half).
|
|
7249
|
+
⚠️ ABSENT = an older server (<7.87.1) ⇒ cannot tell whether the push face carries the live family;
|
|
7250
|
+
do NOT read "no `live_pending` frame arrived yet" as "this deployment never pushes live" — the two
|
|
7251
|
+
are indistinguishable on an older server. Fall back to `GET /v1/approvals`'s `livePending` pull face.
|
|
6979
7252
|
streamApproval:
|
|
6980
7253
|
type: boolean
|
|
6981
7254
|
description: >
|
|
@@ -7328,6 +7601,66 @@ components:
|
|
|
7328
7601
|
`WriteProtectionCapability`). `null` = this process cannot say; an ABSENT key means an OLDER worker,
|
|
7329
7602
|
and "no key" is NOT "no table" — an absent seat on the engine side is precisely the DEFAULT TABLE
|
|
7330
7603
|
BEING IN PLACE.
|
|
7604
|
+
webSearch:
|
|
7605
|
+
type: object
|
|
7606
|
+
additionalProperties: true
|
|
7607
|
+
description: >
|
|
7608
|
+
server >= 7.82.1 (S-382; mint `routes/capabilities.ts:236` =
|
|
7609
|
+
`projectWebSearchCapability(deps.webSearchProvider)`) — THIS DEPLOYMENT'S DEFAULT WEBSEARCH BACKEND.
|
|
7610
|
+
The closed set is TYPED FROM the server's `WEB_SEARCH_PROVIDERS` tuple (`plugins/web-search.ts:70`,
|
|
7611
|
+
the repo's single provider word table) plus `"none"`; the predicate is the very
|
|
7612
|
+
`webSearchConfigFromEnv()` product boot USED TO BUILD the backend, so "the bit says brave while
|
|
7613
|
+
tavily is wired" is structurally impossible.
|
|
7614
|
+
🔴 `backend:"none"` AND AN ABSENT KEY ARE OPPOSITE DISPOSITIONS — never fold them: `"none"` = this
|
|
7615
|
+
box has NO search backend (render "unavailable"); ABSENT = an older worker (< 7.82.1) ⇒ render
|
|
7616
|
+
"cannot tell". Reading absence as `"none"` renders a correctly-configured older deployment as
|
|
7617
|
+
having no search.
|
|
7618
|
+
🔴 NO INTERNALS: the endpoint (a SearXNG instance URL), the API key and the quota never ride this
|
|
7619
|
+
wire, and the per-request `settings.webSearch` override is deliberately NOT echoed — this bit
|
|
7620
|
+
advertises the DEPLOYMENT DEFAULT and must not drift with the caller.
|
|
7621
|
+
required: [backend]
|
|
7622
|
+
properties:
|
|
7623
|
+
backend: { type: string, description: 'brave | tavily | searxng | none — closed set typed from the server''s provider tuple; unknown words are a newer server, not an error.' }
|
|
7624
|
+
mcpReconnect:
|
|
7625
|
+
type: boolean
|
|
7626
|
+
description: >
|
|
7627
|
+
server >= 7.85.0 (S-381 / core 7.22.0 #857; mint `routes/capabilities.ts:264` =
|
|
7628
|
+
`Boolean(deps.runStore) && Boolean(deps.sessionStorage?.ownerOf)`) — presence of
|
|
7629
|
+
`POST /v1/sessions/{sessionId}/mcp/reconnect`. FACILITY DEPENDENCY, two conjuncts: a durable run
|
|
7630
|
+
ledger (the verb reaches the live stream through the run row) AND the session-ownership face (the
|
|
7631
|
+
inbound owner gate is fail-closed; without it the whole door is 501). Both 501s derive from the SAME
|
|
7632
|
+
deps as this bit, so "says yes but 501s" is structurally impossible.
|
|
7633
|
+
🔴 True only promises the ENDPOINT IS THERE: not that there is a live run right now (otherwise 409
|
|
7634
|
+
`steering.not_running`), and not that this run declared any MCP server at all (undeclared ⇒ HTTP 200
|
|
7635
|
+
with `outcome:"unsupported"`, which is not an error). ABSENT = an older worker ⇒ probe by 404/405,
|
|
7636
|
+
do not render a dead affordance.
|
|
7637
|
+
executionLane:
|
|
7638
|
+
type: object
|
|
7639
|
+
additionalProperties: true
|
|
7640
|
+
description: >
|
|
7641
|
+
server >= 7.86.0 (S-426; mint `routes/capabilities.ts:602` =
|
|
7642
|
+
`projectExecutionLaneCapability(deps.config)`) — THE EXECUTION LANE, SELF-REPORTED. Before it, "do
|
|
7643
|
+
this engine's tools run on THIS machine" could only be guessed from a three-way circumstantial
|
|
7644
|
+
conjunction (shell spawned it AND no remote URL AND `REMOTE_EXEC` in {unset, host}) — none of which
|
|
7645
|
+
the engine said, and which drifts the moment the shell talks to a remote engine (it reads ITS OWN
|
|
7646
|
+
box's env).
|
|
7647
|
+
`provider` is typed from the server's `RemoteExecProvider` (`execution-lane-caps.ts:50`, derived
|
|
7648
|
+
from the `ServiceConfigFlat["remoteExec"]` discriminated union); `REMOTE_EXEC` UNSET ⇒ `"host"` is
|
|
7649
|
+
CONTRACT, not a fallback. Honest cost: the server internally has a finer word (`in-process` — that
|
|
7650
|
+
lane mounts NO hands at all) which this bit folds into `host`, so a consumer cannot tell "hands on
|
|
7651
|
+
this box" from "no hands at all"; that is a DIFFERENT AXIS, not a reason to re-point the lane word.
|
|
7652
|
+
`toolsOnThisHost` is owned by `task-cwd.ts`'s `toolsRunOnThisHost` — the SAME FUNCTION that decides
|
|
7653
|
+
whether a self-scanned skill gets a `baseDir`.
|
|
7654
|
+
🔴 THE IMPLICATION IS ONE-WAY: `toolsOnThisHost:false` ⇒ `skills[].baseDir` is never sent (that is
|
|
7655
|
+
the promise); the converse does NOT hold — `true` is only necessary (a flat `skills/<name>.md` has
|
|
7656
|
+
no directory of its own; plugin-sourced skills go through a loader that never passes the switch),
|
|
7657
|
+
so under `true` read `baseDir` as PRESENT-IFF.
|
|
7658
|
+
🔴 NO INTERNALS: k8s apiUrl / ssh host / adb serial / image / credentials / mountPath never ride here.
|
|
7659
|
+
An ABSENT key must NOT be read as either side — that is exactly the state this bit was minted to end.
|
|
7660
|
+
required: [provider, toolsOnThisHost]
|
|
7661
|
+
properties:
|
|
7662
|
+
provider: { type: string, description: 'host | e2b | k8s | ssh | adb | local-docker | device — typed from the server''s RemoteExecProvider; `REMOTE_EXEC` unset ⇒ `host`.' }
|
|
7663
|
+
toolsOnThisHost: { type: boolean, description: 'The tools run on the process that answered this request (server-absolute paths mean something to the model''s tools).' }
|
|
7331
7664
|
WriteProtectionCapability:
|
|
7332
7665
|
type: object
|
|
7333
7666
|
additionalProperties: false
|
|
@@ -7671,16 +8004,24 @@ components:
|
|
|
7671
8004
|
MetricsSummaryOps:
|
|
7672
8005
|
type: object
|
|
7673
8006
|
description: >
|
|
7674
|
-
200 body of GET /metrics/summary — `{ model, ...MetricsSummary }` (server
|
|
7675
|
-
observability/metrics.ts `MetricsSummary`
|
|
7676
|
-
|
|
7677
|
-
|
|
8007
|
+
200 body of GET /metrics/summary — `{ model, ...MetricsSummary }` (server `boot/stage-10-http-server`
|
|
8008
|
+
+ `observability/metrics.ts` `MetricsSummary` / `summarize()`; every key unconditional).
|
|
8009
|
+
🔴 OPEN as of sdk 9.7.0, and that is a RULING with a receipt: this schema was closed at the 2026-07-30
|
|
8010
|
+
census and by 7.87.0 the producer had moved THREE keys ahead of it (`limitCeilingAfterAnswer` since
|
|
8011
|
+
#677, `httpErrorsByClass` / `httpErrorsByRoute` since S-451) — i.e. a REAL summary body from a current
|
|
8012
|
+
server failed this schema outright, which is the same disease `LivePendingRow` shipped with. A
|
|
8013
|
+
RESPONSE PROJECTION is therefore declared key-by-key AND left open (the producer ships independently
|
|
8014
|
+
and adds keys additively between SDK releases); REQUEST bodies stay closed, because there the enforcer
|
|
8015
|
+
is the server's own strict gate.
|
|
8016
|
+
⚠️ The SDK DOES wrap this face (`client.metrics.summary()` → `MetricsSummaryOps`, since the
|
|
8017
|
+
2026-07-17 coverage batch). The old `x-sdk: none` key on the path said otherwise and was simply stale;
|
|
8018
|
+
it is gone. What remains true is the CREDENTIAL: a `metricsToken` (or a full authToken), not a principal.
|
|
7678
8019
|
required:
|
|
7679
8020
|
[model, runsActive, tasks, tokensTotal, costUsd, costUsdByModel, costUnknown, unpricedCalls,
|
|
7680
8021
|
taskDurationAvgSec, brainFirstTokenAvgMs, brainCallAvgMs, cacheHitRateAvg, promptCacheLowHit,
|
|
7681
8022
|
rateLimited, costQuotaRejected, budgetExceeded, degraded, degenerate, memoryConsolidationOps,
|
|
7682
8023
|
planCacheRecurrence, cascade, verifications, councilRuns, toolErrors]
|
|
7683
|
-
additionalProperties:
|
|
8024
|
+
additionalProperties: true
|
|
7684
8025
|
properties:
|
|
7685
8026
|
model: { type: string }
|
|
7686
8027
|
runsActive: { type: integer }
|
|
@@ -7715,6 +8056,33 @@ components:
|
|
|
7715
8056
|
verifications: { type: object, additionalProperties: { type: integer } }
|
|
7716
8057
|
councilRuns: { type: object, additionalProperties: { type: integer } }
|
|
7717
8058
|
toolErrors: { type: object, additionalProperties: { type: integer } }
|
|
8059
|
+
limitCeilingAfterAnswer:
|
|
8060
|
+
type: object
|
|
8061
|
+
additionalProperties: { type: integer }
|
|
8062
|
+
description: >-
|
|
8063
|
+
server #677 — answer-time ceiling hits (walltime/turns) by origin. ⚠️ A DIFFERENT DENOMINATOR from
|
|
8064
|
+
`budgetExceeded`: this one is minted on the deps-level notice seat and covers EVERY engine run
|
|
8065
|
+
(background children, delegated nested legs, the leader lane), while `budgetExceeded` is only
|
|
8066
|
+
incremented by the HTTP submit leg's `finalizeTaskResult`. Adding the two is only meaningful inside
|
|
8067
|
+
that narrower total.
|
|
8068
|
+
httpErrorsByClass:
|
|
8069
|
+
type: object
|
|
8070
|
+
additionalProperties: { type: integer }
|
|
8071
|
+
description: >-
|
|
8072
|
+
S-451 (server >= 7.87.0) — HTTP FAILURES in the window, aggregated by status CLASS. The keys are
|
|
8073
|
+
only `"4xx"` / `"5xx"` (2xx/3xx never enter). ⚠️ `"4xx"` INCLUDES `499` (the server's label for
|
|
8074
|
+
"client disconnected before the response finished", which is normal on SSE long-connections) — to
|
|
8075
|
+
look at a real storm, read `httpErrorsByRoute` alongside it. An EMPTY OBJECT means zero failures in
|
|
8076
|
+
the window (the key is always present; absence is NOT how zero is spelled). The whole key absent =
|
|
8077
|
+
an older server.
|
|
8078
|
+
httpErrorsByRoute:
|
|
8079
|
+
type: object
|
|
8080
|
+
additionalProperties: { type: integer }
|
|
8081
|
+
description: >-
|
|
8082
|
+
S-451 (server >= 7.87.0) — the TOP 10 route labels by failures (4xx+5xx combined), descending. The
|
|
8083
|
+
label is the low-cardinality form (ids folded to `:id`; anything off the route table lands on
|
|
8084
|
+
`"other"`). This is the "where did those 180 404s actually land" cell. Empty object = zero failures;
|
|
8085
|
+
key absent = an older server.
|
|
7718
8086
|
|
|
7719
8087
|
ModelInfo:
|
|
7720
8088
|
type: object
|
|
@@ -8528,7 +8896,18 @@ components:
|
|
|
8528
8896
|
elapsedMs: { type: integer }
|
|
8529
8897
|
tokens: { type: integer }
|
|
8530
8898
|
# tokenDir REMOVED (engine ruling 2026-06-28): dead field, 0 producer / 0 consumer — spec caught up 2026-07-26.
|
|
8531
|
-
queuedCount:
|
|
8899
|
+
queuedCount:
|
|
8900
|
+
type: integer
|
|
8901
|
+
description: >-
|
|
8902
|
+
🔴 PHANTOM KEY — NO server version ever emits it (verified against the published 7.87.0 bytes:
|
|
8903
|
+
the row type declares it and the fleet route's per-key table forwards it, but NO producer writes it
|
|
8904
|
+
— not `publishTask`'s call sites, not core's bg-sink tick, not the durable reconcile leg — and the
|
|
8905
|
+
wire contract does not mention it either). The server deleted it as a DEAD KEY (S-459, zero
|
|
8906
|
+
behavioural impact since `!== undefined` was always false). It stays declared here only because
|
|
8907
|
+
removing an exported field is BREAKING = major-only; it is registered with its deletion condition
|
|
8908
|
+
in `test/_keyset-divergence.ts` (`FLEET_TASK_ROW_PHANTOM_KEYS`) and goes with the next major.
|
|
8909
|
+
DO NOT read it as "queue depth is already reported" — a permanently absent optional key is a FALSE
|
|
8910
|
+
LEAD. Real queue depth needs a NEW key WITH a producer, not this one resurrected.
|
|
8532
8911
|
awaitingPlanApproval: { type: boolean }
|
|
8533
8912
|
currentAction: { type: string, description: "The child's most recent tool intent as one human-readable line (UNTRUSTED argument head, redacted server-side)." }
|
|
8534
8913
|
currentTool:
|
|
@@ -8883,9 +9262,21 @@ components:
|
|
|
8883
9262
|
description: >-
|
|
8884
9263
|
A live (in-stream) pending ask inside its streamApproval window (#315, server ≥7.43). Decide it via
|
|
8885
9264
|
POST /v1/tool-approvals/{approvalId}/respond — NOT the /decide route (that is the durable rows' door).
|
|
8886
|
-
|
|
8887
|
-
|
|
8888
|
-
|
|
9265
|
+
Optional flags are only-if-true (absent is NEVER encoded as false).
|
|
9266
|
+
🔴 THE "tool input/args are NOT listed" NARROWING IS GONE (server >= 7.87.0 / S-454 P1). The old reason
|
|
9267
|
+
was "details ride the stream frame" — but a SUSPENDED ask never has a stream frame (at mint time it had
|
|
9268
|
+
no delivery target at all, so it suspends for a 1h window), and for it that reason is structurally
|
|
9269
|
+
false: without the card body on this row a consumer can list the ask and still not render it. The row
|
|
9270
|
+
therefore carries `frame` — the very card frame that ask WOULD have emitted — for EVERY live row (the
|
|
9271
|
+
rule set does not fork on `suspended`: an ask that does have a live stream carrying one extra copy of
|
|
9272
|
+
its own card is harmless).
|
|
9273
|
+
🔴 THIS SCHEMA IS OPEN (`additionalProperties: true`) as of sdk 9.7.0, and that is a RULING, not a slip:
|
|
9274
|
+
it was closed, and when 7.87.0 minted `frame` every strict consumer stopped accepting a perfectly legal
|
|
9275
|
+
row — the whole `livePending` array became unreadable rather than "one field short". A producer that
|
|
9276
|
+
ships independently of this SDK adds keys additively between SDK releases, so a RESPONSE projection is
|
|
9277
|
+
declared key-by-key AND left open; the REQUEST bodies stay closed (there the enforcer is the server's
|
|
9278
|
+
own strict gate, so closing is description, not guesswork).
|
|
9279
|
+
additionalProperties: true
|
|
8889
9280
|
required: [approvalId, toolName, ts, expiresAtMs]
|
|
8890
9281
|
properties:
|
|
8891
9282
|
approvalId: { type: string, description: 'The SAME id the respond endpoint takes (wire uuidv7).' }
|
|
@@ -8897,6 +9288,20 @@ components:
|
|
|
8897
9288
|
governanceForced: { type: boolean, enum: [true] }
|
|
8898
9289
|
fromSubagent: { type: boolean, enum: [true], description: 'The ask originates from a delegated subagent.' }
|
|
8899
9290
|
originTaskId: { type: string, description: 'S-52 C2 (server ≥7.52, A-075.87): the delegated child taskId (= the child''s core runSourceTaskId, uuid) — correlates this live ask to the child run row. Minted by the SAME conditional spread as `fromSubagent`: the two keys are always both present or both absent (三键恒等亲证在案).' }
|
|
9291
|
+
contentKind: { type: string, description: 'S-62 (server >= 7.6x): content-question kind — `content_ask` when the gated tool is AskUserQuestion (the SAME word as the two durable read faces and core''s summarize face). Absent = an ordinary tool ask. Display/triage only; it NEVER takes part in decision routing.' }
|
|
9292
|
+
frame:
|
|
9293
|
+
allOf: [{ $ref: '#/components/schemas/ToolApprovalFrame' }]
|
|
9294
|
+
description: >-
|
|
9295
|
+
server >= 7.87.0 (S-454 P1; cli L-397) — THE CARD FRAME THIS ASK WOULD HAVE EMITTED. It is the SAME
|
|
9296
|
+
OBJECT as the server's `PendingApproval.frame` (the one construction site in `askBroadcast`, already
|
|
9297
|
+
redacted / byte-capped / window-keys filled), shared by the suspended and the reachable path — so
|
|
9298
|
+
this is NOT a second card projection computed for the list, and there is no second redaction surface.
|
|
9299
|
+
🔴 INVARIANT: `frame.approvalId === row.approvalId` (verified at 7.87.0's merge review). When one ask
|
|
9300
|
+
was registered as pending more than once and the FIRST registration was cancelled, the frame's id
|
|
9301
|
+
used to point at the dead registration (responding by it 404s); the server now hands out a shallow
|
|
9302
|
+
copy with the id aligned to THIS row whenever the two differ. Always decide by the row's id.
|
|
9303
|
+
🔴 ABSENT = an older server (<= 7.86.0); from 7.87.0 it is ALWAYS present (a required slot on the
|
|
9304
|
+
entry) and is never minted as `null`.
|
|
8900
9305
|
|
|
8901
9306
|
ApprovalStaleCurrentPending:
|
|
8902
9307
|
type: object
|
|
@@ -9502,12 +9907,38 @@ components:
|
|
|
9502
9907
|
description: >
|
|
9503
9908
|
ASSISTANT-WIRE-CONTRACT §4c — `POST /v1/assistant/tasks/:id/plan_review` body (3-state). `editedPlan` is
|
|
9504
9909
|
REQUIRED iff `decision==="edit"` (FORBIDDEN otherwise → 400).
|
|
9910
|
+
🔴 THE BODY KEY SET IS CLOSED (server >= 7.86.0 / S-438): an unrecognised top-level key is refused with
|
|
9911
|
+
400 `request.body_shape` plus the `unknownKeys` / `unsupportedKeys` machine tables — refused rather
|
|
9912
|
+
than dropped, because a misspelled `permissionModeAfter` would otherwise silently leave the task
|
|
9913
|
+
read-only after its plan was approved.
|
|
9505
9914
|
required: [decision]
|
|
9506
9915
|
additionalProperties: false
|
|
9507
9916
|
properties:
|
|
9508
9917
|
decision: { type: string, enum: [approve, edit, reject] }
|
|
9509
9918
|
editedPlan: { type: string, description: 'the operator''s revised plan — required iff decision==="edit".' }
|
|
9510
9919
|
reason: { type: string }
|
|
9920
|
+
permissionModeAfter:
|
|
9921
|
+
type: string
|
|
9922
|
+
enum: [default, acceptEdits]
|
|
9923
|
+
description: >
|
|
9924
|
+
server >= 7.86.0 (S-433, P1; mints `routes/approvals-assistant.ts:1021/1024/1148`) — WHICH
|
|
9925
|
+
PERMISSION MODE THIS TASK CONTINUES IN once the plan is approved. Before this key, approving a plan
|
|
9926
|
+
was a BLANK CHEQUE: the plan was approved while the read-only clamp stayed on and every write kept
|
|
9927
|
+
asking. `acceptEdits` = writes stop asking; `default` = keep asking (AND is the folded value when
|
|
9928
|
+
the key is absent, so an older shell executes plans unchanged).
|
|
9929
|
+
🔴 THREE 400s, each with its own code: a value outside the two-word closed set ⇒
|
|
9930
|
+
`request.field_invalid` (ZERO widening arm — this bit decides whether the model has hands after
|
|
9931
|
+
approval, so folding a misspelling into either mode is a silent downgrade on the security axis);
|
|
9932
|
+
sending it with `edit`/`reject` ⇒ `request.field_conflict` (core: those two send the plan back to be
|
|
9933
|
+
redone and the read-only clamp stays — dropping it there would let the caller believe the mode was
|
|
9934
|
+
settled); sending it for a task that was NOT submitted with `permissionMode:"plan"` ⇒
|
|
9935
|
+
`request.field_conflict` (approving the plan lifts no read-only restriction there).
|
|
9936
|
+
⚠️ DIRECT-DOOR deployments: this key rides the HMAC envelope's TRAILING EXTENSION OBJECT (server
|
|
9937
|
+
7.87.0: `{answer?, permissionModeAfter?}`, present keys in lexicographic order; 7.86.0's bare
|
|
9938
|
+
trailing slot is DELETED with zero double-read) ⇒ a signer must sign the new shape, and mixed
|
|
9939
|
+
versions are 401 in both directions.
|
|
9940
|
+
⚠️ The key name is final: `permissionModeAfter` (the build brief once said `nextPermissionMode`,
|
|
9941
|
+
which never shipped — zero aliases).
|
|
9511
9942
|
|
|
9512
9943
|
SessionRecord:
|
|
9513
9944
|
type: object
|
|
@@ -9782,11 +10213,18 @@ components:
|
|
|
9782
10213
|
description: >
|
|
9783
10214
|
`GET /v1/sessions/{sessionId}/memory-status` 200 body (server >=7.53, S-53 seam①): `sessionId` + the
|
|
9784
10215
|
five `SessionMemoryStatus` keys copied ONE BY ONE when present (the server forbids spreading and forbids
|
|
9785
|
-
defaults — the five conditional copies ARE the closed set)
|
|
10216
|
+
defaults — the five conditional copies ARE the closed set), plus the DEPLOYMENT bit
|
|
10217
|
+
`autoConsolidationArmed` (server >= 7.85.0, always present). Per-key absence meaning = exactly
|
|
9786
10218
|
`SessionMemoryStatus` (a healthy zero-history session omits `optOutSource` and `lastCaptureAt`). Keys
|
|
9787
|
-
are mirrored locally rather than via allOf so the
|
|
9788
|
-
|
|
9789
|
-
|
|
10219
|
+
are mirrored locally rather than via allOf so the schema stays self-describing (spec rule: a closed
|
|
10220
|
+
schema plus allOf must mirror every key).
|
|
10221
|
+
🔴 OPEN as of sdk 9.7.0 — same ruling, same receipt as `MetricsSummaryOps` / `LivePendingRow`: this was
|
|
10222
|
+
closed at S-53 and server 7.85.0 then added a sixth key, so a REAL body from a current server failed the
|
|
10223
|
+
schema. A response PROJECTION is declared key-by-key AND left open (the producer ships independently and
|
|
10224
|
+
adds keys additively between SDK releases); request bodies stay closed.
|
|
10225
|
+
⚠️ SCOPE OF THAT RULING IN THIS BATCH: only the schemas this batch had to GROW were opened (the growth
|
|
10226
|
+
itself is the proof). Re-judging every other closed response schema is a separate car.
|
|
10227
|
+
additionalProperties: true
|
|
9790
10228
|
required: [sessionId]
|
|
9791
10229
|
properties:
|
|
9792
10230
|
sessionId: { type: string, description: 'The session the status is about (echo of the path segment, percent-decoded).' }
|
|
@@ -9795,6 +10233,87 @@ components:
|
|
|
9795
10233
|
foldedCount: { type: integer, description: 'See SessionMemoryStatus.foldedCount.' }
|
|
9796
10234
|
optOutSource: { type: string, enum: [record, fault], description: 'See SessionMemoryStatus.optOutSource — absent = no opt-out record (healthy), never a fault by itself.' }
|
|
9797
10235
|
lastCaptureAt: { type: integer, description: 'See SessionMemoryStatus.lastCaptureAt (ms epoch) — absent on a session with no committed contribution (healthy) as well as on an unreadable ledger.' }
|
|
10236
|
+
autoConsolidationArmed:
|
|
10237
|
+
type: boolean
|
|
10238
|
+
description: >-
|
|
10239
|
+
server >= 7.85.0 (S-403 / core 7.22.0 #863 W1; mint `routes/sessions.ts:744` =
|
|
10240
|
+
`deps.memoryAutoConsolidationArmed`, computed ONCE at boot through core's single reader
|
|
10241
|
+
`autoRunOnRecommendationEffective`) — IS AUTOMATIC MEMORY CONSOLIDATION ARMED ON THIS DEPLOYMENT.
|
|
10242
|
+
🔴 NOT one of the five core-derived session keys: those are per-SESSION derivations whose absence
|
|
10243
|
+
is meaningful per key; this one is a DEPLOYMENT fact and is ALWAYS PRESENT on >= 7.85.0. Absent =
|
|
10244
|
+
an older worker ⇒ "unknown"; NEVER fold it to `false` (that renders a box which is auto-egressing
|
|
10245
|
+
its whole memory store to the consolidation model as one that is not).
|
|
10246
|
+
🔴 `true` = a terminal harvest that crosses the threshold auto-runs one host consolidation (memory
|
|
10247
|
+
content auto-egresses to the configured model). A DIFFERENT QUESTION from `captureOptedOut` (does
|
|
10248
|
+
THIS SESSION enter long-term memory) — do not read them together.
|
|
10249
|
+
The same fact is readable by an operator as `wiring_manifest.autoConsolidation` (present-iff armed);
|
|
10250
|
+
one fact, one source — the tenant face has only this key.
|
|
10251
|
+
|
|
10252
|
+
McpReconnectServerStatus:
|
|
10253
|
+
type: object
|
|
10254
|
+
additionalProperties: true
|
|
10255
|
+
description: >
|
|
10256
|
+
The `status` segment of `POST /v1/sessions/{sessionId}/mcp/reconnect` (server >= 7.85.0 / S-381; mint
|
|
10257
|
+
`routes/mcp-reconnect.ts` `projectStatus`) — core's `McpServerStatus` projected BY THIS FACE.
|
|
10258
|
+
⚠️ The SAME core type is projected TWICE with DIFFERENT key sets and the two are deliberately NOT
|
|
10259
|
+
merged: the `/mcp` panel projects `{name, status, serverInfo?, toolNames?, error?}` only, while this
|
|
10260
|
+
face adds `errorCode` / `httpStatus` / `delivered` / `transportClosed`. Folding them would make the
|
|
10261
|
+
panel's consumers wait for four keys that never come.
|
|
10262
|
+
🔴 BRANCH ON THE MACHINE FIELDS, NEVER PARSE `reason`: `errorCode` is core's `McpFailureKind` word
|
|
10263
|
+
(owner is the engine — switch with a default arm, do not narrow), `httpStatus` 401 is a re-authorize
|
|
10264
|
+
door, `spawn_failed` is a missing binary.
|
|
10265
|
+
required: [name, status]
|
|
10266
|
+
properties:
|
|
10267
|
+
name: { type: string }
|
|
10268
|
+
status: { type: string, description: 'connected | failed (open set — the word table owner is the engine).' }
|
|
10269
|
+
errorCode: { type: string, description: 'core `McpFailureKind`. Open set; switch with a default arm.' }
|
|
10270
|
+
httpStatus: { type: integer, description: 'Only alongside errorCode `http_status` (401/403 = re-authorize; 5xx = that end is down). ABSENT rather than 0.' }
|
|
10271
|
+
delivered: { type: string, enum: [yes, no, unknown], description: 'Did the request reach that server — read together with errorCode.' }
|
|
10272
|
+
serverInfo:
|
|
10273
|
+
type: object
|
|
10274
|
+
properties:
|
|
10275
|
+
name: { type: string }
|
|
10276
|
+
version: { type: string }
|
|
10277
|
+
error: { type: string, description: 'Remote-authored text: server-redacted and truncated to 200 chars. HUMAN-READ ONLY, never parsed.' }
|
|
10278
|
+
transportClosed: { type: boolean, description: 'Whether that transport is already closed (the old connection is always closed BEFORE the new dial — see the "one transaction" note on the operation).' }
|
|
10279
|
+
|
|
10280
|
+
SessionMcpReconnectResult:
|
|
10281
|
+
type: object
|
|
10282
|
+
additionalProperties: true
|
|
10283
|
+
description: >
|
|
10284
|
+
200 body of `POST /v1/sessions/{sessionId}/mcp/reconnect` (server >= 7.85.0 / S-381 / core 7.22.0 #857;
|
|
10285
|
+
mint `routes/mcp-reconnect.ts` `projectResult`). Branch on `outcome` — all three states are HTTP 200.
|
|
10286
|
+
🔴 The `unsupported` arm (this run declared NO MCP) is an ALWAYS-FIVE-KEY body
|
|
10287
|
+
`{taskId, sessionId, server, outcome, reason}`: with no core result the server coins no
|
|
10288
|
+
`prefix`/`toolCount`/`added`/`removed`/`toolNames`, so reading `added` unconditionally is a bug.
|
|
10289
|
+
🔴 `toolNames` is PRESENT-IFF core supplied `tools` (not keyed off `outcome`) and is a SUPERSET of what
|
|
10290
|
+
the model finally sees (core filters again before mounting) — see the operation description.
|
|
10291
|
+
required: [taskId, sessionId, server, outcome]
|
|
10292
|
+
properties:
|
|
10293
|
+
taskId: { type: string, description: 'The live run this re-dial rode (this session''s active run) — follow it via GET /v1/runs/{taskId}/events.' }
|
|
10294
|
+
sessionId: { type: string, description: 'Echo of the path segment (percent-decoded).' }
|
|
10295
|
+
server: { type: string, description: 'The named server that was re-dialed.' }
|
|
10296
|
+
outcome: { type: string, description: 'accepted | refused | unsupported — the ONLY discriminator (all three are 200). `unsupported` is the SERVER''s word, not core''s.' }
|
|
10297
|
+
prefix: { type: string, description: 'The tool-name prefix domain of that server. Absent on the `unsupported` arm.' }
|
|
10298
|
+
toolCount: { type: integer, description: 'Tool count in that domain after the dial. Absent (never 0) on the `unsupported` arm.' }
|
|
10299
|
+
added: { type: array, items: { type: string } }
|
|
10300
|
+
removed: { type: array, items: { type: string } }
|
|
10301
|
+
toolNames:
|
|
10302
|
+
type: array
|
|
10303
|
+
items: { type: string }
|
|
10304
|
+
description: 'The roster THIS re-dial asserted for its own prefix domain (present-iff core supplied `tools`). A SUPERSET of what gets mounted; `[]` (failed dial) and ABSENT (never touched the connection) are different sentences.'
|
|
10305
|
+
reason: { type: string, description: 'Human sentence (server-redacted, 200-char bound). NEVER parse it — the machine face is `status`.' }
|
|
10306
|
+
status: { $ref: '#/components/schemas/McpReconnectServerStatus' }
|
|
10307
|
+
listingIncomplete:
|
|
10308
|
+
type: object
|
|
10309
|
+
additionalProperties: true
|
|
10310
|
+
description: 'The listing walk did not finish ⇒ do NOT treat `toolNames` as complete. `reason` is core''s word; `pages` is how many pages were walked.'
|
|
10311
|
+
required: [reason, pages]
|
|
10312
|
+
properties:
|
|
10313
|
+
reason: { type: string }
|
|
10314
|
+
pages: { type: integer }
|
|
10315
|
+
budgetMs: { type: integer }
|
|
10316
|
+
error: { type: string }
|
|
9798
10317
|
|
|
9799
10318
|
# ── 2c session-sync (P1d) — the cloud-as-a-SYNC-PEER schemas ──────────────────────────────────────────────
|
|
9800
10319
|
SyncEntry:
|
|
@@ -10042,6 +10561,12 @@ components:
|
|
|
10042
10561
|
The two 501 families are deliberately NOT interchangeable: `capability.*` means this deployment did
|
|
10043
10562
|
not wire that surface (change the deployment / hide the entry point), `feature.*` means the surface
|
|
10044
10563
|
exists but its switch is off (ask an admin to turn it on).
|
|
10564
|
+
🔴 `request.path_malformed` IS NOW THE WHOLE FAMILY'S ANSWER (server >= 7.86.0 / S-430): an id
|
|
10565
|
+
segment that is not valid percent-encoding answers 400 on EVERY id-bearing door. Twelve of those
|
|
10566
|
+
doors used to let the bare `decodeURIComponent` throw a `URIError` straight through the top-level
|
|
10567
|
+
catch and answered 500 `internal.error` — a caller-side mistake reported as a server fault, which
|
|
10568
|
+
also made it a retry-worthy-looking code. A consumer that branched on 500 for those paths must move
|
|
10569
|
+
the branch to the 400 (the fix is the caller's either way: re-encode the id).
|
|
10045
10570
|
error: { type: string }
|
|
10046
10571
|
errorMessage: { type: string }
|
|
10047
10572
|
activeTaskId:
|
|
@@ -11178,6 +11703,41 @@ components:
|
|
|
11178
11703
|
that wires none reports nothing at all. The server never invents `mounted: false` when it cannot
|
|
11179
11704
|
read the bit — the whole section goes absent instead (a minted false would read as "really not
|
|
11180
11705
|
mounted").
|
|
11706
|
+
writeProtection:
|
|
11707
|
+
allOf: [{ $ref: '#/components/schemas/WiringManifestWriteProtection' }]
|
|
11708
|
+
description: >
|
|
11709
|
+
core 7.20.1 (#853 / clay C-R55 丙), server >= 7.80.1 — TENANT-visible: which TARGET READING this
|
|
11710
|
+
leg's write-protection judge got. EFFECTIVE half only.
|
|
11711
|
+
🔴 ABSENCE IS A THIRD STATE (core present-iff a judge was compiled; a deployment with
|
|
11712
|
+
`writeProtectedPaths: []` omits the whole section) — NEVER fold it to `"spelling-only"`, which
|
|
11713
|
+
means "a judge IS there and only reads spellings".
|
|
11714
|
+
⚠️ Same name, different thing from `capabilities.writeProtection` / the diagnostics endpoint's
|
|
11715
|
+
`writeProtection`: those are THIS SERVER'S assembled NAME TABLE (a boot product); this is core's
|
|
11716
|
+
per-leg SELF-REPORT of the judge's reading.
|
|
11717
|
+
readDeny:
|
|
11718
|
+
allOf: [{ $ref: '#/components/schemas/WiringManifestReadDeny' }]
|
|
11719
|
+
description: >
|
|
11720
|
+
core 7.22.0 (#889 / C-R60), server >= 7.85.0 — OPERATOR face only (stripped on every tenant
|
|
11721
|
+
stream, and deliberately NOT carried by `GET /v1/diagnostics/wiring` either): which BUILT-IN
|
|
11722
|
+
sensitive-path read-deny TIERS materially activate rows on this deployment.
|
|
11723
|
+
🔴 PRESENT IFF at least one built-in row judges this leg. Absence = no built-in row judges it (a
|
|
11724
|
+
POSITIVE fact), NOT "no read is deny-judged" — a deployment's own `READ_DENY_PATTERNS` is
|
|
11725
|
+
deliberately never reported (pattern TEXT is deployment material, not an enum).
|
|
11726
|
+
🔴 AN EMPTY ARRAY IS NEVER EMITTED ("off" and "on to nothing" are one fact) ⇒ treat `[]` as a bad
|
|
11727
|
+
producer. The tier vocabulary is owned by core — do not narrow it on the consumer side.
|
|
11728
|
+
⚠️ Deployment semantics flipped in core 7.22.0: `READ_DENY_BUILTIN_TIERS` UNSET = OFF.
|
|
11729
|
+
autoConsolidation:
|
|
11730
|
+
allOf: [{ $ref: '#/components/schemas/WiringManifestAutoConsolidation' }]
|
|
11731
|
+
description: >
|
|
11732
|
+
core 7.21.0 (#761), server >= 7.82.0 — OPERATOR face only (stripped on every tenant stream): is
|
|
11733
|
+
automatic memory consolidation ARMED.
|
|
11734
|
+
🔴 PRESENT IFF armed, and the only present shape is `{ onRecommendation: true }` (core's construct
|
|
11735
|
+
gate refuses an armed-but-unrunnable state). ABSENT = NOT ARMED (a positive fact) — never read it
|
|
11736
|
+
as `false`, never as "unknown". Armed means a terminal harvest over the threshold auto-runs one
|
|
11737
|
+
host consolidation: memory content auto-egresses to the configured model, which is why it sits in
|
|
11738
|
+
the operator family beside `governance`.
|
|
11739
|
+
⚠️ The tenant-readable twin of the same fact is `GET /v1/sessions/{sessionId}/memory-status`'s
|
|
11740
|
+
`autoConsolidationArmed` (always present, a real boolean). One fact, one source, two faces.
|
|
11181
11741
|
governance:
|
|
11182
11742
|
type: object
|
|
11183
11743
|
additionalProperties: false
|
|
@@ -14105,6 +14665,16 @@ components:
|
|
|
14105
14665
|
WiringManifest:
|
|
14106
14666
|
type: object
|
|
14107
14667
|
description: >
|
|
14668
|
+
🔴 ONE RULE FOR EVERY "EFFECTIVE HALF ONLY" KEY (sdk 9.7.0, after a codex adversarial-review [high]):
|
|
14669
|
+
this schema's only consumer today is `GET /v1/diagnostics/wiring`'s `static` half, and every key marked
|
|
14670
|
+
"effective half only" (`leg`, `ask.effective`, `configFingerprint`, `tools`, `hooks`, `lsp`,
|
|
14671
|
+
`modelGate`, `autoMode`, `writeProtection`, `readDeny`, `autoConsolidation`) is ALWAYS ABSENT on that
|
|
14672
|
+
wire ⇒ THEIR ABSENCE HERE CARRIES NO INFORMATION. The per-key absence readings ("absent = not armed",
|
|
14673
|
+
"absent = a third state", …) hold for the LIVE FRAME (`Event_wiring_manifest`) and must not be carried
|
|
14674
|
+
over to this response. For the facts behind those keys: a tenant reads
|
|
14675
|
+
`GET /v1/sessions/{sessionId}/memory-status` (`autoConsolidationArmed`); an operator subscribes to that
|
|
14676
|
+
leg's live stream (the governance segment and these three sections exist only on a projection that
|
|
14677
|
+
KNOWS the caller's identity).
|
|
14108
14678
|
core's assembly self-evidence manifest (core >=5.14.0 `WiringManifest`). TWO HALVES, ONE shape (a
|
|
14109
14679
|
consumer must line the two faces up, so they are not split into separate types) — but which keys
|
|
14110
14680
|
belong to which half is DETERMINATE, not "usually": `leg`, `ask.effective` and `configFingerprint`
|
|
@@ -14248,6 +14818,78 @@ components:
|
|
|
14248
14818
|
core 7.18.0 (#789), server >= 7.78.0 — the LSP seam. EFFECTIVE half only; present iff a manager
|
|
14249
14819
|
seat resolved. `mounted` and `lane` are INDEPENDENT facts — never fold them into one.
|
|
14250
14820
|
Full per-key contract: see Event_wiring_manifest.lsp.
|
|
14821
|
+
writeProtection:
|
|
14822
|
+
allOf: [{ $ref: '#/components/schemas/WiringManifestWriteProtection' }]
|
|
14823
|
+
description: >
|
|
14824
|
+
core 7.20.1 (#853), server >= 7.80.1 — TENANT-visible: which target reading this leg's
|
|
14825
|
+
write-protection judge got. EFFECTIVE HALF ONLY ⇒ ALWAYS ABSENT ON THIS ENDPOINT, and absence
|
|
14826
|
+
HERE carries no information (see this schema's own description for the one rule that governs
|
|
14827
|
+
every effective-half-only key). The three-state reading ("absence = no judge at all, never fold
|
|
14828
|
+
it to `spelling-only`") is the LIVE FRAME's reading — do not apply it to this response.
|
|
14829
|
+
Full per-key contract: see Event_wiring_manifest.writeProtection.
|
|
14830
|
+
readDeny:
|
|
14831
|
+
allOf: [{ $ref: '#/components/schemas/WiringManifestReadDeny' }]
|
|
14832
|
+
description: >
|
|
14833
|
+
core 7.22.0 (#889), server >= 7.85.0 — OPERATOR face only, effective half only, AND the server
|
|
14834
|
+
explicitly rules it off this endpoint ⇒ always absent here; absence HERE says nothing about the
|
|
14835
|
+
deployment's tiers. The present-iff / never-empty-array reading belongs to the operator LIVE
|
|
14836
|
+
projection. Full per-key contract: see Event_wiring_manifest.readDeny.
|
|
14837
|
+
autoConsolidation:
|
|
14838
|
+
allOf: [{ $ref: '#/components/schemas/WiringManifestAutoConsolidation' }]
|
|
14839
|
+
description: >
|
|
14840
|
+
core 7.21.0 (#761), server >= 7.82.0 — OPERATOR face only, EFFECTIVE HALF ONLY ⇒ ALWAYS ABSENT ON
|
|
14841
|
+
THIS ENDPOINT. 🔴 DO NOT read absence here as "not armed": that renders a deployment which IS
|
|
14842
|
+
auto-egressing its whole memory store as one that is not. The present-iff-armed reading belongs to
|
|
14843
|
+
the operator LIVE projection; for "is this deployment armed", read
|
|
14844
|
+
`GET /v1/sessions/{sessionId}/memory-status`'s `autoConsolidationArmed` (always present, a real
|
|
14845
|
+
boolean). Full per-key contract: see Event_wiring_manifest.autoConsolidation.
|
|
14846
|
+
WiringManifestWriteProtection:
|
|
14847
|
+
# sdk 9.7.0:与 `WiringManifestMcpEntry` / `WiringManifestLspSeam` 同一条裁定 —— 同一段形被 live 帧与
|
|
14848
|
+
# 静态诊断面两处引用,内联就是两份会各自漂的镜像,所以具名单源。
|
|
14849
|
+
type: object
|
|
14850
|
+
additionalProperties: false
|
|
14851
|
+
description: >
|
|
14852
|
+
The `wiring_manifest.writeProtection` section (core 7.20.1 #853 / clay C-R55 丙) — which TARGET
|
|
14853
|
+
READING this leg's write-protection judge got. `"target"` = the judge holds the execution environment
|
|
14854
|
+
and re-judges on the RESOLVED target when the spelling missed (a write that reaches a protected row
|
|
14855
|
+
through an alias or a symlink is cleared); `"spelling-only"` = no environment, the spelling IS the
|
|
14856
|
+
whole verdict. ABSENCE (the section itself) is a THIRD state — no judge was compiled at all.
|
|
14857
|
+
required: [targetView]
|
|
14858
|
+
properties:
|
|
14859
|
+
targetView: { type: string, description: "core's two-word closed set (`spelling-only` / `target`); read as open — the vocabulary owner is the engine." }
|
|
14860
|
+
|
|
14861
|
+
WiringManifestReadDeny:
|
|
14862
|
+
type: object
|
|
14863
|
+
additionalProperties: false
|
|
14864
|
+
description: >
|
|
14865
|
+
The `wiring_manifest.readDeny` section (core 7.22.0 #889 / C-R60) — the BUILT-IN sensitive-path
|
|
14866
|
+
read-deny tiers that MATERIALLY activate rows on this leg, in core's own tier order (NOT the tiers the
|
|
14867
|
+
operator typed: one whose every row the exclude list removed is not named). OPERATOR audience.
|
|
14868
|
+
required: [builtinTiers]
|
|
14869
|
+
properties:
|
|
14870
|
+
builtinTiers:
|
|
14871
|
+
type: array
|
|
14872
|
+
minItems: 1
|
|
14873
|
+
items: { type: string }
|
|
14874
|
+
description: >
|
|
14875
|
+
Tier names, core-owned vocabulary (today `credentials` / `shell-history` / `browser` / `wallet` /
|
|
14876
|
+
`agent-config`) — passed through verbatim, never narrowed here. AN EMPTY ARRAY IS NEVER EMITTED.
|
|
14877
|
+
|
|
14878
|
+
WiringManifestAutoConsolidation:
|
|
14879
|
+
type: object
|
|
14880
|
+
additionalProperties: false
|
|
14881
|
+
description: >
|
|
14882
|
+
The `wiring_manifest.autoConsolidation` section (core 7.21.0 #761) — PRESENT IFF automatic memory
|
|
14883
|
+
consolidation is EFFECTIVELY armed on this deployment. OPERATOR audience: armed means memory content
|
|
14884
|
+
auto-egresses to the configured consolidation model after a qualifying terminal harvest, and it is not
|
|
14885
|
+
observable to the caller at all (the run happens host-side, fire-and-forget, after the harvest).
|
|
14886
|
+
required: [onRecommendation]
|
|
14887
|
+
properties:
|
|
14888
|
+
onRecommendation:
|
|
14889
|
+
type: boolean
|
|
14890
|
+
enum: [true]
|
|
14891
|
+
description: "core's ONLY present shape — the construct gate refuses an armed-but-unrunnable state, so this is a SHAPE, not a value."
|
|
14892
|
+
|
|
14251
14893
|
WiringManifestHookEntry:
|
|
14252
14894
|
# sdk 9.5.0:与 `WiringManifestMcpEntry` 同一条裁定 —— 同一份行形被 live 帧与静态诊断面两处引用,
|
|
14253
14895
|
# 内联就是两份会各自漂的镜像,所以一开始就具名。
|