@sema-agent/client-core 0.66.0 → 0.67.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.
@@ -1,93 +1,118 @@
1
1
  /**
2
- * src/autoModeUnavailable.ts — 「这只 ask 是因为**分类器跑不了**才问人」的事实读器 + 唯一措辞铸点
3
- * (0.63.0 件⑧;core 7.10.0 #616 —— **0.65.0 随 core 7.12.0 收窄,见下方「熔断族退役」段**)。
2
+ * src/autoModeUnavailable.ts — 「这一次 deny 是**分类器那一轮没能裁决**」的事实读器 + 唯一措辞铸点
3
+ * (0.63.0 件⑧ 起;**0.67.0 随 core 7.14.0 整只换面,见下方「卡面退役 / 拒绝面继任」段**)。
4
4
  *
5
5
  * -- 它答的是哪一问 ---------------------------------------------------------------------------
6
- * auto 模式下,门会就一只 ask 去咨询分类器。分类器**没跑成**时,引擎在这只 ask 上盖一格
7
- * `classifierUnavailable: { cause }` —— 卡上那句「问你是因为分类器这次跑不了(超时)」的事实来源。
8
- * 🔴 **它是显示元数据,不是裁决位**:没有任何东西读它来决定放行与否(引擎在门上已经判完了),
6
+ * auto 模式下,门会就一只调用去咨询分类器。分类器结束一轮有**三种**收场,而只有两种带一个**结构位**:
7
+ * · `unavailable` —— 分类器**跑不了**(模型那条腿抛了/被拒/跑过上限):一条**说得出口**的 fail-closed
8
+ * 拒绝,过一会儿重试就行;
9
+ * · `parse_error` —— 分类器**跑了并且答了**,只是答在裁决契约之外:按 BLOCK 处置,并像一次裁决一样
10
+ * 被拒绝上限计数;
11
+ * · **缺席**(在 `deniedBy: "classifier"` 旁)—— 分类器**自己的裁决**(一次 `<block>yes`)。
12
+ * 🔴 **它是显示元数据,不是裁决位**:引擎在门上已经判完了,没有任何东西读它来决定放行与否,
9
13
  * 所以消费端只拿它渲一句话,**绝不**据它翻转任何裁决。
10
- * 🔴 **缺席 ≠「分类器跑成了」**:绝大多数 ask 根本没咨询过分类器(部署没武装 auto、或这只 ask 走的
11
- * 是别的门)。缺席只意味着「这只 ask 上没有这条事实」。
14
+ * 🔴 **缺席 ≠「分类器跑成了」**:绝大多数 deny 根本没咨询过分类器(部署没武装 auto、或这次走的是
15
+ * 别的层)。缺席只意味着「这条门记录上没有这条事实」。
12
16
  *
13
- * -- 🔴 熔断族退役(0.65.0;core 7.12.0 CHANGELOG「Removed (BREAKING)」)--------------------------
14
- * core 7.12.0 把 `AUTO_MODE_BREAKER_CAUSES` 与 `WiringManifest.autoMode.breaker` 整族删掉,
15
- * `AUTO_MODE_UNAVAILABLE_CAUSES` 同批收成 `["error","timeout"]`(`dist/core/auto-mode.d.ts:9` 真字节);
16
- * server 7.70.0([6927])读面同批收窄,`classifierUnavailable.cause` 不再发 `breaker_open`。
17
- * ⇒ 本模块把 `AUTO_MODE_BREAKER_CAUSES` / `ClassifierBreakerView` / `classifierBreakerOf` 与
18
- * `breaker_open` 这个词**干净切**(clean-cut,不留别名、不留兼容读):留一个读不到的读器只会让
19
- * 三端继续为一件上游已经不发的事实写渲染分支,而那正是「校旧物」的门自己要拦的形。
20
- * 🔴 **`cause` 的开集读一个字节不动**:一台比本端**新**的引擎发一个新成因词时照收(见
21
- * {@link classifierUnavailableOf});退役掉的只是本端**认得**的那张表里的一个成员 —— 旧引擎真发
22
- * `breaker_open` 时它照旧过境,只是走 {@link classifierUnavailableDetail} 的兜底句
23
- * (「这个词比这一端新/旧」那一类),不再冒充一句本端自铸的解释。
17
+ * -- 🔴 卡面退役 / 拒绝面继任(0.67.0;core 7.14.0 `docs/wire-consumption/7.14.0.md` 的
18
+ * **Retired keys (BREAKING)** 表逐字)----------------------------------------------------
19
+ * core 7.14.0 把 `classifierUnavailable` 这一格**从六个载体上整族删掉**(`PermissionDeniedPayload` /
20
+ * `PermissionResult` 的 deny 与 ask 两臂 / `AskRequest` / `AskCarry`·`AskRequestCarry` /
21
+ * `PendingAction.tool_approval`·`CheckpointSummary`),其中 **ask 那几处自 #661 起引擎就没写过**
22
+ * (退役表逐字 "never engine-written since #661" / "dead since #661")。继任者是**一个**结构位:
23
+ * `GateDisposition.denied.cause ∈ {"unavailable","parse_error"}`(`dist/core/gate-outcome.d.ts:168`
24
+ * `CLASSIFIER_DENY_CAUSES`),挂在 `tool_end.gate` / `PermissionDeniedPayload.gate` / 耐久行的
25
+ * resolved outcome 上,且只在 `deniedBy ∈ {classifier, policy}` 旁(`DENIED_BY_MAY_CARRY_CAUSE`)。
26
+ * ⇒ 本模块把 `ClassifierUnavailableView` / `classifierUnavailableOf` / `classifierUnavailableDetail`
27
+ * **干净切**(clean-cut,不留别名、不留兼容读),换成 {@link classifierDenyCauseOf} 与
28
+ * {@link classifierDenyCauseDetail}。留一个读不到的读器只会让三端继续为一件上游已经不发的事实写
29
+ * 渲染分支,而那正是「校旧物」的门自己要拦的形(登记在 `run-retired-vocabulary-census-test.mjs`)。
30
+ * 🔴 **旧耐久行的处置 = 忽略**:一条 pre-7.14 的 park 行上仍带着 `classifierUnavailable` 时,本包
31
+ * **不读、不渲、不崩** —— 投影按新形办(退役表逐字「a stored key on an old row is ignored by the
32
+ * projection」)。
24
33
  *
25
- * -- 🔴 判据轴与成因轴不是一张表 --------------------------------------------------------------
26
- * · {@link AUTO_MODE_UNAVAILABLE_CAUSES}(`error` / `timeout`)—— 「这一轮分类**为什么没跑成**」。
27
- * 这是 `classifierUnavailable.cause` 的值域(词表属主 = core)。
28
- * · core `AutoModeVerdict` 的**判据轴**另有一个 `parse_error` 臂 —— 分类器**跑了并且答了**,
29
- * 只是答在契约之外(`dist/core/auto-mode.d.ts:29-32` 逐字:「The model responded but not in the
30
- * `<block>…` contract shape. Handled as a BLOCK …」)。它**一个字节都不 stamp** 到
31
- * `classifierUnavailable` 上,那是**另一句话**。
32
- * 🔴 本读器因此把 {@link NEVER_STAMPED_CAUSES}(今天 = `{parse_error}`)判**缺席** —— 把一个判据词
33
- * 读成「没跑成」的成因,就是替引擎编一件它明说没发生的事。0.63.0 起的黑盒判据 G-19 钉的正是这
34
- * 一条,退役熔断族**没有改动它的行为**(修前这一格由「熔断轴 − 不可用轴」的差集派生,而差集的
35
- * 右操作数随熔断表一起没了;排除的**理由**从来就不在熔断轴上,现按理由的真出处直写)。
34
+ * -- 🔴 两条轴不是一张表(退役后仍然成立,只是坐标换了)--------------------------------------
35
+ * · {@link CLASSIFIER_DENY_CAUSES}(`unavailable` / `parse_error`)—— 「这一轮分类**是怎么收场的**」,
36
+ * `GateDisposition.cause` 的值域(词表属主 = core)。**闭集**:core 的 `screenGateOutcome` 把
37
+ * 出集的 cause 列为记录缺陷(不变量 I5),而有缺陷的门记录 server 整条不上帧。
38
+ * · {@link AUTO_MODE_UNAVAILABLE_CAUSES}(`error` / `timeout`)—— 一个 `unavailable` 背后的
39
+ * **为什么**。core 退役表逐字:「The WHY behind an `unavailable` (error vs timeout) is **not a
40
+ * branching fact**: the deny sentence spells it and the `auto_mode.classified` trace frame carries
41
+ * it」。⚠️ **本包今天不投影那条 trace 帧**(`src/` 全树 `auto_mode` 零命中),所以这张表在本包
42
+ * 手上**没有到达任何一条投影臂的路**:它留在公面上是作为三端共用的**词表抄件**(core 7.16.0 仍
43
+ * 导出它,故不构成退役),消费端**不得**期待它出现在某个 deny cause 旁边 —— 那一格的值域是上一条。
36
44
  *
37
45
  * -- 为什么是镜像而不是 import ---------------------------------------------------------------
38
- * 这张表与 `classifierUnavailable` 的型面在 sdk 8.8.0 与 agent-types 上**都还没有**(亲验:两棵树
39
- * 全树零命中),唯一的出处是 `@sema-agent/core` 的 `dist/core/auto-mode.js` / `tool-policy.d.ts`。
40
- * 而 core **不是本包消费者的依赖**(既非 peer 也非 runtime dep),本包的 `.d.ts` 一旦引用它,装了
41
- * 本包却没装 core 的下游会当场编译不过。⇒ 与 `engineNoticeCodes.ts` / `toolResult.ts` /
46
+ * `@sema-agent/core` **不是本包消费者的依赖**(既非 peer 也非 runtime dep),本包的 `.d.ts` 一旦引用
47
+ * 它,装了本包却没装 core 的下游会当场编译不过。⇒ 与 `engineNoticeCodes.ts` / `toolResult.ts` /
42
48
  * `retryStatus.ts` 同一条处置:**按真字节镜像,把代价交给门** ——
43
49
  * `run-auto-mode-unavailable-test.mjs` 对**实装 devDep core** 的产物逐词双向对账,core 一动这里就先红。
44
- * 🔴 **候上游导出即换**:sdk 哪天镜像了这张表与那一格,本模块的表应当整只退役改成从 sdk 取。
50
+ * 🔴 **候上游导出即换**:sdk 哪天镜像了 `CLASSIFIER_DENY_CAUSES` 与 `GateDisposition.cause`,
51
+ * 本模块的表应当整只退役改成从 sdk 取。
45
52
  */
46
53
  /**
47
- * 一轮分类**为什么没跑成**(core `AUTO_MODE_UNAVAILABLE_CAUSES`;逐词逐序镜像,core 7.12.0 起两词)。
48
- * · `error` —— 模型那条腿抛了/被拒(分类时的路由失败也读在这里:派生路由的前置在任何 decide
49
- * 之前就回落了,所以没有单独的词);
50
- * · `timeout` —— 往返上限到了。
51
-
54
+ * 一轮分类**怎么收场的** —— `GateDisposition.denied.cause` 的值域
55
+ * (core `CLASSIFIER_DENY_CAUSES`,`gate-outcome.d.ts:168` 逐词逐序镜像,7.14.0 起两词)。
56
+ * · `unavailable` —— 分类器跑不了(模型那条腿抛了/被拒/跑过上限);一次说得出口的 fail-closed 拒绝,
57
+ * 这次调用**可以过一会儿再试**;
58
+ * · `parse_error` —— 分类器跑了并且答了,只是答在裁决契约之外;**为安全起见拦下**,并像一次裁决一样
59
+ * 被拒绝上限计数 —— 与上一条的下一步**不同**,所以两个词、两句话。
60
+ *
52
61
  * 🔴 形制:`Object.freeze` 的数组,**不是**只在类型面只读的 `readonly T[]` —— 后者一行 `.splice()`
53
62
  * 就能改,而公面消费者拿到的正是这个实例(本仓已定谳的病形,同 `RESUME_RETRY_LATER_CODES`)。
54
63
  */
55
- export declare const AUTO_MODE_UNAVAILABLE_CAUSES: readonly string[];
56
- /** 「分类器这次跑不了」的事实(只有成因一格 —— 它是显示元数据,不是裁决位)。 */
57
- export interface ClassifierUnavailableView {
58
- /** {@link AUTO_MODE_UNAVAILABLE_CAUSES} 之一(**开集读**:比本端新的引擎的新词照收)。 */
59
- cause: string;
60
- }
64
+ export declare const CLASSIFIER_DENY_CAUSES: readonly string[];
65
+ /**
66
+ * 这个词是不是 {@link CLASSIFIER_DENY_CAUSES} 的成员(core `isClassifierDenyCause` 的镜像)。
67
+ *
68
+ * 🔴 **这一格与 `origin` / `ruleOffersAbsence` 的开集纪律刻意不同**,理由在**上游的形**上:
69
+ * `cause` 出闭集时 core 的 `screenGateOutcome` 判**记录缺陷**(不变量 I5),而有缺陷的门记录
70
+ * server **整条不上帧** ⇒ 一个表外的 cause **结构上到不了消费端**;它到达的形式是 `gate` 整键缺席。
71
+ * 所以在这一格上按闭集判不会「把一个合法的新词吞成缺席」——真读到表外词只说明那条记录本不该长
72
+ * 这样,而把它渲成一句成因就是替引擎编事实。(`deniedBy` 那张表在 `gateVocabulary.ts` 上同一条。)
73
+ */
74
+ export declare function isClassifierDenyCause(v: unknown): boolean;
61
75
  /**
62
- * 一只 ask(或一条 durable park 行的 `tool_approval` 载荷)→ 「分类器跑不了」的事实;
63
- * 没有这条事实 / 形坏 ⇒ `undefined`,绝不抛出。
76
+ * 一条**门记录**(`tool_end.gate` / `PermissionDeniedPayload.gate` / 耐久行的 resolved outcome,
77
+ * 或本包 `gateOutcomeOf` 投出的 {@link import('./gateOutcome.js').GateOutcomeView})→
78
+ * 「这次 deny 是分类器那一轮**没能裁决**」的成因词;不是这一形 ⇒ `undefined`,绝不抛出。
64
79
  *
65
- * 🔴 **两处同一只读器**:`AskRequest.classifierUnavailable` 与
66
- * `PendingAction.tool_approval.classifierUnavailable`(durable park 行的孪生位)**键路同形**,
67
- * 所以一只读器吃两处 —— 各写一份就是两份台账各漂各的,本包一贯要根治的形。
68
- * 🔴 **`cause` 按开集读 + 一条有出处的排除**(**0.64.2 订正,行为面**;修前是「unavailable 闭集」):
69
- * · **非空串即收** —— 词表的属主是 core,而 server 侧除了「非空串」之外**不做词表校验**
70
- * (engine fixture `approval-card.js:240` 的 `z.string().min(1)`)。在这里抄一份闭集表,只会在
71
- * core 加词那天把一个**合法**值判没,而丢掉的正是「这次不可用是**新出现的那一类**」这条信息
72
- * —— 与 `AskRequest.origin` / `ruleOffersAbsence` / `denialLimitFallback.limit` 逐字同一条纪律。
73
- * 修前那条路是可复现的:一台比本端新的引擎发一个新成因词 ⇒ 卡上那一行**整段消失**,而
74
- * {@link classifierUnavailableDetail} 早就为这一形备好了兜底句(「a word newer than this client」)
75
- * —— 那句话在修前**永远不可达**,这本身就是判据写错了的证据。
76
- * ⚠️ 0.65.0 起 `breaker_open` 也走这一条(core 7.12.0 已删该词)—— **旧引擎的合法帧照旧过境**,
77
- * 只是本端不再为它自铸一句解释。
78
- * · **{@link NEVER_STAMPED_CAUSES} 里的词仍判缺席** —— 今天只有 `parse_error`。这不是「表外词一律拒」,
79
- * 而是一条**有出处的排除**:core 的判据轴逐字说它「跑了并且答了」,读它就是替引擎编一件
80
- * 它明说没发生的事。0.63.0 起的黑盒判据 G-19 钉的正是这一条,行为**一字未变**。
81
- * 🔴 **只交 `cause` 一格**:顺手把整只 ask 的别的键带出来会长成第二份 ask 读面。
82
- * 🔴 **端不许在自己那一侧再补一张闭集表**:未知词的正解是渲兜底句(措辞铸点已经有),不是不渲。
80
+ * 🔴 **一只读器吃三处**:wire 的原始 `gate` 对象、包投出的视图、以及**带 `gate` 的整只帧**
81
+ * —— 三者键路同形(`disposition.kind` / `disposition.cause`),各写一份就是三份台账各漂各的,
82
+ * 本包一贯要根治的形。
83
+ * 🔴 **只在 `kind === "denied"` 上答**:`allowed` 臂上不存在这一位;一条读不出处置词的记录是坏记录,
84
+ * 同样答缺席。
85
+ * 🔴 **闭集判据**(理由见 {@link isClassifierDenyCause}):表外词 ⇒ 缺席。
86
+ * 🔴 **不校 `deniedBy`**:core 的 `DENIED_BY_MAY_CARRY_CAUSE` 说的是 cause 只能站在 `classifier` /
87
+ * `policy`(继承链上祖先的冻结分类器)旁边,那是**引擎的段内不变量**,server 已按它铸;包再校一遍
88
+ * 只会在上游合法演进的当天把一个真读数判没(与 `gateOutcome.ts` 顶注的 I1–I4 同一条处置)。
89
+ * 🔴 **缺席不是断言**:缺席同时覆盖「分类器自己裁决 block 了」「这次不是分类器轮」「本部署没接分类器」
90
+ * 三形,端**禁**读成「分类器好着呢」。
83
91
  */
84
- export declare function classifierUnavailableOf(ask: unknown): ClassifierUnavailableView | undefined;
92
+ export declare function classifierDenyCauseOf(gate: unknown): string | undefined;
93
+ /**
94
+ * 一轮分类**为什么**跑不了(core `AUTO_MODE_UNAVAILABLE_CAUSES`;逐词逐序镜像,core 7.12.0 起两词)。
95
+ * · `error` —— 模型那条腿抛了/被拒(分类时的路由失败也读在这里:派生路由的前置在任何 decide
96
+ * 之前就回落了,所以没有单独的词);
97
+ * · `timeout` —— 往返上限到了。
98
+ *
99
+ * ⚠️ **本表在本包手上没有到达任何投影臂的路**(0.67.0 如实登记,见模块顶注第三段):它是一个
100
+ * `auto_mode.classified` **trace 帧**上的量,而本包不投影 trace 帧。留在公面上的唯一身份是
101
+ * **三端共用的词表抄件**(core 7.16.0 仍导出它 ⇒ 不构成退役);消费端**不得**期待它出现在
102
+ * {@link classifierDenyCauseOf} 的产物旁边。
103
+ *
104
+ * 🔴 形制:`Object.freeze` 的数组,理由同 {@link CLASSIFIER_DENY_CAUSES}。
105
+ */
106
+ export declare const AUTO_MODE_UNAVAILABLE_CAUSES: readonly string[];
85
107
  /**
86
- * 一个成因词 → 一句人话。**唯一措辞铸点**(三端共用;端零自拼)。
108
+ * 一个 deny 成因词 → 一句人话。**唯一措辞铸点**(三端共用;端零自拼)。
87
109
  *
88
110
  * 🔴 **按自有属性查表**(与本包其余措辞铸点同一条纪律):`Object.freeze` 不移除原型,裸下标会让
89
111
  * 一个来自 wire 的 `constructor` / `toString` 命中 `Object.prototype` 上的**函数**并被当成一句话。
90
- * 🔴 表外词 / 坏值 ⇒ 一句**兜底**:仍然告诉用户「这只 ask 是分类器那条腿引出来的」,但**不冒充**
91
- * 三句里的任何一句(成因读不懂 ≠ 成因是别的什么);原样带上那个词供运维追问上游。
112
+ * 🔴 表外词 / 坏值 ⇒ 一句**兜底**:仍然告诉用户「这次拒是分类器那条腿引出来的」,但**不冒充**
113
+ * 两句里的任何一句;原样带上那个词供运维追问上游。
114
+ * ⚠️ 与 {@link classifierDenyCauseOf} 的闭集判据**不矛盾**:那一层答「这条事实在不在」(闭集,
115
+ * 出集 = 坏记录),本层答「拿到一个词怎么渲」——**渲判据面**的消费端(以及一条从旧持久态恢复
116
+ * 回来的视图)可能手里就是一个表外词,它必须渲出一句诚实的话,而不是整屏崩或冒充一句已知的。
92
117
  */
93
- export declare function classifierUnavailableDetail(cause: unknown): string;
118
+ export declare function classifierDenyCauseDetail(cause: unknown): string;
@@ -1,135 +1,168 @@
1
1
  /**
2
- * src/autoModeUnavailable.ts — 「这只 ask 是因为**分类器跑不了**才问人」的事实读器 + 唯一措辞铸点
3
- * (0.63.0 件⑧;core 7.10.0 #616 —— **0.65.0 随 core 7.12.0 收窄,见下方「熔断族退役」段**)。
2
+ * src/autoModeUnavailable.ts — 「这一次 deny 是**分类器那一轮没能裁决**」的事实读器 + 唯一措辞铸点
3
+ * (0.63.0 件⑧ 起;**0.67.0 随 core 7.14.0 整只换面,见下方「卡面退役 / 拒绝面继任」段**)。
4
4
  *
5
5
  * -- 它答的是哪一问 ---------------------------------------------------------------------------
6
- * auto 模式下,门会就一只 ask 去咨询分类器。分类器**没跑成**时,引擎在这只 ask 上盖一格
7
- * `classifierUnavailable: { cause }` —— 卡上那句「问你是因为分类器这次跑不了(超时)」的事实来源。
8
- * 🔴 **它是显示元数据,不是裁决位**:没有任何东西读它来决定放行与否(引擎在门上已经判完了),
6
+ * auto 模式下,门会就一只调用去咨询分类器。分类器结束一轮有**三种**收场,而只有两种带一个**结构位**:
7
+ * · `unavailable` —— 分类器**跑不了**(模型那条腿抛了/被拒/跑过上限):一条**说得出口**的 fail-closed
8
+ * 拒绝,过一会儿重试就行;
9
+ * · `parse_error` —— 分类器**跑了并且答了**,只是答在裁决契约之外:按 BLOCK 处置,并像一次裁决一样
10
+ * 被拒绝上限计数;
11
+ * · **缺席**(在 `deniedBy: "classifier"` 旁)—— 分类器**自己的裁决**(一次 `<block>yes`)。
12
+ * 🔴 **它是显示元数据,不是裁决位**:引擎在门上已经判完了,没有任何东西读它来决定放行与否,
9
13
  * 所以消费端只拿它渲一句话,**绝不**据它翻转任何裁决。
10
- * 🔴 **缺席 ≠「分类器跑成了」**:绝大多数 ask 根本没咨询过分类器(部署没武装 auto、或这只 ask 走的
11
- * 是别的门)。缺席只意味着「这只 ask 上没有这条事实」。
14
+ * 🔴 **缺席 ≠「分类器跑成了」**:绝大多数 deny 根本没咨询过分类器(部署没武装 auto、或这次走的是
15
+ * 别的层)。缺席只意味着「这条门记录上没有这条事实」。
12
16
  *
13
- * -- 🔴 熔断族退役(0.65.0;core 7.12.0 CHANGELOG「Removed (BREAKING)」)--------------------------
14
- * core 7.12.0 把 `AUTO_MODE_BREAKER_CAUSES` 与 `WiringManifest.autoMode.breaker` 整族删掉,
15
- * `AUTO_MODE_UNAVAILABLE_CAUSES` 同批收成 `["error","timeout"]`(`dist/core/auto-mode.d.ts:9` 真字节);
16
- * server 7.70.0([6927])读面同批收窄,`classifierUnavailable.cause` 不再发 `breaker_open`。
17
- * ⇒ 本模块把 `AUTO_MODE_BREAKER_CAUSES` / `ClassifierBreakerView` / `classifierBreakerOf` 与
18
- * `breaker_open` 这个词**干净切**(clean-cut,不留别名、不留兼容读):留一个读不到的读器只会让
19
- * 三端继续为一件上游已经不发的事实写渲染分支,而那正是「校旧物」的门自己要拦的形。
20
- * 🔴 **`cause` 的开集读一个字节不动**:一台比本端**新**的引擎发一个新成因词时照收(见
21
- * {@link classifierUnavailableOf});退役掉的只是本端**认得**的那张表里的一个成员 —— 旧引擎真发
22
- * `breaker_open` 时它照旧过境,只是走 {@link classifierUnavailableDetail} 的兜底句
23
- * (「这个词比这一端新/旧」那一类),不再冒充一句本端自铸的解释。
17
+ * -- 🔴 卡面退役 / 拒绝面继任(0.67.0;core 7.14.0 `docs/wire-consumption/7.14.0.md` 的
18
+ * **Retired keys (BREAKING)** 表逐字)----------------------------------------------------
19
+ * core 7.14.0 把 `classifierUnavailable` 这一格**从六个载体上整族删掉**(`PermissionDeniedPayload` /
20
+ * `PermissionResult` 的 deny 与 ask 两臂 / `AskRequest` / `AskCarry`·`AskRequestCarry` /
21
+ * `PendingAction.tool_approval`·`CheckpointSummary`),其中 **ask 那几处自 #661 起引擎就没写过**
22
+ * (退役表逐字 "never engine-written since #661" / "dead since #661")。继任者是**一个**结构位:
23
+ * `GateDisposition.denied.cause ∈ {"unavailable","parse_error"}`(`dist/core/gate-outcome.d.ts:168`
24
+ * `CLASSIFIER_DENY_CAUSES`),挂在 `tool_end.gate` / `PermissionDeniedPayload.gate` / 耐久行的
25
+ * resolved outcome 上,且只在 `deniedBy ∈ {classifier, policy}` 旁(`DENIED_BY_MAY_CARRY_CAUSE`)。
26
+ * ⇒ 本模块把 `ClassifierUnavailableView` / `classifierUnavailableOf` / `classifierUnavailableDetail`
27
+ * **干净切**(clean-cut,不留别名、不留兼容读),换成 {@link classifierDenyCauseOf} 与
28
+ * {@link classifierDenyCauseDetail}。留一个读不到的读器只会让三端继续为一件上游已经不发的事实写
29
+ * 渲染分支,而那正是「校旧物」的门自己要拦的形(登记在 `run-retired-vocabulary-census-test.mjs`)。
30
+ * 🔴 **旧耐久行的处置 = 忽略**:一条 pre-7.14 的 park 行上仍带着 `classifierUnavailable` 时,本包
31
+ * **不读、不渲、不崩** —— 投影按新形办(退役表逐字「a stored key on an old row is ignored by the
32
+ * projection」)。
24
33
  *
25
- * -- 🔴 判据轴与成因轴不是一张表 --------------------------------------------------------------
26
- * · {@link AUTO_MODE_UNAVAILABLE_CAUSES}(`error` / `timeout`)—— 「这一轮分类**为什么没跑成**」。
27
- * 这是 `classifierUnavailable.cause` 的值域(词表属主 = core)。
28
- * · core `AutoModeVerdict` 的**判据轴**另有一个 `parse_error` 臂 —— 分类器**跑了并且答了**,
29
- * 只是答在契约之外(`dist/core/auto-mode.d.ts:29-32` 逐字:「The model responded but not in the
30
- * `<block>…` contract shape. Handled as a BLOCK …」)。它**一个字节都不 stamp** 到
31
- * `classifierUnavailable` 上,那是**另一句话**。
32
- * 🔴 本读器因此把 {@link NEVER_STAMPED_CAUSES}(今天 = `{parse_error}`)判**缺席** —— 把一个判据词
33
- * 读成「没跑成」的成因,就是替引擎编一件它明说没发生的事。0.63.0 起的黑盒判据 G-19 钉的正是这
34
- * 一条,退役熔断族**没有改动它的行为**(修前这一格由「熔断轴 − 不可用轴」的差集派生,而差集的
35
- * 右操作数随熔断表一起没了;排除的**理由**从来就不在熔断轴上,现按理由的真出处直写)。
34
+ * -- 🔴 两条轴不是一张表(退役后仍然成立,只是坐标换了)--------------------------------------
35
+ * · {@link CLASSIFIER_DENY_CAUSES}(`unavailable` / `parse_error`)—— 「这一轮分类**是怎么收场的**」,
36
+ * `GateDisposition.cause` 的值域(词表属主 = core)。**闭集**:core 的 `screenGateOutcome` 把
37
+ * 出集的 cause 列为记录缺陷(不变量 I5),而有缺陷的门记录 server 整条不上帧。
38
+ * · {@link AUTO_MODE_UNAVAILABLE_CAUSES}(`error` / `timeout`)—— 一个 `unavailable` 背后的
39
+ * **为什么**。core 退役表逐字:「The WHY behind an `unavailable` (error vs timeout) is **not a
40
+ * branching fact**: the deny sentence spells it and the `auto_mode.classified` trace frame carries
41
+ * it」。⚠️ **本包今天不投影那条 trace 帧**(`src/` 全树 `auto_mode` 零命中),所以这张表在本包
42
+ * 手上**没有到达任何一条投影臂的路**:它留在公面上是作为三端共用的**词表抄件**(core 7.16.0 仍
43
+ * 导出它,故不构成退役),消费端**不得**期待它出现在某个 deny cause 旁边 —— 那一格的值域是上一条。
36
44
  *
37
45
  * -- 为什么是镜像而不是 import ---------------------------------------------------------------
38
- * 这张表与 `classifierUnavailable` 的型面在 sdk 8.8.0 与 agent-types 上**都还没有**(亲验:两棵树
39
- * 全树零命中),唯一的出处是 `@sema-agent/core` 的 `dist/core/auto-mode.js` / `tool-policy.d.ts`。
40
- * 而 core **不是本包消费者的依赖**(既非 peer 也非 runtime dep),本包的 `.d.ts` 一旦引用它,装了
41
- * 本包却没装 core 的下游会当场编译不过。⇒ 与 `engineNoticeCodes.ts` / `toolResult.ts` /
46
+ * `@sema-agent/core` **不是本包消费者的依赖**(既非 peer 也非 runtime dep),本包的 `.d.ts` 一旦引用
47
+ * 它,装了本包却没装 core 的下游会当场编译不过。⇒ 与 `engineNoticeCodes.ts` / `toolResult.ts` /
42
48
  * `retryStatus.ts` 同一条处置:**按真字节镜像,把代价交给门** ——
43
49
  * `run-auto-mode-unavailable-test.mjs` 对**实装 devDep core** 的产物逐词双向对账,core 一动这里就先红。
44
- * 🔴 **候上游导出即换**:sdk 哪天镜像了这张表与那一格,本模块的表应当整只退役改成从 sdk 取。
50
+ * 🔴 **候上游导出即换**:sdk 哪天镜像了 `CLASSIFIER_DENY_CAUSES` 与 `GateDisposition.cause`,
51
+ * 本模块的表应当整只退役改成从 sdk 取。
45
52
  */
46
53
  /**
47
- * 一轮分类**为什么没跑成**(core `AUTO_MODE_UNAVAILABLE_CAUSES`;逐词逐序镜像,core 7.12.0 起两词)。
48
- * · `error` —— 模型那条腿抛了/被拒(分类时的路由失败也读在这里:派生路由的前置在任何 decide
49
- * 之前就回落了,所以没有单独的词);
50
- * · `timeout` —— 往返上限到了。
51
-
54
+ * 一轮分类**怎么收场的** —— `GateDisposition.denied.cause` 的值域
55
+ * (core `CLASSIFIER_DENY_CAUSES`,`gate-outcome.d.ts:168` 逐词逐序镜像,7.14.0 起两词)。
56
+ * · `unavailable` —— 分类器跑不了(模型那条腿抛了/被拒/跑过上限);一次说得出口的 fail-closed 拒绝,
57
+ * 这次调用**可以过一会儿再试**;
58
+ * · `parse_error` —— 分类器跑了并且答了,只是答在裁决契约之外;**为安全起见拦下**,并像一次裁决一样
59
+ * 被拒绝上限计数 —— 与上一条的下一步**不同**,所以两个词、两句话。
60
+ *
52
61
  * 🔴 形制:`Object.freeze` 的数组,**不是**只在类型面只读的 `readonly T[]` —— 后者一行 `.splice()`
53
62
  * 就能改,而公面消费者拿到的正是这个实例(本仓已定谳的病形,同 `RESUME_RETRY_LATER_CODES`)。
54
63
  */
55
- export const AUTO_MODE_UNAVAILABLE_CAUSES = Object.freeze(['error', 'timeout']);
64
+ export const CLASSIFIER_DENY_CAUSES = Object.freeze(['unavailable', 'parse_error']);
65
+ /** {@link CLASSIFIER_DENY_CAUSES} 的运行期成员判据(判据用它,别在端上再抄一张表)。 */
66
+ const DENY_CAUSE_SET = new Set(CLASSIFIER_DENY_CAUSES);
56
67
  /**
57
- * **判据轴独占、且 core 明说「不 stamp」的成因词**(0.65.0 起是本文件唯一的排除集)——今天恰是
58
- * `{parse_error}`。
68
+ * 这个词是不是 {@link CLASSIFIER_DENY_CAUSES} 的成员(core `isClassifierDenyCause` 的镜像)。
59
69
  *
60
- * 🔴 **它的出处是理由,不是另一张表**:core `AutoModeVerdict` 的 `parse_error` 臂逐字说的是
61
- * 「分类器跑了并且答了,只是答在契约之外,按 BLOCK 处置」——一个**有裁决**的回合,而
62
- * `classifierUnavailable` 这一格答的是「这一轮**没跑成**」。两句话互斥,所以这个词到达这一格
63
- * 时只能是坏数据,读它就是替引擎编事实。
64
- * ⚠️ **形制变更的如实登记**(0.65.0):修前这只集合是 `AUTO_MODE_BREAKER_CAUSES` 减
65
- * `AUTO_MODE_UNAVAILABLE_CAUSES` 的**差集**。core 7.12.0 删掉熔断表之后那条派生的右操作数不存在
66
- * 了 —— 而排除的理由从头到尾都不是「它在熔断轴上」,是上面那一条。所以这里按理由的真出处直写
67
- * 一张单员表,而不是为了保住「派生」的形去挽留一张上游已经删掉的表。core 哪天再给判据轴加一个
68
- * 「答了但不算数」的臂,加员与它的一句人话同批落在本文件,门会先红。
70
+ * 🔴 **这一格与 `origin` / `ruleOffersAbsence` 的开集纪律刻意不同**,理由在**上游的形**上:
71
+ * `cause` 出闭集时 core 的 `screenGateOutcome` 判**记录缺陷**(不变量 I5),而有缺陷的门记录
72
+ * server **整条不上帧** ⇒ 一个表外的 cause **结构上到不了消费端**;它到达的形式是 `gate` 整键缺席。
73
+ * 所以在这一格上按闭集判不会「把一个合法的新词吞成缺席」——真读到表外词只说明那条记录本不该长
74
+ * 这样,而把它渲成一句成因就是替引擎编事实。(`deniedBy` 那张表在 `gateVocabulary.ts` 上同一条。)
69
75
  */
70
- const NEVER_STAMPED_CAUSES = new Set(['parse_error']);
76
+ export function isClassifierDenyCause(v) {
77
+ return typeof v === 'string' && DENY_CAUSE_SET.has(v);
78
+ }
71
79
  /**
72
- * 一只 ask(或一条 durable park 行的 `tool_approval` 载荷)→ 「分类器跑不了」的事实;
73
- * 没有这条事实 / 形坏 ⇒ `undefined`,绝不抛出。
80
+ * 一条**门记录**(`tool_end.gate` / `PermissionDeniedPayload.gate` / 耐久行的 resolved outcome,
81
+ * 或本包 `gateOutcomeOf` 投出的 {@link import('./gateOutcome.js').GateOutcomeView})→
82
+ * 「这次 deny 是分类器那一轮**没能裁决**」的成因词;不是这一形 ⇒ `undefined`,绝不抛出。
74
83
  *
75
- * 🔴 **两处同一只读器**:`AskRequest.classifierUnavailable` 与
76
- * `PendingAction.tool_approval.classifierUnavailable`(durable park 行的孪生位)**键路同形**,
77
- * 所以一只读器吃两处 —— 各写一份就是两份台账各漂各的,本包一贯要根治的形。
78
- * 🔴 **`cause` 按开集读 + 一条有出处的排除**(**0.64.2 订正,行为面**;修前是「unavailable 闭集」):
79
- * · **非空串即收** —— 词表的属主是 core,而 server 侧除了「非空串」之外**不做词表校验**
80
- * (engine fixture `approval-card.js:240` 的 `z.string().min(1)`)。在这里抄一份闭集表,只会在
81
- * core 加词那天把一个**合法**值判没,而丢掉的正是「这次不可用是**新出现的那一类**」这条信息
82
- * —— 与 `AskRequest.origin` / `ruleOffersAbsence` / `denialLimitFallback.limit` 逐字同一条纪律。
83
- * 修前那条路是可复现的:一台比本端新的引擎发一个新成因词 ⇒ 卡上那一行**整段消失**,而
84
- * {@link classifierUnavailableDetail} 早就为这一形备好了兜底句(「a word newer than this client」)
85
- * —— 那句话在修前**永远不可达**,这本身就是判据写错了的证据。
86
- * ⚠️ 0.65.0 起 `breaker_open` 也走这一条(core 7.12.0 已删该词)—— **旧引擎的合法帧照旧过境**,
87
- * 只是本端不再为它自铸一句解释。
88
- * · **{@link NEVER_STAMPED_CAUSES} 里的词仍判缺席** —— 今天只有 `parse_error`。这不是「表外词一律拒」,
89
- * 而是一条**有出处的排除**:core 的判据轴逐字说它「跑了并且答了」,读它就是替引擎编一件
90
- * 它明说没发生的事。0.63.0 起的黑盒判据 G-19 钉的正是这一条,行为**一字未变**。
91
- * 🔴 **只交 `cause` 一格**:顺手把整只 ask 的别的键带出来会长成第二份 ask 读面。
92
- * 🔴 **端不许在自己那一侧再补一张闭集表**:未知词的正解是渲兜底句(措辞铸点已经有),不是不渲。
84
+ * 🔴 **一只读器吃三处**:wire 的原始 `gate` 对象、包投出的视图、以及**带 `gate` 的整只帧**
85
+ * —— 三者键路同形(`disposition.kind` / `disposition.cause`),各写一份就是三份台账各漂各的,
86
+ * 本包一贯要根治的形。
87
+ * 🔴 **只在 `kind === "denied"` 上答**:`allowed` 臂上不存在这一位;一条读不出处置词的记录是坏记录,
88
+ * 同样答缺席。
89
+ * 🔴 **闭集判据**(理由见 {@link isClassifierDenyCause}):表外词 ⇒ 缺席。
90
+ * 🔴 **不校 `deniedBy`**:core 的 `DENIED_BY_MAY_CARRY_CAUSE` 说的是 cause 只能站在 `classifier` /
91
+ * `policy`(继承链上祖先的冻结分类器)旁边,那是**引擎的段内不变量**,server 已按它铸;包再校一遍
92
+ * 只会在上游合法演进的当天把一个真读数判没(与 `gateOutcome.ts` 顶注的 I1–I4 同一条处置)。
93
+ * 🔴 **缺席不是断言**:缺席同时覆盖「分类器自己裁决 block 了」「这次不是分类器轮」「本部署没接分类器」
94
+ * 三形,端**禁**读成「分类器好着呢」。
93
95
  */
94
- export function classifierUnavailableOf(ask) {
95
- if (typeof ask !== 'object' || ask === null || Array.isArray(ask))
96
+ export function classifierDenyCauseOf(gate) {
97
+ const rec = gateRecordOf(gate);
98
+ if (rec === undefined)
99
+ return undefined;
100
+ const d = rec.disposition;
101
+ if (typeof d !== 'object' || d === null || Array.isArray(d))
96
102
  return undefined;
97
- const raw = ask.classifierUnavailable;
98
- if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
103
+ const dd = d;
104
+ if (dd.kind !== 'denied')
99
105
  return undefined;
100
- const cause = raw.cause;
101
- if (typeof cause !== 'string' || cause === '')
106
+ return isClassifierDenyCause(dd.cause) ? dd.cause : undefined;
107
+ }
108
+ /**
109
+ * 把三种入形归一成「一条带 `disposition` 的门记录」。
110
+ * 🔴 顺序有意:载体**自己**带 `disposition` 时就是门记录本体;否则看 `gate` 那一格(整只帧 / 耐久行)。
111
+ * 两条都读不出 ⇒ `undefined`(不猜第三个载体名)。
112
+ */
113
+ function gateRecordOf(v) {
114
+ if (typeof v !== 'object' || v === null || Array.isArray(v))
102
115
  return undefined;
103
- if (NEVER_STAMPED_CAUSES.has(cause))
116
+ const o = v;
117
+ if (o.disposition !== undefined)
118
+ return o;
119
+ const g = o.gate;
120
+ if (typeof g !== 'object' || g === null || Array.isArray(g))
104
121
  return undefined;
105
- return { cause };
122
+ return g;
106
123
  }
107
124
  /**
108
- * 逐成因一句人话。**三句**刻意逐字互异 —— 对用户是三条不同的下一步。
109
- * 🔴 `parse_error` 那一句说的是「分类器**跑了、答了**,只是答在契约之外」,与另外两句的
110
- * 「没跑成」是两回事;它**永远不会**从 {@link classifierUnavailableOf} 出来(那一格按
111
- * {@link NEVER_STAMPED_CAUSES} 排除),放在这里是给**渲判据面**的消费端用同一个铸点。
112
- * 🔴 **`breaker_open` 那一句 0.65.0 随熔断族退役**(core 7.12.0 已删该词):旧引擎发来的
113
- * `breaker_open` 从此走下面的兜底句,原样带上那个词 —— 编一句本端自铸的熔断解释,就是在描述
114
- * 一个这一代引擎里已经不存在的机制。
125
+ * 一轮分类**为什么**跑不了(core `AUTO_MODE_UNAVAILABLE_CAUSES`;逐词逐序镜像,core 7.12.0 起两词)。
126
+ * · `error` —— 模型那条腿抛了/被拒(分类时的路由失败也读在这里:派生路由的前置在任何 decide
127
+ * 之前就回落了,所以没有单独的词);
128
+ * · `timeout` —— 往返上限到了。
129
+ *
130
+ * ⚠️ **本表在本包手上没有到达任何投影臂的路**(0.67.0 如实登记,见模块顶注第三段):它是一个
131
+ * `auto_mode.classified` **trace 帧**上的量,而本包不投影 trace 帧。留在公面上的唯一身份是
132
+ * **三端共用的词表抄件**(core 7.16.0 仍导出它 ⇒ 不构成退役);消费端**不得**期待它出现在
133
+ * {@link classifierDenyCauseOf} 的产物旁边。
134
+ *
135
+ * 🔴 形制:`Object.freeze` 的数组,理由同 {@link CLASSIFIER_DENY_CAUSES}。
136
+ */
137
+ export const AUTO_MODE_UNAVAILABLE_CAUSES = Object.freeze(['error', 'timeout']);
138
+ /**
139
+ * 逐成因一句人话。**两句**刻意逐字互异 —— 对用户是两条不同的下一步:
140
+ * · `unavailable` —— 「分类器这一轮**跑不了**」⇒ 可以过一会儿再试同一次调用;
141
+ * · `parse_error` —— 「分类器**跑了、答了**,只是答在契约之外」⇒ 这是一次为安全起见的拦阻,
142
+ * 重试解决不了,要去看那一轮的裁决散文 / 调分类器。
143
+ * 🔴 两句**不许合并**:合并等于把「稍后重试」与「别重试」渲成同一句。
115
144
  */
116
- const CAUSE_SENTENCES = Object.freeze({
117
- error: 'asked because the auto-mode classifier could not run (it errored)',
118
- timeout: 'asked because the auto-mode classifier could not run (it timed out)',
119
- parse_error: 'the auto-mode classifier answered outside its contract (its reply could not be parsed)',
145
+ const DENY_CAUSE_SENTENCES = Object.freeze({
146
+ unavailable: 'denied because the auto-mode classifier could not run this round (the call may be retried later)',
147
+ parse_error: 'denied because the auto-mode classifier answered outside its contract (its reply could not be parsed, so the call was blocked)',
120
148
  });
121
149
  /**
122
- * 一个成因词 → 一句人话。**唯一措辞铸点**(三端共用;端零自拼)。
150
+ * 一个 deny 成因词 → 一句人话。**唯一措辞铸点**(三端共用;端零自拼)。
123
151
  *
124
152
  * 🔴 **按自有属性查表**(与本包其余措辞铸点同一条纪律):`Object.freeze` 不移除原型,裸下标会让
125
153
  * 一个来自 wire 的 `constructor` / `toString` 命中 `Object.prototype` 上的**函数**并被当成一句话。
126
- * 🔴 表外词 / 坏值 ⇒ 一句**兜底**:仍然告诉用户「这只 ask 是分类器那条腿引出来的」,但**不冒充**
127
- * 三句里的任何一句(成因读不懂 ≠ 成因是别的什么);原样带上那个词供运维追问上游。
154
+ * 🔴 表外词 / 坏值 ⇒ 一句**兜底**:仍然告诉用户「这次拒是分类器那条腿引出来的」,但**不冒充**
155
+ * 两句里的任何一句;原样带上那个词供运维追问上游。
156
+ * ⚠️ 与 {@link classifierDenyCauseOf} 的闭集判据**不矛盾**:那一层答「这条事实在不在」(闭集,
157
+ * 出集 = 坏记录),本层答「拿到一个词怎么渲」——**渲判据面**的消费端(以及一条从旧持久态恢复
158
+ * 回来的视图)可能手里就是一个表外词,它必须渲出一句诚实的话,而不是整屏崩或冒充一句已知的。
128
159
  */
129
- export function classifierUnavailableDetail(cause) {
130
- const known = typeof cause === 'string' && Object.hasOwn(CAUSE_SENTENCES, cause) ? CAUSE_SENTENCES[cause] : undefined;
160
+ export function classifierDenyCauseDetail(cause) {
161
+ const known = typeof cause === 'string' && Object.hasOwn(DENY_CAUSE_SENTENCES, cause)
162
+ ? DENY_CAUSE_SENTENCES[cause]
163
+ : undefined;
131
164
  if (known !== undefined)
132
165
  return known;
133
166
  const word = typeof cause === 'string' && cause.length > 0 ? cause : '(none)';
134
- return `asked because of the auto-mode classifier; its reported cause ${word} is a word newer than this client`;
167
+ return `denied by the auto-mode classifier lane; its reported cause ${word} is a word newer than this client`;
135
168
  }
@@ -31,11 +31,13 @@ export interface ClassifierStatusView {
31
31
  * 「这个会话上,auto 分类器现在是什么状态」——三态,或 `undefined`(**说不出来**)。
32
32
  *
33
33
  * @param autoMode `wiring_manifest` 的 `autoMode` 段(投影后的或原始的都吃;本函数自己窄读)
34
- * @param ask 可选:**本轮**那只 ask(或 durable park 行的 `tool_approval` 载荷)
34
+ * @param ask 可选:**本轮那一次观测** —— 一只 ask / 一条 durable park 行的 `tool_approval`
35
+ * 载荷 / 一只带 `gate` 的 `tool_end` 帧 / 一条门记录本体。三形键路同形,同一把读器吃(0.67.0:
36
+ * 否定事实的载体从 ask 上的 `classifierUnavailable` 改成门记录上的 `disposition.cause`)。
35
37
  *
36
- * 优先序(承重,理由见模块顶注):**本轮事实(否定 + 肯定)> 这条腿的 `armed`**——先看 `ask` 上的
37
- * 本轮不可用事实,再看 `ask` 上那条「分类器真的跑过」的肯定事实,最后才看这条腿武没武装。
38
- * `ask` 带本轮事实时,`autoMode` 段缺席也照样答得出(两条本轮臂都不依赖那一段)。
38
+ * 优先序(承重,理由见模块顶注):**本轮事实(否定 + 肯定)> 这条腿的 `armed`**——先看观测座上的
39
+ * 本轮不可用事实,再看那条「分类器真的跑过」的肯定事实,最后才看这条腿武没武装。
40
+ * 观测座带本轮事实时,`autoMode` 段缺席也照样答得出(两条本轮臂都不依赖那一段)。
39
41
  *
40
42
  * 🔴 **`undefined` 是一个诚实的答案,不是一个坏路径**:
41
43
  * · `autoMode` 段缺席(老 mint / 外部 derive)⇒ 这一端**没有**分类器的健康读数;
@@ -47,7 +49,7 @@ export declare function classifierStatusOf(autoMode: unknown, ask?: unknown): Cl
47
49
  /**
48
50
  * 一次状态读数 → 一句人话。**唯一措辞铸点**(三端共用;端零自拼)。
49
51
  *
50
- * 三句逐字互异,且与 `classifierUnavailableDetail` 的三句**零重合**(两张面答两个问题)。
52
+ * 三句逐字互异,且与 `classifierDenyCauseDetail` 的两句**零重合**(两张面答两个问题)。
51
53
  * 🔴 **表外态不抛**:型面挡不住 wire,也挡不住一份从持久态恢复回来的视图 —— 一个
52
54
  * `{state:'breaker_open'}`(0.65.0 退役掉的那个词,可能来自旧持久态)必须渲出一句诚实的话,
53
55
  * 而不是把整屏带崩,也不冒充三句里的任何一句。