@sema-agent/sdk 8.1.0 → 8.2.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 +133 -0
- package/dist/events.d.ts +5 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +5 -1
- package/dist/events.js.map +1 -1
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/resources/tool-approvals.d.ts +291 -16
- package/dist/resources/tool-approvals.d.ts.map +1 -1
- package/dist/resources/tool-approvals.js +87 -0
- package/dist/resources/tool-approvals.js.map +1 -1
- package/dist/types.d.ts +44 -0
- package/dist/types.d.ts.map +1 -1
- package/openapi.yaml +419 -26
- package/package.json +1 -1
package/openapi.yaml
CHANGED
|
@@ -3358,31 +3358,21 @@ paths:
|
|
|
3358
3358
|
decision: { type: string, enum: [allow, allow_session, deny] }
|
|
3359
3359
|
updatedInput: { description: 'ctrl+g edited tool args (replaces the asked args on allow).' }
|
|
3360
3360
|
persistRule:
|
|
3361
|
-
|
|
3362
|
-
# 刻意**不**封闭:server 的 `parseToolApprovalResponse` 只读 `rule`,对象里多给的键它**忽略**
|
|
3363
|
-
# (与 `updatedInput` 同一条宽收姿势)。写 `additionalProperties:false` 会让照 spec 生成的
|
|
3364
|
-
# 客户端拒发一个 server 其实会受理的请求 —— 那是我们自己造的漂移,不是上游的约束。
|
|
3365
|
-
required: [rule]
|
|
3366
|
-
properties:
|
|
3367
|
-
rule: { type: string, minLength: 1, maxLength: 512, description: 'Non-empty (the server parser rejects an empty string); at most MAX_RULE_TEXT_CHARS = 512.' }
|
|
3361
|
+
allOf: [{ $ref: '#/components/schemas/PersistRuleSelection' }]
|
|
3368
3362
|
description: >
|
|
3369
|
-
|
|
3370
|
-
`rule`
|
|
3371
|
-
the
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
comparison, so it is never `rule_not_offered`). Absence has THREE possible causes — no rule
|
|
3383
|
-
store wired, the engine minted no candidate for this command (compound / redirect /
|
|
3384
|
-
substitution), or the command bytes were unreadable — so do NOT diagnose the deployment's
|
|
3385
|
-
rule lane from one frame or from that refusal.
|
|
3363
|
+
The person ticked "don't ask again". TWO MUTUALLY EXCLUSIVE ARMS — the candidate/edited arm
|
|
3364
|
+
`{ rule, edited? }` and the batch arm `{ batchOfferIndex }`; the shapes, the server's four
|
|
3365
|
+
verbatim 400 texts, the two capability bits to probe first and the `deny` posture are on
|
|
3366
|
+
the PersistRuleSelection schema. Allow-family only (on `deny` the whole field is ignored).
|
|
3367
|
+
|
|
3368
|
+
⚠️ If the frame carried neither `ruleOffers` nor `ruleSuggestions`, do not send this key —
|
|
3369
|
+
there is nothing redeemable for THIS card and the answer is necessarily
|
|
3370
|
+
`rulePersisted:false` + `rule_lane_unavailable` (the missing material short-circuits BEFORE
|
|
3371
|
+
any candidate comparison, so it is never `rule_not_offered`). Absence has several causes —
|
|
3372
|
+
no rule store wired, the engine minted no offer for this command (compound / redirect /
|
|
3373
|
+
substitution), the command bytes were unreadable, or this ask belongs to one of the seven
|
|
3374
|
+
families that never get offers — so do NOT diagnose the deployment's rule lane from one
|
|
3375
|
+
frame or from that one refusal.
|
|
3386
3376
|
note:
|
|
3387
3377
|
type: string
|
|
3388
3378
|
maxLength: 2048
|
|
@@ -6603,6 +6593,39 @@ components:
|
|
|
6603
6593
|
`noteRecorded` (a live-card-only deployment has no row to write, and a flapping store cannot write
|
|
6604
6594
|
one; both answer `false` while the decision itself still stands). This bit only says "this build
|
|
6605
6595
|
knows the key" — same family as `permissionRulesRevoke`, deliberately gated on no facility.
|
|
6596
|
+
respondFreeFormRules:
|
|
6597
|
+
type: boolean
|
|
6598
|
+
description: >
|
|
6599
|
+
server >= 7.44.0 (#340), hard-coded true in the capability object — the VERSION PROBE for the
|
|
6600
|
+
free-text arm of POST /v1/tool-approvals/{id}/respond (`persistRule.edited: true`): the rule to
|
|
6601
|
+
persist is text the person HAND-EDITED on the card, not one of the engine's candidates.
|
|
6602
|
+
|
|
6603
|
+
PROBE IT BEFORE RENDERING THE EDITABLE INPUT. The live respond body validator is a NON-STRICT
|
|
6604
|
+
hand-written parser, so an older worker DROPS the unknown `edited` key and judges the text AS A
|
|
6605
|
+
CANDIDATE, answering `rule_not_offered` — honest, but a person reads it as "I typed it wrong".
|
|
6606
|
+
Absent means an older worker: do not render the affordance.
|
|
6607
|
+
|
|
6608
|
+
IT DOES NOT PROMISE THIS ONE WILL LAND (deliberately no facility predicate, same family as
|
|
6609
|
+
`approvalDecisionNote`): that is per-call and answered by `rulePersisted` / `ruleRefusal` —
|
|
6610
|
+
`edit-unsupported` (this card has no binding anchor, STOP rendering the box) versus
|
|
6611
|
+
`edit-rejected` (fix it and try again). Whether the LANE exists at all is `permissionRules`,
|
|
6612
|
+
computed per caller; read the two bits together.
|
|
6613
|
+
respondBatchRuleOffers:
|
|
6614
|
+
type: boolean
|
|
6615
|
+
description: >
|
|
6616
|
+
server >= 7.46.0 (design/377), hard-coded true — the VERSION PROBE for the batch arm of the same
|
|
6617
|
+
endpoint (`persistRule.batchOfferIndex`): the person ticked a `kind: "batch"` offer, and the
|
|
6618
|
+
selection key is that offer's index in the frame's `ruleOffers`.
|
|
6619
|
+
|
|
6620
|
+
PROBE IT BEFORE RENDERING A REDEEMABLE BATCH AFFORDANCE: an older worker drops the unknown key and
|
|
6621
|
+
then fails the `rule` shape check with a 400 — honest, but it cannot tell a batch was ticked. With
|
|
6622
|
+
the bit absent, render batch offers read-only.
|
|
6623
|
+
|
|
6624
|
+
Same posture as above: it only says "this binary knows this key". Whether THIS redemption lands is
|
|
6625
|
+
answered per call by `rulePersisted` / `ruleRefusal`, with `persistedRules` on success and (server
|
|
6626
|
+
>= 7.48.0) the per-entry anchors `persistedRuleAnchors` — that additive key deliberately gets NO
|
|
6627
|
+
second capability bit, since it rides the same batch redemption channel and a consumer just reads
|
|
6628
|
+
whether it is there.
|
|
6606
6629
|
promptSuggestions: { type: boolean, description: "Follow-up prompt suggestions are generated." }
|
|
6607
6630
|
modelUsage:
|
|
6608
6631
|
type: boolean
|
|
@@ -8023,6 +8046,27 @@ components:
|
|
|
8023
8046
|
display order; an EXACT entry, when present, is always first. core contract caps candidates at 2;
|
|
8024
8047
|
the server enforces a tolerance cap of 4 on read-back — lay out for 4. Absent = pre-column row
|
|
8025
8048
|
(parked before the column existed) OR no candidates; render both as "no candidates", never an error.
|
|
8049
|
+
ruleOffers:
|
|
8050
|
+
type: array
|
|
8051
|
+
minItems: 1
|
|
8052
|
+
items: { $ref: '#/components/schemas/RuleOffer' }
|
|
8053
|
+
description: >
|
|
8054
|
+
server >= 7.46.0 (core 5.58.0, design/375) — the discriminated-union successor of the retired
|
|
8055
|
+
`ruleSuggestions` above on the DURABLE queue row (core `PendingAction.ruleOffers`, the same
|
|
8056
|
+
contract as the sync leg's `AskRequest.ruleOffers`). A >= 7.46.0 row carries only the new key;
|
|
8057
|
+
pre-5.58 rows fail the new narrow read entry by entry and land as "no supply" (no compatibility
|
|
8058
|
+
read — a stale schema means re-ask).
|
|
8059
|
+
|
|
8060
|
+
🔴 THE INDEX ON THIS LEG IS NOT A SELECTION KEY. This projection drops malformed offers one by one
|
|
8061
|
+
and COMPACTS, so an excision shifts every later index forward. That is only sound because this leg
|
|
8062
|
+
has NO redemption mouth (POST /v1/approvals/{sessionId}/decide takes no rule field). Using it as
|
|
8063
|
+
`persistRule.batchOfferIndex` would redeem a different rule than the one the person clicked. The
|
|
8064
|
+
live card leg is the opposite (pure prefix truncation, no per-entry drops) and is where redemption
|
|
8065
|
+
actually happens.
|
|
8066
|
+
|
|
8067
|
+
🔴 DISPLAY / TRIAGE ONLY, and ABSENT MEANS THERE REALLY IS NO SUPPLY (rule lane unarmed, the
|
|
8068
|
+
command yields no rule, no rule could silence this ask, or an old row) — never "the projection
|
|
8069
|
+
dropped it", which is precisely the bug this key exists to close. Never `null`, never `[]`.
|
|
8026
8070
|
governanceForced:
|
|
8027
8071
|
type: boolean
|
|
8028
8072
|
enum: [true]
|
|
@@ -9889,6 +9933,38 @@ components:
|
|
|
9889
9933
|
replica ⇒ redeemable; after a restart / on another replica that `approvalId` 404s and the person
|
|
9890
9934
|
must use the durable decide leg, which carries NO rule field in v1 (its body is strict — an extra
|
|
9891
9935
|
`persistRule` is a loud 400, not a silent drop).
|
|
9936
|
+
ruleOffers:
|
|
9937
|
+
type: array
|
|
9938
|
+
minItems: 1
|
|
9939
|
+
items: { $ref: '#/components/schemas/RuleOffer' }
|
|
9940
|
+
description: >
|
|
9941
|
+
server >= 7.46.0 (core 5.58.0, design/375), "tool_approval" only — the ENGINE-minted "don't ask
|
|
9942
|
+
again" OPTIONS for this ask, as a DISCRIMINATED UNION (`single` | `batch`). It is the BREAKING
|
|
9943
|
+
REPLACEMENT of `ruleSuggestions` above: a >= 7.46.0 engine mints the old key nowhere, there is no
|
|
9944
|
+
alias, and the two keys are declared side by side only so one type surface covers both generations.
|
|
9945
|
+
|
|
9946
|
+
🔴 READ `kind` TO TELL THE ARMS APART, never a positional assumption. ORDER IS CONTRACT: a
|
|
9947
|
+
whole-string exact `single` is index 0 when present and a `batch` is always last; core's own
|
|
9948
|
+
cardinality is <= 2 while the server enforces a tolerance cap of 4.
|
|
9949
|
+
|
|
9950
|
+
🔴 INDEX SEMANTICS DIFFER PER LEG, and only one leg can redeem. On THIS leg (and on the
|
|
9951
|
+
`approval_request` card and the durable replay of both) the server prefix-truncates and never drops
|
|
9952
|
+
an entry, so the index equals core's offer index and IS a legal selection key for
|
|
9953
|
+
`persistRule.batchOfferIndex`. On the durable queue row (PendingCheckpoint.ruleOffers) the server
|
|
9954
|
+
drops malformed entries and COMPACTS, so indexes shift there and that leg has no redemption mouth
|
|
9955
|
+
at all. A consumer that drops entries itself must keep every survivor's ORIGINAL wire index, or
|
|
9956
|
+
suppress its persistence actions entirely (core's normative clause) — a compacted index means the
|
|
9957
|
+
k-th option the person clicked is not the k-th rule the server redeems.
|
|
9958
|
+
|
|
9959
|
+
🔴 PRESENCE IS THE PROMISE (same as `ruleSuggestions`): present only where a permission-rule store
|
|
9960
|
+
is really wired. The converse does not hold — a wired deployment still omits it for mandated /
|
|
9961
|
+
requiresRealApproval / shadowed / hook-produced / inherited-unresolved / ancestor-resolved /
|
|
9962
|
+
anonymous asks. Absence reads as "THIS CARD has no such option", never as "this deployment has no
|
|
9963
|
+
rule lane" (that is `capabilities.permissionRules`) and never as "your rule broke" (that is
|
|
9964
|
+
`persistedRuleShadowed`).
|
|
9965
|
+
|
|
9966
|
+
⚠️ REDEMPTION SCOPE is the live respond leg keyed by this frame's `approvalId` only; a replayed
|
|
9967
|
+
offer on another replica is not a promise that it is still redeemable.
|
|
9892
9968
|
persistedRuleShadowed:
|
|
9893
9969
|
type: string
|
|
9894
9970
|
description: >
|
|
@@ -10123,6 +10199,15 @@ components:
|
|
|
10123
10199
|
the PRESENCE-IS-THE-PROMISE clause (present only where a rule store is wired) and the redemption
|
|
10124
10200
|
scope are stated verbatim on ToolApprovalFrame.ruleSuggestions — the live frame, the stored
|
|
10125
10201
|
`card_json` and the replayed frame carry the SAME material.
|
|
10202
|
+
ruleOffers:
|
|
10203
|
+
type: array
|
|
10204
|
+
minItems: 1
|
|
10205
|
+
items: { $ref: '#/components/schemas/RuleOffer' }
|
|
10206
|
+
description: >
|
|
10207
|
+
server >= 7.46.0 (core 5.58.0, design/375) — the discriminated-union replacement of
|
|
10208
|
+
`ruleSuggestions` above. Same material as the live frame and the stored `card_json` (the server
|
|
10209
|
+
projects all three through one `copyRuleOffer`), so semantics, order-is-contract, presence-is-the-
|
|
10210
|
+
promise and the per-leg index rules are stated verbatim on ToolApprovalFrame.ruleOffers.
|
|
10126
10211
|
persistedRuleShadowed:
|
|
10127
10212
|
type: string
|
|
10128
10213
|
description: >
|
|
@@ -10163,13 +10248,269 @@ components:
|
|
|
10163
10248
|
description: '`exact` = this one command only (`Bash(git status)`); `prefix` = word-boundary prefix (`Bash(git status:*)`). Closed vocabulary — an unknown value is not a candidate this contract describes; do not render it as redeemable.'
|
|
10164
10249
|
command: { type: string, maxLength: 512, description: 'The command pattern inside the rule, for a shell that would rather not re-parse `rule`.' }
|
|
10165
10250
|
|
|
10251
|
+
RuleOfferMatch:
|
|
10252
|
+
type: string
|
|
10253
|
+
description: >
|
|
10254
|
+
The MATCH FORM of a persisted rule — the server's `PERSISTED_RULE_MATCHES`, verbatim (the vocabulary
|
|
10255
|
+
is OWNED BY CORE; the server only transcribes it and pins set equality, so core adding a member turns
|
|
10256
|
+
the server red at compile time).
|
|
10257
|
+
|
|
10258
|
+
`exact` = this one command only (`Bash(git status)`). `prefix` = the historical colon-star
|
|
10259
|
+
word-boundary prefix (`Bash(git status:*)`) and the ONLY spelling for a compound prefix form; it stays
|
|
10260
|
+
a permanent compatibility read. `wildcard` = the trailing space-star form (`Bash(npm run *)`), the
|
|
10261
|
+
same predicate as `prefix` on a single command body, minted by core's suggestion face since
|
|
10262
|
+
design/382 / #510. `subpath` = the `Read(//abs-dir/**)` directory form, which never holds a command.
|
|
10263
|
+
|
|
10264
|
+
🔴 FOUR MEMBERS, not "the three today's miner emits": this type describes what a consumer may RECEIVE,
|
|
10265
|
+
and the server's wire validator accepts all four. A consumer type narrower than the mint point is a
|
|
10266
|
+
lie — it lets a `switch` claim exhaustiveness the wire does not honour. (The retired `RuleSuggestion`
|
|
10267
|
+
declared only two, which is exactly why a legal `wildcard` candidate used to be dropped as malformed.)
|
|
10268
|
+
enum: [exact, prefix, wildcard, subpath]
|
|
10269
|
+
|
|
10270
|
+
RuleOfferUncoveredReason:
|
|
10271
|
+
type: string
|
|
10272
|
+
description: >
|
|
10273
|
+
Why one segment of a compound command is STILL not covered after redeeming the batch (design/382
|
|
10274
|
+
§3.5; the server's `UNCOVERED_SEGMENT_REASONS`, core-owned). `redirection` = a redirection is asked
|
|
10275
|
+
every time, permanently. `no_rule_form` = there is no rule form that could cover this segment.
|
|
10276
|
+
`cap_overflow` = deduplication plus the batch cap pushed it out.
|
|
10277
|
+
enum: [redirection, no_rule_form, cap_overflow]
|
|
10278
|
+
|
|
10279
|
+
RuleOfferUncoveredDetail:
|
|
10280
|
+
type: object
|
|
10281
|
+
description: >
|
|
10282
|
+
design/382 §3.5, ADDITIVE — one "why is this segment still uncovered" row. `segment` is the folded
|
|
10283
|
+
segment's ORIGINAL BYTES (command family: UNTRUSTED for display, and subject to the same redaction /
|
|
10284
|
+
truncation discipline as a member's `segment`).
|
|
10285
|
+
additionalProperties: false
|
|
10286
|
+
required: [segment, reason]
|
|
10287
|
+
properties:
|
|
10288
|
+
segment: { type: string, maxLength: 512 }
|
|
10289
|
+
reason: { $ref: '#/components/schemas/RuleOfferUncoveredReason' }
|
|
10290
|
+
|
|
10291
|
+
RuleOfferBatchMember:
|
|
10292
|
+
description: >
|
|
10293
|
+
ONE MEMBER of a `batch` offer — the rule folded out of one segment of a compound command
|
|
10294
|
+
(design/382 §2.3 B3; discriminated on `kind`, core-owned closed vocabulary).
|
|
10295
|
+
|
|
10296
|
+
`segment` records WHICH folded segment this member came from. Core states it explicitly as a RENDERING
|
|
10297
|
+
SEAT that never participates in adjudication, and it is UNTRUSTED for display exactly like
|
|
10298
|
+
`rule` / `command` / `directory`. ⚠️ Only `segment` may be TRUNCATED by the server (an honest
|
|
10299
|
+
elision); `rule` / `command` / `directory` sit on the left-hand side of the redemption equality and
|
|
10300
|
+
are NEVER truncated.
|
|
10301
|
+
|
|
10302
|
+
🔴 AN UNRECOGNISED MEMBER `kind` DROPS THE WHOLE BATCH, never the single member (core's normative
|
|
10303
|
+
degradation clause): a batch missing one member renders "yes to N" as "yes to N-1", which is worse
|
|
10304
|
+
than not rendering it. Any `single` offers beside it are complete and honest, so they stay.
|
|
10305
|
+
oneOf:
|
|
10306
|
+
- type: object
|
|
10307
|
+
additionalProperties: false
|
|
10308
|
+
required: [kind, rule, match, command, segment]
|
|
10309
|
+
properties:
|
|
10310
|
+
kind: { type: string, enum: [command] }
|
|
10311
|
+
rule: { type: string, maxLength: 512 }
|
|
10312
|
+
match: { $ref: '#/components/schemas/RuleOfferMatch' }
|
|
10313
|
+
command: { type: string, maxLength: 512 }
|
|
10314
|
+
segment: { type: string, maxLength: 512 }
|
|
10315
|
+
- type: object
|
|
10316
|
+
additionalProperties: false
|
|
10317
|
+
required: [kind, rule, directory, segment]
|
|
10318
|
+
properties:
|
|
10319
|
+
kind: { type: string, enum: [directoryRead] }
|
|
10320
|
+
rule: { type: string, maxLength: 512, description: 'The `Read(//abs-dir/**)` directory-read grant minted from a `cd` segment.' }
|
|
10321
|
+
directory: { type: string, maxLength: 512, description: 'Lexically normalised absolute directory — a rendering seat, so the shell need not re-parse `rule`. This arm carries NO `match` / `command`.' }
|
|
10322
|
+
segment: { type: string, maxLength: 512 }
|
|
10323
|
+
discriminator:
|
|
10324
|
+
propertyName: kind
|
|
10325
|
+
|
|
10326
|
+
RuleOffer:
|
|
10327
|
+
description: >
|
|
10328
|
+
ONE "don't ask again" OPTION on an approval card (core 5.58.0 design/375 §3.1; server >= 7.46.0 emits
|
|
10329
|
+
the same shape on THREE legs — the live `tool_approval` frame, the stored `card_json`, and the durable
|
|
10330
|
+
park row). It is the BREAKING replacement of `RuleSuggestion`, with no alias.
|
|
10331
|
+
|
|
10332
|
+
🔴 READ `kind`. ORDER IS CONTRACT: a whole-string exact `single` is index 0 when present, a `batch` is
|
|
10333
|
+
always last, core's cardinality is <= 2 and the server's enforcement cap is 4.
|
|
10334
|
+
|
|
10335
|
+
🔴 TWO ARMS, TWO SELECTION KEYS (see PersistRuleSelection). The `single` arm is redeemed by echoing its
|
|
10336
|
+
`rule` text VERBATIM — the server locates it BY TEXT, so a hand-built string is refused
|
|
10337
|
+
(`rule_not_offered`) by construction. The `batch` arm has no single text to copy (it is ONE yes for
|
|
10338
|
+
every member, with no per-member ticking), so it is redeemed BY INDEX, and the server additionally
|
|
10339
|
+
checks an anti-drift equality before honouring it: the batch re-minted from the adjudicated command
|
|
10340
|
+
must have the SAME member text set as the one the person saw, or the redemption is refused and the way
|
|
10341
|
+
forward is to re-trigger the command for a fresh card.
|
|
10342
|
+
|
|
10343
|
+
🔴 INDEX SEMANTICS DIFFER PER LEG — the live card leg's index is a legal selection key, the durable
|
|
10344
|
+
queue row's is not (that projection compacts). Both statements are on the two `ruleOffers` properties.
|
|
10345
|
+
oneOf:
|
|
10346
|
+
- type: object
|
|
10347
|
+
additionalProperties: false
|
|
10348
|
+
required: [kind, rule, match, command]
|
|
10349
|
+
properties:
|
|
10350
|
+
kind: { type: string, enum: [single] }
|
|
10351
|
+
rule: { type: string, maxLength: 512, description: 'The canonical rule text to echo back verbatim on redemption (server MAX_RULE_TEXT_CHARS = 512).' }
|
|
10352
|
+
match: { $ref: '#/components/schemas/RuleOfferMatch' }
|
|
10353
|
+
command: { type: string, maxLength: 512, description: 'The command pattern inside the rule, for a shell that would rather not re-parse `rule`.' }
|
|
10354
|
+
- type: object
|
|
10355
|
+
additionalProperties: false
|
|
10356
|
+
required: [kind, rules, uncoveredSegments]
|
|
10357
|
+
properties:
|
|
10358
|
+
kind: { type: string, enum: [batch] }
|
|
10359
|
+
rules:
|
|
10360
|
+
type: array
|
|
10361
|
+
minItems: 1
|
|
10362
|
+
items: { $ref: '#/components/schemas/RuleOfferBatchMember' }
|
|
10363
|
+
description: 'The per-segment members (segment order, deduplicated by rule text). Core contract 1..5; the server tolerates up to 8.'
|
|
10364
|
+
uncoveredSegments:
|
|
10365
|
+
type: integer
|
|
10366
|
+
minimum: 0
|
|
10367
|
+
description: >
|
|
10368
|
+
The HONEST SURPLUS at mint time: how many segments of this compound command would still not be
|
|
10369
|
+
allowed by any rule after redeeming this batch, under the coverage snapshot taken then. `0`
|
|
10370
|
+
means "redeem this and the whole command is covered, as of that snapshot" — a statement about
|
|
10371
|
+
the snapshot, not a lasting guarantee (a concurrent rule revocation makes it stale).
|
|
10372
|
+
uncoveredDetail:
|
|
10373
|
+
type: array
|
|
10374
|
+
items: { $ref: '#/components/schemas/RuleOfferUncoveredDetail' }
|
|
10375
|
+
description: >
|
|
10376
|
+
design/382 §3.5, ADDITIVE — the per-segment reason behind `uncoveredSegments`. When present the
|
|
10377
|
+
row count equals `uncoveredSegments`, but THE COUNT IS THE SOURCE OF TRUTH: the server drops a
|
|
10378
|
+
malformed detail seat without dropping the batch, so an absent `uncoveredDetail` is NOT an
|
|
10379
|
+
assertion that nothing is uncovered.
|
|
10380
|
+
discriminator:
|
|
10381
|
+
propertyName: kind
|
|
10382
|
+
|
|
10383
|
+
PersistedRuleAnchor:
|
|
10384
|
+
type: object
|
|
10385
|
+
description: >
|
|
10386
|
+
server >= 7.48.0 (P-39), ADDITIVE — one self-describing attribution anchor for
|
|
10387
|
+
`ToolApprovalRespondAck.persistedRules`, index-aligned with it.
|
|
10388
|
+
|
|
10389
|
+
The disease it closes: per-entry attribution of `persistedRules` used to be inferable only from
|
|
10390
|
+
POSITION, and position was not provable on the wire (the ack order came from the server's RE-MINTED
|
|
10391
|
+
batch walk, matching the documented display order only by coincidence — the anti-drift check is a SET
|
|
10392
|
+
equality, order is not in it).
|
|
10393
|
+
|
|
10394
|
+
🔴 BOTH INDEXES LIVE IN THE OFFER SPACE, so a consumer needs no mapping: `offerIndex` echoes the
|
|
10395
|
+
request's `persistRule.batchOfferIndex` VERBATIM (correlation: "this ack really is for the redemption
|
|
10396
|
+
I sent"), and `memberIndex` is the position inside that batch's `rules[]` on the frame. Core's
|
|
10397
|
+
candidate space is deliberately kept off the wire.
|
|
10398
|
+
additionalProperties: false
|
|
10399
|
+
required: [offerIndex, memberIndex, rule]
|
|
10400
|
+
properties:
|
|
10401
|
+
offerIndex: { type: integer, minimum: 0 }
|
|
10402
|
+
memberIndex: { type: integer, minimum: 0 }
|
|
10403
|
+
rule: { type: string, maxLength: 512, description: 'The canonical text this member actually landed as (equal to the same-index entry of `persistedRules`).' }
|
|
10404
|
+
|
|
10405
|
+
PersistRuleSelection:
|
|
10406
|
+
description: >
|
|
10407
|
+
The body field `persistRule` of POST /v1/tool-approvals/{id}/respond — TWO MUTUALLY EXCLUSIVE ARMS,
|
|
10408
|
+
matching the server's `ParsedPersistRule` one for one.
|
|
10409
|
+
|
|
10410
|
+
🔴 THERE IS NO `kind` ON THE WIRE: that is the server's own post-parse discriminator. The arms are told
|
|
10411
|
+
apart by WHICH FIELD IS PRESENT.
|
|
10412
|
+
|
|
10413
|
+
🔴 MUTUAL EXCLUSION IS A LOUD 400, not "pick one silently" (server constant
|
|
10414
|
+
PERSIST_RULE_BATCH_EXCLUSIVE_ERROR, verbatim): "persistRule.batchOfferIndex is mutually exclusive with
|
|
10415
|
+
persistRule.rule / persistRule.edited — send exactly one arm". Two arms are two DIFFERENT
|
|
10416
|
+
authorisations; guessing between them is deciding for the person.
|
|
10417
|
+
|
|
10418
|
+
🔴 MALFORMED IS ALSO A 400, never folded (PERSIST_RULE_BATCH_INDEX_ERROR, verbatim):
|
|
10419
|
+
"persistRule.batchOfferIndex must be a non-negative integer when present" — a negative, fractional or
|
|
10420
|
+
non-numeric index that got coerced points at ANOTHER offer. Likewise
|
|
10421
|
+
PERSIST_RULE_EDITED_FLAG_ERROR: "persistRule.edited must be a boolean when present" — a non-boolean is
|
|
10422
|
+
NEVER silently treated as false, because that would quietly fold a "I want to store free text" back
|
|
10423
|
+
into the candidate arm and answer `rule_not_offered`, leaving the person to think their own rule was
|
|
10424
|
+
rejected. And `rule` must be a non-empty string of at most 512 characters.
|
|
10425
|
+
|
|
10426
|
+
🔴 NO SCOPE IN THE BODY: a top-level `scope` and a `persistRule.scope` are both loud 400s
|
|
10427
|
+
("this endpoint does not accept a rule scope — the server mints it from where the approval happened").
|
|
10428
|
+
Letting the caller name a scope is letting a consent given inside project A be written as a standing
|
|
10429
|
+
allow over project B (or globally) — the most expensive widening available on this axis.
|
|
10430
|
+
|
|
10431
|
+
⚠️ ON `decision: "deny"` THE WHOLE FIELD IS NOT PARSED AT ALL (tolerated, ignored, no 400 — same
|
|
10432
|
+
posture as `updatedInput`): having refused the operation, consenting to it persistently is meaningless.
|
|
10433
|
+
So the refusals above apply to the allow family; only the TOP-LEVEL `scope` 400 is decision-independent.
|
|
10434
|
+
oneOf:
|
|
10435
|
+
- type: object
|
|
10436
|
+
title: candidate-or-edited-arm
|
|
10437
|
+
required: [rule]
|
|
10438
|
+
# 🔴 互斥必须 EXPLICIT(codex R2-F1,验真后修):两臂都是开集,所以每一臂都能把对面臂的键
|
|
10439
|
+
# 当「多余键」收下 —— `oneOf` 数的是「几臂命中」,于是 `{rule, batchOfferIndex:-1}` 会因为
|
|
10440
|
+
# 批臂被 `minimum:0` 判假而**恰好命中一臂**,schema 放行、server 报互斥 400。靠「另一臂恰好
|
|
10441
|
+
# 判假」表达互斥是间接后果,不是约束。
|
|
10442
|
+
not: { required: [batchOfferIndex] }
|
|
10443
|
+
properties:
|
|
10444
|
+
rule:
|
|
10445
|
+
type: string
|
|
10446
|
+
minLength: 1
|
|
10447
|
+
maxLength: 512
|
|
10448
|
+
description: >
|
|
10449
|
+
With `edited` absent or false this is the CANDIDATE ARM: echo back, VERBATIM, the `rule` of the
|
|
10450
|
+
chosen `kind: "single"` entry of the frame's `ruleOffers` (or, against a <= 7.45 server, of
|
|
10451
|
+
`ruleSuggestions`). Do not hand-build it, re-case it or append `:*` — the server locates it by
|
|
10452
|
+
text and refuses otherwise (`rule_not_offered`). With `edited: true` this is the FREE-TEXT ARM
|
|
10453
|
+
and the string is whatever the person typed.
|
|
10454
|
+
edited:
|
|
10455
|
+
type: boolean
|
|
10456
|
+
description: >
|
|
10457
|
+
server >= 7.44.0 (#340), ADDITIVE — capability bit `respondFreeFormRules`, PROBE IT FIRST.
|
|
10458
|
+
`true` says the text above was hand-edited on the card, so the candidate-table equality is not
|
|
10459
|
+
applied; the engine judges it instead through three gates (the shared validator, a COVERAGE
|
|
10460
|
+
gate requiring the edited rule to still hold the command being adjudicated, and scope
|
|
10461
|
+
inherited from this card). On success the ack carries `persistedRule` = the canonical spelling
|
|
10462
|
+
that really landed. An older worker drops the unknown key and judges the text AS A CANDIDATE,
|
|
10463
|
+
answering `rule_not_offered` — honest, but easily misread as "I wrote it wrong".
|
|
10464
|
+
|
|
10465
|
+
⚠️ Together with `updatedInput` the persistence is refused (`rule_input_edited`): the call that
|
|
10466
|
+
takes effect is the edited one while the candidates came from the ORIGINAL command. The
|
|
10467
|
+
decision itself still stands.
|
|
10468
|
+
# 🔴 刻意**不封闭**(codex R1-F2,验真后修;main 的同一格早有同款注释,换形时别把它丢了):
|
|
10469
|
+
# server 的 `parseToolApprovalResponse` 是手写 parser,只读它认识的键,对象里多给的键**忽略**
|
|
10470
|
+
# (与 `updatedInput` 同一条宽收姿势)。写 `additionalProperties:false` 会让照 spec 生成的客户端
|
|
10471
|
+
# 拒发一个 server 其实会受理的请求 —— 例如一个把整只候选对象 `{rule, match, command}` 原样转发的
|
|
10472
|
+
# 存量壳,重新 codegen 之后就发不出去了。那是我们自己造的漂移,不是上游的约束。
|
|
10473
|
+
# 互斥**不靠封闭表达**:`oneOf` 的语义是「恰好命中一臂」,两臂各自 `required` 不同 ⇒
|
|
10474
|
+
# `{rule, batchOfferIndex}` 同时命中两臂 ⇒ 判假。开集与互斥两件事各自成立。
|
|
10475
|
+
- type: object
|
|
10476
|
+
title: batch-arm
|
|
10477
|
+
required: [batchOfferIndex]
|
|
10478
|
+
# 同上一臂:`{batchOfferIndex:0, edited:false}` 曾因为文本臂缺 `rule` 判假而恰好命中本臂。
|
|
10479
|
+
not: { anyOf: [{ required: [rule] }, { required: [edited] }] }
|
|
10480
|
+
properties:
|
|
10481
|
+
batchOfferIndex:
|
|
10482
|
+
type: integer
|
|
10483
|
+
minimum: 0
|
|
10484
|
+
description: >
|
|
10485
|
+
server >= 7.46.0 (design/377), ADDITIVE — capability bit `respondBatchRuleOffers`, PROBE IT
|
|
10486
|
+
FIRST. The person ticked a `kind: "batch"` offer; the value is that offer's INDEX in the
|
|
10487
|
+
frame's `ruleOffers`. A conjunctive batch has no single text to copy, so the index is the only
|
|
10488
|
+
possible selection key.
|
|
10489
|
+
|
|
10490
|
+
🔴 NARROWLY OPENED TO BATCH ONLY: an index that points nowhere, or points at a `single`, yields
|
|
10491
|
+
`rulePersisted:false` + `rule_not_offered` — what was never rendered can never be redeemed, and
|
|
10492
|
+
the `single` arm's anti-forgery anchor is its TEXT equality, which an index may not bypass.
|
|
10493
|
+
🔴 An older worker drops the unknown key and then fails the `rule` shape check with a 400
|
|
10494
|
+
— honest, but it cannot tell that a batch was ticked. If the capability bit is absent, do not
|
|
10495
|
+
render a redeemable batch affordance.
|
|
10496
|
+
# 同上一臂:刻意不封闭(理由逐字见那一段)。
|
|
10497
|
+
|
|
10166
10498
|
RuleRefusalReason:
|
|
10167
10499
|
type: string
|
|
10168
10500
|
description: >
|
|
10169
10501
|
Why a "don't ask again" did NOT get persisted (#154 车二; the server's `RuleRefusalReason`, verbatim).
|
|
10170
10502
|
A CLOSED set so a shell can branch instead of matching prose. The two naming styles are NOT a typo:
|
|
10171
|
-
the
|
|
10172
|
-
underscored ones are minted by the server's gates.
|
|
10503
|
+
the SEVEN hyphenated members are derived from the consent lane's own refusal reasons
|
|
10504
|
+
(server `CardRulePersisted`), the SIX underscored ones are minted by the server's gates.
|
|
10505
|
+
|
|
10506
|
+
`edit-unsupported` and `edit-rejected` (server >=7.44.0, #340) belong to the FREE-TEXT arm and ask the
|
|
10507
|
+
shell for OPPOSITE things: the first means the edit affordance does not work on this card (no binding
|
|
10508
|
+
anchor, or the deployment has it off) so STOP RENDERING THE INPUT BOX; the second means the engine's
|
|
10509
|
+
gates refused this particular text, so "fix it and try again" — and since #345 (server >=7.51.0) the
|
|
10510
|
+
independently decidable half of that judgement happens BEFORE the decision settles (a 400 with the card
|
|
10511
|
+
still alive), so trying again really works. `scope-unresolved` (core 7.0.0 #490②) is the death of the
|
|
10512
|
+
silent global: with neither an explicit scope nor a cwd the lane refuses loudly instead of defaulting to
|
|
10513
|
+
global, which makes it a ROUTINE answer on a deployment that has not wired `ruleScopeRootFor`.
|
|
10173
10514
|
|
|
10174
10515
|
🔴 A refusal NEVER flips the decision. The person's "allow this once" has already taken effect and
|
|
10175
10516
|
reached the engine; turning the whole respond into a 4xx would make a real allow vanish. The honest
|
|
@@ -10200,16 +10541,30 @@ components:
|
|
|
10200
10541
|
note on `rule_lane_unavailable` above already says never to disable the feature globally on it, and
|
|
10201
10542
|
the same holds here. The one correct source for "does this worker serve rules at all" is
|
|
10202
10543
|
`capabilities.permissionRules`. Keep offering "don't ask again" on the next ask; just not on this one.
|
|
10544
|
+
`rule_material_absent` (server >=7.48.0, #363) — DURABLE (PARKED) LATE-DECISION LEG ONLY: THIS ROW
|
|
10545
|
+
carries no material to mint a rule from. Three causes, one handling: the ask never entered the rule
|
|
10546
|
+
lane (governance-forced / no owner / the engine minted no candidate / the command bytes are not
|
|
10547
|
+
readable out of the args), the row predates those columns, or the card on the row will not parse.
|
|
10548
|
+
🔴 ALSO SPLIT FROM `rule_lane_unavailable` ON PURPOSE, for the same reason as the previous member:
|
|
10549
|
+
that word says "this deployment serves no rules at all" and a shell may stop rendering the affordance
|
|
10550
|
+
on it, whereas the truth here is "the lane is fine, only THIS ROW has no material". Folding them lets
|
|
10551
|
+
one late park answer condemn a healthy deployment's whole lane. Silently ignoring it is worse still
|
|
10552
|
+
(no silent fail-open on the approval axis): a person who ticked "don't ask again" and saw nothing
|
|
10553
|
+
happen will not tick it twice.
|
|
10203
10554
|
enum:
|
|
10204
10555
|
- no-candidates
|
|
10205
10556
|
- unknown-candidate
|
|
10206
10557
|
- confirm-refused
|
|
10207
10558
|
- redeem-refused
|
|
10559
|
+
- edit-unsupported
|
|
10560
|
+
- edit-rejected
|
|
10561
|
+
- scope-unresolved
|
|
10208
10562
|
- rule_lane_unavailable
|
|
10209
10563
|
- rule_input_edited
|
|
10210
10564
|
- rule_not_offered
|
|
10211
10565
|
- rule_store_error
|
|
10212
10566
|
- rule_governance_forced
|
|
10567
|
+
- rule_material_absent
|
|
10213
10568
|
|
|
10214
10569
|
ApprovalRiskAxes:
|
|
10215
10570
|
type: object
|
|
@@ -11864,6 +12219,44 @@ components:
|
|
|
11864
12219
|
next ask" (`rule_input_edited`) from "there was nothing redeemable on this card"
|
|
11865
12220
|
(`rule_lane_unavailable`). 🔴 `rule_lane_unavailable` covers four causes, two of them per-card — do
|
|
11866
12221
|
NOT read it as "this deployment has no rule lane" (see the RuleRefusalReason schema).
|
|
12222
|
+
persistedRule:
|
|
12223
|
+
type: string
|
|
12224
|
+
maxLength: 512
|
|
12225
|
+
description: >
|
|
12226
|
+
server >= 7.44.0 (#340), ADDITIVE — FREE-TEXT ARM ONLY: the CANONICAL SPELLING of the rule that
|
|
12227
|
+
actually landed (core may normalise `Bash(adb *)` into `Bash(adb:*)`). Echo THIS back to the
|
|
12228
|
+
person, not the bytes they typed into the box.
|
|
12229
|
+
|
|
12230
|
+
🔴 ABSENCE HAS THREE MEANINGS, none of them "the rule is gone": (1) this respond used the
|
|
12231
|
+
CANDIDATE arm — the server's `editedArmEcho` is the single mint point for this key, so a candidate
|
|
12232
|
+
respond's ack is byte-for-byte unchanged; (2) nothing landed (read `rulePersisted` / `ruleRefusal`);
|
|
12233
|
+
(3) the respond carried no `persistRule` at all.
|
|
12234
|
+
persistedRules:
|
|
12235
|
+
type: array
|
|
12236
|
+
items: { type: string, maxLength: 512 }
|
|
12237
|
+
description: >
|
|
12238
|
+
server >= 7.46.0 (design/377), ADDITIVE — BATCH ARM ONLY: the canonical text of EVERY member that
|
|
12239
|
+
landed in this one redemption. A conjunctive batch is one yes for the whole set; if any member is
|
|
12240
|
+
refused the ack is `rulePersisted:false` + `ruleRefusal:"redeem-refused"` and the members that DID
|
|
12241
|
+
land are not rolled back (their dots are on record and a re-triggered card dedupes idempotently).
|
|
12242
|
+
|
|
12243
|
+
🔴 ORDER: from server 7.48.0 on, the DISPLAY order of the requested offer
|
|
12244
|
+
(`ruleOffers[batchOfferIndex].rules`) is a PROMISE, not a coincidence; on 7.46-7.47 it inherited the
|
|
12245
|
+
server's re-minted walk order instead. For per-entry attribution read `persistedRuleAnchors`
|
|
12246
|
+
rather than inferring it from position.
|
|
12247
|
+
🔴 Not the same thing as `persistedRule`: the singular key belongs to the single/edited arm, the
|
|
12248
|
+
plural one to the batch arm, and the two never appear together.
|
|
12249
|
+
persistedRuleAnchors:
|
|
12250
|
+
type: array
|
|
12251
|
+
items: { $ref: '#/components/schemas/PersistedRuleAnchor' }
|
|
12252
|
+
description: >
|
|
12253
|
+
server >= 7.48.0 (P-39), ADDITIVE and with NO capability bit — the per-entry, self-describing
|
|
12254
|
+
attribution anchors for `persistedRules`, INDEX-ALIGNED with it.
|
|
12255
|
+
|
|
12256
|
+
Minted ONLY on the batch arm (the single/edited arm has no member coordinates to speak of), and
|
|
12257
|
+
omitted WHOLESALE whenever the server cannot prove attribution — an honest absence, never a
|
|
12258
|
+
fabricated index. A consumer that sees it absent knows this ack can only be read positionally, and
|
|
12259
|
+
`persistedRules` itself is unchanged to the byte (older consumers ignore the unknown key).
|
|
11867
12260
|
noteRecorded:
|
|
11868
12261
|
type: boolean
|
|
11869
12262
|
description: >
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/sdk",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.2.0",
|
|
4
4
|
"description": "Typed, zero-runtime-dependency SDK for the Sema agent fleet usage plane. The shared substrate for all doors (CC/Codex MCP façade + web). Server-side only — tokens never enter the browser.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BUSL-1.1",
|