@sema-agent/sdk 8.7.0 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +111 -3
- 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 +18 -0
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js.map +1 -1
- package/dist/health.d.ts +17 -0
- package/dist/health.d.ts.map +1 -1
- package/dist/health.js.map +1 -1
- package/dist/index.d.ts +6 -6
- 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/rules.d.ts +85 -8
- package/dist/resources/rules.d.ts.map +1 -1
- package/dist/resources/rules.js +9 -1
- package/dist/resources/rules.js.map +1 -1
- package/dist/resources/tool-approvals.d.ts +31 -5
- 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 +236 -26
- package/dist/types.d.ts.map +1 -1
- package/openapi.yaml +605 -68
- package/package.json +1 -1
package/openapi.yaml
CHANGED
|
@@ -2511,6 +2511,13 @@ paths:
|
|
|
2511
2511
|
approve carried no `answer` (the ONLY pre-claim rejection left on that leg; the retired codes
|
|
2512
2512
|
`decide.parked_answer_unsupported` / `decide.parked_question_unsupported` are gone — an answer HAS
|
|
2513
2513
|
a hookpoint now and a parked question CAN be approved, with an answer). SDK → DecideUnsupportedError.
|
|
2514
|
+
PARKED **WORKFLOW-CHILD** variant (server >= 7.69.0, S-185):
|
|
2515
|
+
`decide.workflow_remember_unsupported` — `remember:"session"` is REFUSED on that lane, fail-closed,
|
|
2516
|
+
BEFORE anything is delivered (zero side effects; re-issue without `remember`). The repo-wide rule for
|
|
2517
|
+
granting an exemption is "the engine COMMITTED this decision", and that lane's 200 does not carry the
|
|
2518
|
+
proof (the child's binding / updatedInput / answer checks run later, inside the workflow's own
|
|
2519
|
+
resume), so an exemption granted here could outlive a decision the engine then rejects. The body
|
|
2520
|
+
carries `runId` (the WORKFLOW run) and NO `taskId`. SDK → DecideUnsupportedError, `.runId`.
|
|
2514
2521
|
content:
|
|
2515
2522
|
application/json:
|
|
2516
2523
|
schema: { $ref: '#/components/schemas/ErrorResponse' }
|
|
@@ -2545,6 +2552,18 @@ paths:
|
|
|
2545
2552
|
`gate_not_plan_review` (§4c) → SDK `GateNotToolApprovalError`. Client action: route by the row's
|
|
2546
2553
|
`gateKind` — `plan_review` → `POST /v1/assistant/tasks/{taskId}/plan_review`, `resource_limit` →
|
|
2547
2554
|
`POST /v1/assistant/tasks/{taskId}/resume`. Retrying the same body never succeeds.
|
|
2555
|
+
SEVENTH group (server >= 7.69.0 / S-185, the PARKED WORKFLOW-CHILD lane): `decide.workflow_host_unknown`
|
|
2556
|
+
and `decide.workflow_host_not_parked`. The pending belongs to a workflow child that parked on an
|
|
2557
|
+
approval gate, and the deployment could not hand the decision to that workflow's HOST session (the
|
|
2558
|
+
engine's only deployment-facing channel for a parked workflow ordinal is a resume of the host, which
|
|
2559
|
+
then re-invokes `Workflow({resumeFromRunId})`). Both bodies carry `runId` (the WORKFLOW run) and NO
|
|
2560
|
+
`taskId`. NOTHING was consumed: the child's checkpoint stays PENDING and the card is still listable
|
|
2561
|
+
and re-decidable. `_host_not_parked` = the host is not currently parked awaiting this run (it may
|
|
2562
|
+
have been interrupted, taking an already-delivered decision with it — the decision is not persisted
|
|
2563
|
+
anywhere) → retry once it parks again, or resume that `runId` yourself. `_host_unknown` = the run
|
|
2564
|
+
carries no originating session at all (a directly started `runWorkflow`, not a Workflow tool call) →
|
|
2565
|
+
NO retry value, a person resumes that `runId`. SDK → DecideWorkflowHostError (`.errorCode` splits the
|
|
2566
|
+
two recovery verbs, `.runId` is the handle).
|
|
2548
2567
|
SIXTH group (A-075.10, TIME-BASED retry-later pair): `resume.usage_window_exhausted` (#449 G1,
|
|
2549
2568
|
core 5.60.1 — the deployment governance window on this run's ledger key is full; NOTHING was
|
|
2550
2569
|
consumed or unpinned, the SAME token with the SAME decision redeems once the window slides) and
|
|
@@ -4620,10 +4639,18 @@ paths:
|
|
|
4620
4639
|
x-status: gated # server routes/rules.ts handleRuleList;同 cc-import 的部署谓词。SDK rules.list() 消费。
|
|
4621
4640
|
summary: List the rules that are LIVE under a principal (keyset-paged, cursor bound to the revision).
|
|
4622
4641
|
description: >
|
|
4623
|
-
List the persisted
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
4642
|
+
List the persisted rules currently live under one principal (tombstones already folded) — all THREE
|
|
4643
|
+
behaviors from server >= 7.67.0, not the allow bucket alone.
|
|
4644
|
+
Ordering is deterministic — `(scope, rule, behavior)` lexicographic from server >= 7.67.0, `(scope, rule)`
|
|
4645
|
+
before it — because keyset paging's whole premise is that the same data comes back in the same order every
|
|
4646
|
+
time. The order is decided by the server, NOT by the store (core's `list()` promises none).
|
|
4647
|
+
|
|
4648
|
+
🔴 THE CURSOR CARRIES A FORMAT GENERATION, BUMPED TO v2 IN 7.67.0 (the sort key gained `behavior`, since a
|
|
4649
|
+
same-text deny/allow pair collided under the old two-part key and the strict `>` silently dropped the
|
|
4650
|
+
sibling row at a page boundary). During a rolling upgrade or rollback the two generations REFUSE each
|
|
4651
|
+
other's cursors with a 400 rather than each walking its own key shape — a governance listing going wrong
|
|
4652
|
+
under an all-200 conversation is far more expensive than one re-listing. The cursor stays OPAQUE and the
|
|
4653
|
+
handling is unchanged: echo it back verbatim, and on a 400 drop it and re-list from the top.
|
|
4627
4654
|
|
|
4628
4655
|
🔴 THE CURSOR IS BOUND TO `(rev, principal, scope)` AND A MISMATCH IS REFUSED, NOT RESET. A rule set
|
|
4629
4656
|
is read WHOLE, so there is no tearing WITHIN a page — tearing happens BETWEEN pages, when somebody
|
|
@@ -4688,12 +4715,16 @@ paths:
|
|
|
4688
4715
|
tags: [rules]
|
|
4689
4716
|
operationId: rulesRevoke
|
|
4690
4717
|
x-status: gated # server routes/rules.ts handleRuleRevoke;同 cc-import 的部署谓词。SDK rules.revoke() 消费。
|
|
4691
|
-
summary: Revoke a persisted rule BY CONTENT — `(rule, scope)`, with no id in the path.
|
|
4718
|
+
summary: Revoke a persisted rule BY CONTENT — `(behavior, rule, scope)`, with no id in the path.
|
|
4692
4719
|
description: >
|
|
4693
|
-
Revoke one persisted
|
|
4694
|
-
path segment on purpose: a rule's identity IS that
|
|
4695
|
-
`removePersistedRule` primitive (which this endpoint always goes
|
|
4696
|
-
form over the add dots.
|
|
4720
|
+
Revoke one persisted rule identified by its CONTENT triple `(behavior, rule, scope)` — a pair before
|
|
4721
|
+
server 7.67.0. There is no `:id` path segment on purpose: a rule's identity IS that triple, the server
|
|
4722
|
+
mints no row id, and the engine's `removePersistedRule` primitive (which this endpoint always goes
|
|
4723
|
+
through) works in observed-remove form over the add dots.
|
|
4724
|
+
|
|
4725
|
+
🔴 `behavior` IS REQUIRED AND HAS NO DEFAULT (server >= 7.67.0; omitting it is a 400
|
|
4726
|
+
`request.body_shape`). Defaulting it would let a revoke aimed at an `allow` delete the same-text `deny` —
|
|
4727
|
+
a refusal the operator meant to keep — while answering 200.
|
|
4697
4728
|
|
|
4698
4729
|
🔴 IDEMPOTENT, AND DELIBERATELY NOT A 404. Revoking twice, or revoking something that was never
|
|
4699
4730
|
there, is `200 {status:"no-op"}` — a "did this rule ever exist?" 404 would be an existence oracle
|
|
@@ -4725,7 +4756,7 @@ paths:
|
|
|
4725
4756
|
content:
|
|
4726
4757
|
application/json:
|
|
4727
4758
|
schema: { $ref: '#/components/schemas/RuleRevokeResult' }
|
|
4728
|
-
'400': { $ref: '#/components/responses/BadRequest' } # request.body_shape — body is not {rule, scope, principal?}, or `scope` is not `global`/`project:<non-empty root>`
|
|
4759
|
+
'400': { $ref: '#/components/responses/BadRequest' } # request.body_shape — body is not {behavior, rule, scope, principal?} (server >= 7.67.0: `behavior` is REQUIRED and has no default), or `scope` is not `global`/`project:<non-empty root>`
|
|
4729
4760
|
'401': { $ref: '#/components/responses/Unauthorized' }
|
|
4730
4761
|
'403': { $ref: '#/components/responses/Forbidden' } # auth.operator_only — revoking another principal's rule without explicit OPERATOR_PRINCIPALS membership
|
|
4731
4762
|
'429': { $ref: '#/components/responses/RateLimited' }
|
|
@@ -7169,6 +7200,39 @@ components:
|
|
|
7169
7200
|
note:
|
|
7170
7201
|
type: string
|
|
7171
7202
|
|
|
7203
|
+
PostureKnobReading:
|
|
7204
|
+
type: object
|
|
7205
|
+
additionalProperties: false
|
|
7206
|
+
description: >
|
|
7207
|
+
server >= 7.67.0 (S-178) — ONE deployment knob's reading: its value, WHO SET IT, and one operator-facing
|
|
7208
|
+
pointer. Same shape as `ReadFacePosture`, whose value key is called `face` instead of `value`; both exist,
|
|
7209
|
+
neither replaces the other.
|
|
7210
|
+
|
|
7211
|
+
🔴 WHY NOT A BARE VALUE: a bare boolean or number cannot answer "why is THIS machine on this setting, and
|
|
7212
|
+
how do I pin it back". From 7.67.0 a single-user turnkey worker (REQUIRE_PRINCIPAL unset) derives
|
|
7213
|
+
`DURABLE_APPROVAL` ON and a 24h approval window when neither is set, so a default FLIP has to be visible
|
|
7214
|
+
on an operator surface or nobody sees it.
|
|
7215
|
+
|
|
7216
|
+
`source` is the CLOSED four-word set (exhaustive switch on the server — adding a word is a compile error
|
|
7217
|
+
there), in PRECEDENCE order: `env` = pinned by this machine's env var (deployment sovereignty; beats any
|
|
7218
|
+
published value) · `center` = applied from the config-center (restart-to-apply) · `posture` = derived from
|
|
7219
|
+
the deployment shape (single-user turnkey) · `engine-default` = nothing pinned, the built-in default is in
|
|
7220
|
+
force. Only `env`/`center` count as "an operator asked for this"; a posture-derived `true` does not.
|
|
7221
|
+
`note` is PROSE for a human (per-knob, and on the `posture` arm it is that knob's own fact sentence) —
|
|
7222
|
+
classify on `source`, never by matching `note`.
|
|
7223
|
+
|
|
7224
|
+
🔴 `value` IS NARROWED AT THE REFERENCE POINT, not here: the reading is one shape over many knobs, and a
|
|
7225
|
+
second copy of the record per value type is how the three keys drift apart. Each `serverGates` row pins
|
|
7226
|
+
its own `value` type beside the `$ref`.
|
|
7227
|
+
required: [value, source, note]
|
|
7228
|
+
properties:
|
|
7229
|
+
value:
|
|
7230
|
+
description: 'The knob''s effective value. Typed by the referencing site (boolean / integer / …).'
|
|
7231
|
+
source:
|
|
7232
|
+
type: string
|
|
7233
|
+
enum: [env, center, posture, engine-default]
|
|
7234
|
+
note: { type: string }
|
|
7235
|
+
|
|
7172
7236
|
SkillSpec:
|
|
7173
7237
|
type: object
|
|
7174
7238
|
description: A per-request skill (passed as an object; core-native TaskSpec.skills shape).
|
|
@@ -7386,6 +7450,107 @@ components:
|
|
|
7386
7450
|
items: { type: string }
|
|
7387
7451
|
description: 'E7 (SHIPPED) — the /effort picker default set (minimal/low/medium/high); present ONLY for a reasoning model.'
|
|
7388
7452
|
atMentionable: { type: boolean, description: '#233/A-002.8 (SHIPPED) — @model mention allowlist verdict; present ONLY when the deployment configured an at-mention allowlist. Absent = no allowlist verdict, NOT "not mentionable" — do not narrow absence to false.' }
|
|
7453
|
+
routePairing:
|
|
7454
|
+
$ref: '#/components/schemas/RoutePairingStatus'
|
|
7455
|
+
description: >-
|
|
7456
|
+
#345 (server >=7.45, core 5.57.0 `routePairingStatus`) — this row's key<->URL pairing posture.
|
|
7457
|
+
🔴 ALWAYS PRESENT on a >=7.45 worker: when the verdict cannot be reached the value is the WORD
|
|
7458
|
+
`unknown`, not a missing key ("this worker does not adjudicate" and "this row could not be
|
|
7459
|
+
adjudicated" must stay distinguishable on the wire). Declared here 2026-09-10 alongside `compat`:
|
|
7460
|
+
this schema is CLOSED, and the key has been minted unconditionally since 7.45 — so every real
|
|
7461
|
+
/v1/models response has been structurally rejected by a strict validator for the whole time (same
|
|
7462
|
+
class as `hasBidiControls` on InboxRow). Optional here because the SDK's supported server floor is
|
|
7463
|
+
3.0.0; the discriminant is the PEER'S VERSION, not this key's presence.
|
|
7464
|
+
compat:
|
|
7465
|
+
$ref: '#/components/schemas/ModelCompat'
|
|
7466
|
+
description: >-
|
|
7467
|
+
S-188 (server >=7.69.0, settings-schema >=1.10.0) — this row's wire-compatibility declaration for the
|
|
7468
|
+
openai-completions lane. ABSENT = this model declared nothing; see ModelCompat for the two causes of
|
|
7469
|
+
absence and why absence must NOT be rendered as "the operator did not write one".
|
|
7470
|
+
|
|
7471
|
+
ModelCompat:
|
|
7472
|
+
# 新(8.9.0 / S-188):`ModelInfo.compat` 的形 = core `OpenAICompletionsCompat` 的逐字镜像。
|
|
7473
|
+
# 词表在服务端**已判过**(src/model-compat.ts 的 readModelCompat:整只判形,任一键不合即整只丢),
|
|
7474
|
+
# 所以这里写闭集 enum 是**如实**的,不是本仓自己加的一道门 —— 消费方不必再验一遍。
|
|
7475
|
+
type: object
|
|
7476
|
+
description: >
|
|
7477
|
+
One model row's wire-compatibility declaration for the **openai-completions** lane (core
|
|
7478
|
+
`OpenAICompletionsCompat`, mirrored key-for-key; server >= 7.69.0 / S-188). It says how THIS gateway takes
|
|
7479
|
+
the thinking parameter and what the max-tokens field is called — no URL, no credential.
|
|
7480
|
+
🔴 ABSENT has TWO indistinguishable causes: (a) the model genuinely declared nothing (the engine infers
|
|
7481
|
+
from baseUrl / model id), or (b) the operator wrote one and the server DROPPED IT WHOLE (shape / word /
|
|
7482
|
+
unknown key), logging a named warning and falling back to the engine's inference. So a panel must NOT
|
|
7483
|
+
render "no compat" as "the operator did not configure one".
|
|
7484
|
+
🔴 WHOLE-OR-NOTHING: the server refuses a partially valid declaration rather than keeping the good keys
|
|
7485
|
+
(half a face on the wire is worse than none), so on the wire this object is always complete-and-legal or
|
|
7486
|
+
entirely absent.
|
|
7487
|
+
🔴 An EMPTY object never reaches the wire: the server's single mint point folds "all keys empty" (an
|
|
7488
|
+
empty `reasoningEffortLevels` counts as unset) into "do not mint the key". `compat: {}` is therefore an
|
|
7489
|
+
unrecognisable byte, not "declared but says nothing" — hence minProperties: 1.
|
|
7490
|
+
additionalProperties: false
|
|
7491
|
+
minProperties: 1
|
|
7492
|
+
properties:
|
|
7493
|
+
supportsReasoningEffort: { type: boolean, description: 'Does this endpoint accept `reasoning_effort` at all. Absent => the engine auto-detects from the URL.' }
|
|
7494
|
+
reasoningEffortLevels:
|
|
7495
|
+
type: array
|
|
7496
|
+
minItems: 1
|
|
7497
|
+
items: { $ref: '#/components/schemas/ModelThinkingLevel' }
|
|
7498
|
+
description: >-
|
|
7499
|
+
The effort tiers THIS endpoint really accepts (a subset of the six-rung ladder). Absent => the
|
|
7500
|
+
engine's conservative default (minimal|low|medium|high), so a higher requested tier clamps DOWN
|
|
7501
|
+
instead of 422-ing. Never empty on the wire (an empty declaration is folded into absence server-side).
|
|
7502
|
+
maxTokensField:
|
|
7503
|
+
type: string
|
|
7504
|
+
enum: [max_tokens, max_completion_tokens]
|
|
7505
|
+
description: 'Which field carries max tokens on this lane. Absent => inferred from the model id.'
|
|
7506
|
+
requiresReasoningContentOnAssistantMessages: { type: boolean, description: 'Whether every replayed assistant message must carry an empty `reasoning_content` when reasoning is on.' }
|
|
7507
|
+
thinkingFormat:
|
|
7508
|
+
$ref: '#/components/schemas/ModelThinkingFormat'
|
|
7509
|
+
description: 'How this gateway takes the thinking on/off parameter. Absent => the engine infers (default `openai`, i.e. `reasoning_effort`).'
|
|
7510
|
+
|
|
7511
|
+
ModelThinkingLevel:
|
|
7512
|
+
# 六档思考梯(core ThinkingLevel)。CLOSED:server 铸 Model 那一刻逐词判成员,出集词让整只 compat 被丢。
|
|
7513
|
+
type: string
|
|
7514
|
+
enum: [minimal, low, medium, high, xhigh, max]
|
|
7515
|
+
description: >-
|
|
7516
|
+
One rung of the six-rung thinking ladder (core `ThinkingLevel`). CLOSED — the server judges membership
|
|
7517
|
+
when it mints the Model, and an out-of-set word makes it drop the WHOLE `compat` declaration, so a word
|
|
7518
|
+
outside this set cannot reach the wire. (`off` is NOT a rung: it means "no model-level default" and
|
|
7519
|
+
belongs to a different knob.)
|
|
7520
|
+
|
|
7521
|
+
ModelThinkingFormat:
|
|
7522
|
+
# thinkingFormat 七词闭集(core OpenAICompletionsCompat["thinkingFormat"])。同上,server 已判成员。
|
|
7523
|
+
type: string
|
|
7524
|
+
enum: [openai, openrouter, deepseek, together, zai, qwen, qwen-chat-template]
|
|
7525
|
+
description: >-
|
|
7526
|
+
The spelling this gateway takes the thinking parameter in (core
|
|
7527
|
+
`OpenAICompletionsCompat.thinkingFormat`). CLOSED, judged server-side before it reaches the wire.
|
|
7528
|
+
`openai` = `reasoning_effort` · `openrouter` = `reasoning:{effort}` · `deepseek` = `thinking:{type}` plus
|
|
7529
|
+
`reasoning_effort` · `together` = `reasoning:{enabled}` plus `reasoning_effort` · `zai` / `qwen` =
|
|
7530
|
+
top-level `enable_thinking` · `qwen-chat-template` = `chat_template_kwargs.enable_thinking` — the ONLY
|
|
7531
|
+
spelling that can turn thinking OFF on a think-by-default vLLM/Qwen gateway.
|
|
7532
|
+
|
|
7533
|
+
RoutePairingStatus:
|
|
7534
|
+
# #345 core RoutePairingStatus(`ok:${RoutePairingPosture}` | `broken:*` 两词 | unknown)。
|
|
7535
|
+
# 🔴 **真开集,刻意不写 enum**:开/闭按**产方是否真判成员**定(8.4.0 拆 `CheckpointGate` 顶层 enum /
|
|
7536
|
+
# 把 `AskOrigin` 改真开集的同一条判据)。core 的 `routePairingStatus` 把 `verdict.posture` **直接内插**
|
|
7537
|
+
# 进 `ok:${…}`,server 的铸点与 core 的这只函数**全路径没有一处运行期成员判定** —— 一只自带
|
|
7538
|
+
# `adjudicateRoute` 的 brain 供什么词就上什么词。写一条会执法的 enum = 假闭集,会把一台合法 worker
|
|
7539
|
+
# 的真响应判违约。已知七词写在 description 里(消费端 switch 认已知词 + 一条 default 臂)。
|
|
7540
|
+
type: string
|
|
7541
|
+
x-open-enum: true
|
|
7542
|
+
description: >-
|
|
7543
|
+
A model row's key<->URL pairing posture (core `RoutePairingStatus`; server >= 7.45). OPEN on read —
|
|
7544
|
+
the seven words below are today's set, branch on the known ones and keep a `default` arm. NON-SECRET — no
|
|
7545
|
+
URL, no credential.
|
|
7546
|
+
`ok:per-model` this model has its own credential · `ok:paired` deployment credential, and the model's URL
|
|
7547
|
+
is on the deployment's declared root · `ok:unpinned` deployment credential but the deployment declared no
|
|
7548
|
+
root, so the pairing is UNVERIFIABLE · `ok:keyless` no credential anywhere (a local unauthenticated
|
|
7549
|
+
gateway) · `broken:credential_mismatch` / `broken:credential_missing` a real request WILL be refused by
|
|
7550
|
+
the engine (flag it red) · `unknown` could not be adjudicated — render "unknown", NEVER "good" or "bad".
|
|
7551
|
+
The verdict's frame of reference is the gateway root as of BRAIN CONSTRUCTION, so a live re-pointing of
|
|
7552
|
+
the deployment root only shows up after a restart (both this reading and the real request judge from the
|
|
7553
|
+
same snapshot).
|
|
7389
7554
|
|
|
7390
7555
|
ElicitResponse:
|
|
7391
7556
|
type: object
|
|
@@ -7588,8 +7753,25 @@ components:
|
|
|
7588
7753
|
additionalProperties: false
|
|
7589
7754
|
properties:
|
|
7590
7755
|
label: { type: string, description: 'display label (redacted — LLM-authored).' }
|
|
7591
|
-
status:
|
|
7592
|
-
|
|
7756
|
+
status:
|
|
7757
|
+
type: string
|
|
7758
|
+
description: >-
|
|
7759
|
+
Lifecycle status — core `WorkflowItemStatus` passed through verbatim (running|completed|failed|parked;
|
|
7760
|
+
OPEN on read, the vocabulary's owner is core). 🔴 `parked` (server >= 7.69.0 / core 7.10.0 #642) is an
|
|
7761
|
+
AGENT-ROW word only: this ordinal durably paused at an approval gate and the whole run suspended on it
|
|
7762
|
+
(a phase or group never parks — a parked agent ends the run, so the RUN-level status vocabulary does
|
|
7763
|
+
not have this word). The row's redemption key (`parkedCheckpointToken`) is a resume CAPABILITY and is
|
|
7764
|
+
NEVER projected onto the wire — it appears nowhere in this document by design.
|
|
7765
|
+
displayStatus:
|
|
7766
|
+
type: string
|
|
7767
|
+
description: >-
|
|
7768
|
+
core `deriveAgentDisplayStatus`: running|queued|done|failed|interrupted|parked — the canonical glyph
|
|
7769
|
+
vocabulary, one source of truth shared with the shell. 🔴 `parked` is the SIXTH arm (core 7.10.0
|
|
7770
|
+
#642, server >= 7.69.0): a durable approval gate is holding this agent. Before it existed a parked
|
|
7771
|
+
agent fell through to another word, i.e. a card waiting on a human rendered as a glyph that asks for
|
|
7772
|
+
no action. ALWAYS PRESENT — the server derives it with that pure function on every row. Open on read
|
|
7773
|
+
(the vocabulary's owner is core), but the function has no default arm, so a word outside this set
|
|
7774
|
+
means core grew the table.
|
|
7593
7775
|
taskStatus: { type: string, description: 'the underlying task''s terminal TaskStatus (open vocabulary; core enum verbatim).' }
|
|
7594
7776
|
callKey: { type: string, description: 'the stable deterministic identity of this ctx.agent call (resume-journal key).' }
|
|
7595
7777
|
groupId: { type: string, description: 'the nesting ctx.workflow sub-group this agent ran under; absent = top-level.' }
|
|
@@ -7636,7 +7818,7 @@ components:
|
|
|
7636
7818
|
additionalProperties: false
|
|
7637
7819
|
properties:
|
|
7638
7820
|
title: { type: string, description: 'phase title (redacted — LLM-authored).' }
|
|
7639
|
-
status: { type: string, description: 'core WorkflowItemStatus, plus "pending" for a meta-preregistered phase not yet adopted.' }
|
|
7821
|
+
status: { type: string, description: 'core WorkflowItemStatus (running|completed|failed; `parked` is an agent-row word only), plus "pending" for a meta-preregistered phase not yet adopted.' }
|
|
7640
7822
|
startedAt: { type: integer, description: 'adoption time for a pre-registered phase (0 while still pending).' }
|
|
7641
7823
|
endedAt: { type: integer }
|
|
7642
7824
|
durationMs: { type: integer, description: 'endedAt − startedAt; absent until the phase ends.' }
|
|
@@ -7654,7 +7836,7 @@ components:
|
|
|
7654
7836
|
properties:
|
|
7655
7837
|
groupId: { type: string }
|
|
7656
7838
|
parentGroupId: { type: string, description: 'absent = top-level (a child of the implicit root).' }
|
|
7657
|
-
status: { type: string, description: 'core WorkflowItemStatus (running
|
|
7839
|
+
status: { type: string, description: 'core WorkflowItemStatus (running|completed|failed; a group never parks — `parked` is an agent-row word only).' }
|
|
7658
7840
|
startedAt: { type: integer }
|
|
7659
7841
|
endedAt: { type: integer }
|
|
7660
7842
|
durationMs: { type: integer, description: 'endedAt − startedAt; absent until the group ends.' }
|
|
@@ -7880,13 +8062,31 @@ components:
|
|
|
7880
8062
|
resultChars: { type: integer, description: 'server >=7.9.0 (A-002.6): the ORIGINAL result length in characters; present only alongside resultTruncated.' }
|
|
7881
8063
|
tokens: { type: integer, description: 'present only when the result carried stats.' }
|
|
7882
8064
|
turns: { type: integer, description: 'present only when the result carried stats.' }
|
|
8065
|
+
parked:
|
|
8066
|
+
type: boolean
|
|
8067
|
+
enum: [true]
|
|
8068
|
+
description: >-
|
|
8069
|
+
server >= 7.69.0 / core 7.10.0 #642 — this ordinal is PARKED on a durable approval gate, waiting for a
|
|
8070
|
+
decision. 🔴 NEVER minted as `false`: the park arm and the result arm carry the SAME payload (a
|
|
8071
|
+
TaskResult whose terminal cause is `paused`), and the read face adds exactly this one bit, so an
|
|
8072
|
+
ordinary row is simply ABSENT. Branch on `parked === true`, never on `!parked`. `status` is NOT
|
|
8073
|
+
rewritten by this layer — it is that TaskResult's persisted plane status (`suspended` on a human
|
|
8074
|
+
gate). 🔴 The oversize-stub arm (WorkflowJournalEntryTruncated) structurally CANNOT carry this key —
|
|
8075
|
+
see its note; absence there is not an assertion.
|
|
7883
8076
|
|
|
7884
8077
|
WorkflowJournalEntryTruncated:
|
|
7885
8078
|
# 新(census 批2 四段):the truncated arm (workflows.ts) — an honest stub for a row whose
|
|
7886
8079
|
# stored TaskResult exceeded the per-row 64KiB gate (SQL stores) or byte length (file/in-memory
|
|
7887
8080
|
# fallback): the server never pulls the oversized payload into process memory.
|
|
7888
8081
|
type: object
|
|
7889
|
-
description:
|
|
8082
|
+
description: >-
|
|
8083
|
+
A journal row too large to project — an honest "too big to show" stub, never a silent 2000-char
|
|
8084
|
+
truncation of the real result.
|
|
8085
|
+
🔴 DELIBERATELY carries NO `parked` (server 7.69.0, one mint point shared by all three arms): a stub by
|
|
8086
|
+
definition carries no payload-derived key — the SQL paging leg never pulled the payload back when the row
|
|
8087
|
+
is over the bound, so it structurally cannot answer that bit, and minting it only on the file/in-memory
|
|
8088
|
+
leg would fork the wire shape by backend. To decide "is this ordinal parked", read the `parked` of a
|
|
8089
|
+
NON-stub row, or `agents[].status === "parked"` on GET /v1/workflows/:id. Absence here is NOT an assertion.
|
|
7890
8090
|
required: [callKey, ordinal, truncated, resultBytes]
|
|
7891
8091
|
additionalProperties: false
|
|
7892
8092
|
properties:
|
|
@@ -8386,6 +8586,21 @@ components:
|
|
|
8386
8586
|
is guaranteed by all four shapes; discriminate: `idempotent` -> replay; `decision` without `sessionId` ->
|
|
8387
8587
|
parked receipt; `sessionId` + `bindingEnforced` (+ `status`) -> task-level acceptance / terminal.
|
|
8388
8588
|
`taskId` rides `getActiveTaskId` on the terminal leg (omitted when there is no active row).
|
|
8589
|
+
🔴 A FIFTH PROVENANCE, not a fifth key set (server >= 7.69.0 / S-185 — the PARKED WORKFLOW-CHILD lane):
|
|
8590
|
+
when the pending belongs to a workflow child, the success body is byte-identically the TASK-LEVEL
|
|
8591
|
+
ACCEPTANCE shape, but it is a wake of the workflow's **HOST session** — `taskId` is the host's FRESHLY
|
|
8592
|
+
MINTED run id, not the child's. Two readings a consumer MUST get right:
|
|
8593
|
+
(1) 🔴 the 200 is a **DELIVERY acceptance**, NOT "the gate is resolved". The child's checkpoint stays
|
|
8594
|
+
PENDING until the workflow engine itself resolves it, so the card can still be on `/v1/approvals` right
|
|
8595
|
+
after the 200 — NEVER remove the card from a UI on the strength of this 200; poll the HOST run
|
|
8596
|
+
(`GET /v1/runs/{taskId}`) for progress instead. The background-redemption lane's 200 and the task-level
|
|
8597
|
+
lane's 200 DO mean the engine committed the decision — one sentence ("approved and in effect") cannot be
|
|
8598
|
+
used for both lanes.
|
|
8599
|
+
(2) delivery can still be LOST: if the host run is interrupted before it re-invokes `Workflow`, the
|
|
8600
|
+
decision goes with that invocation (it is not persisted anywhere) and a retry answers 409
|
|
8601
|
+
`decide.workflow_host_not_parked`. Because the card was pending throughout, nothing is forged and the
|
|
8602
|
+
approval can be re-decided once the host parks again.
|
|
8603
|
+
The durable approval-queue projection is UNCHANGED — no key marks the workflow origin on any read face.
|
|
8389
8604
|
required: []
|
|
8390
8605
|
additionalProperties: false
|
|
8391
8606
|
properties:
|
|
@@ -8652,6 +8867,12 @@ components:
|
|
|
8652
8867
|
this spec over-declared decide bindings that never appeared on inbox wire at all. Four client repos were
|
|
8653
8868
|
structurally zero-consumers, so server 3.4.0 narrowed the wire and this schema follows. For the tool face
|
|
8654
8869
|
and decide bindings use /v1/approvals (PendingCheckpoint); for task attribution use /v1/assistant/tasks.
|
|
8870
|
+
🔴 STILL CLOSED (sdk 8.9.0). Four keys the server had been minting onto this row went undeclared until
|
|
8871
|
+
now — `requiresRealApproval` / `denialLimitFallback` / `origin` (server >= 7.57.0) and
|
|
8872
|
+
`classifierUnavailable` (server >= 7.69.0) — so a strict validator rejected every row that carried one
|
|
8873
|
+
and a generated client could not read them at all. They are declared below rather than tolerated: this
|
|
8874
|
+
row stays closed, which is the whole point of the shape (an undeclared key is drift, and drift should be
|
|
8875
|
+
loud).
|
|
8655
8876
|
type: object
|
|
8656
8877
|
required: [sessionId, scope, objective, input]
|
|
8657
8878
|
additionalProperties: false
|
|
@@ -8678,11 +8899,47 @@ components:
|
|
|
8678
8899
|
the /v1/approvals row's — the reading of absence does not). Absence = "not detected", never
|
|
8679
8900
|
"confirmed clean". Declared here because this schema is CLOSED — without the key a strict validator
|
|
8680
8901
|
rejects every >=7.53 inbox row that carries it.
|
|
8902
|
+
requiresRealApproval:
|
|
8903
|
+
type: boolean
|
|
8904
|
+
enum: [true]
|
|
8905
|
+
description: >-
|
|
8906
|
+
server >= 7.57.0 (core 7.4.0 #557), ADDITIVE, present ONLY when true: this parked ask demanded REAL
|
|
8907
|
+
HUMAN judgment — an inbox can say "only a person can clear this" without decoding the gate kind.
|
|
8908
|
+
🔴 NEVER minted as `false` (the OMIT contract this row's other flags follow); absence is "not marked",
|
|
8909
|
+
never "confirmed clearable without a person". The value comes WHOLE from core (`summarizeCheckpoint`
|
|
8910
|
+
projects the park row's own bit) — the server neither recomputes nor redacts it.
|
|
8911
|
+
denialLimitFallback:
|
|
8912
|
+
$ref: '#/components/schemas/DenialLimitFallback'
|
|
8913
|
+
description: >-
|
|
8914
|
+
server >= 7.57.0 (core 7.4.0 #557), ADDITIVE — this parked ask IS the auto-mode classifier's
|
|
8915
|
+
DENIAL-LIMIT fallback, carrying the counts that tripped the bound. 🔴 On a PARKED row
|
|
8916
|
+
`autoDenyAfterMs` is ALWAYS `0` and a consumer MUST NOT start a countdown from it: the window is a
|
|
8917
|
+
fact of the ask's ROUTE, armed only at a hand-out to a LIVE approver, and nothing counts down on the
|
|
8918
|
+
parked lane (a park's expiry is the row's own `deadline`/ttl). Render the counts and the limit; there
|
|
8919
|
+
is no countdown to render here. Core screens the four members before writing the row, so a
|
|
8920
|
+
half-shaped value reads as ABSENT rather than reaching a consumer as a malformed card.
|
|
8921
|
+
origin:
|
|
8922
|
+
$ref: '#/components/schemas/AskOrigin'
|
|
8923
|
+
description: >-
|
|
8924
|
+
server >= 7.57.0 (core 7.5.0), ADDITIVE — WHICH AUTHORITY raised this parked ask, the same word the
|
|
8925
|
+
synchronous card renders by (engine-stamped at the gate, never a policy's claim). Core writes it onto
|
|
8926
|
+
the row ONLY when it is a member of the set, so an out-of-set word is not expected on THIS face — but
|
|
8927
|
+
the vocabulary's owner is still core and it has grown before, so read it the same way as everywhere
|
|
8928
|
+
else: branch known words, keep a `default` arm, and treat an unknown one as "unknown origin", never
|
|
8929
|
+
as "no origin". A row minted before the bit existed reads absent (unreported), not "no authority".
|
|
8681
8930
|
objective: { type: ['string', 'null'], description: 'task objective, null if unavailable.' }
|
|
8682
8931
|
input:
|
|
8683
8932
|
description: >
|
|
8684
8933
|
REDACTED tool-args preview for display (null on gates without one, e.g. plan_review). Untyped by
|
|
8685
8934
|
design (per-tool shape). The raw `toolInput` is deliberately NOT on this row.
|
|
8935
|
+
classifierUnavailable:
|
|
8936
|
+
$ref: '#/components/schemas/ClassifierUnavailable'
|
|
8937
|
+
description: >-
|
|
8938
|
+
server >= 7.69.0 (core 7.10.0 #616), ADDITIVE — WHY this PARKED ask reached a human. The inbox is the
|
|
8939
|
+
TRIAGE face, so `breaker_open` matters most here: it forecasts an approval storm rather than a
|
|
8940
|
+
one-off question. The value comes WHOLE from core (`summarizeCheckpoint` echoes it only when the
|
|
8941
|
+
cause is a member of the engine's set), so the server neither recomputes nor re-screens it. See
|
|
8942
|
+
ClassifierUnavailable for the open-`cause` rule and the three shapes absence covers.
|
|
8686
8943
|
|
|
8687
8944
|
InboxList:
|
|
8688
8945
|
type: object
|
|
@@ -9344,6 +9601,14 @@ components:
|
|
|
9344
9601
|
activeTaskId:
|
|
9345
9602
|
type: string
|
|
9346
9603
|
description: On a 409 — the task currently holding the session lock (see the Conflict response note).
|
|
9604
|
+
runId:
|
|
9605
|
+
type: string
|
|
9606
|
+
description: >-
|
|
9607
|
+
server >= 7.69.0 (S-185) — on the three `decide.workflow_*` codes ONLY: the WORKFLOW run's id. It is
|
|
9608
|
+
the RECOVERY HANDLE (wait for the host session to park and re-decide, or resume that run directly).
|
|
9609
|
+
🔴 NOT interchangeable with `taskId`: the workflow-child park lane mints no taskId at all (this
|
|
9610
|
+
decision never landed on any run, so minting one would be a lie), and the other two decide lanes
|
|
9611
|
+
mint no runId. Branch by `errorCode`, never fall back from one handle to the other.
|
|
9347
9612
|
|
|
9348
9613
|
# ── Streaming event taxonomy (the UX 命脉). Each SSE `data:` line is one JSON-serialized AgentEvent. ──
|
|
9349
9614
|
# TWO vocabularies (the pinned wire contract Drift 3). ⚠️ The two lines below are the SHAPE-DEFINING
|
|
@@ -9568,12 +9833,16 @@ components:
|
|
|
9568
9833
|
DeniedBy:
|
|
9569
9834
|
type: string
|
|
9570
9835
|
description: >
|
|
9571
|
-
WHO REFUSED a call — the LAYER whose verdict is the deny (core
|
|
9572
|
-
CLOSED). The other question a deny raises — who ASKED — is
|
|
9573
|
-
share one seven-word list in which `classifier` meant both.
|
|
9836
|
+
WHO REFUSED a call — the LAYER whose verdict is the deny (core `DENIED_BY_VALUES`; 8 words in 7.6.0,
|
|
9837
|
+
NINE from core 7.9.0 / server >= 7.67.0, CLOSED). The other question a deny raises — who ASKED — is
|
|
9838
|
+
answered by `AskOrigin`; the two used to share one seven-word list in which `classifier` meant both.
|
|
9574
9839
|
`policy` = the deployment `ToolPolicy` denied (directly, or re-checking an approved edit), or the
|
|
9575
9840
|
approval-edit chain hit its round cap · `hook` = a PreToolUse hook denied, threw, or never answered ·
|
|
9576
|
-
`org` = an organization policy rule denied · `
|
|
9841
|
+
`org` = an organization policy rule denied · `persisted_rule` = the person's OWN standing `deny` row
|
|
9842
|
+
denied (the deny list of their settings, imported — reachable from core 7.9.0 #625's three-behavior
|
|
9843
|
+
persisted rules). It is the personal-store sibling of `org`: same VETO family (it can overturn an approval
|
|
9844
|
+
a person already gave, and it re-judges an edited command), which is why it is neither `policy` nor
|
|
9845
|
+
`ask_resolution` · `classifier` = the auto-mode classifier denied directly ·
|
|
9577
9846
|
`plan_mode` = plan mode's read-only block on a write tool · `compliance` = the compliance call-time
|
|
9578
9847
|
lock · `write_protection` = an approved edit was rewritten onto a write-protected path no approval
|
|
9579
9848
|
covers · `ask_resolution` = the ask's own settlement IS the refusal (detail on `GateOutcome.settlement`).
|
|
@@ -9582,7 +9851,7 @@ components:
|
|
|
9582
9851
|
record WHOLE (report + withhold). A word outside this set therefore cannot reach a consumer — it arrives
|
|
9583
9852
|
as the `gate` key being ABSENT, never as an unfamiliar word. Same rule for `Settlement.kind` and for
|
|
9584
9853
|
`GateOutcome.origin`.
|
|
9585
|
-
enum: [policy, hook, org, classifier, plan_mode, compliance, write_protection, ask_resolution]
|
|
9854
|
+
enum: [policy, hook, org, persisted_rule, classifier, plan_mode, compliance, write_protection, ask_resolution]
|
|
9586
9855
|
|
|
9587
9856
|
Settlement:
|
|
9588
9857
|
description: >
|
|
@@ -9751,7 +10020,7 @@ components:
|
|
|
9751
10020
|
# 服务对缺陷记录是整条不上帧 ⇒ 词表外的值在这条 wire 上到不了消费端。审批帧那一面不判成员,
|
|
9752
10021
|
# 所以 `AskOrigin` 本体保持真开(见该 schema 的长注)。这张 enum 是本 spec 里该词表的**唯一**
|
|
9753
10022
|
# 执法点;core 加词时改这一处。
|
|
9754
|
-
- enum: [content_question, unresolvable, org_unavailable, org_rule, hook, ask_rule, denial_limit_fallback, shell_gate_tighten, safety_tighten, policy]
|
|
10023
|
+
- enum: [content_question, unresolvable, org_unavailable, org_rule, rule_store_unavailable, hook, ask_rule, denial_limit_fallback, shell_gate_tighten, safety_tighten, policy]
|
|
9755
10024
|
description: >
|
|
9756
10025
|
WHO ASKED. Same-present/same-absent as `settlement` (I1). CLOSED here (and only here) — see the
|
|
9757
10026
|
enum note above.
|
|
@@ -10292,33 +10561,7 @@ components:
|
|
|
10292
10561
|
🔴 The engine''s `error` free text is DELIBERATELY NOT PROJECTED (it is remote-author text, and core
|
|
10293
10562
|
already owns the single redaction mint point for it). The actionable cause is `errorCode`.
|
|
10294
10563
|
A row missing `name` or `status` is dropped individually — the other servers'' rows still ship.
|
|
10295
|
-
items:
|
|
10296
|
-
type: object
|
|
10297
|
-
additionalProperties: false
|
|
10298
|
-
required: [name, status]
|
|
10299
|
-
properties:
|
|
10300
|
-
name: { type: string, description: 'The declared server name.' }
|
|
10301
|
-
status: { type: string, description: 'Three-word engine set (connected / failed / …). Open on read.' }
|
|
10302
|
-
source: { type: string, description: 'The declarer''s own layer label, echoed verbatim by core (single-line, length-capped).' }
|
|
10303
|
-
toolCount: { type: integer, description: 'Tools mounted. ABSENT rather than 0 — a minted 0 would read as "connected with no tools".' }
|
|
10304
|
-
errorCode:
|
|
10305
|
-
type: string
|
|
10306
|
-
description: >
|
|
10307
|
-
core >= 7.6.0 — the `McpFailureKind` word (`connect_refused` / `connection_failed` /
|
|
10308
|
-
`connection_closed` / `http_status` / `not_mcp_response` / `spawn_failed` / `timeout` /
|
|
10309
|
-
`protocol` / `invalid_config` / `unknown`). The retired 7.5.0 spellings — `network` and the
|
|
10310
|
-
`http_<status>` encoding — are GONE; an HTTP status now rides `httpStatus`.
|
|
10311
|
-
Deliberately NOT enumerated here: the vocabulary''s single owner is the engine, and mirroring
|
|
10312
|
-
it would swallow a newly minted word as a violation. Switch with a `default` arm.
|
|
10313
|
-
delivered:
|
|
10314
|
-
allOf: [{ $ref: '#/components/schemas/McpDelivered' }]
|
|
10315
|
-
description: 'core >= 7.6.0 (S6-B) — did the request reach that server. Read WITH `errorCode`.'
|
|
10316
|
-
httpStatus:
|
|
10317
|
-
type: integer
|
|
10318
|
-
description: >
|
|
10319
|
-
core >= 7.6.0 (S6-B) — the status the endpoint answered with; rides ONLY with
|
|
10320
|
-
`errorCode === "http_status"` (401/403 = re-authorize, 5xx = that end is down). ABSENT rather
|
|
10321
|
-
than 0.
|
|
10564
|
+
items: { $ref: '#/components/schemas/WiringManifestMcpEntry' }
|
|
10322
10565
|
governance:
|
|
10323
10566
|
type: object
|
|
10324
10567
|
additionalProperties: false
|
|
@@ -10453,6 +10696,28 @@ components:
|
|
|
10453
10696
|
message varies with the memoryProvenance mode — matching on message text WILL break). `code` is an OPEN
|
|
10454
10697
|
set (the server whitelist grows with core's code register): render a known code specially, fall back to
|
|
10455
10698
|
`message` for an unknown one — never drop the frame.
|
|
10699
|
+
🔴 This list is NOT the full register and is not meant to become one (server 7.69.0's whitelist holds 21
|
|
10700
|
+
codes): `code` is OPEN, and only the codes with an EXTRA rendering rule are written out here.
|
|
10701
|
+
`mcp.injection_dropped` (server >= 7.68.0) — one entry of the CALLER'S OWN request-leg MCP injection was
|
|
10702
|
+
not mounted; `{sessionId, server, reason, field?}`, `reason` a four-word set (`malformed_entry` /
|
|
10703
|
+
`name_reserved_by_deployment` / `gate_closed` / `over_cap`). The recovery verb is on the USER's side
|
|
10704
|
+
(rename it, fix that key, drop a few servers) — without this notice all they see is "my .mcp.json seems
|
|
10705
|
+
to do nothing". Minted at ASSEMBLY time, so `detail` structurally has NO runId; the join key is the
|
|
10706
|
+
`server` name. ⚠️ The DELIVERY surface changed at server 7.69.0: 7.68.0 delivered it on the two FRESH
|
|
10707
|
+
legs only, and 7.69.0 delivers it on the RESUME family too (all four resume legs share one
|
|
10708
|
+
spec-resolver, which now passes the notice seat). Consequence for consumers that assert on ledger
|
|
10709
|
+
CONTENTS: a resume leg's ledger MAY now carry one extra `engine_notice` row it did not before — assert
|
|
10710
|
+
"may appear", never an exact row count or order (and consume idempotently: reconnect replay shows it again).
|
|
10711
|
+
`delegation.ask_unresolvable` (server >= 7.69.0 / core 7.10.0 #648) — an `ask` reached a FINAL DENY with
|
|
10712
|
+
NOBODY having ruled on it: the approver consulted for the call answered `unavailable` and no durable park
|
|
10713
|
+
caught it afterwards. The deny itself is unchanged (`tool_end.gate.settlement.kind:"approver_unavailable"`
|
|
10714
|
+
— that sentence IS the tool result); this code is the half the person watching notices could not see.
|
|
10715
|
+
`{sessionId, toolName, toolCallId, settlementKind, parkLaneExisted}`; `toolCallId` joins to the
|
|
10716
|
+
`tool_approval` card frame, `settlementKind` is passed through verbatim (the vocabulary's owner is core).
|
|
10717
|
+
Deduplicated ONCE PER TOOL CALL. 🔴 `parkLaneExisted` is a DISCRIMINANT, not a count: `true` = a park lane
|
|
10718
|
+
was armed but declined/failed (check the store, check authorisation), `false` = there was no lane at all
|
|
10719
|
+
(wire one up). The two recovery verbs differ, so a consumer MUST render them apart — NEVER fold them into
|
|
10720
|
+
one "nobody approved it".
|
|
10456
10721
|
Starter whitelist (server 7.36): `memory.session_polluted` `{reason, sessionId?}` ·
|
|
10457
10722
|
`memory.harvest_quarantined` `{count, moved, escalated, reason?}` — `moved` and `escalated` MUST NOT be
|
|
10458
10723
|
subtracted from each other (an in-place tombstone counts as both) · `memory.delegation_static_mark_waived`
|
|
@@ -11129,6 +11394,12 @@ components:
|
|
|
11129
11394
|
# 分两段:server ≤7.4.0 默认 OFF,≥7.5.0 默认 ON(BREAKING,见 server CHANGELOG)。──
|
|
11130
11395
|
# 与上面的 ToolApprovalFrame 是**并行加帧,不替换**:开关打开时同一只 ask 出两帧(先 tool_approval,
|
|
11131
11396
|
# 后 approval_request),两帧同带 `approvalId` ⇒ 消费端按它去重、优先渲染新帧。
|
|
11397
|
+
classifierUnavailable:
|
|
11398
|
+
$ref: '#/components/schemas/ClassifierUnavailable'
|
|
11399
|
+
description: >-
|
|
11400
|
+
server >= 7.69.0 (core 7.10.0 #616), ADDITIVE, present only when the classifier was consulted and
|
|
11401
|
+
could not run — WHY this ask reached a human. See ClassifierUnavailable for the open-`cause` rule and
|
|
11402
|
+
the three shapes absence covers.
|
|
11132
11403
|
ApprovalRequestFrame:
|
|
11133
11404
|
type: object
|
|
11134
11405
|
description: >
|
|
@@ -11261,6 +11532,12 @@ components:
|
|
|
11261
11532
|
# 但本 schema 此前只在帧上公示过。长 description 的单一真源在 ToolApprovalFrame 的同名键。
|
|
11262
11533
|
ruleOffersAbsence: { $ref: '#/components/schemas/RuleOffersAbsence' }
|
|
11263
11534
|
denialLimitFallback: { $ref: '#/components/schemas/DenialLimitFallback' }
|
|
11535
|
+
classifierUnavailable:
|
|
11536
|
+
$ref: '#/components/schemas/ClassifierUnavailable'
|
|
11537
|
+
description: >-
|
|
11538
|
+
server >= 7.69.0 (core 7.10.0 #616), ADDITIVE — WHY this ask reached a human. Frame and card carry
|
|
11539
|
+
the SAME value (one narrow-read function server-side). See ClassifierUnavailable for the open-`cause`
|
|
11540
|
+
rule and the three shapes absence covers.
|
|
11264
11541
|
|
|
11265
11542
|
RuleSuggestion:
|
|
11266
11543
|
type: object
|
|
@@ -11284,6 +11561,30 @@ components:
|
|
|
11284
11561
|
command: { type: string, maxLength: 512, description: 'The command pattern inside the rule, for a shell that would rather not re-parse `rule`.' }
|
|
11285
11562
|
|
|
11286
11563
|
# ─── S-139(sdk 8.3.0):对 server 7.60.0 整体重对账带进来的四张型面 ───────────────────────
|
|
11564
|
+
ClassifierUnavailable:
|
|
11565
|
+
# 8.9.0:三面(tool_approval 帧 / ApprovalCard / InboxRow)同形同值 ⇒ 具名单源,内联三份会各自漂。
|
|
11566
|
+
type: object
|
|
11567
|
+
description: >-
|
|
11568
|
+
WHY this ask reached a human (server >= 7.69.0, core 7.10.0 #616): the auto-mode classifier was consulted
|
|
11569
|
+
and could not run. Carried on all THREE faces with the same value — the live `tool_approval` frame, the
|
|
11570
|
+
`ApprovalCard` (`card_json` / `approval_request`), and the `GET /v1/assistant/inbox` row. Under the auto
|
|
11571
|
+
mode most calls are answered by the classifier, so the ones that DO reach a person are often exactly the
|
|
11572
|
+
ones it could not answer — and the causes want different handling (the gateway is broken / it is too slow
|
|
11573
|
+
/ the breaker is OPEN, i.e. every following ask will arrive too). The inbox is the TRIAGE face, which is
|
|
11574
|
+
where `breaker_open` is worth the most: it forecasts an approval storm, not a one-off question.
|
|
11575
|
+
🔴 `cause` is an OPEN string and is deliberately NOT enumerated here. The vocabulary's single owner is the
|
|
11576
|
+
engine (today: `error` / `timeout` / `breaker_open`); mirroring it would swallow a newly minted word as a
|
|
11577
|
+
violation — the same rule as `AskOrigin` and the MCP `errorCode`. Switch on the known words and KEEP A
|
|
11578
|
+
DEFAULT ARM.
|
|
11579
|
+
🔴 ABSENCE IS NOT AN ASSERTION. It covers three shapes at once: the classifier ANSWERED (this ask is one
|
|
11580
|
+
it ruled should go to a person) · this ask was not ELIGIBLE for the classifier · this deployment wired no
|
|
11581
|
+
classifier at all. Never read absence as any claim about classifier health, and never as "the classifier
|
|
11582
|
+
is fine". (`parse_error` does not set this bit, per the engine's contract.)
|
|
11583
|
+
additionalProperties: false
|
|
11584
|
+
required: [cause]
|
|
11585
|
+
properties:
|
|
11586
|
+
cause: { type: string, description: 'The engine''s cause word. OPEN — branch known words, default arm for the rest.' }
|
|
11587
|
+
|
|
11287
11588
|
AskOrigin:
|
|
11288
11589
|
type: string
|
|
11289
11590
|
x-open-enum: true
|
|
@@ -11293,7 +11594,7 @@ components:
|
|
|
11293
11594
|
# 当天,一个**合法**的审批卡就会被严格消费端拒收。`x-open-enum` 只是意图标记,不解除 enum 的执法 ——
|
|
11294
11595
|
# 这正是本车刚从 `CheckpointGate` 拆掉的那个假开集,不在这里换个地方重犯。
|
|
11295
11596
|
# 闭集**执法**只属于**筛过的**那一面(`GateOutcome.origin`:引擎的 `screenGateOutcome` 判成员,
|
|
11296
|
-
# 出集即整条不上帧),所以那张 enum
|
|
11597
|
+
# 出集即整条不上帧),所以那张 enum 落在**那个引用点**,恰一处。已知十一词见下面的描述。
|
|
11297
11598
|
description: >
|
|
11298
11599
|
WHO raised an ask (`ToolApprovalFrame.origin`; server >= 7.57.0 / core 7.5.0, S-125③/#564) — core's
|
|
11299
11600
|
`ASK_ORIGINS`, ENGINE-STAMPED at the gate (core's words: "engine-stamped at the gate, never a policy's
|
|
@@ -11315,6 +11616,11 @@ components:
|
|
|
11315
11616
|
word could not. Both are classifier-eligible (arming auto mode is the deployment's explicit choice to
|
|
11316
11617
|
let the classifier resolve those classes).
|
|
11317
11618
|
|
|
11619
|
+
🔴 core 7.9.0 ADDS AN ELEVENTH WORD (server >= 7.67.0): `rule_store_unavailable` = the gate's own
|
|
11620
|
+
PERSISTED-RULE lane could not be READ for this call, so its deny/ask rows are unenforceable and the gate
|
|
11621
|
+
fails closed to a person. It is the personal-store sibling of `org_unavailable` (a governance source that
|
|
11622
|
+
cannot be read means ASK, never a silent allow).
|
|
11623
|
+
|
|
11318
11624
|
The SAME type is the `origin` member of `GateOutcome` on `tool_end` — one vocabulary, two faces.
|
|
11319
11625
|
|
|
11320
11626
|
RuleOffersAbsence:
|
|
@@ -11356,8 +11662,13 @@ components:
|
|
|
11356
11662
|
waits for a person) is for RENDERING THE COUNTDOWN ONLY. Never start a second timer from it: the window
|
|
11357
11663
|
is executed by the ENGINE, and two overlapping windows are worse than the original defect and silent.
|
|
11358
11664
|
🔴 ABSENCE IS NOT AN ASSERTION — the vast majority of asks are not fallback cards.
|
|
11359
|
-
⚠️
|
|
11360
|
-
a
|
|
11665
|
+
⚠️ TWO LEGS CARRY IT, and they differ in ONE member. The live/card leg (`ToolApprovalFrame` /
|
|
11666
|
+
`ApprovalCard` / `card_json`) can carry a NON-ZERO `autoDenyAfterMs`; the DURABLE leg — the parked row,
|
|
11667
|
+
projected onto `InboxRow` since server >= 7.57.0 (core's park twin of the same member) — always carries
|
|
11668
|
+
`0` there, because nothing counts down on a parked lane (see InboxRow.denialLimitFallback).
|
|
11669
|
+
(Corrected 2026-09-10 against core's checkpoint-summary type and the server's inbox projection: this
|
|
11670
|
+
sentence used to read "the durable (parked) leg does NOT carry this key today", which stopped being
|
|
11671
|
+
true when the park twin landed.)
|
|
11361
11672
|
properties:
|
|
11362
11673
|
consecutive: { type: integer, description: 'Consecutive refusals at the moment the limit tripped.' }
|
|
11363
11674
|
total: { type: integer, description: 'Cumulative refusals at the moment the limit tripped.' }
|
|
@@ -12943,12 +13254,18 @@ components:
|
|
|
12943
13254
|
core's assembly self-evidence manifest (core >=5.14.0 `WiringManifest`). TWO HALVES, ONE shape (a
|
|
12944
13255
|
consumer must line the two faces up, so they are not split into separate types) — but which keys
|
|
12945
13256
|
belong to which half is DETERMINATE, not "usually": `leg`, `ask.effective` and `configFingerprint`
|
|
12946
|
-
are EFFECTIVE-half only (the live `wiring_manifest` event) — as are the
|
|
12947
|
-
|
|
12948
|
-
|
|
13257
|
+
are EFFECTIVE-half only (the live `wiring_manifest` event) — as are the four per-leg sections
|
|
13258
|
+
`modelGate`, `autoMode`, `mcp` and `tools`, which describe what THIS leg did and have no static
|
|
13259
|
+
counterpart — and the STATIC half
|
|
12949
13260
|
(GET /v1/diagnostics/wiring) NEVER mints them; every other section, plus `governance`, is always
|
|
12950
13261
|
present on the static half. Fields stay optional because core may add or drop sections and that
|
|
12951
13262
|
must not hard-break a client — read by half, and never wait on a key the half never sends.
|
|
13263
|
+
🔴 EVERY section is declared HERE, each saying which half mints it (sdk 8.9.0). Before that, `tools`
|
|
13264
|
+
was declared while its three effective-half siblings were not, and `permissionRules` — which the
|
|
13265
|
+
engine mints UNCONDITIONALLY, i.e. on BOTH halves — was missing altogether, so a generated client
|
|
13266
|
+
reading GET /v1/diagnostics/wiring could not see it at all. "Two halves, one shape" is the stated
|
|
13267
|
+
doctrine of this schema; omitting a section because one half does not mint it contradicts it and
|
|
13268
|
+
splits consumers into two groups reading two different manifests.
|
|
12952
13269
|
🔴 `governance` and `configFingerprint` appear ONLY on an operator-scoped face. A tenant stream
|
|
12953
13270
|
gets neither — and NOT just the section: core hashes the WHOLE manifest unsalted and governance is
|
|
12954
13271
|
four booleans, so the fingerprint alone would let a tenant brute-force sixteen combinations against
|
|
@@ -13019,11 +13336,100 @@ components:
|
|
|
13019
13336
|
description: >
|
|
13020
13337
|
server >= 7.66.0 (design/388 B-4) — the leg's whole tool roster. EFFECTIVE half only (the diagnostics
|
|
13021
13338
|
endpoint's static half never carries it); absent as a whole when core's typebox check rejects it.
|
|
13339
|
+
permissionRules:
|
|
13340
|
+
type: object
|
|
13341
|
+
description: >
|
|
13342
|
+
core >= 5.18.0 (design/179) / >= 5.23.0 (design/182 §7/§9), server >= 7.6.0 — is a PERSISTED
|
|
13343
|
+
PERMISSION RULE store wired, how far does a "don't ask again" travel, and is an org governance layer
|
|
13344
|
+
armed over that lane. 🔴 BOTH HALVES: the engine mints this section unconditionally, so it rides the
|
|
13345
|
+
static `GET /v1/diagnostics/wiring` face too — read it before offering a "don't ask again"
|
|
13346
|
+
affordance. `false` is a REAL reading ("no rule lane on this worker"), never "too old to know"; only
|
|
13347
|
+
the whole key being absent means "never sent it". TENANT-visible (deliberately outside the
|
|
13348
|
+
operator-only `governance` section). Full per-key contract: see Event_wiring_manifest.permissionRules.
|
|
13349
|
+
properties:
|
|
13350
|
+
storeWired: { type: boolean }
|
|
13351
|
+
syncWired: { type: boolean }
|
|
13352
|
+
orgGoverned: { type: boolean }
|
|
13353
|
+
mcp:
|
|
13354
|
+
type: array
|
|
13355
|
+
description: >
|
|
13356
|
+
core >= 7.5.0 (#562) / >= 7.6.0 (S6-B), server >= 7.60 — one row per MCP server THIS leg declared.
|
|
13357
|
+
EFFECTIVE half only. 🔴 An empty array is NOT absence (`[]` = "this leg declared no servers").
|
|
13358
|
+
Full per-key contract: see Event_wiring_manifest.mcp.
|
|
13359
|
+
items: { $ref: '#/components/schemas/WiringManifestMcpEntry' }
|
|
13360
|
+
modelGate:
|
|
13361
|
+
type: object
|
|
13362
|
+
description: >
|
|
13363
|
+
core >= 7.3.x (design/385 片2b), server >= 7.58 — which tools the tool-MODEL gate removed from THIS
|
|
13364
|
+
session and how to get them back. EFFECTIVE half only; ALL-OR-NOTHING (absent = nothing was gated,
|
|
13365
|
+
never an empty section). Full contract: see Event_wiring_manifest.modelGate.
|
|
13366
|
+
required: [class, removed, restore]
|
|
13367
|
+
properties:
|
|
13368
|
+
class: { type: string }
|
|
13369
|
+
removed: { type: array, items: { type: string } }
|
|
13370
|
+
restore: { type: string }
|
|
13371
|
+
autoMode:
|
|
13372
|
+
type: object
|
|
13373
|
+
description: >
|
|
13374
|
+
core >= 7.3.1 (#529), server >= 7.59 — did AUTO mode arm on THIS leg, and if not which arm it stopped
|
|
13375
|
+
at. EFFECTIVE half only. 🔴 `reason` is core's closed word list passed through VERBATIM (branch known
|
|
13376
|
+
words, keep a default arm); ABSENCE IS NOT "not applicable" — never fold it to `armed: false`. The
|
|
13377
|
+
server projects exactly these two keys (core's `breaker` sub-fact is NOT projected onto the wire).
|
|
13378
|
+
Full contract: see Event_wiring_manifest.autoMode.
|
|
13379
|
+
required: [armed, reason]
|
|
13380
|
+
properties:
|
|
13381
|
+
armed: { type: boolean }
|
|
13382
|
+
reason: { type: string }
|
|
13383
|
+
WiringManifestMcpEntry:
|
|
13384
|
+
# 8.9.0:从 `Event_wiring_manifest.mcp.items` 的内联形**提取**为具名 schema —— 同一份行形现在被
|
|
13385
|
+
# 两处引用(live 帧与 `WiringManifest` 的静态/两半形),内联会立刻变成两份会各自漂的镜像。
|
|
13386
|
+
type: object
|
|
13387
|
+
description: >
|
|
13388
|
+
One row of a leg's MCP wiring manifest: did that declared server connect, if not which failure class,
|
|
13389
|
+
and how many tools it mounted. A row missing `name` or `status` is dropped INDIVIDUALLY — the other
|
|
13390
|
+
servers' rows still ship. 🔴 The engine's `error` free text is DELIBERATELY NOT PROJECTED (remote-author
|
|
13391
|
+
text; core owns the single redaction mint point). The actionable cause is `errorCode`.
|
|
13392
|
+
additionalProperties: false
|
|
13393
|
+
required: [name, status]
|
|
13394
|
+
properties:
|
|
13395
|
+
name: { type: string, description: 'The declared server name.' }
|
|
13396
|
+
status: { type: string, description: 'Three-word engine set (connected / failed / …). Open on read.' }
|
|
13397
|
+
source: { type: string, description: 'The declarer''s own layer label, echoed verbatim by core (single-line, length-capped).' }
|
|
13398
|
+
toolCount: { type: integer, description: 'Tools mounted. ABSENT rather than 0 — a minted 0 would read as "connected with no tools".' }
|
|
13399
|
+
errorCode:
|
|
13400
|
+
type: string
|
|
13401
|
+
description: >
|
|
13402
|
+
core >= 7.6.0 — the `McpFailureKind` word (`connect_refused` / `connection_failed` /
|
|
13403
|
+
`connection_closed` / `http_status` / `not_mcp_response` / `spawn_failed` / `timeout` /
|
|
13404
|
+
`protocol` / `invalid_config` / `unknown`). The retired 7.5.0 spellings — `network` and the
|
|
13405
|
+
`http_<status>` encoding — are GONE; an HTTP status now rides `httpStatus`.
|
|
13406
|
+
Deliberately NOT enumerated here: the vocabulary's single owner is the engine, and mirroring
|
|
13407
|
+
it would swallow a newly minted word as a violation. Switch with a `default` arm.
|
|
13408
|
+
delivered:
|
|
13409
|
+
allOf: [{ $ref: '#/components/schemas/McpDelivered' }]
|
|
13410
|
+
description: 'core >= 7.6.0 (S6-B) — did the request reach that server. Read WITH `errorCode`.'
|
|
13411
|
+
httpStatus:
|
|
13412
|
+
type: integer
|
|
13413
|
+
description: >
|
|
13414
|
+
core >= 7.6.0 (S6-B) — the status the endpoint answered with; rides ONLY with
|
|
13415
|
+
`errorCode === "http_status"` (401/403 = re-authorize, 5xx = that end is down). ABSENT rather
|
|
13416
|
+
than 0.
|
|
13417
|
+
|
|
13022
13418
|
ServerWiringGates:
|
|
13023
13419
|
type: object
|
|
13024
13420
|
description: >
|
|
13025
|
-
The server's OWN assembly predicates (the half core's manifest does not cover)
|
|
13026
|
-
|
|
13421
|
+
The server's OWN assembly predicates (the half core's manifest does not cover) — server
|
|
13422
|
+
`routes/diagnostics.ts` `buildServerWiringGates`.
|
|
13423
|
+
|
|
13424
|
+
🔴 BREAKING (server >= 7.67.0 / S-178): the three POSTURE-FAMILY knobs are now `{value, source, note}`
|
|
13425
|
+
readings instead of bare values, and two of those rows are NEW. `streamApproval` is a GATE reading (a
|
|
13426
|
+
different axis: is this leg live) and `checkpointStore` is a presence bit — neither is a knob, so neither
|
|
13427
|
+
changed by one byte. Migration: `serverGates.durableApproval` -> `serverGates.durableApproval.value`.
|
|
13428
|
+
|
|
13429
|
+
⚠️ THIS SCHEMA DESCRIBES ONE GENERATION (same treatment as `TaskResult.terminal` in SDK 8.4.0): against a
|
|
13430
|
+
worker < 7.67.0 `durableApproval` is still a bare boolean on the wire and the other two keys are absent.
|
|
13431
|
+
The discriminator is the PEER'S VERSION, not the presence of these keys.
|
|
13432
|
+
required: [streamApproval, durableApproval, streamAskWindowMs, sessionAutoTitle, checkpointStore]
|
|
13027
13433
|
additionalProperties: false
|
|
13028
13434
|
properties:
|
|
13029
13435
|
streamApproval:
|
|
@@ -13033,7 +13439,39 @@ components:
|
|
|
13033
13439
|
same five-way predicate that gates `capabilities.streamApproval` and the decision endpoint's
|
|
13034
13440
|
501 — so the diagnostics page and the consumer-facing behavior cannot disagree.
|
|
13035
13441
|
enum: [active, no_tool_approval, protocol_disabled, no_backend, volatile_ask_ledger, no_park_facility]
|
|
13036
|
-
durableApproval:
|
|
13442
|
+
durableApproval:
|
|
13443
|
+
allOf:
|
|
13444
|
+
- { $ref: '#/components/schemas/PostureKnobReading' }
|
|
13445
|
+
- properties: { value: { type: boolean } }
|
|
13446
|
+
description: >
|
|
13447
|
+
`DURABLE_APPROVAL` (the durable-checkpoint gate's master switch) AND who set it. 🔴 On a single-user
|
|
13448
|
+
turnkey worker (REQUIRE_PRINCIPAL unset) the ABSENT default is now ON — conjoined with a durable store
|
|
13449
|
+
actually being present, so a `DB_BACKEND=memory` worker still mints nothing. A multi-tenant worker's
|
|
13450
|
+
absent default is unchanged (off). Pin it back with `DURABLE_APPROVAL=false` on the machine
|
|
13451
|
+
(`source: "env"` always wins).
|
|
13452
|
+
streamAskWindowMs:
|
|
13453
|
+
allOf:
|
|
13454
|
+
- { $ref: '#/components/schemas/PostureKnobReading' }
|
|
13455
|
+
# 🔴 `number`,不是 `integer`:server 的 `parseNumOrFail`(config.ts)只判 `Number.isFinite`,
|
|
13456
|
+
# 于是 `STREAM_ASK_WINDOW_MS=300000.5` 是一个**能启动**的部署,而这个值原样上诊断面 ——
|
|
13457
|
+
# 写 `integer` 会把一台合法 worker 的真实响应判违约。下界 0 是**真有**的拒启门(负窗拒启)。
|
|
13458
|
+
- properties: { value: { type: number, minimum: 0 } }
|
|
13459
|
+
description: >
|
|
13460
|
+
The approval window in milliseconds AND who set it. 🔴 On a single-user turnkey worker the absent
|
|
13461
|
+
default moved from 5 minutes to 24 HOURS (a person stepping away for ten minutes should not come back
|
|
13462
|
+
to a run that settled itself unattended); multi-tenant is unchanged at 300000. Pin it back with
|
|
13463
|
+
`STREAM_ASK_WINDOW_MS=300000`.
|
|
13464
|
+
⚠️ POSTURE ONLY FILLS AN EMPTY SEAT: where the derived 24h would collide with the existing
|
|
13465
|
+
cross-knob refuse-to-start inequality (`WINDOW + ADHOC_GRACE < ORPHAN_TTL`), posture YIELDS back to the
|
|
13466
|
+
engine default 300000 and reports `source: "engine-default"` rather than refusing to boot a deployment
|
|
13467
|
+
that changed nothing.
|
|
13468
|
+
sessionAutoTitle:
|
|
13469
|
+
allOf:
|
|
13470
|
+
- { $ref: '#/components/schemas/PostureKnobReading' }
|
|
13471
|
+
- properties: { value: { type: boolean } }
|
|
13472
|
+
description: >
|
|
13473
|
+
`SESSION_AUTO_TITLE` AND who set it. Its default is the SAME on every deployment shape (true), so this
|
|
13474
|
+
knob has NO posture arm — an unset one reads `source: "engine-default"` and says so in `note`.
|
|
13037
13475
|
checkpointStore: { type: boolean, description: 'A checkpoint store is wired (= the park facility exists).' }
|
|
13038
13476
|
|
|
13039
13477
|
WiringDiagnostics:
|
|
@@ -13706,12 +14144,32 @@ components:
|
|
|
13706
14144
|
kind: { type: string, enum: [project] }
|
|
13707
14145
|
root: { type: string, minLength: 1, description: 'The canonical directory this rule is scoped to. NEVER empty — an empty prefix contains EVERY cwd, which would silently promote a project rule to a global one.' }
|
|
13708
14146
|
|
|
14147
|
+
RuleBehavior:
|
|
14148
|
+
type: string
|
|
14149
|
+
description: >
|
|
14150
|
+
WHICH OF THE THREE a persisted rule is (core `RULE_BEHAVIORS`, closed; listed in PRECEDENCE order, so
|
|
14151
|
+
when rules of more than one behavior speak for one call the earliest member decides).
|
|
14152
|
+
|
|
14153
|
+
🔴 A RULE'S IDENTITY IS THE TRIPLE (behavior, text, scope) — core `sameRuleIdentity`; server >= 7.67.0 /
|
|
14154
|
+
core 7.9.0 #625. The same text under two behaviors is TWO DIFFERENT ROWS, which is why the revoke body
|
|
14155
|
+
requires this field and deliberately gives it NO default: a delete aimed at an `allow` that got defaulted
|
|
14156
|
+
onto the same-text `deny` removes a refusal the operator meant to keep, and answers 200.
|
|
14157
|
+
`allow` = the standing form of one recorded human approval ("don't ask again" on a card); `deny` / `ask` =
|
|
14158
|
+
the standing forms of "never run this" and "ask me every time", the same content-form rules the engine
|
|
14159
|
+
reads out of a settings file's deny/ask lists. ONE grammar, ONE canonical spelling and ONE matcher family
|
|
14160
|
+
serve all three — the behavior is a FIELD BESIDE the text, never part of it.
|
|
14161
|
+
enum: [deny, ask, allow]
|
|
14162
|
+
|
|
13709
14163
|
RuleCandidate:
|
|
13710
14164
|
type: object
|
|
13711
|
-
description:
|
|
14165
|
+
description: >
|
|
14166
|
+
One candidate rule: WHICH BEHAVIOR it is, the canonical text, and the scope it would land in (core
|
|
14167
|
+
`RuleCandidate`). `behavior` is new in server >= 7.67.0 / core 7.9.0 #625 — a card mints `allow`
|
|
14168
|
+
candidates only (a card is one person's yes), while the settings import now carries all three lists.
|
|
13712
14169
|
additionalProperties: false
|
|
13713
|
-
required: [rule, scope]
|
|
14170
|
+
required: [behavior, rule, scope]
|
|
13714
14171
|
properties:
|
|
14172
|
+
behavior: { $ref: '#/components/schemas/RuleBehavior' }
|
|
13715
14173
|
rule: { type: string, maxLength: 512 }
|
|
13716
14174
|
scope: { $ref: '#/components/schemas/RuleScope' }
|
|
13717
14175
|
|
|
@@ -13781,8 +14239,14 @@ components:
|
|
|
13781
14239
|
`uncovered` is a TWO-KEY RECORD, not a list: the two layers this version does not read are named in the
|
|
13782
14240
|
TYPE, so an empty list, a missing member or a duplicate is not expressible. A preview that reported only
|
|
13783
14241
|
the layers it read would be claiming a completeness it does not have.
|
|
14242
|
+
|
|
14243
|
+
🔴 server >= 7.67.0 / core 7.9.0 #625 — THE DENY AND ASK LISTS ARE IMPORTED TOO. Until then only the allow
|
|
14244
|
+
bucket was read and deny/ask entries were counted into an `uncovered.denyAskBuckets` seat that said "left
|
|
14245
|
+
in place"; now every candidate carries its own `behavior` and that seat is GONE — an entry that did not
|
|
14246
|
+
become a candidate is in `skipped` with its reason. A consumer rendering `uncovered.denyAskBuckets` reads
|
|
14247
|
+
`candidates[].behavior` instead.
|
|
13784
14248
|
additionalProperties: false
|
|
13785
|
-
required: [candidates, skipped, layers, uncovered]
|
|
14249
|
+
required: [candidates, skipped, translated, layers, uncovered]
|
|
13786
14250
|
properties:
|
|
13787
14251
|
candidates:
|
|
13788
14252
|
type: array
|
|
@@ -13792,6 +14256,25 @@ components:
|
|
|
13792
14256
|
type: array
|
|
13793
14257
|
items: { $ref: '#/components/schemas/CcImportSkippedRule' }
|
|
13794
14258
|
description: 'Entries that did not become candidates. `reason` is PROSE, not a code — see the schema.'
|
|
14259
|
+
translated:
|
|
14260
|
+
type: array
|
|
14261
|
+
description: >
|
|
14262
|
+
ALWAYS EMPTY in this version — a compatibility seat core keeps on the wire shape (core
|
|
14263
|
+
`ImportPreview.translated`, verbatim). It once carried entries whose MATCH FORM was rewritten on the
|
|
14264
|
+
way in (the other product's space-star suggestion form translated to this lane's colon-star). That
|
|
14265
|
+
translation is retired: space-star is now a first-class match form of this lane's own grammar, so an
|
|
14266
|
+
entry's match form imports as the file states it and nothing is ever pushed here. A reader rendering a
|
|
14267
|
+
"rewritten spellings" column from this seat renders an empty column, correctly.
|
|
14268
|
+
⚠️ Declared here from SDK 8.8.0 on: core has always emitted the key, so a closed schema without it
|
|
14269
|
+
judged every real 200 body a violation (a staleness fix, not a behavior change).
|
|
14270
|
+
items:
|
|
14271
|
+
type: object
|
|
14272
|
+
additionalProperties: false
|
|
14273
|
+
required: [from, to, scope]
|
|
14274
|
+
properties:
|
|
14275
|
+
from: { type: string }
|
|
14276
|
+
to: { type: string }
|
|
14277
|
+
scope: { $ref: '#/components/schemas/RuleScope' }
|
|
13795
14278
|
layers:
|
|
13796
14279
|
type: array
|
|
13797
14280
|
items: { $ref: '#/components/schemas/CcImportLayerReport' }
|
|
@@ -13831,9 +14314,12 @@ components:
|
|
|
13831
14314
|
indeterminate, release the claim and answer 503 `state.rule_import_retry` (server rules-consent.ts) —
|
|
13832
14315
|
so this schema honestly describes the two-value `status` only.
|
|
13833
14316
|
additionalProperties: false
|
|
13834
|
-
required: [candidateIndex, rule, scope, status, alreadyRedeemed, dot]
|
|
14317
|
+
required: [candidateIndex, behavior, rule, scope, status, alreadyRedeemed, dot]
|
|
13835
14318
|
properties:
|
|
13836
14319
|
candidateIndex: { type: integer, description: 'Index into the prepare preview''s candidate set (candidate space).' }
|
|
14320
|
+
behavior:
|
|
14321
|
+
allOf: [{ $ref: '#/components/schemas/RuleBehavior' }]
|
|
14322
|
+
description: 'server >= 7.67.0 / core 7.9.0 #625 — which of the three the landed row is (same value as the same-index candidate in the preview; one leg of the identity triple).'
|
|
13837
14323
|
rule: { type: string, description: 'Canonical rule text.' }
|
|
13838
14324
|
scope: { $ref: '#/components/schemas/RuleScope' }
|
|
13839
14325
|
status:
|
|
@@ -13915,27 +14401,71 @@ components:
|
|
|
13915
14401
|
PersistedRule:
|
|
13916
14402
|
type: object
|
|
13917
14403
|
description: >
|
|
13918
|
-
One LIVE persisted
|
|
14404
|
+
One LIVE persisted rule as `GET /v1/rules` puts it on the wire (server `PersistedRuleWireRow`).
|
|
13919
14405
|
Tombstones are already folded — every row here is live at `RuleListResult.rev`.
|
|
13920
14406
|
🔴 `scope` is the DISCRIMINANT STRING (`global` / `project:<root>`), NOT the `RuleScope` object the
|
|
13921
14407
|
import lane speaks: this is the exact byte sequence the revoke body wants back, so echo it VERBATIM
|
|
13922
14408
|
rather than re-serializing a parsed form (an equivalent-but-differently-spelled root matches nothing).
|
|
13923
|
-
|
|
14409
|
+
|
|
14410
|
+
🔴 THREE BEHAVIORS, NOT ALLOW-ONLY (server >= 7.67.0 / core 7.9.0 #625). Until 7.66.0 this schema said
|
|
14411
|
+
"the rule lane is ALLOW-only; deny/ask rules are the tightening direction and never appear here" — that
|
|
14412
|
+
sentence is now false, and `behavior` is the leg of the identity triple a revoke has to echo back.
|
|
13924
14413
|
additionalProperties: false
|
|
13925
|
-
required: [rule, scope, tool, match, command, adds]
|
|
14414
|
+
required: [behavior, rule, scope, tool, match, command, adds, source, status]
|
|
13926
14415
|
properties:
|
|
14416
|
+
behavior:
|
|
14417
|
+
allOf: [{ $ref: '#/components/schemas/RuleBehavior' }]
|
|
14418
|
+
description: 'Which of the three this row is. Part of the row''s IDENTITY — pass it back VERBATIM to revoke, or the delete aims at another behavior''s same-text row.'
|
|
13927
14419
|
rule: { type: string, description: 'Canonical rule text — `Bash(git status)` (exact) or `Bash(git status:*)` (prefix). Pass back VERBATIM to revoke.' }
|
|
13928
14420
|
scope: { type: string, description: 'Scope discriminant string: `global`, or `project:<root>` with a NON-EMPTY root. Pass back VERBATIM to revoke.' }
|
|
13929
|
-
tool:
|
|
14421
|
+
tool:
|
|
14422
|
+
type: string
|
|
14423
|
+
# 🔴 只钉非空,**不借**输入面那条 `maxLength: 190`(server 的 `LocalImportRowSchema` 帽的是**收进来的**
|
|
14424
|
+
# 行;列举面出去的行不过那道尺)。借一条别的面的约束来判这一面,迟早把一条合法行判成违约。
|
|
14425
|
+
minLength: 1
|
|
14426
|
+
description: >
|
|
14427
|
+
Which tool this row speaks for. 🔴 NOT A CLOSED SET from server >= 7.67.0 / core 7.9.0 #625 (the
|
|
14428
|
+
`enum: [Bash]` written here through SDK 8.7.0 is gone): the tool set is DERIVED from the engine
|
|
14429
|
+
catalogue's path-target declarations — `Bash` is the command grammar, any tool declaring a
|
|
14430
|
+
`pathTarget` (`Read` / `Write` / `Edit` / `Glob` / `Grep` / `NotebookEdit`, …) is the path grammar.
|
|
14431
|
+
The test is "does the engine's rule grammar speak for this name", and that roster lives in the
|
|
14432
|
+
engine's catalogue, not on this wire. READ IT; do not switch over a closed list. A name the grammar
|
|
14433
|
+
does not know is still a 400 on the input faces.
|
|
13930
14434
|
match:
|
|
13931
14435
|
type: string
|
|
13932
|
-
enum: [exact, prefix]
|
|
13933
|
-
description:
|
|
14436
|
+
enum: [exact, prefix, wildcard, subpath, path]
|
|
14437
|
+
description: >
|
|
14438
|
+
The match form (core `PersistedRuleMatch`, five members). `exact` = this one command only ·
|
|
14439
|
+
`prefix` = word-boundary prefix (the historical colon-star spelling, and the only spelling for a
|
|
14440
|
+
compound prefix) · `wildcard` = the space-star form, the SAME predicate as `prefix` on a single
|
|
14441
|
+
command body · `subpath` = the directory (read-containment) form, which never admits a command ·
|
|
14442
|
+
`path` = NEW in server >= 7.67.0, the path-family pattern grammar for deny/ask/allow rules (`//abs`,
|
|
14443
|
+
`~/`, root-relative, cwd-relative; `*` within a segment, `**` across segments).
|
|
14444
|
+
SDK 8.7.0 and earlier declared only the first two — a consumer rendering this cell off a closed list
|
|
14445
|
+
must add the other three.
|
|
13934
14446
|
command: { type: string, description: 'What the matcher actually compares against, parsed out of `rule` at mint time and stored so no consumer re-parses. Trust this + `match`, not your own parse of `rule`.' }
|
|
13935
14447
|
adds:
|
|
13936
14448
|
type: array
|
|
13937
14449
|
description: 'Every LIVE add of this rule (see RuleAdd — a real set, deliberately not folded).'
|
|
13938
14450
|
items: { $ref: '#/components/schemas/RuleAdd' }
|
|
14451
|
+
source:
|
|
14452
|
+
type: string
|
|
14453
|
+
enum: [user, project, session]
|
|
14454
|
+
description: >
|
|
14455
|
+
Which source this row came from (server >= 7.57.0 / core 7.5.0 design/389). DERIVED FROM `scope`
|
|
14456
|
+
(`global`<->`user`, `project`<->`project`, `session`<->`session`) — the row never carries a second
|
|
14457
|
+
source byte that could drift from its scope. With only the durable partition wired today the reachable
|
|
14458
|
+
values are `user | project`.
|
|
14459
|
+
⚠️ The IMPORT faces do NOT accept this key (nor `status`): POSTing a listing row straight back to
|
|
14460
|
+
local-import is a 400. Declared here from SDK 8.8.0 on — the server has emitted it since 7.57.0, so a
|
|
14461
|
+
closed schema without it judged every real listing row a violation.
|
|
14462
|
+
status:
|
|
14463
|
+
type: string
|
|
14464
|
+
enum: [live, shadowed-by-org]
|
|
14465
|
+
description: >
|
|
14466
|
+
The row's STANDING (server >= 7.57.0 / design/389): `live`, or `shadowed-by-org` when an org deny
|
|
14467
|
+
covers its command pattern. With no org partition wired today it is always `live` — branch on BOTH
|
|
14468
|
+
values now rather than optimising the constant away. Same staleness note as `source`.
|
|
13939
14469
|
|
|
13940
14470
|
RuleListResult:
|
|
13941
14471
|
type: object
|
|
@@ -13963,8 +14493,15 @@ components:
|
|
|
13963
14493
|
# 封闭:server 侧 zod 是 `.strict()` —— 把 `scope` 拼成 `scopes` 的客户端应当场知道,而不是
|
|
13964
14494
|
# 拿到一个「删掉了 global 那条」的意外结果。
|
|
13965
14495
|
additionalProperties: false
|
|
13966
|
-
required: [rule, scope]
|
|
14496
|
+
required: [behavior, rule, scope]
|
|
13967
14497
|
properties:
|
|
14498
|
+
behavior:
|
|
14499
|
+
allOf: [{ $ref: '#/components/schemas/RuleBehavior' }]
|
|
14500
|
+
description: >
|
|
14501
|
+
🔴 REQUIRED from server >= 7.67.0 (omit it and the body is a 400 `request.body_shape`) — WHICH
|
|
14502
|
+
BEHAVIOR's row to revoke, VERBATIM as the listing gave it. 🔴 DELIBERATELY NO DEFAULT: a revoke aimed
|
|
14503
|
+
at an `allow` that got defaulted onto the same-text `deny` deletes a refusal that was meant to stay,
|
|
14504
|
+
and hands the caller a 200. core's `sameRuleIdentity` names this exact failure.
|
|
13968
14505
|
rule: { type: string, minLength: 1, maxLength: 1024, description: 'The canonical rule text, VERBATIM as `RuleListResult.rules[].rule` gave it.' }
|
|
13969
14506
|
scope: { type: string, minLength: 1, maxLength: 4104, description: 'The scope discriminant string — `global` or `project:<root>` with a NON-EMPTY root (an empty root would match every cwd). VERBATIM as the listing gave it. Cap = "project:".length + server MAX_CWD_CHARS (8+4096) — the 1088 previously written here was the same stale independent copy the server itself fixed (rules.ts MAX_RULE_SCOPE_CHARS 顶注), found by the A-335 anchor sweep.' }
|
|
13970
14507
|
principal: { type: string, minLength: 1, maxLength: 190, description: 'OPERATOR-ONLY cross-tenant revoke. Absent (or your own principal) = revoke your own rule.' }
|