@sema-agent/client-core 0.41.0 → 0.43.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.
@@ -122,6 +122,88 @@ export interface FsApprovalWireDeps {
122
122
  /** 结构等值(键序无关深比较)——updatedInput「真编辑过」判定用。zod parse 会产新引用与重排,
123
123
  * 引用比较/JSON.stringify 串比较都会假报「编辑过」。 */
124
124
  export declare function structurallyEqual(a: unknown, b: unknown): boolean;
125
+ /**
126
+ * 一条「不再询问」候选的匹配形(server/core 闭词表,两代同词)。
127
+ */
128
+ export type RuleOfferMatch = 'exact' | 'prefix';
129
+ /**
130
+ * `batch` 臂的一个成员 —— 复合命令**某一段**的规则(core `SegmentRuleSuggestion` 逐字)。
131
+ * `segment` = 这条成员是从哪一段折叠后的命令文本铸出来的:**渲染座,永不参与裁决**;
132
+ * 它与 `rule`/`command` 同属 **UNTRUSTED-for-display**(原始 post-rewrite 命令字节)。
133
+ * 上游形与本包归一形**共用这一个**(成员上没有下标这回事,两代/两腿逐字相同)。
134
+ */
135
+ export interface RuleOfferBatchMember {
136
+ rule: string;
137
+ match: RuleOfferMatch;
138
+ command: string;
139
+ segment: string;
140
+ }
141
+ /**
142
+ * **wire 上的**一条 offer —— 与上游 core `RuleOffer` 逐字同形(**没有** `offerIndex`)。
143
+ *
144
+ * 🔴 为什么与归一形 {@link RuleOffer} 分开声明(异源对抗复审 [medium] 采纳):`offerIndex` 是
145
+ * **本包铸的座**(见 {@link RuleOffer} 顶注),把它写进 {@link ToolApprovalFrame.ruleOffers} 的
146
+ * 入参型 = 要求宿主为一个上游根本不发的字段负责 —— 真实 7.46.0 帧会**类型不合**,宿主只能强转,
147
+ * 或者自己伪造一个本该由窄读器按**当前这条腿**算出来的下标(而两条腿的下标语义还不一样)。
148
+ * ⇒ 入参面(帧/行)用本形,出参面(卡入参)用带下标的归一形,两者刻意不互相赋值。
149
+ */
150
+ export type WireRuleOffer = {
151
+ kind: 'single';
152
+ rule: string;
153
+ match: RuleOfferMatch;
154
+ command: string;
155
+ } | {
156
+ kind: 'batch';
157
+ rules: readonly RuleOfferBatchMember[];
158
+ uncoveredSegments: number;
159
+ };
160
+ /**
161
+ * 一张审批卡上的**一个**「不再询问」选项(core 5.58.0 design/375 §3.1 的判别联合,server ≥7.46.0
162
+ * 起在 `tool_approval` 帧 / `card_json` / durable park 行**三腿同形**发出)。
163
+ *
164
+ * ── 为什么本包自铸这个形(记账,不是偷懒)────────────────────────────────────────────────
165
+ * 上游 wire 形的类型供给口是 `@sema-agent/sdk`,而 sdk 7.2.0(今天 npm 上的最新版)的
166
+ * `ToolApprovalFrame` / `PendingCheckpoint` 上**只有退役键 `ruleSuggestions`**、没有 `RuleOffer`
167
+ * 这个名字 —— 与 `requiresRealApproval`(#283)/ 窗三键([4851])同形:**server 已真发、SDK 锚未跟**。
168
+ * 照那两例的先例:先追 server,不等 SDK;键集侧进对账门的 `AHEAD_OF_ANCHOR` 带退出条件登记
169
+ * (SDK 补上当天自红逼删)。语义来源锚 = engine 7.46.0 fixture 的
170
+ * `@sema-agent/core/dist/core/permission-rule-model.d.ts`(`RuleOffer` 的逐字契约)+
171
+ * `@sema-agent/server/dist/approval-card.d.ts`(两臂 zod 形与两条腿的下标语义)。
172
+ *
173
+ * ── `offerIndex` 是本包铸的座(上游形上没有这一位)──────────────────────────────────────
174
+ * core 的契约原话:消费端丢掉不认识的成员时**必须保住每个留下来成员的原始 wire 下标**,做不到就
175
+ * 「must suppress its persistence actions entirely (fail toward asking)」—— 因为 `batch` 臂的兑付
176
+ * 键就是**下标**(`persistRule.batchOfferIndex`)。本包的窄读器是**逐条丢坏的**(一条坏 offer 不该
177
+ * 让另一条真 offer 消失,与 server `boundedRuleOffers` 同向),压紧就会让下标前移 ⇒ 人点的第 k 个
178
+ * 与服务端兑的第 k 个指向两条不同规则。所以窄读时把**原始下标**显式记在这一位上,兑付时按它回报。
179
+ * 🔴 `offerIndex` 的定义域 = 「本包在**这条腿上**收到的那个数组里的下标」:
180
+ * · **活卡帧腿**(`ToolApprovalFrame.ruleOffers`):server 同步腿是纯前缀截、零逐条丢弃 ⇒ 它恒等于
181
+ * core 的 offer index,**是**合法选择键,`surfaceToolApprovalFrameAndRespond` 按它回兑;
182
+ * · **durable 行腿**(`PendingCheckpoint.ruleOffers` → {@link ApprovalCardRequest.ruleOffersReadOnly}):
183
+ * server `boundedRuleOffers` 已经逐条丢弃并压紧过一次 ⇒ 行上的下标本就不是 core 的 offer index。
184
+ * 这条腿**根本没有兑付口**(`/decide` 体无规则位),所以那一位只是展示/对账座,**禁**当选择键。
185
+ */
186
+ export type RuleOffer = {
187
+ kind: 'single';
188
+ /** 见本联合顶注:原始 wire 下标(活卡帧腿=选择键;durable 腿=展示座)。 */
189
+ offerIndex: number;
190
+ /** 宿主要兑付的规范规则文本(ENGINE 铸,客户端只能原样回报)。 */
191
+ rule: string;
192
+ match: RuleOfferMatch;
193
+ /** 规则里的命令模式,给不想重解析的宿主直接渲。UNTRUSTED-for-display。 */
194
+ command: string;
195
+ } | {
196
+ kind: 'batch';
197
+ /** 见本联合顶注。**batch 臂的兑付就是按这个数**(文本不可抄:合取批没有单条文本)。 */
198
+ offerIndex: number;
199
+ /** 1..5 条逐段规则(段序,按规则文本去重)。勾这一项 = 对**全体成员**一次性说是,
200
+ * 合取批**没有成员级选中**(要更窄的答案就选 single 臂或只批这一次)。 */
201
+ rules: RuleOfferBatchMember[];
202
+ /** 铸卡时刻的诚实余量披露:这批兑完之后,按当时的覆盖快照仍未被任何规则放行的段数。
203
+ * 0 = 「兑完这批,这条复合命令在那份快照的口径下就全覆盖了」。它是**对铸卡快照的陈述**,
204
+ * 不是长期保证(并发删规则会让它过时)。 */
205
+ uncoveredSegments: number;
206
+ };
125
207
  /** 三选卡的原始决断(vendor 卡的三个选项 + abort + 表面失败)。allow_session=第 2 项
126
208
  * (vendor 产出非空 permissionUpdates:setMode acceptEdits / cwd 外 addDirectories)。 */
127
209
  /** allow 决断臂(命名形,typeshape B4 口径;0.26.0 因 persistRule 位达 4 成员抽名)。 */
@@ -130,8 +212,50 @@ export interface ApprovalCardAllowDecision {
130
212
  allowSession: boolean;
131
213
  updatedInput?: unknown;
132
214
  /** #225 件1:卡上选中的持久规则候选**原文**({@link ApprovalCardRequest.ruleSuggestions} 之一);
133
- * 缺席/空串 = 本次不兑付。表外文本会在编排层被丢键留痕(server 亦拒 rule_not_offered)。 */
215
+ * 缺席/空串 = 本次不兑付。表外文本会在编排层被丢键留痕(server 亦拒 rule_not_offered)。
216
+ * ⚠️ 0.42.0 起本位有一个**兄弟位** {@link persistRuleEdited} —— 带上它就是「这段文本是人手改的
217
+ * 自由文本」,此时本位**不再**要求是候选之一(见该位注)。本位自身的类型与字节一字未动。 */
134
218
  persistRule?: string;
219
+ /**
220
+ * #225 编辑臂(0.42.0;server #340 `respondFreeFormRules`,[5071] wire 形):{@link persistRule}
221
+ * 里那段文本是**人在卡上手打/改过的自由文本**,不是帧候选表里的原文。
222
+ *
223
+ * 🔴 **缺席 ≠ false**:缺席 = 既有候选臂(表内核对照旧、语义与 0.41.0 逐字节相同);
224
+ * `true` = 编辑臂。刻意只收 `true` 一个值(机读位是二值的,「在场但不是 true」没有语义)。
225
+ * 🔴 **它不是放行凭据**,是**出身声明**:带上它只会让编排层放弃「必须是候选之一」那道表核
226
+ * (见 {@link surfaceToolApprovalFrameAndRespond} 的兑付段),真正的判官仍在引擎侧
227
+ * (core `confirmRuleApproval` 同函数体)。客户端**绝不**在这里替引擎预判文本合不合法 ——
228
+ * 在边界复读解析器就是装第二个更严的判官,`Bash(adb *)` 这类肌肉记忆形会被当场误杀
229
+ * ([5071] 定界②,core [5075] 复核确认)。要「边打字边校验」请用
230
+ * {@link import('./editedRuleTextPrecheck.js').precheckEditedRuleText} 的注入口 —— 那是**引擎
231
+ * 自己那只**判官,不是第二份。
232
+ * 🔴 **能力位在场才发**:`true` 而 {@link ToolApprovalFrameLaneOpts.respondFreeFormRulesCapable}
233
+ * 未确认 ⇒ 编排层**整条丢 persistRule**(决断照送),见该位注。
234
+ */
235
+ persistRuleEdited?: true;
236
+ /**
237
+ * #334 批臂(0.43.0;server ≥7.46.0 `ParsedPersistRule` 的 `{kind:'batch', batchOfferIndex}` 那一支,
238
+ * design/377):人在卡上勾的是一条 **`kind:'batch'` 的合取批 offer**。
239
+ *
240
+ * 值 = 那条 offer 的 {@link RuleOffer.offerIndex}(**原始 wire 下标**)。
241
+ * 🔴 **为什么批臂传下标而不是文本**:合取批没有单条可抄的文本(它是 1..5 条规则的一次性授权),
242
+ * 文本等式在这一臂上不存在 ⇒ 选择键只能是下标。single 臂的防伪锚仍是**文本等式**,下标
243
+ * **不许**旁路它(server 侧同款:`batchOfferIndex` 窄开只指 batch)。
244
+ * 🔴 **三臂互斥**(server `PERSIST_RULE_BATCH_EXCLUSIVE_ERROR`,同场**响亮 400 且连决断一起拒**):
245
+ * 本位与 {@link persistRule}/{@link persistRuleEdited} 同场 ⇒ 编排层**整条持久臂丢弃 + 留痕**,
246
+ * 决断照送。两臂说的是两次不同的授权,替人挑一个 = 替人改主意;而让 400 把决断一起打掉,
247
+ * 是把附带愿望的失败升级成主动作的失败(方向与 note 超限那条同一纪律)。
248
+ * 🔴 **表核不让位**:本位只在「该下标在**本帧**的 offers 里真是一条 `kind:'batch'`」时上 wire ——
249
+ * 越界 / 指向 single / 负数 / 非整数一律丢键留痕(server 会 400,包边界先挡下来,决断不受连累)。
250
+ * 🔴 **必须由宿主供给能力位**(异源对抗复审四轮 [medium] 订正 —— 本段初稿写的是「批臂供给自闸、
251
+ * 没有独立能力位」,与实装**相反**,而这段注释随 `.d.ts` 出包 = 三端读到的接入契约):
252
+ * 编排层只在 {@link ToolApprovalFrameLaneOpts.respondBatchRuleOffersCapable} `=== true` 时把本位
253
+ * 送上 wire。**宿主不供给这一位 ⇒ 现代引擎上每一次批臂勾选都会被丢**(决断照送 + 一条如实通知)。
254
+ * 理由见那一位的顶注:帧上有 batch offer 只证明**出帧端**认识新形,不证明**收 respond 的那台**
255
+ * 认识它(滚动升级 / 多副本 / 代理),而旧解析器对本键是**响亮 400 且连决断一起拒**。
256
+ * 缺席 = 这次不是批臂(候选臂/编辑臂/不兑付),现状字节不变。
257
+ */
258
+ persistRuleBatchOfferIndex?: number;
135
259
  }
136
260
  /**
137
261
  * deny 决断臂(命名形,typeshape B4 口径;0.28.0 因 `reason` 位抽名,与 0.26.0
@@ -211,26 +335,56 @@ export interface ApprovalCardRequest {
211
335
  * `agentName` UNTRUSTED-for-display。子代 ask 才在场。 */
212
336
  delegation?: ToolApprovalDelegation;
213
337
  /**
214
- * 持久规则候选(#225 件1,原样来自 {@link ToolApprovalFrame.ruleSuggestions} 的合形项)——
215
- * 卡据此渲「不再询问」档;缺席/空 = 卡形与 0.25.0 字节不变(不渲该档)。
216
- * 🔴 卡只许把其中一条的 `rule` **原样**放进决断的 `persistRule`(不拼、不改写);
217
- * 出身裁剪(MANDATED ask 不提供本档)候 core 5.25.0 出身键 wire 过境,见 #144/[3438]/[3442]。
338
+ * 持久规则候选(#225 件1;0.43.0 起是 {@link RuleOffer} 判别联合)——卡据此渲「不再询问」档;
339
+ * 缺席/空 = 卡形不渲该档。
340
+ *
341
+ * 🔴 **包内单一形出口**(#334/[5223],0.43.0 **BREAKING**):本键**同时**是两代 wire 的落位 ——
342
+ * 新引擎的 {@link ToolApprovalFrame.ruleOffers}(server ≥7.46.0)与旧引擎的
343
+ * {@link ToolApprovalFrame.ruleSuggestions}(≤7.45,归一成 `kind:'single'`)经**同一把**窄读器
344
+ * 合形后落这里。**新键优先、两键同场时旧键整条不看**(绝不混编两代素材)。
345
+ * ⇒ 消费端(cli/web/desktop)读包内这一个形,不感知上游换键。
346
+ * ⚠️ 0.42.0 及更早本键的类型是 sdk `RuleSuggestion[]`(无 `kind`/无 `offerIndex`);
347
+ * 换形是**破坏性**的,呈卡端要按 `kind` 分臂渲(single 一行、batch 一组 + 余量披露)。
348
+ * 🔴 兑付两条路,别混:
349
+ * · `kind:'single'` ⇒ 把该条的 `rule` **原样**放进决断的
350
+ * {@link ApprovalCardAllowDecision.persistRule}(不拼、不改写);
351
+ * · `kind:'batch'` ⇒ 把该条的 `offerIndex` 放进
352
+ * {@link ApprovalCardAllowDecision.persistRuleBatchOfferIndex}(批没有单条文本可抄)。
353
+ * 两位**互斥**,同场 ⇒ 编排层整条持久臂丢弃(见那一位的注)。
354
+ * 🔴 `rule`/`command`/`rules[].segment` 全部 **UNTRUSTED-for-display**。
218
355
  */
219
- ruleSuggestions?: RuleSuggestion[];
356
+ ruleOffers?: RuleOffer[];
220
357
  /**
221
- * 持久规则候选的**只读**对偶([C170] 答问②半场,0.29.0)——原样来自 durable 队列行
222
- * `PendingCheckpoint.ruleSuggestions`(server 7.16.0 [3684]② 补齐的供给)的合形项。
223
- * 🔴 {@link ruleSuggestions} **刻意分键不合流**:那一位的契约是「卡渲可选中的『不再询问』档
224
- * → 决断带 {@link ApprovalCardAllowDecision.persistRule} 回兑」,兑付口=同副本活卡腿
225
- * `respond.persistRule`;而 durable 腿的 `/decide` 体**无规则位**(SDK 6.17.2 PendingCheckpoint
226
- * JSDoc 逐字:display/triage-only)——把行上候选落进那一位,就是一个按下去规则不落地的假
227
- * affordance。本键的不变量是**决断字节**:决断绝不因它带 `persistRule`(或任何规则位)——
358
+ * 持久规则候选的**只读**对偶([C170] 答问②半场,0.29.0;0.43.0 {@link ruleOffers} 换形)——
359
+ * 原样来自 durable 队列行 `PendingCheckpoint.ruleOffers`(server 7.46.0)或旧键
360
+ * `PendingCheckpoint.ruleSuggestions`(server 7.16.0 [3684]② 起的供给,≤7.45)的合形项,
361
+ * 经与活卡腿**同一把**窄读器归一。
362
+ * 🔴 {@link ruleOffers} **刻意分键不合流**:那一位的契约是「卡渲可选中的『不再询问』档
363
+ * 决断带 {@link ApprovalCardAllowDecision.persistRule} / `persistRuleBatchOfferIndex` 回兑」,
364
+ * 兑付口=同副本活卡腿 `respond`;而 durable 腿的 `/decide` 体**无规则位**(SDK 6.17.2
365
+ * PendingCheckpoint JSDoc 逐字:display/triage-only)——把行上候选落进那一位,就是一个按下去
366
+ * 规则不落地的假 affordance。本键的不变量是**决断字节**:决断绝不因它带任何规则位 ——
228
367
  * 「只读」限定的是 wire 回兑通道,不是屏面。渲成可选中项是**允许**的,前提=兑付走客户端
229
368
  * **本地**落规则(cli 1.0.76 起的形:选中 → 客户端写自己的 settings `permissions.allow` 并把
230
369
  * 成败如实上屏,决断仍与两态时代逐字节相同;这正是 CC 第三态的原形——本地文件写)。
370
+ * 🔴 **本腿的 `offerIndex` 不是选择键**:server `boundedRuleOffers` 已逐条丢弃并压紧过一次,
371
+ * 行上的下标本就不等于 core 的 offer index;而这条腿压根没有兑付口 ⇒ 它只是展示/对账座。
231
372
  * 缺席 = 无候选/老行/坏形(三者同形,不猜);既有卡口不读本键 ⇒ 卡形字节不变。
232
373
  */
233
- ruleSuggestionsReadOnly?: RuleSuggestion[];
374
+ ruleOffersReadOnly?: RuleOffer[];
375
+ /**
376
+ * #341/[5214]③(0.43.0;server ≥7.46.0 E-14 / [4537]② Trojan Source 族;**ADDITIVE**,
377
+ * `"tool_approval"` only)——原样来自 {@link ToolApprovalFrame.inputHasBidi}:这只 ask 的
378
+ * **工具输入**里含 bidi 控制符(LRM/RLM、嵌入/覆写、隔离符)。
379
+ *
380
+ * 🔴 **缺席绝不折成 `false`**(类型 `true`,与 {@link governanceForced}/{@link requiresRealApproval}
381
+ * 同族):缺席同时覆盖「真的没扫到」与「args 序列化不了所以扫不了」两形,读成「已确认干净」
382
+ * 就是对用户下一个证不出的断言。
383
+ * 🔴 **披露位,不是清洗位;本包字节零改**:清洗会改掉即将被执行的那串字节(卡上显示的与真跑的
384
+ * 不是同一个东西),比不披露更坏。显形(转义/高亮/加标记)归端 —— 壳侧基线是
385
+ * `renderUntrustedCommandText`(core 导出的展示基线)。本包只保证这一位到得了卡口。
386
+ */
387
+ inputHasBidi?: true;
234
388
  /**
235
389
  * 被越级的持久 allow 规则**原文**(#144,原样来自 {@link ToolApprovalFrame.persistedRuleShadowed}
236
390
  * 的合形值)——壳据此渲「你的规则仍在,只是这次调用被要求逐次确认」;缺席 = 卡形与 0.27.0
@@ -412,13 +566,39 @@ export interface ToolApprovalFrame {
412
566
  */
413
567
  delegation?: ToolApprovalDelegation;
414
568
  /**
415
- * server 7.12.0(#154 车二 / design/179,ADDITIVE)——**引擎铸的**持久规则候选。只在
416
- * `capabilities.permissionRules` 谓词为真且引擎对本次裁决真铸出候选时在场;缺席 = 这张卡
417
- * 不提供「不再询问」档(老 server / 规则店未接 / 引擎判无候选,三者同形,不猜)。
418
- * 🔴 文本由 ENGINE 铸,不由客户端拼:回决时只能把其中一条**原样**报回
419
- * (`respond` `persistRule.rule`),报表外文本 = server 拒 `rule_not_offered`。
569
+ * ⚠️ **退役键**(server 7.45;7.46.0 起同文件 0 命中 —— engine fixture 直证)
570
+ * server ≥7.12.0(#154 车二 / design/179)起的**引擎铸的**持久规则候选,7.46.0 被
571
+ * {@link ruleOffers} 整体取代(#334/[5223],core 5.58.0 design/375 的判别联合换形)。
572
+ *
573
+ * 🔴 **保读不保写**:本包对它只做**归一读**(`kind:'single'` {@link RuleOffer}),理由 =
574
+ * 7.44 及更旧的引擎今天仍在场(pin 未抬的部署、混舰队)。新键在场时**本键整条不看**。
575
+ * 包内出口只有 {@link ApprovalCardRequest.ruleOffers} 一个,消费端不感知这次换键。
576
+ * 🔴 本键留在 interface 与 {@link TOOL_APPROVAL_FRAME_KEYS_MIRROR} 里**不是**遗留包袱:
577
+ * SDK 7.2.0 的运行期锚 `TOOL_APPROVAL_FRAME_KEYS` 仍含它,删掉会让对账门的
578
+ * 「SDK 锚的每个键本仓都有」当场红 —— 退役登记要等 SDK 跟着 server 一起退。
420
579
  */
421
580
  ruleSuggestions?: RuleSuggestion[];
581
+ /**
582
+ * server ≥7.46.0(#334/[5223];core 5.58.0 design/375;**BREAKING 换形**,`"tool_approval"` only。
583
+ * 来源锚 = engine 7.46.0 fixture `@sema-agent/server/dist/tool-approval.d.ts` 的
584
+ * `ruleOffers?: readonly RuleOffer[]` 声明 + `@sema-agent/core/dist/core/permission-rule-model.d.ts`
585
+ * 的 `RuleOffer` 逐字契约)——引擎为这次 ask 铸的「不再询问」选项,判别联合 `single | batch`。
586
+ *
587
+ * 🔴 **序即契约**:whole-string exact 的 `single` 在场时恒 index 0、`batch` 至多一条且恒末位;
588
+ * core 契约基数 ≤2,server 侧执法帽 `MAX_RULE_OFFERS`=4(容忍余量,消费端按 ≤4 布局)。
589
+ * **选择键是原始下标** —— 本包的窄读器逐条丢坏形并把原始下标记在 {@link RuleOffer.offerIndex}
590
+ * 上(压紧会让「人点的第 k 个」与「服务端兑的第 k 个」指向两条不同规则)。
591
+ * 🔴 **在场性即承诺**:只在**规则店真装配**时在场;缺席 = 这张卡不提供「不再询问」档
592
+ * (老 server / 规则店未接 / 引擎判无候选,三者同形,不猜)。
593
+ * 🔴 文本由 ENGINE 铸,不由客户端拼:single 臂回决只能把 `rule` **原样**报回
594
+ * (`respond` 体 `persistRule.rule`,报表外文本 = server 拒 `rule_not_offered`);
595
+ * batch 臂没有单条文本可抄,回决报**下标**(`respond` 体 `persistRule.batchOfferIndex`)。
596
+ * 🔴 本键**领先** SDK 运行期锚一代(sdk 7.2.0 的 `TOOL_APPROVAL_FRAME_KEYS` 只有退役键
597
+ * `ruleSuggestions`)⇒ 对账门(run-approval-frame-keys-test.mjs)AHEAD_OF_ANCHOR 带退出条件登记。
598
+ * 耐久路对偶 = `PendingCheckpoint.ruleOffers`(同契约、**下标语义不同**:那条腿 server 已逐条
599
+ * 丢弃压紧过,且无兑付口 ⇒ 下标只是展示座),由 {@link surfaceFsApprovalAndDecide} 消费。
600
+ */
601
+ ruleOffers?: readonly WireRuleOffer[];
422
602
  /**
423
603
  * server ≥7.13.0(#144 / core 5.25.0,[3438] 接力契约 / [3443] 主件;**ADDITIVE**,
424
604
  * `"tool_approval"` only。来源锚 = engine fixture `@sema-agent/server/dist/tool-approval.d.ts`
@@ -519,6 +699,40 @@ export interface ToolApprovalFrame {
519
699
  * run-durable-card-display-keys-test.mjs ⑨ 段;上游补位后按 {@link probeCause} 的双源合流形跟批。
520
700
  */
521
701
  requiresRealApproval?: true;
702
+ /**
703
+ * #341/[5214]③(server ≥7.46.0;E-14 / [4537]② Trojan Source 族;**ADDITIVE**,`"tool_approval"` only。
704
+ * 来源锚 = engine 7.46.0 fixture `@sema-agent/server/dist/tool-approval.d.ts` 的 `inputHasBidi?: true`
705
+ * 声明 + 同包 `dist/tool-approval.js` 的条件 stamp;7.44 fixture 同文件 0 命中)——这只 ask 的
706
+ * **工具输入**(`AskRequest.args`)里含 bidi 控制符(LRM/RLM、嵌入/覆写、隔离符)。
707
+ *
708
+ * 🔴 **真才带,缺席绝不编 `false`**(与 {@link governanceForced}/{@link requiresRealApproval} 同形)。
709
+ * 缺席同时覆盖「真的没有」与「args 序列化不了(循环引用/抛错的 toJSON/BigInt)所以扫不了」两形,
710
+ * 消费端**禁**把缺席读成「已确认干净」。
711
+ * 🔴 **披露位,不是清洗位;字节零改**:审批卡是唯一一处把模型写的命令交给**人眼**判断的面,而
712
+ * bidi 覆写让人眼读到的顺序与真正执行的字节顺序不同 —— 人批准的是 A、跑起来的是 B。清洗会改掉
713
+ * 即将被执行的那串字节,**比不披露更坏**;server 只报「有」,显形归端。本包只做透传
714
+ * (帧 → {@link ApprovalCardRequest.inputHasBidi})。
715
+ * 🔴 判据**不看 `message`**(引擎/策略写的说明文本,不是待执行输入)——两个来源折进一个布尔会让
716
+ * 端无法判断该给哪一段加显形标记。
717
+ * 🔴 本键**领先** SDK 运行期锚一代(sdk 7.2.0 尚无)⇒ 对账门 AHEAD_OF_ANCHOR 带退出条件登记。
718
+ * 耐久路今天**无对偶**(server 7.46.0 的 `PendingCheckpoint`/`riskDescriptor` 均未声明本键;
719
+ * server 侧那份在**卡内**,不是行上的键)⇒ durable 行 → 卡那条腿不 stamp。
720
+ */
721
+ inputHasBidi?: true;
722
+ /**
723
+ * #329 随批小件([4845]-5;**ADDITIVE**,`"tool_approval_complete"` only,真才带;server 7.44.0 及
724
+ * 更早已在场 —— 0.43.0 同形族扫补进镜像的存量漏键,见 {@link TOOL_APPROVAL_FRAME_KEYS_MIRROR} 处注)——
725
+ * `outcome:"expired"` 一词三义(park / 当场 deny / 无设施 deny)里 **park 那一义的显式判别位**:
726
+ * 在场 ⇔ 这条 ask 按 park 路由收尾且墓碑已落(⇔ 同一把 `approvalId` 仍可打 `respond` 走迟到受理)。
727
+ *
728
+ * 🔴 **缺席禁读作「真 deny」**:无店部署 / deny 政策 / 被连坐 VOID 的兄弟都发不出这个键,
729
+ * 缺席只是「无 park 证据」。
730
+ * 🔴 本包今天**只镜像键、不消费**:它只出现在 `tool_approval_complete` 上,而那种帧根本不走卡口
731
+ * (`surfaceToolApprovalFrameAndRespond` 消费的是 `tool_approval`)。要把「卡失效」改渲
732
+ * 「已转后台候批」是一次**行为面**改动(消费者=端的完成帧处理面),按宪法三问单独立项。
733
+ * 登记在此 = 写明理由的未消费,不是没人发现的丢键。
734
+ */
735
+ parked?: true;
522
736
  /**
523
737
  * server ≥7.34.0(#288/[4429]② —— cli 自己请托的窗三键,**ADDITIVE**,`"tool_approval"` only;
524
738
  * 来源锚 = sema-server src/tool-approval.ts 的三键声明+emit 前赋值,字段注逐字「新壳借此给旧族帧
@@ -554,7 +768,7 @@ export interface ToolApprovalDelegation {
554
768
  * `TOOL_APPROVAL_FRAME_KEYS` 比对——SDK additive 增键时对账当天红,不再人肉追平。
555
769
  * 下面两个类型钉保证镜像与 interface 本身不可能漂移(少键/多键都是编译错)。
556
770
  */
557
- export declare const TOOL_APPROVAL_FRAME_KEYS_MIRROR: readonly ["type", "approvalId", "toolCallId", "toolName", "sourceTaskId", "fromSubagent", "sourceAgentName", "message", "args", "argsOmitted", "governanceForced", "ruleSuggestions", "persistedRuleShadowed", "probeCause", "ruleEvidence", "requiresRealApproval", "expiresInMs", "expiresAtMs", "serverNowMs", "delegation", "outcome"];
771
+ export declare const TOOL_APPROVAL_FRAME_KEYS_MIRROR: readonly ["type", "approvalId", "toolCallId", "toolName", "sourceTaskId", "fromSubagent", "sourceAgentName", "message", "args", "argsOmitted", "governanceForced", "ruleSuggestions", "ruleOffers", "inputHasBidi", "parked", "persistedRuleShadowed", "probeCause", "ruleEvidence", "requiresRealApproval", "expiresInMs", "expiresAtMs", "serverNowMs", "delegation", "outcome"];
558
772
  /**
559
773
  * 子代帧判别:显式键 fromSubagent(core 1.378 RB-39②)优先;缺席退 sourceTaskId 在场性权宜式
560
774
  * (server 1.258 [1549]①3,旧代际兼容)。
@@ -598,8 +812,47 @@ export type ToolApprovalRespondDecision = 'allow' | 'allow_session' | 'deny';
598
812
  export interface RespondToolApprovalOpts {
599
813
  signal?: AbortSignal;
600
814
  updatedInput?: unknown;
601
- /** #225 件1:兑付键 —— 帧候选之一的**原文**(编排层已做表内核对与 deny 剥除)。 */
815
+ /** #225 件1:兑付键 —— 帧候选之一的**原文**(编排层已做表内核对与 deny 剥除)。
816
+ * ⚠️ 带 {@link persistRuleEdited} 时表核已让位,本位是人手改的自由文本(类型不变)。 */
602
817
  persistRule?: string;
818
+ /**
819
+ * #225 编辑臂(0.42.0;server #340,[5071]):{@link persistRule} 是**自由文本**而非候选原文。
820
+ *
821
+ * 🔴 **注入面的映射义务**(本包只到这一格,wire 体由宿主/SDK 铸):server 的 respond 体形是
822
+ * `persistRule: { rule, edited: true }`。本包刻意保持**扁平兄弟位**(`persistRule: string`
823
+ * 字节不变 + 一个可选布尔),由注入面把两位合成那个嵌套形:
824
+ * `persistRule !== undefined ? { rule: persistRule, ...(persistRuleEdited === true ? { edited: true } : {}) } : undefined`
825
+ * 改成嵌套形会是既有 `persistRule: string` 消费者的 BREAKING,而这一批的纲领是 additive。
826
+ * 🔴 **缺席 = 候选臂**(server 侧 `rule_not_offered` 语义一字不变);老 server 收到带 `edited`
827
+ * 的体会诚实降级为 `rule_not_offered`,这正是 [5071] 选这个键名而不是顶层新键的理由
828
+ * (顶层新键在老 server 上是静默 200 什么都不落,坏于诚实降级)。
829
+ */
830
+ persistRuleEdited?: true;
831
+ /**
832
+ * #334 批臂(0.43.0;server ≥7.46.0,design/377):人勾的是一条**合取批** offer,选择键 =
833
+ * 该 offer 在帧上的**原始 wire 下标**(编排层已做「该下标真是 batch offer」的表核与三臂互斥)。
834
+ *
835
+ * 🔴 **注入面的映射义务**(本包只到这一格,wire 体由宿主/SDK 铸):server 的 respond 体形是
836
+ * `persistRule: { batchOfferIndex }`,与文本臂 `persistRule: { rule, edited? }` **同一个字段名、
837
+ * 三臂互斥**。本包刻意保持**扁平兄弟位**(既有 `persistRule: string` 字节不变),由注入面合成:
838
+ * ```
839
+ * persistRuleBatchOfferIndex !== undefined
840
+ * ? { batchOfferIndex: persistRuleBatchOfferIndex }
841
+ * : persistRule !== undefined
842
+ * ? { rule: persistRule, ...(persistRuleEdited === true ? { edited: true } : {}) }
843
+ * : undefined
844
+ * ```
845
+ * 🔴 **三位绝不同时上体**:server 对同场组合响亮 400 且**连决断一起拒**。编排层已保证本包发出的
846
+ * opts 上文本臂与批臂不会同场,注入面只需按上面的优先序取一个,**不要**自己再补一个 fallback。
847
+ * 🔴 **宿主必须供给能力位**(异源对抗复审五轮 [medium] 订正 —— 本段初稿写的是「批臂供给自闸、
848
+ * 老引擎零受迫」,与实装**相反**,而这段注释随 `.d.ts` 出包 = 三端读到的接入契约,与
849
+ * {@link ApprovalCardAllowDecision.persistRuleBatchOfferIndex} 那段同名残余同批清):
850
+ * 编排层只在 {@link ToolApprovalFrameLaneOpts.respondBatchRuleOffersCapable} `=== true` 时填本位。
851
+ * **宿主漏供这一位 ⇒ 现代引擎上每一次批臂勾选都被丢**(决断照送 + 一条 `surfaceRuleArmNotSent`
852
+ * 通知)。「老引擎不铸 batch offer 所以本位恒缺席」这句**单独看仍然成立**,但它证不出安全性:
853
+ * 滚动升级 / 多副本 / 代理下,**出帧的那台**与**收 respond 的那台**可以不是同一个版本。
854
+ */
855
+ persistRuleBatchOfferIndex?: number;
603
856
  /** #229(server ≥7.15.0):回决备注 —— 与 durable 腿 `AskDecisionBody.note` **同词同源同一列**
604
857
  * (`decision_note`,≤2048)。任何 decision 都可带(deny 的「为什么拒」正是审计面上最值钱的
605
858
  * 一条);真落行与否看 ack 的 {@link surfaceToolApprovalFrameAndRespond} 消费的 `noteRecorded`。
@@ -607,6 +860,48 @@ export interface RespondToolApprovalOpts {
607
860
  note?: string;
608
861
  }
609
862
  export type RespondToolApprovalFn = (approvalId: string, decision: ToolApprovalRespondDecision, opts?: RespondToolApprovalOpts) => Promise<ToolApprovalRespondAck | void>;
863
+ /**
864
+ * respond 回执的**包内视图** —— sdk `ToolApprovalRespondAck` 的 additive 超集(#334,0.43.0)。
865
+ *
866
+ * 为什么是超集而不是直接用 sdk 那个形:server 7.46.0 的兑付回执上有两个**规范文本回显位**,而
867
+ * sdk 7.2.0 的 `ToolApprovalRespondAck` 声明里还没有它们(与 `ruleOffers` 同一次锚滞后)。缺这两位,
868
+ * 「规则真正存成了什么样」在包边界上被吞掉 —— 而编辑臂的落盘文本**可能与人敲的原字节不同**
869
+ * (core 会把 `Bash(adb *)` 规范成 `Bash(adb:*)`),批臂更是一次落多条。
870
+ *
871
+ * 🔴 **超集只加不改**:继承 sdk 那个形的每一位,语义与字节零改动;既有消费端读 `rulePersisted` /
872
+ * `ruleRefusal` / `noteRecorded` 的写法一字不用动(返回型变宽对读方是源码兼容的)。
873
+ * 🔴 sdk 锚补上这两位那天,本形按 `probeCause` 先例回收(直接用 sdk 形)。
874
+ */
875
+ export interface ToolApprovalRespondAckView extends ToolApprovalRespondAck {
876
+ /**
877
+ * server ≥7.44(#340 编辑臂回显 `editedArmEcho`;7.46 沿用)——**编辑臂**兑付成功时,规则店里
878
+ * **真正落盘**的那条规范文本。
879
+ * 🔴 **界面要回显的是这一份,不是输入框里那一份**:core 会规范化人敲的拼写。
880
+ * 只在编辑臂 ∧ 兑付成功 ∧ 落的是 single 时在场;候选臂/批臂/失败/老 server ⇒ 缺席(≠ 空串)。
881
+ */
882
+ persistedRule?: string;
883
+ /**
884
+ * server ≥7.46.0(design/377 批臂回执)——**批臂**兑付成功时落地的**全体**成员规范文本,
885
+ * **展示序**(= batch offer 的成员序,壳逐条回显)。「落地」含 persisted 与 deduped 两种
886
+ * (等价规则已在店 = 同意已生效)。
887
+ * 🔴 **复数是契约不是巧合**:合取批一次授权多条规则,把它折成一条(或只报第一条)会让人以为
888
+ * 自己只批了一条。非数组 / 含非串成员 / 空数组 ⇒ 整只降缺席(半份清单比没有清单更坏)。
889
+ * 非批臂 / 兑付失败 / 老 server ⇒ 字段省略;**缺席 ≠ 空**。
890
+ *
891
+ * 🔴 **在册残余:逐条归属今天 wire 上证不出**(异源对抗复审三轮 [medium] 采纳为**登记**,不是修复)。
892
+ * 本包对这一位做到的相关性是 **id + decision + 基数**({@link surfaceToolApprovalFrameAndRespond}
893
+ * 的臂相关性门):条数必须等于所选 batch 的成员数。基数抓得到「少报/多报」,**抓不到**
894
+ * 「条数对但内容是另一批规则」—— 因为兑付回执上**没有任何锚**能把这 N 条与那只 offer 对上
895
+ * (回执不回 `batchOfferIndex`,成员也没有稳定 id;而文本等式在这一位上不成立:落盘的是
896
+ * **规范化后**的文本,与 offer 上的文本按设计可以不同,拿文本去比就是装第二个判官)。
897
+ * 🔴 ⇒ **端的呈现口径**:把它渲成「**引擎报告**落盘的规则」,**不是**「你刚才选的那批规则」。
898
+ * 两句话在正常情形下指同一件事,但只有前一句是本包能背书的。
899
+ * 正位解需要**上游补一个 additive 锚**(回执带 `batchOfferIndex`,或成员带稳定标识/摘要)——
900
+ * 已登记 `docs/INTEGRATION-CLIENTS.md` §7 缺口 **P-39**,按接入文档宪法向 server 点名索要,
901
+ * 补上当天本注按 `probeCause` 先例跟批收紧。
902
+ */
903
+ persistedRules?: readonly string[];
904
+ }
610
905
  /**
611
906
  * respond 回执的**结构化读口**(wire 是 JSON:注入面可能是旧 liveClient 的 raw fetch,也可能是
612
907
  * 比本包新一版的 SDK)。坏形一律降 `undefined` —— 与 `controlRouter.errCodes` 同族纪律:
@@ -618,7 +913,7 @@ export type RespondToolApprovalFn = (approvalId: string, decision: ToolApprovalR
618
913
  * 当成合规 ack 放行。**相关性**(「这个 ack 是不是**这一次**审批的回执」)不在本函数判 —— 本函数
619
914
  * 只认形,对不对得上由 {@link surfaceToolApprovalFrameAndRespond} 拿着 frame 与刚发出的 decision 核。
620
915
  */
621
- export declare function readToolApprovalRespondAck(v: unknown): ToolApprovalRespondAck | undefined;
916
+ export declare function readToolApprovalRespondAck(v: unknown): ToolApprovalRespondAckView | undefined;
622
917
  /**
623
918
  * 一张 `tool_approval` 帧消费完的产物(FIX①,2026-08-07 **BREAKING**:此前是裸的
624
919
  * `ToolApprovalRespondDecision | 'unresolved'` 字符串)。
@@ -627,9 +922,82 @@ export declare function readToolApprovalRespondAck(v: unknown): ToolApprovalResp
627
922
  export interface ToolApprovalFrameOutcome {
628
923
  /** 用户(或 fail-closed)落定的决断;`'unresolved'` = respond 没送达(引擎按 TTL 自决)。 */
629
924
  decision: ToolApprovalRespondDecision | 'unresolved';
630
- /** server 的 200 ack。**缺席 = 未知**(注入面回 void / server / respond 失败),绝不当成 false。 */
631
- ack?: ToolApprovalRespondAck;
925
+ /** server 的 200 ack(0.43.0 起是 {@link ToolApprovalRespondAckView} —— sdk 形的 additive 超集,
926
+ * 多两个规范文本回显位)。**缺席 = 未知**(注入面回 void / 旧 server / respond 失败),绝不当成 false。 */
927
+ ack?: ToolApprovalRespondAckView;
928
+ /**
929
+ * #225 件5(0.42.0):respond **抛错**那一支的原文交还位。
930
+ *
931
+ * 🔴 修的是一条真断链:此前本函数的 catch 只写一行 `debug` 然后返 `{decision:'unresolved'}`,
932
+ * 于是 server 的响亮 400(`persistRule.rule` 空/超长、`edited` 非布尔、顶层 `scope`、
933
+ * `edit-rejected` 的拒句 …… [5071] G4/G5/G6)在**包边界上被吞掉** —— 宿主的错误反馈面
934
+ * 无论怎么写都拿不到那句话,人在卡上改了规则被拒,屏上什么都不会说。
935
+ * 🔴 **不改 `decision` 的语义**:`'unresolved'` 仍是 `'unresolved'`(respond 没落定 = 引擎按
936
+ * TTL/abort 自决,这一位的含义一字未动);本位是**附加**的诊断面,不是新的决断态。
937
+ * 🔴 **缺席 = 没有拒绝原文可交**(respond 成功 / 抛的东西上**三位皆读不出**),绝不造一句。
938
+ * 在场时**三位都可能缺席其二** —— 有 `status` 没文本(应答体为空)、有文本没 `status`
939
+ * (传输层失败)都是真实形。
940
+ */
941
+ respondRefusal?: ToolApprovalRespondRefusal;
942
+ }
943
+ /**
944
+ * #225 件5:respond 抛错的**结构化原文**(不是新的决断态,见
945
+ * {@link ToolApprovalFrameOutcome.respondRefusal})。
946
+ *
947
+ * 三位**各自独立**防御读、各自缺席不铸:`status` 只认有限数、`errorCode` 只认非空串、
948
+ * `message` 只认真读得出来的文本(取值序与 try 保护见 {@link readToolApprovalRespondRefusal})。
949
+ * 🔴 **三位皆缺席时整只不铸**(异源复审 [medium] 采纳,0.42.0):此前 `message` 是必填、读不出时
950
+ * 无条件退 `String(err)`,于是 `throw {}` 会得到一句 `"[object Object]"` —— 那不是 server 说的话,
951
+ * 是本层编的([honest-absence-not-fabricated-zero])。
952
+ * 🔴 **原样交还,零加工**:不 trim、不截断、不改写、不按识别表过滤。server 的拒句是给人看的
953
+ * 指路文本([5071] 三条 400 拒句各自成文),包边界任何一次改写都会让宿主呈的不再是引擎说的那句;
954
+ * 长度夹取与呈现归端(与 `message`/`approver` 同族口径)。
955
+ * 🔴 **UNTRUSTED-for-display**:它是 HTTP 应答体上的文本,只渲染,绝不回喂模型/工具入参。
956
+ */
957
+ interface ToolApprovalRespondRefusalFields {
958
+ /** HTTP 状态(在场 = 引擎真应答了;缺席 = 传输层失败/注入面自抛,不许反推成 0)。 */
959
+ status?: number;
960
+ /** 机读码(开集,原样;缺席 = 应答没带码)。 */
961
+ errorCode?: string;
962
+ /**
963
+ * 拒句原文(server 的响亮拒文案)。
964
+ * 🔴 **缺席 = 这个抛出物上读不出任何可用文本**(异源复审 [medium] 采纳,0.42.0)——
965
+ * 此前本位是必填、读不出时无条件铸 `String(err)`,于是 `throw {}` 会得到一句
966
+ * `"[object Object]"`、`throw null` 得到 `"null"`:那**不是** server 说的话,是本层编的,
967
+ * 与本形头注承诺的「读不出原文就缺席」直接冲突([honest-absence-not-fabricated-zero])。
968
+ */
969
+ message?: string;
632
970
  }
971
+ /**
972
+ * 🔴 **「在场即至少有一位」写进类型**(异源复审 [medium] 采纳,0.42.0):三位全 optional 的
973
+ * interface 在类型上允许 `{}`,而实现明确承诺「三位皆缺席时整只返 `undefined`」——
974
+ * 消费端于是既不能依赖「对象在场 = 至少有一条诊断信息」,也没法穷举安全渲染。
975
+ * 用**三选一联合**把那条运行期不变量抬到编译期:`{}` 从此不可赋值。
976
+ */
977
+ export type ToolApprovalRespondRefusal = (ToolApprovalRespondRefusalFields & {
978
+ status: number;
979
+ }) | (ToolApprovalRespondRefusalFields & {
980
+ errorCode: string;
981
+ }) | (ToolApprovalRespondRefusalFields & {
982
+ message: string;
983
+ });
984
+ /**
985
+ * 从 respond 抛出来的东西上读 {@link ToolApprovalRespondRefusal}。**永不抛、永不造**。
986
+ *
987
+ * 键位口径与 {@link import('../wireErrorTriage.js').classifyTurnWireError} 同源([2055] 死键
988
+ * 纪律:只认活键 `errorCode`,退役的 `code` 槽不做兼容)。
989
+ *
990
+ * @returns 三位**全缺席**时返回 `undefined` —— 一个三位皆空的 refusal 对象是「有拒句」的假象。
991
+ *
992
+ * 🔴 **文本取值序**(异源复审 [medium] 修):`err.message` 非空串 > 抛出物本身是非空串 >
993
+ * 受保护的 `String(err)`,且**只接受**真有内容的结果 —— `[object Object]` / `null` /
994
+ * `undefined` 三种占位串一律当作「读不出」。
995
+ * 🔴 **`String(err)` 用 try 包住**:抛出物可以自带 `Symbol.toPrimitive` / `toString` 钩子并在里面
996
+ * 抛错。本函数的唯一调用点在 `surfaceToolApprovalFrameAndRespond` 的 catch 里,那里的契约是
997
+ * 「respond 失败 ⇒ 返回 unresolved」;让一个不可信的转换钩子把这条收敛路径变成 reject,
998
+ * 等于给注入面开了一个「让整次审批消费抛出去」的口。
999
+ */
1000
+ export declare function readToolApprovalRespondRefusal(err: unknown): ToolApprovalRespondRefusal | undefined;
633
1001
  /** 结构性识别流上的 tool_approval 帧(named SSE frame,payload.type === 帧名)。
634
1002
  * ⚠️ 口径更正(2026-08-08):此处原写「非 AgentEvent arm」—— SDK #185a 起这两个帧**是**
635
1003
  * `AgentEvent` 的臂了(durable 腿也回放),所以结构识别与 union 收窄两条路都成立;本函数仍按
@@ -644,6 +1012,48 @@ export interface ToolApprovalFrameLaneOpts {
644
1012
  * 缺席/false ⇒ 不发(fail-closed 到「不发」侧;决断本身照常送达,现状字节不变)。
645
1013
  */
646
1014
  approvalDecisionNoteCapable?: boolean;
1015
+ /**
1016
+ * server 能力位 `capabilities.respondFreeFormRules` 的读数(宿主从自己的 caps 缓存供给;
1017
+ * server ≥7.44,#340 [5071] 恒真版本位)。
1018
+ * 🔴 形与判据**逐字照** {@link approvalDecisionNoteCapable} 既有形(同一条 SDK 6.16 成文纪律:
1019
+ * **位缺席就别发**)。
1020
+ * 缺席/false ⇒ 编辑臂的 `persistRule` **整条不发**(fail-closed 到「不发」侧;决断本身照常送达,
1021
+ * 现状字节不变)。候选臂**不受本位影响**(它是 0.25.0 起就有的既有通道)。
1022
+ *
1023
+ * 🔴 **失效形订正**(异源对抗复审七轮 [medium];本段初稿把 `note` 那一位的失效形抄了过来):
1024
+ * 编辑臂**不是**「老 server 静默忽略且照回 200」——`[5071]` 选 `persistRule.edited` 这个键名
1025
+ * 的**全部理由**就是让老 server **诚实降级** `rule_not_offered`,而形/上限不合时更是响亮 400
1026
+ * (`PERSIST_RULE_TEXT_ERROR` / `PERSIST_RULE_EDITED_FLAG_ERROR`),**400 连决断一起拒**。
1027
+ * ⇒ 本位存在的理由不是「分辨静默 200」,是「能提前知道这台不供,就别把人的编辑送出去白跑一趟、
1028
+ * 更别让它把决断打掉」。
1029
+ * 🔴 **「老引擎零受迫」这句话有前提**(§7 **P-40**):本位是**per-baseUrl 的布尔缓存**,
1030
+ * **不与收 respond 的那个副本绑定**。引擎温切 / 滚动升级 / 多副本代理下,一个陈旧的 `true`
1031
+ * 仍会把新形送到老副本上。宿主的义务:引擎 respawn/restart 后调
1032
+ * `invalidateEngineCaps(baseUrl, probe?)`;滚动窗口内宁可按 `false` 供(丢持久臂 + 一条
1033
+ * `surfaceRuleArmNotSent` 通知,远好于把人按下的决断打掉)。
1034
+ */
1035
+ respondFreeFormRulesCapable?: boolean;
1036
+ /**
1037
+ * server 能力位 `capabilities.respondBatchRuleOffers` 的读数(宿主从自己的 caps 缓存供给;
1038
+ * server ≥7.46.0,#334 design/377 恒真版本位 —— 直证 = engine 7.46.0 fixture 的
1039
+ * `@sema-agent/server/dist/http/routes/capabilities.js` 里 `respondBatchRuleOffers: true`)。
1040
+ *
1041
+ * 🔴 形与判据**逐字照** {@link respondFreeFormRulesCapable}(同一条 SDK 6.16 成文纪律:
1042
+ * **位缺席就别发**)。缺席/false ⇒ 批臂的 `persistRuleBatchOfferIndex` **整条不发**
1043
+ * (fail-closed 到「不发」侧;决断本身照常送达,现状字节不变)。
1044
+ * 🔴 **为什么「帧上有 batch offer」不能顶替这道门**(异源对抗复审 [high] 采纳,施工时实证
1045
+ * ——该 caps 位在 7.46.0 上确实存在):供给面只证明**出帧的那一端**认识新形,不证明
1046
+ * **收 respond 的那个端点**认识它。滚动升级 / 多副本 / 代理后面站着一台 ≤7.45 的实例时,
1047
+ * 旧解析器对 `persistRule.batchOfferIndex` 走的是「`persistRule.rule` 必须是非空串」那条,
1048
+ * **响亮 400 且连决断一起拒** —— 于是人按下的那次 allow 落不了地,退化成 unresolved/TTL 自决。
1049
+ * 与 note 超限那条同一条纪律:附带愿望的失败绝不许升级成主动作的失败。
1050
+ * 🔴 **本位是 per-baseUrl 缓存,不与 responder 绑定**(§7 **P-40**,与
1051
+ * {@link respondFreeFormRulesCapable} 同族):陈旧的 `true` 仍会把新形送到老副本上 ⇒
1052
+ * 引擎温切后必须 `invalidateEngineCaps(baseUrl, probe?)`,滚动窗口内宁可按 `false` 供。
1053
+ * 🔴 **只闸兑付,不闸展示**:位缺席时 batch offer 照旧上卡(`ApprovalCardRequest.ruleOffers`
1054
+ * 字节不变)—— 端可以渲、可以走本地落规则那条路,只是**这条 wire 兑付通道**不开。
1055
+ */
1056
+ respondBatchRuleOffersCapable?: boolean;
647
1057
  /**
648
1058
  * 这条帧是不是**当下**从 live 流上收到的(异源对抗复审 P1 采纳;壳孪生帧族同名闸的
649
1059
  * 本包席位 —— 壳 `approvalStreamWire` 头注逐字:「账本重放的历史帧带的是铸帧时刻的旧余量,
@@ -656,3 +1066,4 @@ export interface ToolApprovalFrameLaneOpts {
656
1066
  windowIsCurrent?: boolean;
657
1067
  }
658
1068
  export declare function surfaceToolApprovalFrameAndRespond(frame: ToolApprovalFrame, respond: RespondToolApprovalFn, streamArgs: unknown | undefined, signal?: AbortSignal, lane?: ToolApprovalFrameLaneOpts): Promise<ToolApprovalFrameOutcome>;
1069
+ export {};