@sema-agent/client-core 0.66.0 → 0.67.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -73,3 +73,40 @@ export declare const ASK_ORIGIN_WORDS: readonly string[];
73
73
  * **不截断不改写**它 —— 显示封顶归端(它才知道自己的行宽)。
74
74
  */
75
75
  export declare function askOriginDetail(origin: unknown, message?: unknown): string;
76
+ /**
77
+ * 🔴 **`AskOrigin` 那条型面钉 0.67.0 退役,理由要写清**(如实登记,不是偷偷删):
78
+ * 它此前写作 `const _askOriginWordsPin: readonly AskOrigin[] = ASK_ORIGIN_WORDS`,而
79
+ * `AskOrigin = SdkAskOrigin` 的型面带 `(string & {})` 逃生口 ⇒ **任何字符串都满足它**,这条钉
80
+ * 在 core 7.14.0 把 `unresolvable` 换成 `ancestor_marked` 的当天**一声不响**(亲验:换词后
81
+ * `npx tsc --noEmit` 0 红)。一条恒真的钉不是钉,是[gates-fail-in-their-own-ways] 的「无契约表」那一档。
82
+ * ⇒ 判据整只移交给 `run-gate-vocabulary-test.mjs` 的 **B 段**:本表对 **core `ask-origin.js` 的
83
+ * `ASK_ORIGINS` 真字节**双向等值(词表属主是 core,sdk 只是它的镜像且**今天滞后一代**)。
84
+ * 那条对账拿的是运行期真字节,逃生口影响不到它。
85
+ */
86
+ /**
87
+ * **规则店读不出来是哪一头**(core `RULE_STORE_UNREADABLE_KINDS`;逐词逐序镜像,7.14.0 起两词)。
88
+ * · `store` —— 接上的那个店**读不出来**(读失败 / 超时 / 适配器抛了);
89
+ * · `call` —— 店读到了,但**这一次调用**读不出来(收紧 lexer 的 `unreadable`:展开里有读不懂的词 /
90
+ * 引号未闭合 / 语法错 —— 引擎自己那句话在 ask 的 `message` 上)。
91
+ *
92
+ * 🔴 **真闭集**(与 `AskOrigin` 的开集刻意不同):core 在**唯一 stamp 点**把政策自报的值剥掉之后
93
+ * 才盖章(7.14.0 合同逐字「A policy's self-declared value is removed at the stamp」)⇒ 表外值
94
+ * 结构上到不了消费端。
95
+ * 🔴 形制:`Object.freeze` 的数组,理由同上面两张表。
96
+ */
97
+ export declare const RULE_STORE_UNREADABLE_KINDS: readonly string[];
98
+ /** 这个值是不是 {@link RULE_STORE_UNREADABLE_KINDS} 的成员(core `isRuleStoreUnreadable` 的镜像)。
99
+ * 🔴 **唯一判据口**:窄读器与端都调它,谁都不许再抄一份字面量(B-025 的根因就是手抄)。 */
100
+ export declare function isRuleStoreUnreadableKind(v: unknown): boolean;
101
+ /**
102
+ * 一个 `ruleStoreUnreadable` 词 → 一句人话。**唯一铸点**(三端共用;端零自拼)。
103
+ *
104
+ * 🔴 **与 {@link askOriginDetail} 并列不互替**:那一句答「谁问的」(出身),本句答「读不出来的是
105
+ * **哪一头**」。`rule_store_unavailable` 那只 ask 上两句都该在,渲一句丢一句就等于把人指去
106
+ * 错的地方 —— 而这正是 core 把这一位单独铸出来的理由。
107
+ * 🔴 **查表用 `Object.hasOwn`**(冻结不移除原型;理由与本文件另两个铸点逐字相同)。
108
+ * 🔴 表外词 / 坏值 ⇒ 一句**兜底**:原样带上那个词,并明说这次仍然是在问人。
109
+ * ⚠️ 与 {@link isRuleStoreUnreadableKind} 的闭集判据不矛盾:那一层答「这条事实在不在」(闭集,
110
+ * 出集 = 坏形),本层答「拿到一个词怎么渲」—— 一条从旧持久态恢复回来的视图必须渲出一句诚实的话。
111
+ */
112
+ export declare function ruleStoreUnreadableDetail(kind: unknown): string;
@@ -72,7 +72,12 @@ export function gateDeniedByDetail(deniedBy) {
72
72
  */
73
73
  export const ASK_ORIGIN_WORDS = Object.freeze([
74
74
  'content_question',
75
- 'unresolvable',
75
+ // 🔴 0.67.0 / core 7.14.0 **BREAKING 换词,无 alias**:`unresolvable` → `ancestor_marked`
76
+ // (`dist/core/ask-origin.js:1` `ASK_ORIGINS` 真字节;`ask-origin.d.ts:36-38` 是它的定义句)。
77
+ // 换的是**词**不是位:同一条 ladder 臂、同一张资格表。旧词按 clean-cut **不留别名** ——
78
+ // 一条 pre-7.14 的耐久行带 `"unresolvable"` 时它是**非成员**,投影**省略**那一格(见
79
+ // {@link askOriginDetail} 的表外词兜底句;`ASK_ORIGIN_SENTENCES` 里不再有它的一句)。
80
+ 'ancestor_marked',
76
81
  'org_unavailable',
77
82
  'org_rule',
78
83
  'rule_store_unavailable',
@@ -93,7 +98,7 @@ export const ASK_ORIGIN_WORDS = Object.freeze([
93
98
  *
94
99
  * ── 🔴 0.65.0 三句订正(B-080③;core `dist/core/ask-origin.d.ts` 真字节)───────────────────────
95
100
  * 三句修前各自漂开了上游的定义,而漂的方向都是「把引擎真正说的事换成一件别的事」:
96
- * · **`unresolvable`** 修前渲「门拿不定主意」—— core:36-38 逐字是「the call is MARKED by an
101
+ * · **`ancestor_marked`**(0.67.0 前名 `unresolvable`)修前渲「门拿不定主意」—— core:36-38 逐字是「the call is MARKED by an
97
102
  * ancestor (an inherited approver-unavailable float, or a durable mandate floated down because
98
103
  * this task can park it): it must reach the park with no synchronous decision-maker in between」。
99
104
  * 那不是犹豫,是**祖先盖的标记**+一条硬路径约束;渲成「拿不定主意」会让人去找一个不存在的
@@ -109,7 +114,7 @@ export const ASK_ORIGIN_WORDS = Object.freeze([
109
114
  */
110
115
  const ASK_ORIGIN_SENTENCES = Object.freeze({
111
116
  content_question: 'the tool itself asked you a question',
112
- unresolvable: 'an ancestor marked this call (an inherited approver-unavailable float, or a durable mandate): it must reach the park with no synchronous decision-maker in between',
117
+ ancestor_marked: 'an ancestor marked this call (an inherited approver-unavailable float, or a durable mandate): it must reach the park with no synchronous decision-maker in between',
113
118
  org_unavailable: 'the organization policy was unavailable, so this call asks',
114
119
  org_rule: 'an organization rule asks about this call',
115
120
  rule_store_unavailable: 'this call could not be checked against your standing deny/ask rules, so it asks',
@@ -166,11 +171,67 @@ export function askOriginDetail(origin, message) {
166
171
  return `this call asks; its origin ${word} is a word newer than this client, so answer it as usual`;
167
172
  }
168
173
  /**
169
- * **编译期对账钉**(不出公面):本包两张表的成员必须真属于 sdk 的两个联合。
170
- * 🔴 `DeniedBy` 那一张钉得**双向**(它在 wire 上是真闭集,型面也闭);`AskOrigin` 那一张只钉
171
- * 单向(它有逃生口,任何字符串都合法 —— 钉反向等于把开集当闭集用)。
174
+ * **编译期对账钉**(不出公面):`DeniedBy` 那张表的成员必须真属于 sdk 的联合,**双向**
175
+ * (它在 wire 上是真闭集,型面也闭)。
172
176
  */
173
177
  const _deniedByWordsPin = GATE_DENIED_BY_WORDS;
174
178
  void _deniedByWordsPin;
175
- const _askOriginWordsPin = ASK_ORIGIN_WORDS;
176
- void _askOriginWordsPin;
179
+ /**
180
+ * 🔴 **`AskOrigin` 那条型面钉 0.67.0 退役,理由要写清**(如实登记,不是偷偷删):
181
+ * 它此前写作 `const _askOriginWordsPin: readonly AskOrigin[] = ASK_ORIGIN_WORDS`,而
182
+ * `AskOrigin = SdkAskOrigin` 的型面带 `(string & {})` 逃生口 ⇒ **任何字符串都满足它**,这条钉
183
+ * 在 core 7.14.0 把 `unresolvable` 换成 `ancestor_marked` 的当天**一声不响**(亲验:换词后
184
+ * `npx tsc --noEmit` 0 红)。一条恒真的钉不是钉,是[gates-fail-in-their-own-ways] 的「无契约表」那一档。
185
+ * ⇒ 判据整只移交给 `run-gate-vocabulary-test.mjs` 的 **B 段**:本表对 **core `ask-origin.js` 的
186
+ * `ASK_ORIGINS` 真字节**双向等值(词表属主是 core,sdk 只是它的镜像且**今天滞后一代**)。
187
+ * 那条对账拿的是运行期真字节,逃生口影响不到它。
188
+ */
189
+ // ══ 0.67.0 / core 7.14.0 #688 C3 —— 第三张表:`ruleStoreUnreadable` 两词 ═════════════════════
190
+ //
191
+ // 它不是 `AskOrigin` 的第十二个词,而是**一个 origin 词底下的机制位**:`rule_store_unavailable`
192
+ // 这一个词盖了**两种机制**(core `ask-origin.d.ts:98-104` 顶注逐字点名),而对人的下一步**相反**。
193
+ // 词表属主 = core `RULE_STORE_UNREADABLE_KINDS`(`ask-origin.d.ts:116`),本包逐词逐序镜像,
194
+ // `run-gate-vocabulary-test.mjs` 的 C 段对**实装 core 的真字节**双向等值。
195
+ /**
196
+ * **规则店读不出来是哪一头**(core `RULE_STORE_UNREADABLE_KINDS`;逐词逐序镜像,7.14.0 起两词)。
197
+ * · `store` —— 接上的那个店**读不出来**(读失败 / 超时 / 适配器抛了);
198
+ * · `call` —— 店读到了,但**这一次调用**读不出来(收紧 lexer 的 `unreadable`:展开里有读不懂的词 /
199
+ * 引号未闭合 / 语法错 —— 引擎自己那句话在 ask 的 `message` 上)。
200
+ *
201
+ * 🔴 **真闭集**(与 `AskOrigin` 的开集刻意不同):core 在**唯一 stamp 点**把政策自报的值剥掉之后
202
+ * 才盖章(7.14.0 合同逐字「A policy's self-declared value is removed at the stamp」)⇒ 表外值
203
+ * 结构上到不了消费端。
204
+ * 🔴 形制:`Object.freeze` 的数组,理由同上面两张表。
205
+ */
206
+ export const RULE_STORE_UNREADABLE_KINDS = Object.freeze(['store', 'call']);
207
+ const RULE_STORE_UNREADABLE_SET = new Set(RULE_STORE_UNREADABLE_KINDS);
208
+ /** 这个值是不是 {@link RULE_STORE_UNREADABLE_KINDS} 的成员(core `isRuleStoreUnreadable` 的镜像)。
209
+ * 🔴 **唯一判据口**:窄读器与端都调它,谁都不许再抄一份字面量(B-025 的根因就是手抄)。 */
210
+ export function isRuleStoreUnreadableKind(v) {
211
+ return typeof v === 'string' && RULE_STORE_UNREADABLE_SET.has(v);
212
+ }
213
+ /** 逐词一句人话。**两句刻意逐字互异** —— 两条不同的下一步(去修部署的规则店 / 去改自己那条规则)。 */
214
+ const RULE_STORE_UNREADABLE_SENTENCES = Object.freeze({
215
+ store: 'your standing deny/ask rules could not be read at all (the rule store did not answer), so this call asks',
216
+ call: 'this command could not be read against your standing deny/ask rules (the engine’s own sentence is on this ask), so it asks',
217
+ });
218
+ /**
219
+ * 一个 `ruleStoreUnreadable` 词 → 一句人话。**唯一铸点**(三端共用;端零自拼)。
220
+ *
221
+ * 🔴 **与 {@link askOriginDetail} 并列不互替**:那一句答「谁问的」(出身),本句答「读不出来的是
222
+ * **哪一头**」。`rule_store_unavailable` 那只 ask 上两句都该在,渲一句丢一句就等于把人指去
223
+ * 错的地方 —— 而这正是 core 把这一位单独铸出来的理由。
224
+ * 🔴 **查表用 `Object.hasOwn`**(冻结不移除原型;理由与本文件另两个铸点逐字相同)。
225
+ * 🔴 表外词 / 坏值 ⇒ 一句**兜底**:原样带上那个词,并明说这次仍然是在问人。
226
+ * ⚠️ 与 {@link isRuleStoreUnreadableKind} 的闭集判据不矛盾:那一层答「这条事实在不在」(闭集,
227
+ * 出集 = 坏形),本层答「拿到一个词怎么渲」—— 一条从旧持久态恢复回来的视图必须渲出一句诚实的话。
228
+ */
229
+ export function ruleStoreUnreadableDetail(kind) {
230
+ const known = typeof kind === 'string' && Object.hasOwn(RULE_STORE_UNREADABLE_SENTENCES, kind)
231
+ ? RULE_STORE_UNREADABLE_SENTENCES[kind]
232
+ : undefined;
233
+ if (known !== undefined)
234
+ return known;
235
+ const word = typeof kind === 'string' && kind.length > 0 ? kind : '(none)';
236
+ return `your standing deny/ask rules could not be applied to this call; the reported reason ${word} is a word newer than this client, so answer this ask as usual`;
237
+ }
@@ -146,9 +146,16 @@ export function toAnsweredOutput(questions, answer) {
146
146
  for (const entry of answer.answers ?? []) {
147
147
  const q = questions.find(qq => qq.header === entry.header);
148
148
  const key = typeof q?.question === 'string' ? q.question : entry.header;
149
- answers[key] = (entry.selected ?? []).join(', ');
149
+ // 🔴 0.67.1 同形族(与 `adapter/downstream/terminalToSdkResult.ts` 的 `putOwn` 同一条):
150
+ // `key` 来自 wire(问题正文 / header),字面等于 `"__proto__"` 时裸赋值走的是
151
+ // `Object.prototype` 上那只 **accessor** —— `answers` 那一格(串值)整条静默丢失,
152
+ // `annotations` 那一格(对象值)还顺手改了表自己的原型。落键一律 `defineProperty`。
153
+ const put = (t, v) => {
154
+ Object.defineProperty(t, key, { value: v, enumerable: true, writable: true, configurable: true });
155
+ };
156
+ put(answers, (entry.selected ?? []).join(', '));
150
157
  if (entry.note)
151
- annotations[key] = { notes: entry.note };
158
+ put(annotations, { notes: entry.note });
152
159
  }
153
160
  return {
154
161
  type: 'ask-user-question',
@@ -78,7 +78,6 @@
78
78
  * 🔴 方向纪律:reason/note 只做归因,绝不参与裁决;缺席 ⇒ 现状字节不变。
79
79
  */
80
80
  import { type GateCurrentPending, type HitlFailureStage, type HitlSafetyCode, type HitlClientLike } from './hitlBridge.js';
81
- import type { ClassifierUnavailableView } from '../autoModeUnavailable.js';
82
81
  import { type DecideReceiptView } from '../decideReceipt.js';
83
82
  import { RULE_OFFER_MATCHES, RULE_OFFER_BATCH_MEMBER_KINDS, RULE_OFFER_UNCOVERED_REASONS, RULE_OFFERS_ABSENCE_REASONS, DENIAL_LIMIT_KINDS } from '@sema-agent/sdk';
84
83
  import type { RuleSuggestion, ToolApprovalRespondAck, PersistedRuleAnchor as SdkPersistedRuleAnchor, RuleOfferMatch as SdkRuleOfferMatch, RuleOfferBatchMember as SdkRuleOfferBatchMember, RuleOfferUncoveredDetail as SdkRuleOfferUncoveredDetail, RuleOfferUncoveredReason as SdkRuleOfferUncoveredReason, AskOrigin as SdkAskOrigin, RuleOffersAbsence as SdkRuleOffersAbsence, DenialLimitKind as SdkDenialLimitKind, DenialLimitFallback as SdkDenialLimitFallback } from '@sema-agent/sdk';
@@ -235,12 +234,17 @@ export type RuleOfferUncoveredReason = SdkRuleOfferUncoveredReason;
235
234
  */
236
235
  export { RULE_OFFER_MATCHES, RULE_OFFER_BATCH_MEMBER_KINDS, RULE_OFFER_UNCOVERED_REASONS };
237
236
  /**
238
- * 一只 ask 的**出身** —— **sdk 8.3.0 `AskOrigin` 派生**(core `ASK_ORIGINS` 闭八词:
239
- * `content_question` / `unresolvable` / `org_unavailable` / `org_rule` / `hook` / `ask_rule` /
240
- * `denial_limit_fallback` / `policy`;**引擎在门上盖章**,不是策略的自称)。
237
+ * 一只 ask 的**出身** —— **sdk 8.3.0 `AskOrigin` 派生**(core `ASK_ORIGINS`,7.14.0 起十一词:
238
+ * `content_question` / `ancestor_marked` / `org_unavailable` / `org_rule` / `rule_store_unavailable` /
239
+ * `hook` / `ask_rule` / `denial_limit_fallback` / `shell_gate_tighten` / `safety_tighten` / `policy`;
240
+ * **引擎在门上盖章**,不是策略的自称)。
241
241
  *
242
242
  * 🔴 **词表唯一属主是 core**,本别名只是型面供给(闭集给补全,`(string & {})` 让新词读得出来)——
243
243
  * 端一律按**开集**分支:认不得的词渲「未知出身」,**绝不**渲成「没有出身」。
244
+ * ⚠️ **sdk 8.8.0 的联合滞后一代**:它还写着 7.13.x 的 `unresolvable`,而 core 7.14.0 已把那一员
245
+ * **BREAKING 换成 `ancestor_marked`**(无 alias)。本包的**运行期**词表 `ASK_ORIGIN_WORDS`
246
+ * (`gateVocabulary.ts`)跟的是 **core 的真字节**,sdk 追平那天 `run-gate-vocabulary-test.mjs`
247
+ * 的滞后登记会自红逼删。一条 pre-7.14 的耐久行带 `"unresolvable"` ⇒ 非成员 ⇒ 投影**省略**那一格。
244
248
  * 🔴 与 {@link ToolApprovalFrame.governanceForced} **不是**一回事(sdk 顶注逐字):本词答「哪一类
245
249
  * 权威提的问」,那一位答「**本部署运维治理层**是不是这只 ask 的门」;`origin:"policy"` 覆盖
246
250
  * 任何部署 ToolPolicy 的 ask ⇒ 两者顶替不了对方。
@@ -682,29 +686,29 @@ export interface ApprovalCardRequest {
682
686
  */
683
687
  origin?: string;
684
688
  /**
685
- * cli L-174②(0.64.2;server ≥7.69.0 / core 7.10.0 #616)—— 「**问你是因为分类器这次跑不了**」的
686
- * 事实,**双源合流**({@link governanceForced} 同形,两腿在场性可以不一致):
687
- * · **活卡帧腿**:{@link ToolApprovalFrame.classifierUnavailable} 的窄读产物;
688
- * · **durable park 行腿**:`PendingAction.tool_approval.classifierUnavailable` 的孪生位
689
- * (core `checkpoint-store.d.ts` 逐字「the PARK twin of `AskRequest.classifierUnavailable`」),
690
- * 经**同一把**窄读器 —— 两处**键路同形**,各写一份就是两份台账各漂各的。
689
+ * **core 7.14.0 #688 C3**(0.67.0;**ADDITIVE**,`"tool_approval"` only)—— 这只 ask 是因为
690
+ * **持久规则那条腿读不出来**才问人的,而它**具体哪一头**读不出来:
691
+ * · `store` —— 那个人的**持久规则店整体**读不出来(读失败 / 超时 / 适配器抛了);
692
+ * · `call` —— 店读到了,但**这一次调用**对不上他的 deny/ask 规则行(收紧 lexer 的 `unreadable`:
693
+ * 展开里有读不懂的词 / 引号未闭合 / 语法错 —— 引擎自己的那句话在 `message` 上)。
691
694
  *
692
- * 🔴 **键路与 ask / park 行同形是有意的**:端不必为卡再学一个读法 —— `classifierUnavailableOf(req)`
693
- * 直接吃这张卡(0.63.0 件⑧ 的读器,§27h2),措辞走它的孪生铸点 `classifierUnavailableDetail`。
694
- * 🔴 **两层都按开集,显示层只多一条有出处的排除**(0.64.2 订正后两层判据几乎逐字相同):
695
- * · **本层(过境)**只判形 —— `cause` 非空串即收,与 server 唯一铸点 `readClassifierUnavailable`
696
- * 同判据。词表属主在 core,包在边界再校一次闭集,只会在 core 加词那天把一个**合法**值判没,
697
- * 而丢的正是「这次不可用是新出现的那一类」这条信息(与 `origin`/`ruleOffersAbsence` 逐字同规);
698
- * · **显示层**(`classifierUnavailableOf`)同样非空串即收,只排除**熔断轴独占**的词
699
- * (今天 = `parse_error`;core 明说它不 stamp 到这一格)—— 未知词在那一层渲**兜底句**,
700
- * 不是不渲。⚠️ 0.64.2 之前那一层是 unavailable **闭集**,于是一个合法的新成因词过得了本层、
701
- * 却在显示层被判没(异源对抗复审逼出的真病;详见 INTEGRATION §29e)。
702
- * 🔴 **显示元数据,不是裁决位**:不参与、也不改变本次决断的任何字节;缺席**不许**渲成
703
- * 「分类器跑成了」(缺席三形不可分,见帧上同名键)。
704
- * ⚠️ **退役日期已定**(上游发布记录 [6852]/[6853]):core 7.12.0 / server 7.70.0 起帧上这一格是**死键**,
705
- * 事实位随拒绝面走;本包 0.65.0 改读那一面而**不删本键**——缺席臂当天就是正解。
695
+ * **双源合流**(与退役前的 `classifierUnavailable` 两腿装配**同形同位**):
696
+ * · **活卡帧腿**:{@link ToolApprovalFrame.ruleStoreUnreadable} 的窄读产物;
697
+ * · **durable park 行腿**:`PendingAction.tool_approval.ruleStoreUnreadable` 的孪生位
698
+ * (core `checkpoint-store.d.ts:818` / 摘要行 `:1924-1926` 逐字同名),经**同一把**窄读器。
699
+ *
700
+ * 🔴 **在场 ⇔ `origin === "rule_store_unavailable"`**(core 7.14.0 合同逐字):缺席 = 这条腿把店
701
+ * 和这次调用都读通了,**或者压根没接规则店** —— 两形在这一格上不可分,端**禁**把缺席渲成
702
+ * 「你的规则都好着呢」。
703
+ * 🔴 **为什么非有它不可**:`origin` 那一个词盖了**两种机制**,而对人的下一步是**相反**的 ——
704
+ * `store` 要去修部署的规则店,`call` 要去改他自己那条规则/这条命令。只给 origin 的卡会把人
705
+ * 指到错的地方(core `ask-origin.d.ts:98-104` 顶注逐字点名这件事)。
706
+ * 🔴 **闭二词按闭集判**(与 `cause` 同一条,与 `origin`/`ruleOffersAbsence` 的开集纪律不同):
707
+ * core 在**唯一 stamp 点**上把政策自报的值剥掉后才盖章(合同逐字「A policy's self-declared
708
+ * value is removed at the stamp」)⇒ 表外值到不了消费端,真读到就是坏形,渲一句成因就是编事实。
709
+ * 人话两句走**唯一铸点** `ruleStoreUnreadableDetail`(三端零自拼)。
706
710
  */
707
- classifierUnavailable?: ClassifierUnavailableView;
711
+ ruleStoreUnreadable?: string;
708
712
  }
709
713
  /**
710
714
  * 🔴 **拆缝口** —— 弹「三选卡」并等人的决断。壳 = vendored CC `PermissionRequest`;
@@ -889,27 +893,24 @@ export interface ToolApprovalFrame {
889
893
  */
890
894
  denialLimitFallback?: DenialLimitFallback;
891
895
  /**
892
- * **server ≥7.69.0 / core 7.10.0 #616**(cli L-174②;**ADDITIVE**,`"tool_approval"` only。真发直证 =
893
- * engine 7.69.0 fixture `@sema-agent/server/dist/tool-approval.js:847` 的条件 stamp + 同包
894
- * `approval-card.js:244` `readClassifierUnavailable`(`z.object({cause: z.string().min(1).max(200)}).strict()`,
895
- * 与 `card_json` 铸点 `:315` 同一只读器);上游发布记录 [6863] ③ 表逐字「additive;缺席不是断言;`cause`
896
- * core 闭三词但 **wire 上 string**」)——这只 ask 是因为 **auto 模式分类器这一轮跑不了**才问人的,
897
- * `cause` 说的是「为什么没跑成」(core `AUTO_MODE_UNAVAILABLE_CAUSES`)。
896
+ * **core 7.14.0 #688 C3**(0.67.0;**ADDITIVE**,`"tool_approval"` only)——
897
+ * `AskRequest.ruleStoreUnreadable ∈ {"store","call"}`(core `ask-origin.d.ts:116`
898
+ * `RULE_STORE_UNREADABLE_KINDS` / `isRuleStoreUnreadable`;`tool-policy.d.ts:308` 是 ask 臂上的声明)。
899
+ * 「持久规则那条腿读不出来」这件事的**机制位**:`store` = 店整体读不出来 / `call` = 这条调用对不上
900
+ * 那个人的 deny/ask 规则行。
898
901
  *
899
- * 🔴 **显示元数据,不是裁决位**:引擎在门上已经判完了,没有任何东西读它决定放行与否。
900
- * 🔴 **缺席不是断言**:缺席同时覆盖「分类器答上了」「这只 ask 没资格走分类器」「本部署没接分类器」
901
- * 三形(core d.ts 逐字:read presence, never absence)—— 端**禁**读成「分类器好着呢」。
902
- * 🔴 **echo-only**:server 顶注逐字写明它不据本键做任何裁决;据它在下游自铸第二套熔断/回落判据
903
- * = 同一语义面两个写者(源头修复纪律)。本包同律 —— 本批只做过境,**不做熔断相关任何事**。
904
- * ⚠️ **它是一个已定退役日期的键**(上游发布记录 [6852] server 侧落点表逐字:「ask 帧 `classifierUnavailable`
905
- * **死键**;事实位随 deny 载荷 … 三面投影(tool_approval 帧 / card_json / inbox 行)**退役**,
906
- * `permissionDenied.classifierUnavailable` 走 `tool_end.gate` 既有透传」)⇒
907
- * **core 7.11.x 仍写;core 7.12.0 / server 7.70.0 起缺席**。本包 0.65.0 按 [6853] 的排期改从
908
- * 拒绝面渲,**码不删**(死键保留型):缺席臂那天就是正解,退役一个 additive 键不该逼三端各改一次。
902
+ * 🔴 **在场 ⇔ `origin === "rule_store_unavailable"`**(合同逐字)。缺席 = 这条腿把店和这次调用都读
903
+ * 通了,**或**本部署压根没接规则店 —— 两形不可分,端禁读成「你的规则都好着呢」。
904
+ * 🔴 **一个 origin 词盖两种机制,而下一步相反** —— 这正是它单独占一格的理由
905
+ * (core `ask-origin.d.ts:98-104` 顶注)。措辞铸点 `ruleStoreUnreadableDetail`,端零自拼;
906
+ * 引擎在 `message` 上给的**这一次**的散文(lexer 那句话)由 `askOriginDetail(origin, message)`
907
+ * 让位渲出(0.65.0 B-080③ 已有的臂),两者并列不互替。
908
+ * 🔴 **闭二词按闭集判**:core 在唯一 stamp 点剥掉政策自报的值之后才盖章 ⇒ 表外值到不了消费端。
909
909
  * ⚠️ **sdk 8.8.0 的运行期锚 `TOOL_APPROVAL_FRAME_KEYS`(27 项)尚无本键** ⇒ 镜像**领先**一代,
910
910
  * 对账门里挂着一条带退出条件的 `AHEAD_OF_ANCHOR` 登记(sdk 8.9.x 同拍补上那天它自红逼删)。
911
+ * ⚠️ 0.67.0 同批**删掉**了上一代那条同形登记(`classifierUnavailable` 随 core 7.14.0 退役)。
911
912
  */
912
- classifierUnavailable?: ClassifierUnavailableView;
913
+ ruleStoreUnreadable?: string;
913
914
  /**
914
915
  * server ≥7.13.0(#144 / core 5.25.0,[3438] 接力契约 / [3443] 主件;**ADDITIVE**,
915
916
  * `"tool_approval"` only。来源锚 = engine fixture `@sema-agent/server/dist/tool-approval.d.ts`
@@ -1099,7 +1100,7 @@ export interface ToolApprovalDelegation {
1099
1100
  * `TOOL_APPROVAL_FRAME_KEYS` 比对——SDK additive 增键时对账当天红,不再人肉追平。
1100
1101
  * 下面两个类型钉保证镜像与 interface 本身不可能漂移(少键/多键都是编译错)。
1101
1102
  */
1102
- export declare const TOOL_APPROVAL_FRAME_KEYS_MIRROR: readonly ["type", "approvalId", "toolCallId", "toolName", "sourceTaskId", "fromSubagent", "sourceAgentName", "message", "args", "argsOmitted", "governanceForced", "ruleSuggestions", "ruleOffers", "ruleOffersAbsence", "denialLimitFallback", "classifierUnavailable", "origin", "inputHasBidi", "parked", "persistedRuleShadowed", "probeCause", "ruleEvidence", "requiresRealApproval", "expiresInMs", "expiresAtMs", "serverNowMs", "delegation", "outcome"];
1103
+ export declare const TOOL_APPROVAL_FRAME_KEYS_MIRROR: readonly ["type", "approvalId", "toolCallId", "toolName", "sourceTaskId", "fromSubagent", "sourceAgentName", "message", "args", "argsOmitted", "governanceForced", "ruleSuggestions", "ruleOffers", "ruleOffersAbsence", "denialLimitFallback", "ruleStoreUnreadable", "origin", "inputHasBidi", "parked", "persistedRuleShadowed", "probeCause", "ruleEvidence", "requiresRealApproval", "expiresInMs", "expiresAtMs", "serverNowMs", "delegation", "outcome"];
1103
1104
  /**
1104
1105
  * 子代帧判别:显式键 fromSubagent(core 1.378 RB-39②)优先;缺席退 sourceTaskId 在场性权宜式
1105
1106
  * (server 1.258 [1549]①3,旧代际兼容)。
@@ -89,6 +89,14 @@ import { createSessionSlot, DEFAULT_SESSION_KEY } from '../sessionSlot.js';
89
89
  import { readEngineActiveBgTasks } from '../fleet/fleetLedger.js';
90
90
  import { observeCancelByDeny, surfaceRememberNotApplied, surfaceEditNotForwarded, surfaceRuleArmNotSent, surfaceRuleArmRejected } from './hitlHostSurface.js';
91
91
  import { approvalCallKey, liveFrameCallKey } from './gateIdentity.js';
92
+ // 0.67.0(core 7.14.0 #688 C3):`ruleStoreUnreadable` 的**闭二词判据**。词表属主 = core
93
+ // (`RULE_STORE_UNREADABLE_KINDS`),镜像与措辞都在 `gateVocabulary.ts` 那个唯一铸点上 ——
94
+ // 本文件**不抄第二份字面量**(B-025 的根因就是手抄)。
95
+ import { isRuleStoreUnreadableKind } from '../gateVocabulary.js';
96
+ // 🔴 0.67.0:`ClassifierUnavailableView` 的 type-only import 随卡面那一格**整只退役**(core 7.14.0
97
+ // 把 `classifierUnavailable` 从 ask / park / carry / summary 六个载体上删掉,继任者是拒绝面的
98
+ // `GateDisposition.denied.cause`)。事实位今天由 `gateOutcome.ts` 的 `disposition.cause` 与公面读器
99
+ // `classifierDenyCauseOf` 承载,卡上不再有它的对偶 —— 见 `autoModeUnavailable.ts` 顶注。
92
100
  // B-070 / L-200(0.65.0):`/decide` 200 回执的**单一读面**(`handoffTaskId` 换挂句柄 +
93
101
  // `executionOutcome` 缺席=未知)。判定归包,端零自拼。
94
102
  import { readDecideReceipt } from '../decideReceipt.js';
@@ -357,24 +365,20 @@ parkGatedCallId) {
357
365
  // (见 {@link ApprovalCardRequest.persistedRuleShadowed} 的同名警示)。
358
366
  const durableShadowedRule = pending.riskDescriptor?.shadowedRule;
359
367
  const durableRuleOffersAbsence = pending.ruleOffersAbsence;
360
- // cli L-174②(0.64.2):durable 腿的「分类器跑不了」孪生位 —— 载体 = 行上的 `classifierUnavailable`
361
- // (core `checkpoint-store.d.ts` 逐字「the PARK twin of `AskRequest.classifierUnavailable`」,写在
362
- // `PendingAction.tool_approval` 上;行是 `pendingAction` 的**扁平投影**,`ruleOffers`/`hasBidiControls`
368
+ // 0.67.0(core 7.14.0 #688 C3):durable 腿的「规则店读不出来」孪生位 —— 载体 = 行上的
369
+ // `ruleStoreUnreadable`(core `checkpoint-store.d.ts:818` 写在 `PendingAction.tool_approval` 上,
370
+ // 摘要行 `:1924-1926` 同名;行是 `pendingAction` 的**扁平投影**,`ruleOffers`/`hasBidiControls`
363
371
  // 都是同一条路落到顶层的)。sdk 8.8.0 的 `PendingCheckpoint` 尚无本键声明 ⇒ **结构视图读**
364
372
  // (与同函数 `ruleOffers` / `riskDescriptor.probeCause` 同款姿势),类型半场候 sdk 班车。
365
373
  // 🔴 **与 `ruleEvidence` 的「刻意零 stamp」不同裁,理由要说清**:那一位在上游**根本没有耐久对偶**,
366
374
  // 读 `riskDescriptor.ruleEvidence` 是在猜一个载体名(猜对了是白写,猜错了是拿另一个量冒充);
367
- // 本位的对偶**是上游逐字声明的**,键名同形、语义同源 —— 不是猜。
368
- // ⚠️ **今天的供给缺口,如实记(不是「接通了」)**:engine 7.69.0 的两只 checkpoint 店
369
- // (`plugins/local-checkpoint-store.js:144 listPending` / `plugins/checkpoint-store-sql.js:500
370
- // listPending`)把 `pendingAction` 扁平化时**没有**带这一位(`/v1/assistant/inbox` 那条腿带了,
371
- // 但那不是本腿吃的 `/v1/approvals` 行)⇒ 本腿在今天的引擎上**恒零命中**,server 补投那天零改动
372
- // 生效。⚠️ **别把 `governanceForced` 也读进这一格**(异源对抗复审 r2 非阻断③ 订正):那一位
373
- // 确实不在 `listPending` 的产物里,但同包 `http/routes/approvals-assistant.js` 的
374
- // `projectPendingForWire` 会用 `governanceOriginOf(...)` **现算并补到行上**,所以它今天是**到得了**
375
- // 本腿的;`riskDescriptor.shadowedRule` 同理(那条路由里的 `redactPendingDisclosures` 脱敏后随行
376
- // 上 wire,件⑥ 消费的正是它)。本位是这三者里**唯一**今天真的没有供给的一格。
377
- const durableClassifierUnavailable = readClassifierUnavailable(pending.classifierUnavailable);
375
+ // 本位的对偶**是上游逐字声明的**(core 摘要行的顶注逐字:「The row's
376
+ // `PendingAction.tool_approval.ruleStoreUnreadable` (#688 C3), echoed ONLY when the stored word is
377
+ // a」member),键名同形、语义同源 —— 不是猜。
378
+ // ⚠️ **今天的供给缺口,如实记(不是「接通了」)**:本腿吃的是 `/v1/approvals` 行,而两只 checkpoint
379
+ // 店的 `listPending` 把 `pendingAction` 扁平化时带不带这一位由 server 决定;本包这一侧零改动
380
+ // 随 server 补投生效 —— 与上一代 `classifierUnavailable` 的 durable 腿同一格记账。
381
+ const durableRuleStoreUnreadable = readRuleStoreUnreadable(pending.ruleStoreUnreadable);
378
382
  deps.onPresented?.(); // L-80:真要交给卡口了才算呈过(规则直决 / 取件失败都不走到这一行)
379
383
  const card = await surfaceApprovalCard({
380
384
  toolName,
@@ -391,10 +395,10 @@ parkGatedCallId) {
391
395
  ...(pending.hasBidiControls === true ? { hasBidiControls: true } : {}),
392
396
  ...(ruleOffersReadOnly !== undefined ? { ruleOffersReadOnly } : {}),
393
397
  ...(isWireRecordCarrier(durableProbeCause) ? { probeCause: durableProbeCause } : {}),
394
- // cli L-174②(0.64.2):与活卡腿**同一把窄读器、同一个卡位**(键路同形 ⇒ 端一把读器吃两条腿)。
398
+ // 0.67.0:与活卡腿**同一把窄读器、同一个卡位**(键路同形 ⇒ 端一把读器吃两条腿)。
395
399
  // 缺席 ⇒ 键不 stamp;判据与供给缺口见上面那段头注。
396
- ...(durableClassifierUnavailable !== undefined
397
- ? { classifierUnavailable: durableClassifierUnavailable }
400
+ ...(durableRuleStoreUnreadable !== undefined
401
+ ? { ruleStoreUnreadable: durableRuleStoreUnreadable }
398
402
  : {}),
399
403
  // 件⑥(0.64.2):durable 腿的两位对偶(判据与「为什么是行为面」见上面那段头注)。
400
404
  // 🔴 落位是**活卡腿同一个卡位**(`persistedRuleShadowed` / `ruleOffersAbsence`),不是新键:
@@ -559,14 +563,12 @@ export const TOOL_APPROVAL_FRAME_KEYS_MIRROR = [
559
563
  // 🔴 本键与 `requiresRealApproval` 是孪生键,但两者在镜像里各占一格 —— 合并会让「只有一位在场」
560
564
  // 的坏形无处显形。
561
565
  'denialLimitFallback',
562
- // cli L-174②(client-core 0.64.2):server 7.69.0 起真发 `classifierUnavailable`(core 7.10.0 #616;
563
- // engine 7.69.0 fixture `tool-approval.js:847` 条件 stamp + `approval-card.js:244` 窄读器直证)。
564
- // ⚠️ 与 `ruleOffersAbsence`/`denialLimitFallback` **不同形**、与 `persistedRuleShadowed`/`probeCause`
565
- // 同形:sdk 8.8.0 的运行期锚**尚无**本键(node 直读实证:锚 27 项)⇒ 这是一次**领先**,进对账门的
566
- // AHEAD_OF_ANCHOR 带退出条件登记(sdk 8.9.x 同拍补上那天那条登记自红逼删,回到逐元素相等)。
567
- // 🔴 本键有**已定的退役日期**(server 7.70.0 / core 7.12.0 起三面投影退役,上游发布记录 [6852]),但退役
568
- // 走的是「缺席」而不是「删码」([6853] 死键保留型)⇒ 镜像里这一格不因退役而动。
569
- 'classifierUnavailable',
566
+ // 0.67.0(core 7.14.0 #688 C3):`ruleStoreUnreadable`。⚠️ 它**换掉**的是上一代同位的
567
+ // `classifierUnavailable` —— core 7.14.0 把那一格从六个载体上整族退役(ask 那几处自 #661 起就没写过),
568
+ // 本包按 clean-cut 删镜像成员、删读器、删卡位,**不留别名**(登记在 run-retired-vocabulary-census)。
569
+ // ⚠️ 与 `persistedRuleShadowed`/`probeCause` 同形:sdk 8.8.0 的运行期锚**尚无**本键 ⇒ **领先**一代,
570
+ // 进对账门的 AHEAD_OF_ANCHOR 带退出条件登记(sdk 8.9.x 补上那天那条登记自红逼删,回到逐元素相等)。
571
+ 'ruleStoreUnreadable',
570
572
  // S-125③/#564(client-core 0.59.0):server 7.57.0 起真发 `origin`(core 7.5.0 `ASK_ORIGINS`
571
573
  // 八词;engine 7.60.0 fixture 直证)。同上:sdk 8.3.0 锚已含本键 ⇒ 追平,不进领先表。
572
574
  'origin',
@@ -999,36 +1001,22 @@ export function readDenialLimitFallback(v) {
999
1001
  };
1000
1002
  }
1001
1003
  /**
1002
- * `classifierUnavailable` 的**过境窄读器**(cli L-174②,0.64.2)—— 两条卡腿(活卡帧 / durable park
1003
- * 行)共用的**唯一**一把:两处键路同形,各写一份就是两份台账各漂各的。
1004
+ * `ruleStoreUnreadable` 的**过境窄读器**(0.67.0;core 7.14.0 #688 C3)—— 两条卡腿(活卡帧 /
1005
+ * durable park 行)共用的**唯一**一把:两处键路同形,各写一份就是两份台账各漂各的。
1004
1006
  *
1005
- * 🔴 **判据与 server 的唯一铸点同源**(engine 7.69.0 fixture `approval-card.js:240/244`:
1006
- * `z.object({ cause: z.string().min(1).max(200) }).strict()` + envelope 的 safeParse)——
1007
- * · 载体非对象 / null / 数组 ⇒ 缺席;
1008
- * · `cause` 非串 / 空串 ⇒ 缺席(空壳等于说「分类器坏了但说不出为什么」,比不说更坏 —— server
1009
- * d.ts 顶注逐字);
1010
- * · **`cause` 按开集收**:词表属主在 core,server 侧已按 `min(1)` 之外零词表校验过,包再抄一张
1011
- * 闭集表只会在 core 加词那天把一个合法值判没,而丢的正是「这次不可用是新出现的那一类」
1012
- * 这条信息(与 `origin` / `ruleOffersAbsence` / `denialLimitFallback.limit` 逐字同规)。
1013
- * 🔴 **只交 `cause` 一格**(与 `classifierUnavailableOf` 的读数形逐字相同):顺手把载体上别的键
1014
- * 带出来会长成第二份 ask 读面。
1015
- * ⚠️ **与 server `.strict()` 的一格差**(与 {@link readDenialLimitFallback} 同一条,如实写):
1016
- * 载体上**多出的成员**本读器剥后保留 `cause`,server 侧整只拒收 —— 包宽一格,方向是「多键不误杀」。
1017
- * 🔴 **过境 ≠ 显示**:本读器答「这条事实到不到得了卡口」;「渲哪一句」由公面的
1018
- * `classifierUnavailableOf` / `classifierUnavailableDetail` 答 —— 那一层同样非空串即收,只排除
1019
- * **熔断轴独占**的词(`parse_error`)。⚠️ 0.64.2 之前那一层是 unavailable **闭集**,于是一个合法的
1020
- * 新成因词过得了本层却在显示层被判没;订正后两层判据几乎逐字相同,差的只有那条有出处的排除
1021
- * —— 见 {@link ApprovalCardRequest.classifierUnavailable} 与 INTEGRATION §29e。
1007
+ * 🔴 **闭二词按闭集判**(与本文件其余开集位刻意不同,理由在上游的形上):core 在**唯一 stamp 点**
1008
+ * (`gate-lanes.ts` `ruleStoreUnreadableOf`)把政策自报的值**剥掉**之后才盖章,合同逐字
1009
+ * 「A policy's self-declared value is removed at the stamp」⇒ 一个表外值**结构上**到不了消费端;
1010
+ * 真读到就是坏形,而把坏形渲成一句机制解释会把人指到错的地方(`store` 去修部署的店 /
1011
+ * `call` 去改自己那条规则,两条下一步相反)。这与 `origin`/`ruleOffersAbsence` 的开集纪律不矛盾:
1012
+ * 那两位在 server 侧**只判非空串**,core 加词的当天一个**合法**值就会带着帧到达。
1013
+ * 🔴 **只交那个词**:载体是一个裸串(不是对象)—— 不顺手读载体上别的东西。
1014
+ * 🔴 **非串 / 空串 / 表外词 ⇒ 缺席**,绝不折成空串(一格空白的机制解释比没有解释更坏)。
1022
1015
  * **不导出**:它是本模块的边界窄化器,唯一消费点是下面两个卡口调用;导出会给公面再加一个 `unknown`
1023
1016
  * 入参签名(typeshape 门的 unknown-出境棘轮 +1),而它的行为由两条腿的端到端素材覆盖。
1024
1017
  */
1025
- function readClassifierUnavailable(v) {
1026
- if (!isWireRecordCarrier(v))
1027
- return undefined;
1028
- const cause = v.cause;
1029
- if (typeof cause !== 'string' || cause === '')
1030
- return undefined;
1031
- return { cause };
1018
+ function readRuleStoreUnreadable(v) {
1019
+ return isRuleStoreUnreadableKind(v) ? v : undefined;
1032
1020
  }
1033
1021
  /**
1034
1022
  * `match` 是不是**闭词表成员** —— 表来自 sdk 8.2.0 `RULE_OFFER_MATCHES`(`exact` / `prefix` /
@@ -1453,14 +1441,14 @@ export async function surfaceToolApprovalFrameAndRespond(frame, respond, streamA
1453
1441
  // 🔴 stamp 的是**窄读产物**而不是原对象:端拿到的四座恒是有限非负数 + 非空 limit 串,
1454
1442
  // 不必各自再写一遍同样的判断(三端各写一遍 = 三份会漂的判官)。
1455
1443
  ...(denialLimitFallback !== undefined ? { denialLimitFallback } : {}),
1456
- // ── cli L-174②(0.64.2):「问你是因为分类器这次跑不了」的事实透传 ──────────────────────────
1457
- // 经 `readClassifierUnavailable` 单点窄读(判据本体在该函数顶注:`cause` 非空串即收、**开集**、
1458
- // 坏形降缺席)。stamp 的是**窄读产物** `{cause}`,与 durable 腿**同一把读器**(键路同形)。
1459
- // 🔴 缺席 ⇒ 键不 stamp,**绝不折成任何肯定话**:缺席同时覆盖「分类器答上了」「这只 ask 没资格
1460
- // 走分类器」「本部署没接分类器」三形,端禁读成「分类器好着呢」。
1461
- // ⚠️ 本键 core 7.12.0 / server 7.70.0 起是死键(帧上恒缺席,事实位随拒绝面走)—— 那天这条
1462
- // stamp 自然零命中,**不需要**也**不许**在这里加任何熔断/回落判据(echo-only)。
1463
- ...((c) => (c !== undefined ? { classifierUnavailable: c } : {}))(readClassifierUnavailable(frame.classifierUnavailable)),
1444
+ // ── 0.67.0(core 7.14.0 #688 C3):「规则店读不出来是哪一头」的事实透传 ────────────────────
1445
+ // 经 `readRuleStoreUnreadable` 单点窄读(判据本体在该函数顶注:**闭二词**、表外/坏形降缺席)。
1446
+ // 与 durable 腿**同一把读器、同一个卡位**(键路同形 ⇒ 端一把读器吃两条腿)。
1447
+ // 🔴 缺席 ⇒ 键不 stamp,**绝不折成任何肯定话**:缺席同时覆盖「店和这次调用都读通了」与
1448
+ // 「本部署没接规则店」两形,端禁读成「你的规则都好着呢」。
1449
+ // ⚠️ 它**换掉**的是上一代同位的 `classifierUnavailable`(core 7.14.0 整族退役;事实位随拒绝面的
1450
+ // `GateDisposition.denied.cause` 走)—— 那一格按 clean-cut 删码不留别名。
1451
+ ...((k) => (k !== undefined ? { ruleStoreUnreadable: k } : {}))(readRuleStoreUnreadable(frame.ruleStoreUnreadable)),
1464
1452
  // ── S-125③/#564(0.59.0):ask 出身透传 ────────────────────────────────────────────────────
1465
1453
  // 非空串才 stamp,词表**不校**(同 ruleOffersAbsence)。🔴 缺席 ⇒ 键不 stamp,**绝不折成
1466
1454
  // `"policy"`**:那是一个正面事实(出自部署 ToolPolicy),把「老引擎没报」折进去 = 替引擎编话。
package/dist/seam.d.ts CHANGED
@@ -392,6 +392,76 @@ export type ChromeEvent = {
392
392
  */
393
393
  stopReason?: string;
394
394
  }
395
+ /**
396
+ * **L-228(0.67.0;core 7.14.0 提货批件 F-6)—— per-subagent 的 turn usage 分表。**
397
+ *
398
+ * ── 它修的是什么 ─────────────────────────────────────────────────────────────────────────
399
+ * 一条 run 委派出去的那本账,core 终局**只有合计**(`TaskResult.stats.nested` 的
400
+ * `{tokens, turns, tasks, costMicroUsd}`,`task-result.d.ts` #594-660)——**没有 per-task 分项**。
401
+ * 而流里每一条子流的 `turn_end` 都带着它自己那一轮的 usage,本包此前在 §E2 断闸处**折而未读**
402
+ * (`!isSubFlow` 才发 `last_turn_usage` / 才 yield `turn_usage`)⇒ 子代详情面(壳的
403
+ * `engineAgentView`)只渲得出「这个子代的 usage 不知道」。⇒ 分表**只能**由流内子流累加得出,
404
+ * 本臂就是那条通道。
405
+ *
406
+ * ── 🔴 它**不**改主臂的断闸 ──────────────────────────────────────────────────────────────
407
+ * `last_turn_usage` / `turn_usage` 两处 `!isSubFlow` 一个字节不动(§E2:子代的 outputTokens 拿去
408
+ * 对账 leader 的 responseLength 就是 token-jump 那个病)。本臂是**additive 的第三条腿**,
409
+ * `laneProof` 恒是**子流那条**(`{lane:'subagent', parentToolCallId}`)—— 宿主据此就知道这条不是
410
+ * 主会话的账,不会误落进 statusline 的窗口占用。
411
+ *
412
+ * ── 载荷 ─────────────────────────────────────────────────────────────────────────────────
413
+ * `taskId` = 引擎给这只子任务的 id;wire 上缺席时**回落 `parentToolCallId`**(同一只子代的每一轮
414
+ * 至少归得到同一行)。`usage` / `engineUsage` / `usageMissing` / `stopReason` 与
415
+ * {@link ChromeEvent} 的 `last_turn_usage` 臂**逐位同义**(同一只 `turnEndUsage()` 产物,不另铸)。
416
+ *
417
+ * ── 🔴 0.67.1 / B-090:本臂的**射程边界**(如实记,不是遗漏)──────────────────────────────
418
+ * 子流判据(`isSubFlow`)0.67.1 起认**身份信封两键任一在场**(`parentToolCallId` ∪ `sourceTaskId`;
419
+ * core 合同上两键同条件盖章、顶层任务一个都不带)。而**本臂**还多一个前提:车道证明
420
+ * {@link LaneProof} 的子流臂是 `{lane:'subagent', parentToolCallId: string}` ⇒ 一条**只带
421
+ * `sourceTaskId`、没有父调用 id** 的子代轮**发不出本臂**(拿任务 id 去填父调用 id 那一格是身份位
422
+ * 互串,座位门 `checkLaneProof` 也会把缺伴随位的信封吃掉)。
423
+ * ⇒ 那一形上:主臂两处断闸**照断**(不污染主会话用量)、终帧分表
424
+ * `_sema_nested_usage_by_task` **照带那一行**,只有本臂这条**实时增量**腿静默。
425
+ * 🔴 宿主据此:**不许**把「本臂没来过」读成「这只子代没花钱」——收口快照才是那本账的全量面。
426
+ *
427
+ * 宿主消费义务(`required: false`):渲子代详情的 per-agent 用量。不接 = 那一面看不见(不是报错);
428
+ * 🔴 **绝不**把本臂的数加进主会话的 turn 用量 —— 两条腿分工见上;
429
+ * 🔴 `usageMissing` 在场 ⇒ 同行那份 usage 是**下界**,别当已知账落槽。
430
+ */
431
+ | {
432
+ kind: 'subagent_turn_usage';
433
+ laneProof: LaneProof;
434
+ /**
435
+ * **分表的行键** = `sourceTaskId ?? parentToolCallId`;与终帧 `_sema_nested_usage_by_task`
436
+ * 的键**逐字相同**(本臂与那张表的 join 键)。
437
+ * 🔴 它**不是** wire 上的某一个字段名:core 的 `TaskEventIdentity` 上**没有** `taskId`
438
+ * (顶注逐字「NOT a second `taskId` — that field already exists on `task_progress` and a
439
+ * duplicate would bite consumers」),真身份是 {@link sourceTaskId};回落见那一位。
440
+ */
441
+ taskId: string;
442
+ /**
443
+ * core `TaskEventIdentity.sourceTaskId` **原值**:这条子流所属任务的 canonical id
444
+ * (`spec.taskId ?? sessionId`),**只**盖在「作为子代跑」的任务的内容事件上。
445
+ * 缺席 = 引擎没报这一位(老引擎 / 某些腿)⇒ {@link taskId} 回落到 `parentToolCallId`。
446
+ * ⚠️ 回落是**有损**的:同一个父调用下若真有多只子任务,它们会并成一行 —— 这时
447
+ * 终帧那张表的 `partial` 判别位会因行数对不上 `nested.tasks` 而立起来。
448
+ */
449
+ sourceTaskId?: string;
450
+ /** 引擎给这条子流打的父调用 id(`laneProof` 里那一个,平铺一份省得宿主解 union)。 */
451
+ parentToolCallId: string;
452
+ /**
453
+ * 这一轮的 CC `ModelUsage` 镜像。
454
+ * 🔴 **可选**:core 真会发**裸** `{type:'turn_end', usageMissing:true}`(无 usage、无 stopReason)
455
+ * —— 主臂 0.65.1 / B-088 就是为这一形放宽的发臂条件,子代这条腿同律(同形存量不许再犯一次)。
456
+ * 缺席 ⇒ 这一轮**什么账都没报**,读 {@link usageMissing}。
457
+ */
458
+ usage?: ModelUsage;
459
+ engineUsage?: EngineTurnUsage;
460
+ /** 见 `last_turn_usage` 同名位:在场 ⇒ 这一轮的账**不是**一笔已知的账,never false。 */
461
+ usageMissing?: true;
462
+ /** 见 `last_turn_usage` 同名位:原词开集透传,缺席不铸。 */
463
+ stopReason?: string;
464
+ }
395
465
  /**
396
466
  * B3 新臂 ③(runStream 外向边切除,设计稿 §5.2 头号污染源)—— `done{status:"needs_review"}` +
397
467
  * gate `plan_review` 的 park 终帧。此前是 runStream 里一条 `await import('planReviewWire')`,
package/dist/seam.js CHANGED
@@ -34,6 +34,19 @@ const CHROME_ARM_TABLE = {
34
34
  inline_task_stats: { required: true, duty: 'tick/settle/settle_all 转发给 engineInlineTaskStats' },
35
35
  prompt_suggestions: { required: false, duty: '推 composer 上方 chips(绝不回喂模型)' },
36
36
  last_turn_usage: { required: true, duty: '落最近一次 turn 真 usage(statusline 回落源)' },
37
+ // L-228(0.67.0):per-subagent 分表。`required:false` —— 不接 = 子代详情的用量面看不见
38
+ // (一个可选披露面),不属「已发生的行为丢失」那一档;但**不接就永远说不出**每只子代花了多少
39
+ // (core 终局只有合计,没有 per-task 分项)。
40
+ subagent_turn_usage: {
41
+ required: false,
42
+ duty: '可选:按 `taskId`(= `sourceTaskId ?? parentToolCallId`,与终帧分表同键)落 per-subagent 的 turn 用量(子代详情面)。' +
43
+ '🔴 `usage` 可缺席(裸 `usageMissing` 轮),那一形要渲「这一轮的账不知道」而不是跳过;' +
44
+ '🔴 0.67.1:只带 `sourceTaskId`(无父调用 id)的子代轮**发不出本臂**(车道证明铸不出来)——' +
45
+ '那一形的账只在终帧 `_sema_nested_usage_by_task` 上,**不许**把「本臂没来过」读成「这只子代没花钱」;' +
46
+ '🔴 **绝不**把它加进主会话的 turn 用量 / statusline 窗口占用(那是 §E2 断闸要防的 token-jump);' +
47
+ '🔴 `usageMissing` 在场时同行 usage 是**下界**,渲「≥」不渲已知账;' +
48
+ '🔴 终帧的 `_sema_nested_usage_by_task` 是同一份账的**收口快照**,两者**不相加**(本臂是增量、那是合计)',
49
+ },
37
50
  plan_review_park: { required: true, duty: '弹 plan 审批卡(fail-soft,不挡后续终态帧)' },
38
51
  // D-3 / B-068:终局对账。`required:false` —— 不接不等于哑掉(终帧的两个超集键载着同一份事实),
39
52
  // 但**不接就必须自己按那两个键对账**,否则 /cost 恒低报子代与压缩两笔账(B-068 原病)。
@@ -42,6 +55,8 @@ const CHROME_ARM_TABLE = {
42
55
  duty: '可选:终局成本对账(own / nested / compaction 三段 + reconciled = own + nested)。' +
43
56
  '不接 ⇒ 必须改从终帧 `_sema_cost_breakdown` / `_sema_nested_usage` 自己对账,否则子代与压缩两笔账不进成本面;' +
44
57
  '🔴 `costAbsent` / `nestedCostAbsent` 在场时渲「未定价」而不是 $0.00;' +
58
+ '🔴 0.67.0:`usageLowerBound` 在场时这条 run 的每一个 token / 成本数字都是**下界**(至少有一轮没报 usage)——' +
59
+ '渲「≥」或「至少」,绝不渲成一笔已知的账;它与两个 costAbsent 位**正交**(定了价也可能数不全),三句分别说;' +
45
60
  '🔴 流中的 `last_turn_usage` 增量与本臂的终局总账**不相加**(子代花费只经本臂到账,相加 = 重复计账)',
46
61
  },
47
62
  // FIX⑦:账本帧,**不带正文** ⇒ 义务是可选的(记进审计/时间线即可);🔴 硬约束是**不许**据此