@sema-agent/client-core 0.61.0 → 0.62.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.
@@ -123,6 +123,10 @@ export declare function writeProtectionDoctorDetail(reading: WriteProtectionRead
123
123
  /**
124
124
  * operator 面读数 → 一行 detail(行名是 UNTRUSTED 自由文本 ⇒ 呈前**逐条**消毒 + 封长)。
125
125
  *
126
+ * 🔴 消毒与封长的顺序 = **先转义、后按转义结果封长**({@link capForDisplay},0.62.0 起本包该族的
127
+ * 唯一实现)。此前这三处是反的(先按原文 `slice` 再转义),一段纯控制字符的 40 字符原文转义后
128
+ * 能占 240 个显示字符 —— 承诺的列宽预算成了名义值的六倍。同形存量另有 SQL 姿态行一处,同批同改。
129
+ *
126
130
  * 🔴 只给**行数与来源**加上「头几行的名字」,不整表倾泻:一行诊断不是一个表格,而 operator 面的
127
131
  * 真表读法是那个端点本身。
128
132
  * 🔴 `rows: []` 渲的是**显式无表**这句正面事实,不是「读不出」。
@@ -29,7 +29,7 @@
29
29
  * 🔴 与**模式型** write deny(`SENSITIVE_WRITE_PATTERNS`)是**并列机制**,两个读面分开报、
30
30
  * 绝不合成一位:两者的解法不同(一套改模式,一套改名表)。
31
31
  */
32
- import { escapeDisplayControlChars } from './fleetTaskDesc.js';
32
+ import { capForDisplay } from './fleetTaskDesc.js';
33
33
  import { engineWireTarget } from './engineWireTarget.js';
34
34
  import { engineCapsGeneration } from './engineCapsCache.js';
35
35
  /**
@@ -186,17 +186,21 @@ export function writeProtectionDoctorDetail(reading) {
186
186
  /**
187
187
  * operator 面读数 → 一行 detail(行名是 UNTRUSTED 自由文本 ⇒ 呈前**逐条**消毒 + 封长)。
188
188
  *
189
+ * 🔴 消毒与封长的顺序 = **先转义、后按转义结果封长**({@link capForDisplay},0.62.0 起本包该族的
190
+ * 唯一实现)。此前这三处是反的(先按原文 `slice` 再转义),一段纯控制字符的 40 字符原文转义后
191
+ * 能占 240 个显示字符 —— 承诺的列宽预算成了名义值的六倍。同形存量另有 SQL 姿态行一处,同批同改。
192
+ *
189
193
  * 🔴 只给**行数与来源**加上「头几行的名字」,不整表倾泻:一行诊断不是一个表格,而 operator 面的
190
194
  * 真表读法是那个端点本身。
191
195
  * 🔴 `rows: []` 渲的是**显式无表**这句正面事实,不是「读不出」。
192
196
  */
193
197
  export function writeProtectionPostureDetail(posture) {
194
- const source = escapeDisplayControlChars(posture.source.slice(0, WP_DETAIL_MAX));
198
+ const source = capForDisplay(posture.source, WP_DETAIL_MAX);
195
199
  if (posture.rows.length === 0)
196
200
  return `source ${source} · no protected names in effect`;
197
201
  const shown = posture.rows
198
202
  .slice(0, 3)
199
- .map((r) => `${escapeDisplayControlChars(r.name.slice(0, WP_DETAIL_MAX))}(${escapeDisplayControlChars(r.kind.slice(0, WP_DETAIL_MAX))})`)
203
+ .map((r) => `${capForDisplay(r.name, WP_DETAIL_MAX)}(${capForDisplay(r.kind, WP_DETAIL_MAX)})`)
200
204
  .join(', ');
201
205
  const more = posture.rows.length > 3 ? `, +${String(posture.rows.length - 3)} more` : '';
202
206
  const dropped = posture.droppedDefaultRows !== undefined && posture.droppedDefaultRows.length > 0
@@ -23,7 +23,7 @@
23
23
  | peer:wire 契约 | `@sema-agent/sdk` **>=8.4.0**(value-level,非 type-only;**0.60.0 抬版**,四条硬理由见 §24a 与 `scripts/run-sdk-floor-test.mjs` 的 `FLOOR` 注;上一次是 0.59.0 的 `>=8.3.0`)。🔴 支持窗同批收到 **engine ≥7.64.0**:sdk 8.4.0 与 7.63.0 及以前的 wire **不同窗** | `package.json` `peerDependencies` |
24
24
  | peer:会话词汇表 | `@sema-agent/agent-types` **>=0.2.0**(type-only,零运行时) | 同上 |
25
25
  | runtime dep | `diff` ^9.0.0(**唯一**一条;portability 门按**等值**钉死) | `package.json` `dependencies` |
26
- | 公开导出面 | **864** 个运行期符号(+ 43 个测试钩;= 工作树当下的值 = 0.61.0 三件 additive、**零删除**:`projectReadFacePosture`/`readFacePostureDetail`/`readFaceDisagreement`(S-167 READ 容纳面 operator 读器,详见 §25);`0.60.0` 是 **861**,那批是二十五件 additive、**零删除**:`readRunTerminal`/`runTerminalCode`/`runTerminalGateKind`/`runTerminalGateToolName`/`isReviewPark`/`REVIEW_PARK_GATE_KINDS` 六件终局因由读器 + `gateOutcomeOf`/`isDeniedGate`/`gateDeniedBy`/`gateApprover`/`isApprovalWindowExpiredGate`/`isDenialLimitAutoDeniedGate`/`isParkSlaExpiredGate`/`isHumanSettledGate` 八件门记录读器 + `projectWriteProtectionCapability`/`noteEngineCapsForWriteProtection`/`observedWriteProtection`/`forgetWriteProtectionReading`/`writeProtectionDoctorDetail`/`projectWriteProtectionPosture`/`writeProtectionPostureDetail` 七件写保护读面(测试钩 `__resetWriteProtectionReadingsForTests` +1)+ `readWireErrorCode` + 真机边界①的两件 `isGateParkedToolEnd`/`GATE_PARKED_ERROR_CODE` + 异源复审逼出的 `engineCapsGeneration`;`0.59.0` 是 **836**,那批是八件 additive:`RULE_OFFERS_ABSENCE_REASONS`/`DENIAL_LIMIT_KINDS` 两张闭词表再导出 + `engineCapValue` 能力位四态通用读口 + `projectSqlEngineCapability`/`observedSqlEngine`/`noteEngineCapsForSqlEngine`/`forgetSqlEngineReading`/`sqlEngineDoctorDetail` 五件 SQL 姿态读面(测试钩 `__resetSqlEngineReadingsForTests` +1);`0.58.0` 是 **828**,那批是三件 additive:`RULE_OFFER_MATCHES`/`RULE_OFFER_BATCH_MEMBER_KINDS`/`RULE_OFFER_UNCOVERED_REASONS` 三张闭词表再导出 + `RESUME_REFUSAL_CODES`/`RESUME_PLACEMENT_MISMATCH`/`resumeRefusalFromError`/`resumeRefusalContent` 四件文案铸点;`0.57.0` 是 **821**;`0.56.0`/`0.55.0` 是 **815**;已发 `0.54.0`(design/385 十件 additive 含 `AUTHORITY_ENVELOPE_TAGS`);`0.52.0` 是 **805**(L-69⑨ 两件);`0.51.0` 是 **803**;`0.50.0` 是 **800**(S-81 五件);`0.49.0` 是 **795**;`0.48.0` 是 **794**;npm `0.47.0` 是 **790**,`0.46.0` 是 **787**,`0.44.0` 是 **783**,`0.43.1`/`0.43.0` 是 **776**,`0.42.0` 是 **771**,`0.41.0` 是 **767**,`0.39.0` 是 **766**,`0.38.0` 是 **764**;`0.37.0` 是 **753**,见 `CHANGELOG.md`) | `scripts/public-export-baseline.json` 的 `count` / `testHookCount` —— **别手抄进别处,以该文件为准** |
26
+ | 公开导出面 | **869** 个运行期符号(+ 44 个测试钩;= 工作树当下的值 = 0.62.0 五件 additive、**零删除**:`capForDisplay`(呈前消毒 + 封长的共用铸点,详见 §26⑦)/ `SEAT_SEND_MESSAGE_KEY_ORIGINS`(座位载荷逐键出身表,详见 §26③)/ `configureSubagentContentStore`+`subagentContentStoreConfig`+`SUBAGENT_CONTENT_STORE_DEFAULTS`(子代内容账本字节预算,测试钩 `__resetSubagentContentStoreConfigForTests` +1,详见 §26②);`0.61.0` **864**,那批是三件 additive、**零删除**:`projectReadFacePosture`/`readFacePostureDetail`/`readFaceDisagreement`(S-167 READ 容纳面 operator 读器,详见 §25);`0.60.0` 是 **861**,那批是二十五件 additive、**零删除**:`readRunTerminal`/`runTerminalCode`/`runTerminalGateKind`/`runTerminalGateToolName`/`isReviewPark`/`REVIEW_PARK_GATE_KINDS` 六件终局因由读器 + `gateOutcomeOf`/`isDeniedGate`/`gateDeniedBy`/`gateApprover`/`isApprovalWindowExpiredGate`/`isDenialLimitAutoDeniedGate`/`isParkSlaExpiredGate`/`isHumanSettledGate` 八件门记录读器 + `projectWriteProtectionCapability`/`noteEngineCapsForWriteProtection`/`observedWriteProtection`/`forgetWriteProtectionReading`/`writeProtectionDoctorDetail`/`projectWriteProtectionPosture`/`writeProtectionPostureDetail` 七件写保护读面(测试钩 `__resetWriteProtectionReadingsForTests` +1)+ `readWireErrorCode` + 真机边界①的两件 `isGateParkedToolEnd`/`GATE_PARKED_ERROR_CODE` + 异源复审逼出的 `engineCapsGeneration`;`0.59.0` 是 **836**,那批是八件 additive:`RULE_OFFERS_ABSENCE_REASONS`/`DENIAL_LIMIT_KINDS` 两张闭词表再导出 + `engineCapValue` 能力位四态通用读口 + `projectSqlEngineCapability`/`observedSqlEngine`/`noteEngineCapsForSqlEngine`/`forgetSqlEngineReading`/`sqlEngineDoctorDetail` 五件 SQL 姿态读面(测试钩 `__resetSqlEngineReadingsForTests` +1);`0.58.0` 是 **828**,那批是三件 additive:`RULE_OFFER_MATCHES`/`RULE_OFFER_BATCH_MEMBER_KINDS`/`RULE_OFFER_UNCOVERED_REASONS` 三张闭词表再导出 + `RESUME_REFUSAL_CODES`/`RESUME_PLACEMENT_MISMATCH`/`resumeRefusalFromError`/`resumeRefusalContent` 四件文案铸点;`0.57.0` 是 **821**;`0.56.0`/`0.55.0` 是 **815**;已发 `0.54.0`(design/385 十件 additive 含 `AUTHORITY_ENVELOPE_TAGS`);`0.52.0` 是 **805**(L-69⑨ 两件);`0.51.0` 是 **803**;`0.50.0` 是 **800**(S-81 五件);`0.49.0` 是 **795**;`0.48.0` 是 **794**;npm `0.47.0` 是 **790**,`0.46.0` 是 **787**,`0.44.0` 是 **783**,`0.43.1`/`0.43.0` 是 **776**,`0.42.0` 是 **771**,`0.41.0` 是 **767**,`0.39.0` 是 **766**,`0.38.0` 是 **764**;`0.37.0` 是 **753**,见 `CHANGELOG.md`) | `scripts/public-export-baseline.json` 的 `count` / `testHookCount` —— **别手抄进别处,以该文件为准** |
27
27
  | 常驻门 | 以 `scripts/gates-manifest.json` 的 `suites` 长度为准(**本档不抄这个数**) | `scripts/gates-manifest.json`;`npm test` 的名单等值门与它逐名对账 |
28
28
  | 沿革档 | 0.29.0 起建 `CHANGELOG.md`;更早批次记账在 `src/index.ts` 文件头 + `docs/REFACTOR-LEDGER.md` | — |
29
29
 
@@ -111,7 +111,7 @@ SDK 核对「本包 import 的每个值级符号仍然导出」「`TaskStats.cos
111
111
 
112
112
  ## §2 公共导出面地图(按域)
113
113
 
114
- > 全集真源 = `scripts/public-export-baseline.json` 的 `names`(**864** 项)。
114
+ > 全集真源 = `scripts/public-export-baseline.json` 的 `names`(**869** 项)。
115
115
  > 本节**不逐名抄**,只给「域 → 承重导出 → 用途 → 实现锚」。承重导出 = 一个端为了让这个域干活
116
116
  > **必须**直接调到的那几个符号;其余是它们的类型、变体与辅助位。
117
117
  > 单一入口:`import { … } from '@sema-agent/client-core'`(`exports` 只有 `.` 一个;
@@ -135,20 +135,20 @@ SDK 核对「本包 import 的每个值级符号仍然导出」「`TaskStats.cos
135
135
  `WorkflowsGateUnknownDenial` 四形**不在**基线里,`src/selfOrchestrationDenial.ts` 对基线贡献
136
136
  **4** 项运行期导出(三个函数 + `SELF_ORCHESTRATION_RETRY_WITHOUT`)。
137
137
 
138
- 864 项的内部构成(帮助端估读表大小):**256** 项是 `SCREAMING_SNAKE` 常量数据表/词汇表
138
+ 869 项的内部构成(帮助端估读表大小):**258** 项是 `SCREAMING_SNAKE` 常量数据表/词汇表
139
139
  (矩阵、键集、env 名、锚串)而非可调用物;**5** 项是 PascalCase 运行期值
140
140
  (`ControlRouter` / `ControlSafetyError` / `HitlBridge` / `HitlSafetyError` / `DecideTransportRetryExhaustedError`);
141
141
  **41** 项是 `*For(sessionKey, …)` 的 per-session 变体(§6;其中 `engineNamespaceKeyFor` 是命名巧合 —— 参数是 baseUrl 不是 sessionKey,见域 14)。
142
142
 
143
- ### 2b. 域图(16 域,逐域计数之和 = 864)
143
+ ### 2b. 域图(16 域,逐域计数之和 = 869)
144
144
 
145
145
  | # | 域 | 名数 | 承重导出 | 用途 | 实现锚 |
146
146
  |---|---|---|---|---|---|
147
147
  | 1 | **适配内核(下行主链)** | 39 | `adapt` · `createWireToCcAdapter` · `runStream` · `eventToSdkMessage` · `terminalToSdkResult` · `turnUsageToModelUsage` · `isRunStreamActive` · `ADAPTER_DIVERGENCES` · `readRunTerminal` / `runTerminalCode` / `runTerminalGateKind` / `runTerminalGateToolName` / `isReviewPark` / `REVIEW_PARK_GATE_KINDS`(0.60.0 / engine ≥7.64.0:一次 run **怎么结束的**唯一读器 —— 终局从八个平面键换成一条带标因由 `terminal`,四臂 + `unknown` 防御臂 + `plane` 两代字节标;🔴 不认识的终态词绝不折成成功、本包不提供「因由→五词 status」的兼容投影,详见 §24b)| 引擎 SSE `AgentEvent` → 端要渲的**双面输出**:transcript(`SDKMessage`)+ chrome(瞬态 `ChromeEvent`)。**本包存在的理由** | `src/adapt.ts`、`src/adapt/{arms,wireShapes,panelTasks}.ts`(经 `adapt.ts` 再导出)、`src/adapter/runStream.ts`、`src/adapter/downstream/*`、`src/adapter/types.ts`、`src/runTerminal.ts` |
148
148
  | 2 | **seam 公共契约** | 2(其余为 type-only) | `CHROME_ARMS` · `deriveTranscriptId` | 公共词汇 + **id 确定性不变量**(同一条流重放 ⇒ 同一串 id)。`CHROME_ARMS` = 端「我要消费哪些 chrome 臂」的对照清单 | `src/seam.ts` |
149
149
  | 3 | **HITL 决断卡链**(§4/§5 主战场) | 153 | `makeHitlCanUseTool` · `HitlBridge` · `findPendingForTask` · `HitlSafetyError` · `bridgeAskUserQuestionGates` · `surfaceToolApprovalFrameAndRespond` / `surfaceFsApprovalAndDecide` · `readToolApprovalRespondAck` · `installApprovalCardPort(For)` · `installHitlHostSurface(For)` · `armPlanReviewApproval` · `reopenPlanReviewCard` · `decidePlanReview` · `startApprovalsFeed` · `pendingRowIsOwnedByThisSession` · `approvalCallKey`/`liveFrameCallKey`/`planReviewQuestionId` · `registerArmedGateFor`/`wasGateArmedFor`/`clearArmedGateFor` · `waitForGateArmed(For)`/`onGateArmed(For)`/`gateArmedWaitMs`(#244 F1 呈现回执事件源) · `planReviewArmedKey(For)`/`notePlanReviewAnswered(For)`/`notePlanReviewAnsweredIfDecisive(For)`(A-024.4 plan 呈现分代) · `toolEndOutputText` · `isAskTool` · `waitForParkRowBirth` · `classifyAskParkRows` / `askParkRowArm` / `classifyAskParkChainFailure` · `readDecisionNoteAudit` / `decisionNoteAuditLine` · `resumeRunningOptions` / `resumeChoiceFromLabels` · `persistedRulesLaneAvailable`/`persistedRulesGovernanceAvailable` · `classifyRulesFailure` · `listAllPersistedRules` · `classifySkippedReason` · `readRulePersistOutcome`(#244 F2:persist-ack 读口与 `readToolApprovalRespondAck` 合成一处) · `parseLocalAllowRule`(durable 腿本地落规则窄化骨架,谓词经 `LocalAllowRuleDeps` 注入) · `readToolApprovalRespondRefusal`(#225 件5,0.42.0:respond 抛错的结构化原文读口 —— 三位各自防御读、各自缺席不铸、**三位皆缺席时整只返 `undefined`**;原样交还零加工,UNTRUSTED-for-display)· `installEditedRuleTextPrechecker` / `hasEditedRuleTextPrechecker` / `precheckEditedRuleText`([5076] 转出口,0.42.0:core 5.57.0 `precheckEditedRuleText` 的**端口注入形** —— 类型面 + 注入口 + 诚实缺席读口。🔴 **不是** value 级 re-export,理由见 §7 缺口 **P-34**;未装 ⇒ 返 `undefined`,绝不编一个 `{ok:true}`)· `surfaceRuleArmNotSent` / `RULE_NOT_SENT_WARN_TEXT` + `surfaceRuleArmRejected` / `RULE_NOT_SENT_REJECTED_WARN_TEXT`(#334,0.43.0:人在卡上按下的「不再询问」被整条丢弃时的诚实告知——**两条刻意分开**:前者=**引擎能力位未确认**(换台引擎/等探测就好),后者=**这次选择没过包内表核/互斥核**(表外文本/坏下标/两臂同场,换引擎也不会变) —— 编辑臂 `respondFreeFormRules` 与批臂 `respondBatchRuleOffers` 两条同形存量共用一条,与 `surfaceRememberNotApplied` 同族纪律:决断照送、只是规则没存,静默丢掉用户明确意图 = 让人以为功能坏了)· `projectCrashConverged`(L-38,0.49.0:`/v1/approvals` additive 键 `crashConverged` 的分桶投影 —— 崩溃收敛的孤儿审批读面,**缺席 ≠ 空数组**、分桶恰一个合取、坏行丢弃并计数,详见 §12;同批把 `ApprovalsResourceLike.list()` 的返回位 additive 放宽成 `ApprovalsListEnvelope`,老形 `{pending}` 仍可赋值)· `readRuleOffers` / `readRuleOfferSupply`(L-103/[C228],0.57.0 additive 导出:「不再询问」候选两代 wire 键的窄读器**公面化** —— 此前只经卡端口出包,不走卡端口架构的宿主只能自己重铸一把,而它承载的是兑付安全判据(原始下标不前移/逐条丢坏/闭集 kind/两代取舍序),详见 §20)· `RULE_OFFERS_ABSENCE_REASONS` / `DENIAL_LIMIT_KINDS`(0.59.0 / sdk 8.3.0 提货:`ruleOffersAbsence` 三词与 `denialLimitFallback.limit` 二词的**运行期**闭词表再导出 —— 与 0.58.0 那三张同一条纪律:端拿它做判定别再手抄字面量。⚠️ **本包自己不拿这两张表做窄读判定**(两处都按开集透传,词表属主是 core、server 已按闭集拒过),再导出只是给端的 `switch` 一份可数的表,端仍必须带 `default` 臂,详见 §23a)· `DecideTransportRetryExhaustedError`(Inkglow-1085 P0a:decide 出站瞬断重试耗尽的 typed 判别 —— HitlBridge 内建单次退避重试,耗尽走重呈臂不判死 turn;端一般只消费行为,不需要 instanceof) · `gateOutcomeOf` / `isDeniedGate` / `gateDeniedBy` / `gateApprover` / `isApprovalWindowExpiredGate` / `isDenialLimitAutoDeniedGate` / `isParkSlaExpiredGate` / `isHumanSettledGate`(0.60.0 / engine ≥7.64.0:`tool_end.gate` 整条门记录的窄读器 + 判定谓词 —— 取代退役的四个正交词 `settledBy`/`resolution`/`autoDenied`/`approver`。🔴 **判定归包**:「是不是审批窗自己走完的拒绝」旧形上是端各自拼的两键合取,而三条窗词现在分得开、合并读回去等于把刚修好的判别力扔掉;⚠️ 公面型一律带 `View` 后缀 —— 本文件的 `GateOutcome`(决断腿结局)与 sdk 的 `GateOutcome`(引擎门记录)**同名不同义**,详见 §24c)· `isGateParkedToolEnd`(「这次调用没在这里结算」的**第二形**谓词:gate 缺席 + `errorCode:"gate.parked"`;端判这件事必须与 `isApprovalWindowExpiredGate` **两形合读**) · `readWireErrorCode`(B-037 候包件②:decide/取件失败上引擎 wire 机器码的单源读口,结构视图读不 `instanceof`;与本包铸的 `code` 是两个命名空间,详见 §24g)| suspended→decide→resume 环。🔴 **D-1 两元组 verbatim 回显**是字节级断言的安全不变量,端**不许重实现它的任何一段**。🔴 键空间边界(web [C1] d3 拦截):`gateIdentity` 四常量两函数只覆盖 HITL questionId/callKey 空间;seat 的 `TOOL_PERMISSION_REQUEST_ID_DOMAINS`(`plan:` 等)是另一键空间,**两者绝不合并**(合并=座位校验器静默拒全部 plan-review 卡) | `src/hitl/hitlBridge.ts`、`toolApprovalWire.ts`、`askGateWire.ts`、`planReviewWire.ts`、`hitlHostSurface.ts`、`gateIdentity.ts`、`armedGateRegistry.ts`、`parkOwnership.ts`、`parkResolver.ts`、`approvalsFeed.ts`、`frameRouter.ts`(**只挑名导出** `toolEndOutputText`/`ENGINE_ABORT_TOOL_RESULT`/`isAskTool`/`HITL_REJECT_MESSAGE`/`HITL_INTERRUPT_MESSAGE_FOR_TOOL_USE`)、`parkRowBirthWait.ts`、`approvalDecisionNoteAudit.ts`、`askParkRowRouting.ts`、`resumeRunningCard.ts`(#265 上收的判定层)、`persistedRulesWire.ts`、`localAllowRule.ts`(#244 F2 规则侧)、`crashConverged.ts`(L-38 崩溃收敛读面)、`src/gateOutcome.ts` |
150
- | 4 | **子代 wire + 面板侧信道台账** | 84 | `tailEngineSubagent` · `installSubagentActivitySink` · `installSubagentTailMetaSink`(#280 件2:tail meta 帧发布口,`contentFrames` 判别位载体)· `stopEngineTask` + `classifyTaskStopConflict` · `fetchEngineSubagentReport` · `steerEngineSubagent`(0.32.0 未发布 #280 件A:additive 第三参 `childTaskId` —— 端有行上下文时**应当**传,传了就走「台账优先 / 缺席即诚实缺席 + `noteBgOwnerAbsence` 留痕」的 Q3 口径,与 tail·taskOutput·subagentOutput 三腿同姿势、与孪生 resume 腿共用同一个 `resolveOwnerRunId` 判据;**不传**则逐字维持旧行为=回落在飞 run)· `resumeSettledSubagent` + `resolveSubagentResumeContext` + `resolveOwnerRunId` + `classifySubagentResumeFailure` + `subagentResumeAvailable`(#242 批 2 A-028.7:resume 判定半场上收,与 steer 孪生同居;取址三态 = 台账有行用行值 / 指名了行但台账缺席则**诚实缺席绝不回落在飞 run** / 没指名行才回落。出路文案归端)· `recordSubagentOwnerFromProgress` + `getBgParentRunOwner`(A-028.6:「子代 → 宿主 run」**单表**,宿主 run 必须由持 stream-local 值的调用方显式传入,包内绝不从 `activeEngineRunId()` 推断)· `noteBgOwnerAbsence`(#242 批 3 [4000] Q3=B:tail/taskOutput·taskStop/subagentOutput 三腿台账缺席即诚实缺席**绝不回落在飞 run**,缺席 warn 留痕每 (腿,taskId) 一条)· `clearBgTerminalFacts`(#242 批 3 扫码修:复活=新周期,旧周期终态事实作废——fleetLedger 复活两腿按尾段清账,factsAccepted 方向核不再拿上周期终态当先例)· `auditRetainWithoutWake`([4000] Q5:引擎宣示 `subagentResume` + 本端在付 `retainSubagentSessions` + 端未实现 `wakeSubagent` ⇒ 响亮一条;`CLIENT_VERBS.wakeSubagent` 维持 fail-soft)· `subscribeSubagentContent` · `subscribeEngineAgentPanel` · `publishQuestionFrame` / `respondToQuestion` | 驱动与观测委派子代;经 module 级台账喂活体 agent/task 面板。全部**能力位 gate**(§5b) | `src/subagent/*.ts`、`src/subagentContentStore.ts`、`src/engineAgentPanelStore.ts`、`src/engineInlineTaskStats.ts`、`src/engineToolLabelStore.ts`、`src/liveQuestionStore.ts` |
151
- | 5 | **fleet 投影** | 46 | `createFleetLedger` · `projectTasks` · `projectWorkflows` · `projectFleetAgentRows` · `readEngineActiveBgTasks` · `FLEET_TASK_VIEW_KEYS` · `escapeDisplayControlChars`(不可见字符可见化,行标签/描述消毒的共享底座)· `wireCycleSeq` / `wireRetiredBy`(0.38.0 提货补投的 #261 §2 两位:代际号 = SendMessage 复活即 +1,**缺席 ≠ 第一代**;`retiredBy` 在场 = 这条终态是对账腿从 durable run 行投影出来的、**不是**发布方亲报 —— 幽灵行与正常收尾唯一的 wire 判据。两位都只在场才落键) | 老 `fleetClient` 那一刀的成品:**帧体归库、连接归端** —— 端持 SSE 连接,库做行投影 + 保留台账 | `src/fleet/fleetProjection.ts`、`src/fleet/fleetLedger.ts`、`src/fleetAgentPanelProjection.ts`、`src/fleetTaskDesc.ts` |
150
+ | 4 | **子代 wire + 面板侧信道台账** | 87 | `tailEngineSubagent` · `installSubagentActivitySink` · `installSubagentTailMetaSink`(#280 件2:tail meta 帧发布口,`contentFrames` 判别位载体)· `stopEngineTask` + `classifyTaskStopConflict` · `fetchEngineSubagentReport` · `steerEngineSubagent`(0.32.0 未发布 #280 件A:additive 第三参 `childTaskId` —— 端有行上下文时**应当**传,传了就走「台账优先 / 缺席即诚实缺席 + `noteBgOwnerAbsence` 留痕」的 Q3 口径,与 tail·taskOutput·subagentOutput 三腿同姿势、与孪生 resume 腿共用同一个 `resolveOwnerRunId` 判据;**不传**则逐字维持旧行为=回落在飞 run)· `resumeSettledSubagent` + `resolveSubagentResumeContext` + `resolveOwnerRunId` + `classifySubagentResumeFailure` + `subagentResumeAvailable`(#242 批 2 A-028.7:resume 判定半场上收,与 steer 孪生同居;取址三态 = 台账有行用行值 / 指名了行但台账缺席则**诚实缺席绝不回落在飞 run** / 没指名行才回落。出路文案归端)· `recordSubagentOwnerFromProgress` + `getBgParentRunOwner`(A-028.6:「子代 → 宿主 run」**单表**,宿主 run 必须由持 stream-local 值的调用方显式传入,包内绝不从 `activeEngineRunId()` 推断)· `noteBgOwnerAbsence`(#242 批 3 [4000] Q3=B:tail/taskOutput·taskStop/subagentOutput 三腿台账缺席即诚实缺席**绝不回落在飞 run**,缺席 warn 留痕每 (腿,taskId) 一条)· `clearBgTerminalFacts`(#242 批 3 扫码修:复活=新周期,旧周期终态事实作废——fleetLedger 复活两腿按尾段清账,factsAccepted 方向核不再拿上周期终态当先例)· `auditRetainWithoutWake`([4000] Q5:引擎宣示 `subagentResume` + 本端在付 `retainSubagentSessions` + 端未实现 `wakeSubagent` ⇒ 响亮一条;`CLIENT_VERBS.wakeSubagent` 维持 fail-soft)· `subscribeSubagentContent` · `subscribeEngineAgentPanel` · `publishQuestionFrame` / `respondToQuestion` · `configureSubagentContentStore` / `subagentContentStoreConfig` / `SUBAGENT_CONTENT_STORE_DEFAULTS`(0.62.0:子代内容账本的**字节预算** —— 此前只有条数帽而单条无上限,一条 base64 工具结果就能让一个子代槽吃到十几 MB;越限从最早的内容开始丢并留一条可渲染的 `truncated` 记录,总帽淘汰整条最久未用的账本。配置面 fail-loud,详见 §26②)| 驱动与观测委派子代;经 module 级台账喂活体 agent/task 面板。全部**能力位 gate**(§5b) | `src/subagent/*.ts`、`src/subagentContentStore.ts`、`src/engineAgentPanelStore.ts`、`src/engineInlineTaskStats.ts`、`src/engineToolLabelStore.ts`、`src/liveQuestionStore.ts` |
151
+ | 5 | **fleet 投影** | 47 | `createFleetLedger` · `projectTasks` · `projectWorkflows` · `projectFleetAgentRows` · `readEngineActiveBgTasks` · `FLEET_TASK_VIEW_KEYS` · `escapeDisplayControlChars`(不可见字符可见化,行标签/描述消毒的共享底座)· `capForDisplay`(0.62.0:呈前**消毒 + 封长**的共用铸点 —— 先转义、后按转义结果封长,截点避开半截转义 token 与合法代理对;本包该族四处此前各写一份、其中三份顺序是反的,详见 §26⑦)· `wireCycleSeq` / `wireRetiredBy`(0.38.0 提货补投的 #261 §2 两位:代际号 = SendMessage 复活即 +1,**缺席 ≠ 第一代**;`retiredBy` 在场 = 这条终态是对账腿从 durable run 行投影出来的、**不是**发布方亲报 —— 幽灵行与正常收尾唯一的 wire 判据。两位都只在场才落键) | 老 `fleetClient` 那一刀的成品:**帧体归库、连接归端** —— 端持 SSE 连接,库做行投影 + 保留台账 | `src/fleet/fleetProjection.ts`、`src/fleet/fleetLedger.ts`、`src/fleetAgentPanelProjection.ts`、`src/fleetTaskDesc.ts` |
152
152
  | 6 | **请求装配(上行唯一构造口)** | 8 | `buildTaskRequest` · `REQUEST_FIELD_MATRIX` · `unregisteredRequestKeys` · `applyLiveRequestDefaults` · `taskNotificationToPrintFrame` | 两条车道(`interactive`/`print`)出站请求的**唯一**构造器;`unregisteredRequestKeys` 是可执行门 —— 端偷带一个未登记键上 wire 就红 | `src/request/taskRequest.ts`、`src/request/printNotification.ts` |
153
153
  | 7 | **通知与 outstanding 台账** | 51 | `installNotificationQueuePort` · `normalizeTaskNotification` · `taskNotificationDedupKeyFromWire` · `registerOutstandingBgTask` / `registerOutstandingWorkflowRun` · `notificationQueuePortMisses` · `subscribeOutstandingWorkflows` · `outstandingDeliverableWorkflowCount` | `task_notification` 归一 + 去重 + 投递进宿主命令队列的**一把闸**;`outstandingDeliverableWorkflowCount()` 是 headless `-p` 的**退出门** · `classifyPeerNotification` / `renderPeerFrameTranscriptText` / `parsePeerFrameText` / `peerFrameDisplayName` + 三张闭集表(design/385,0.54.0:同一条 `task_notification` 车道上三条**引擎注入帧**的类型化投影 —— 判别位=载体在场而非 summary 文本,详见 §17) | `src/notifications.ts`(11 个 module 台账) |
154
154
  | 8 | **工具结果卡** | 25 | `structuredToToolUseResult` · `readAsyncLaunchedAgentReceipt` · `wireOutputToBody` · `parseModelFacingBash` · `getPatchFromContents` · `toolEndResultToUserFrame` · `flattenToolOutput` | 铸端要渲的 `tool_result` 卡体,含客户端 diff hunk(唯一 runtime dep 的用处) | `src/toolResult.ts`、`src/printToolResultFrame.ts`、`src/diff/patch.ts` |
@@ -156,7 +156,7 @@ SDK 核对「本包 import 的每个值级符号仍然导出」「`TaskStats.cos
156
156
  | 10 | **headless / 部署旋钮 wire** | 61 | `parseSandboxArgv` / `sandboxRequestFields` · `parseLimitsArgv` / `limitsForPrint` · `resolveHeadlessFinalVerify` · `resolveHeadlessPermissionMode` · `resolveHeadlessInteractiveTools` · `armDetachCancel` + `detachCancelArm` + `isDetachArmed` · `withHeadlessR1Reconnect` | `-p`/headless 车道的 env+argv 旋钮。🔴 `detachWire` 是**拆**的补偿:判定与 cancel-arm 台账在库,信号路径的裸 fetch 留宿主(`detachCancelArm()` 是取件口) | `src/sandboxWire.ts`、`scenarioWire.ts`、`finalVerifyWire.ts`、`limitsWire.ts`、`interactiveToolsWire.ts`、`headlessPermissionModeWire.ts`、`headlessReconnectWire.ts`、`detachWire.ts` |
157
157
  | 11 | **模型目录与预算** | 67 | `resolveModelCatalog` · `loadCatalogWithSources` · `PROVIDER_PRESETS` / `MODEL_FAMILIES` · `defaultMaxTokensFor` · `getLiveModelCatalog` / `setLiveModelCatalogRefresher` · `providerAuthMethods` / `beginDeviceCodeAuth` · `providerCatalogRows` / `providerCatalogRowDetail` / `providerPresetById`(#244 F4 族D A-028.17:46 家表的规范折表层 —— 全表不重排、诚实缺席「model id typed in」,cli 目录/web 向导同一份折表)· `TIER_ORDER` / `CC_TIER_ALIASES` / `isTier` / `resolveTierBinding`(A-028.18:档位词表+校验+fail-open 降档派生单源;settings 存储归宿主)· `resolveEntryVision` / `computeDeleteBlockers` / `computeDeleteWarnings`(0.38.0 #318 件③ 上收:Model Hub 供给面的三端公共判定 —— vision 生效值+来源三态、删除断链核(拒删+指路)、删除降级后果(照删但必说)。**零 IO**,读盘那半场留各端;`doc === null` 的两义在调用方分流) | 三层 provider 目录解析(线上 URL → 包内预设 → 用户覆盖)+ per-model `maxTokens` 封顶。线上腿需注入 `CatalogFetchJson`,缺席 ⇒ 整条不启用(`online.reason='no-fetch-port'`);缓存落盘经 `CatalogCachePort` | `src/model/{catalog,catalogLoader,providerAuth,providerPresets,providerCatalog,tierVocabulary}.ts`、`src/liveModelCatalog.ts`、`src/modelBudgetRule.ts`、`src/sessionModelLatch.ts`、`src/effortWire.ts` |
158
158
  | 12 | **workflow 与后台工作视图** | 19 | `projectWorkflowRun` · `createLiveWorkflowSource` · `ensureWorkflowActivityLedger` · `readWorkflowActivityLedger` · `stopWorkflowActivityLedger` · `resetWorkflowActivityLedgers` · `createBackgroundView` · `projectBackgroundView` · `recordWorkflowAgentTaskId` · `agentDisplayStatus` | 活过一个 turn 的长任务读面:workflow run + 跨 session 后台任务归一表(`assistant.tasks` 与 fleet SSE **两源独立降级**) | `src/workflow.ts`、`src/workflowClient.ts`、`src/workflowMonitor.ts`、`src/agentSession/backgroundView.ts`(+ 纯类型 `src/agentSession/contract.ts`) |
159
- | 13 | **座位 IPC 契约** | 33 | `LOCAL_SESSIONS_SPEC` · `SEAT_METHOD_NAMES` · `SEAT_EVENT_TYPES` · `isLocalSessionEvent` · `isToolPermissionRequest` · `toolPermissionRequestId` · `SEAT_VALIDATOR_KEY_COVERAGE` | desktop↔web 座位 IPC 契约的**单一真源**(此前两边各一份、名字零重合 ⇒ 编译器永远不会告诉你它们漂了)。🔴 加 verb 忘了加 `LOCAL_SESSIONS_SPEC` **不报错**:preload 不注册 channel、渲染端读到 `undefined` | `src/seatContract.ts`(**零 import**,纯类型 + 常量 + 纯谓词)。🔴 **证据等级标注(0.42.0,test [5087] 的「语料**种类**缺口」/ cli [5088] 认领件)**:该文件里所有以「CC 如何如何」为形的断言(`212 methods` / `854-channel census` / 方法名逐字保留 / `fQe` 逐字段对照 / 一切 `.vite/build/index.chunk-*.js` 坐标)**证据等级 = 桌面 unpack,本地语料库不可复验** —— 本仓手边可复验的参照语料**只覆盖终端 CLI 形态**的静态产物,拿它去 grep 桌面壳里的符号只会零命中,而零命中在这里**既不证真也不证伪**。复核这些断言**不得**拿本仓语料当反证 |
159
+ | 13 | **座位 IPC 契约** | 34 | `LOCAL_SESSIONS_SPEC` · `SEAT_METHOD_NAMES` · `SEAT_EVENT_TYPES` · `isLocalSessionEvent` · `isToolPermissionRequest` · `toolPermissionRequestId` · `SEAT_VALIDATOR_KEY_COVERAGE` · `SEAT_SEND_MESSAGE_KEY_ORIGINS`(0.62.0:座位载荷**逐键出身**登记 —— 每个位要么写明转投哪个引擎请求位、要么写明为什么不上 wire;常驻门对**装在盘上的** sdk 型面逐名核,详见 §26③)| desktop↔web 座位 IPC 契约的**单一真源**(此前两边各一份、名字零重合 ⇒ 编译器永远不会告诉你它们漂了)。🔴 加 verb 忘了加 `LOCAL_SESSIONS_SPEC` **不报错**:preload 不注册 channel、渲染端读到 `undefined` | `src/seatContract.ts`(**零 import**,纯类型 + 常量 + 纯谓词)。🔴 **证据等级标注(0.42.0,test [5087] 的「语料**种类**缺口」/ cli [5088] 认领件)**:该文件里所有以「CC 如何如何」为形的断言(`212 methods` / `854-channel census` / 方法名逐字保留 / `fQe` 逐字段对照 / 一切 `.vite/build/index.chunk-*.js` 坐标)**证据等级 = 桌面 unpack,本地语料库不可复验** —— 本仓手边可复验的参照语料**只覆盖终端 CLI 形态**的静态产物,拿它去 grep 桌面壳里的符号只会零命中,而零命中在这里**既不证真也不证伪**。复核这些断言**不得**拿本仓语料当反证 |
160
160
  | 14 | **宿主端口与会话槽** | 26 | `installHost` · `installHostFor` · `hostPortMisses(For)` · `DEFAULT_SESSION_KEY` · `hostEnv` · `unrefTimer` · `parseLocaleTag` / `pickUiLanguage`(#244 F4 族D A-028.20:locale tag 手术单源 + UI 语言判定;与 `resolveRegionHint` 双出口成文 —— 语言偏好域 en/zh ≠ 地址可达域 cn/intl/unknown,`zh-Hant` 前者 zh 后者 intl 是设计)· `engineNamespaceKeyFor` / `mergeSessionMapRecord` / `mergeEngineEntry`(A-028.12:会话 id 映射单一键形 + merge 判定;存储经 `SessionMapStorePort` 归端 —— cli 文件锁/原子写,web localStorage)| 进程/端级装配层(settings/fs/queue/timers/session/log/probe),与 per-turn 的 `AdapterContext` **分层**。头注的判定规则:**这个能力每 turn 都会变吗?** 会 ⇒ `ctx`;不会 ⇒ `installHost` | `src/host.ts`、`src/hostEnv.ts`、`src/sessionSlot.ts`、`src/unrefTimer.ts`、`src/env/{localeGeo,localeTag,uiLanguage}.ts`、`src/sessionMap.ts` |
161
161
  | 15 | **控制面与传输** | 99 | `ControlRouter`(+ `ControlSafetyError`)· `makeEngineWireClient` + `resolveWireAuth` · `installEngineWireTarget` · `diagnoseSseIdleTear` / `isSseIdleError` · `attemptActiveRunSelfHeal` + `activeRunBusySignal` + `activeRunSelfHealRow` / `activeRunBusyHeadlessRow` · `kickEngineCapsProbe` / `engineCapTrue` / `invalidateEngineCaps(baseUrl, probe?)`(#307 S25:引擎温切后的 caps 生产失效口 —— kick 自带幂等闸,同 baseUrl 重启后不显式失效就永远读到旧引擎那一版能力位;调用方 = 壳的 respawn/restartEngine。🔴 **推荐两参形**:第二参给替代探测则「推进代际 + 注册新探测」在同一同步块内完成,失效与下一次 kick 之间那个「等待者读到未判」的窗按构造不存在;单参形保留给「只丢缓存、这一刻没有替代探测」的调用方,那种情形下读到未判是诚实结局) · `engineCapValue`(S-131,0.59.0:能力位的**四态**通用读口 `unobserved`/`not_reported`/`null`/`value` —— 既有三口把四种「读不出」全折成 fail-closed 一档,对**放行**问题是对的、对**读面/诊断**问题是错的:`null`(引擎明确说没有,正面事实)会与「老引擎不报」「还没探到」在屏上同形。`value` 位刻意不做形校验,形归各能力位自己的窄读器,详见 §23b)· `projectSqlEngineCapability` / `observedSqlEngine` / `noteEngineCapsForSqlEngine` / `forgetSqlEngineReading` / `sqlEngineDoctorDetail`(S-131,0.59.0:`Capabilities.sql`(server ≥7.60.0)的四态读面**从壳侧上收** —— 归层债,0.58.0 时它长在壳里正是因为包侧没有嵌套对象读口。🔴 **导出名与壳侧那份逐字同名 = drift-lock**;`txnMode: null` 是铸点域内的合法值不是畸形;换代失效口清成未观测而不是留旧值,详见 §23b)· `mapBrainStatusToRetry` · `waitForClaimRelease` + `CLAIM_RELEASED_STATES` / `CLAIM_HELD_STATES` · `atMostOnceFailureClass` / `readSteerDelivery` · `clearRunningChoiceOffer`(Inkglow-1085 P0b①:「Do nothing」登记的清口 —— 端的「重新打开操作菜单」入口;登记在场时 attemptActiveRunSelfHeal 不整卡重弹,not-parked 结局带 `alreadyOffered: true` 判别位,端据此降级渲一行)· `INTERACTIVE_WAY_OUT`(默认出路串单源)· `engineSessionParamFor`(design/285 批 0:`?session=` 派生的 **per-key** 形 —— `hostSessionFor(sessionKey)?.currentSessionId()` + [1501] 空串归一;零参 `engineSessionParam()` = 默认槽兼容层,取值链逐字等价)· `normalizeWirePrincipal`(A-028.10:principal 在场性 trim 原语 —— 全空白=缺席不发头,engineWireTarget 两臂/makeEngineWireClient/壳 livePrincipal 同尺)· `readSessionMemoryStatus` / `classifyMemoryStatusFailure` / `readCaptureOptOut` / `readLastCapture`(S-53 会话记忆姿态读面,0.48.0:失败分诊**码优先**——两个 404 分道 `not_found.session` / `not_found.route`,无码 404 不猜落 failed;五键逐键缺席语义两个合读器,`lastCapture` 三态的判别材料是 `committedCount` 不是本键;IO 归宿主注入 `MemoryStatusClientLike`,详见 §11) · `classifyTurnWireError` / `isWireTransportError` / `isPreStreamDrainingReject` / `isResumeAtRejection` / `drainingRetryDelayMs` / `scenarioDenyFromError` + `WIRE_NETWORK_ERROR_PATTERN`(A-028.11/.13:turn 错误分型判定半场,人话文案与渲染归端)· `resumeRetryLaterFromError`(L-102,0.57.0:resume 族**时间性拒绝**二码的判型半场 —— 这一族里唯一**带得出「等多久」**(`retryAfterSec`)的两个码。🔴 闭集**不是**「哪些码可以等」的名单:同族 `resume.row_recycling` 同样可等(窗口自清)只是没有秒数 ⇒ 本读口返回 `null` 只意味着没命中这两码,详见 §21) · `projectWriteProtectionCapability` / `observedWriteProtection` / `noteEngineCapsForWriteProtection` / `forgetWriteProtectionReading` / `writeProtectionDoctorDetail` / `projectWriteProtectionPosture` / `writeProtectionPostureDetail`(S-138,0.60.0:`Capabilities.writeProtection` (engine ≥7.63.0)的**四态**读面 + operator 面行表 —— 与上面 S-131 那一格**同构同纪律**。🔴 三位刻意不合成一个布尔;`not_reported` **不是**「这台部署没有写保护表」(座位缺席恰是缺省表在岗);逐行 name/kind 走**最小披露**只上 operator 面,详见 §24e)· `projectReadFacePosture` / `readFacePostureDetail` / `readFaceDisagreement`(S-167,0.61.0:operator 面 `diagnostics.wiring.readFace`(engine ≥7.65.0)的窄读器 —— 与上面S-138 那一格**同构同纪律**。🔴 三句「答不出来」逐字互异且没有一句等于「没有 READ 档」;`source` 按开集读;与租户面 `capabilities.readFace` 刻意不合流,只带一个纯比较函数,详见 §25)| 上行通道的**监管**半场(submit / steer / kill / 队列命令定序)+ 传输构造、caps 探测、SSE 断流分诊、**409 active-run 自愈** | `src/controlRouter.ts`、`steering.ts`、`sseIdleTriage.ts`、`retryStatus.ts`、`diagnostics.ts`、`engineWireSdk.ts`、`engineWireTarget.ts`、`src/principalWire.ts`、`src/wireErrorTriage.ts`、`engineSessionParam.ts`、`engineCapsCache.ts`、`liveInitToolFace.ts`、`adapter/activeRunSelfHeal.ts`、`src/sessionMemoryStatus.ts`、`src/writeProtectionCapability.ts`、`src/readFacePosture.ts` |
162
162
  | 16 | **引擎词汇表与包自检** | 59 | `CONFIG_REFUSAL_CODES` / `isConfigRefusalCode` · `STOP_CONFLICT_CODES` · `isInterruptedToolEndCode` · `isRewindFamilyCode` · `CLIENT_VERBS` · `compensationSplitViolations` · `DELEGATION_CAP_CODES` / `isDelegationCapCode` / `DELEGATION_CONCURRENCY_CAP` / `DELEGATION_SESSION_CAP`(0.38.0 #318 件④:core 5.48.0 design/323 委派席位到限**两码,处置不对称禁合并** —— 并发帽=**可等**(兄弟结束即有位)/ 会话累计帽=**等也没用**(这条会话的配额用尽))· `CONFIG_DELEGATION_ENTRY_CAPS`(同批入 `CONFIG_REFUSAL_CODES` 识别表)· `delegationCapDispositionOf` / `MCP_SERVER_REVOKED`(0.39.0 载体到货消费件:core 5.50.0 补 `{ error: code, code }` 孪生拼法后两码真上 `tool_end.errorCode`,0.38.0「先立词不落消费分支」的已知局限自此解除;处置轴 `wait-for-slot` / `reuse-existing-or-await-reap` 机器可读(累计帽=retained-window 帐,行回收配额即回,处置=SendMessage 复用,**非**「换会话/永久耗尽」——0.38.0 段该句系勘误),未知 `delegation.*` 码 ⇒ `undefined`;`mcp.server_revoked` = 操作员 mid-session 吊销 server 后的工具面本地闸(被吊销的 server **名**今天不过 wire 境:detail.server 是进程内位,抬升腿只 lift code——归因渲染候 core 补 typed detail,已点名);载体门 = engine-vocab G3 腿锚 core dist 铸点)· `CAPABILITY_SELF_ORCHESTRATION_REQUIRED`(S-81,server 7.57.0:提交面的 selfOrchestration 准入拒绝码。🔴 **复用码** —— 与其它 `capability.*` 501 同体形而处置不同,消费点必须按**恰等**判、绝不放宽成前缀判;判型与「去键重发一次」归 `src/selfOrchestrationDenial.ts`,详见 §13) · `GATE_PARKED_ERROR_CODE`(0.60.0:「门把这次调用 park 了」的机读码单源 —— `frameRouter` 的连坐/中断判据与 `gateOutcome.isGateParkedToolEnd` 读同一个常量;真机黑盒直证 park 短路帧上**没有 `gate`**,这个码是那一形唯一的信号,详见 §24c)| 三端分臂共用的**去字面化** `errorCode` 词表(病根正是三端各抄一份字面);编译期 verb 闭合门;搬迁补偿登记表 | `src/engineErrorCodes.ts`(计数以 `scripts/public-export-baseline.json` 为准,别手抄;A-028.11/.13 补 `DRAINING_ERROR_CODE`/`SCENARIO_NOT_ALLOWED_ERROR_CODE`/`RESUME_AT_ERROR_CODE_PREFIX`;#318 件④ 补 `delegation.*` 族四位 + `config.delegation_entry_caps`;0.39.0 补三新码消费件三位);S-81 补 `capability.self_orchestration_required` 一位;L-102 补 `resume.*` 时间性拒绝二码 + 闭集 `RESUME_RETRY_LATER_CODES` 三位)、`src/classifierVerdictWire.ts`、`src/compensations.ts`、`src/clientSlice.ts` |
@@ -3852,3 +3852,388 @@ readFaceDisagreement(capFace: unknown, posture: ReadFacePostureView | undefined)
3852
3852
  | `scripts/run-sdk-floor-test.mjs` | 地板 `8.5.0` 与 peer 声明**三位逐位等值**互绑 |
3853
3853
  | `scripts/run-public-surface-test.mjs` | peer 地板 / README 三处逐字核 + 公面导出基线 **861 → 864**(测试钩数未动) |
3854
3854
  | `scripts/run-integration-doc-freshness-test.mjs` | 本节新增坐标在盘 + 入库;§0a/§2/§2b 计数与基线等值 |
3855
+
3856
+ ---
3857
+
3858
+ ## §26 🆕 0.62.0 内容批七件(子代内容两件 · 座位子代两位 · 凭证取值时机 · Esc detach 格 · 终答补差 · 呈前封长收口)
3859
+
3860
+ ### 26a. 本节速览
3861
+
3862
+ | # | 件 | 面 | 破坏性 | 端要做什么 |
3863
+ |---|---|---|---|---|
3864
+ | ① | 子代内容分流补 durable 聚合形(0.62.1 再补工具两臂的幂等闸) | 行为**修复** | 无 | 什么都不用做(自动生效);web 端自守的同款分流**可以退役**,判据见 26b |
3865
+ | ② | 子代内容账本字节预算 | additive 三导出 + 一测试钩 | 无 | 可选:启动时配值;渲染端**建议**接新的 `truncated` 槽位 |
3866
+ | ③ | 座位 lane 子代两位 + 逐键出身表 | additive 两键 + 一导出 | 无 | 想要子代正文/子代续跑的端在 `sendMessage` 里带这两位 |
3867
+ | ④ | 出站凭证位放宽成「串或取值函数」 | additive 型放宽 | 无 | 可选:凭证会变的端把串换成取值函数,不必再重建 client |
3868
+ | ⑤ | Esc 停止判定层补 detach 那一格 | additive 入参键 | 无 | 武装了 detach 的端把 `detachArmed: true` 传进判定口 |
3869
+ | ⑥ | 终答与已上屏正文的补差 | 行为**修复** + additive chrome 臂 | 无 | 转录自动补齐;**建议**接 `result_text_diverged` 臂改口 |
3870
+ | ⑦ | 呈前封长顺序同形四处收口 | additive 一导出 + 行为收紧 | 无 | 端自己有同族铸点的,改接 `capForDisplay` |
3871
+
3872
+ 🔴 七件**全部 additive**:新导出、新可选入参键、新 chrome 臂、新配置口。老宿主一个字节不改照常跑,
3873
+ `peerDependencies` 未动(sdk 地板仍 `>=8.5.0`)。
3874
+
3875
+ ---
3876
+
3877
+ ### 26b. ① 子代内容分流补 durable 聚合形(行为修复)
3878
+
3879
+ **修的是哪一形**:带 `parentToolCallId` 的内容帧属于**子代**,库把它侧信道进子代转录账本、不进主转录
3880
+ (孩子的 token 渲成 leader 的正文是这条腿存在的理由)。而分流判据此前只列了四个**活体**臂
3881
+ (`text_delta` / `reasoning_delta` / `tool_start` / `tool_end`)—— **durable 腿(事件重放)送的是同一段的
3882
+ 聚合形**(`text` / `reasoning`,整段全文,同样带 `parentToolCallId`)。于是重放尾上的聚合帧漏过分流,
3883
+ 一路渲成 leader 自己的 assistant 正文。断线续听与交互续听重放尾都踩这一形。
3884
+
3885
+ **新的输入形**(`SubagentContentEvent`,additive):
3886
+
3887
+ ```ts
3888
+ type: 'text_delta' | 'reasoning_delta' | 'text' | 'reasoning' | 'tool_start' | 'tool_end'
3889
+ text?: string // 聚合两臂的整段全文(刻意不复用 delta 位)
3890
+ ```
3891
+
3892
+ **缺席语义**:`text` 位只在聚合两臂上有意义;缺席/空串 ⇒ 该帧不改变账本(不铸空段)。
3893
+
3894
+ 🔴 **两类语义不可合流**(端若自己也在做同款分流,这是最容易踩的一格):`*_delta` 的语义是**追加**,
3895
+ 聚合帧的语义是**这一段最终是这样**。把聚合帧折成一发增量 ⇒ 已经活体流过、又被重放一遍的段被数**两次**。
3896
+ 库的处置是**吸收**:全文以当前缓冲为前缀 ⇒ 换掉而不是追加(同一条聚合帧重复送达因此**幂等**);
3897
+ 前缀对不上 ⇒ 退回追加、两段都留下(少渲一段比替引擎判定「哪一段才算数」更坏)。段闭合仍由工具帧/收口给。
3898
+
3899
+ **durable 重放的两道幂等闸**(异源复审跟修;端若自己也在做同款分流,这两条一起才够):
3900
+ ①**事件身份** —— 聚合帧的 `eventId` 是精确的幂等键,**跨流也管用**(适配器内那道事件序号去重只在
3901
+ **同一条流**内有效,重连后是新的一条流);②**整段内容** —— 已段闭合上屏过的整段(兜底:上游没给
3902
+ 身份,或本进程这条流是重连后新起的)。另有第三形:被预算**裁过头部**的那一段,缓冲此时只剩尾巴,
3903
+ 单看前缀会判成新内容整只追加回来 ⇒ 判据是「全文以缓冲结尾 ⇒ 同一段、整帧丢弃」。
3904
+
3905
+ 🔴 **工具两臂同样有闸,身份换成 `toolCallId`**(**0.62.1 补**;端若自己也在做同款分流,这一条同样要):
3906
+ 0.62.0 只给聚合两臂上了闸,`tool_start` / `tool_end` **一道都没有** —— 断线重连、durable 腿整轮重放时,
3907
+ 同一次工具调用在子代查看态里排出**两张卡**,结果正文也被记两遍(内容字节预算跟着虚高)。0.62.1 起判据
3908
+ 形与散文两臂相同(先问身份、再问账本),但**身份是 `toolCallId`、账本是查看态 `items` 上那张卡**,
3909
+ **刻意不共用聚合帧那本有界身份记号**:那本账的额度是按「聚合帧只在重放腿上来」定的,而工具帧**活体
3910
+ 也来**,跟着写会把还在屏上的散文身份整窗挤掉 —— 修一个臂的重放去重、弄坏另一个臂的,不算修。
3911
+ 逐条:
3912
+ - `tool_start` 见到「这个 `toolCallId` 的卡已在账」⇒ **不铸第二张**;
3913
+ - 但「卡在账」**不等于**「这一发 `tool_start` 处理过」—— 收口先到时库会合成一张占位卡(名 `Tool`、
3914
+ 参数 `{}`),迟到/重放的 `tool_start` 带的才是真的 ⇒ **就地补齐名字与参数**(不覆盖已落的结果,
3915
+ 字节按差额记账,重放多少次都不虚涨);段边界怎么给见下面那一条;
3916
+ - `tool_end` **先问「卡还开着吗」**:开着就落结果 —— 一张在飞的卡若被任何一道闸拦掉收口,它就
3917
+ **永远在飞**(屏上一直显示在跑),那比多渲一次结果坏得多;
3918
+ - 卡在账、却已不在开着表上 = 结果**早就落过**。这一档**不是**「整帧丢弃」那么简单,因为同一次调用
3919
+ 真的会有**第二对** `tool_start`/`tool_end`:门把调用 park 掉、批复之后恢复执行时,引擎复用
3920
+ **同一个** `toolCallId` 再发一对,第二对带的才是真结果(批复还可能**改过参数**,两次 start 的
3921
+ 参数因此可以不同)。所以判据不是「见过没见过」而是**阶段的先后**;
3922
+ - **阶段水位**:库为每张卡记住它当下承载的那一阶段(已落账的 `tool_start` / `tool_end` 身份),
3923
+ 按 `eventId` 的先后判新旧 —— 比水位**旧或就是同一条帧** ⇒ 整帧丢弃(重放不重复计字节、旧一阶段
3924
+ 不许把屏上的真结果或改后参数盖回去);**更新** ⇒ 后到的才算数,就地替换/更新(按差额记账)。
3925
+ 🔴 **可比性的前提**:`eventId` 是上游铸的**时间可排序**句柄(uuidv7);端若自己造内容帧喂进来,
3926
+ 这一位必须同样时间可排序,否则新旧判不出。**缺席** ⇒ 判不出 ⇒ 退回「后到的落账」(重放送的是
3927
+ 同一份值,落账是空操作)。水位与卡**同生共死**(卡被帽丢掉时一并清掉);
3928
+ - `tool_start` 除了不铸第二张卡,还负责**段边界**:这张卡**还没落过** `tool_start`(= 它是收口先到时
3929
+ 合成的占位卡)时,这一发是它的第一个段边界 ⇒ **先段闭合再补齐名参**;水位已在(重放或下一阶段)
3930
+ ⇒ **不**段闭合,边界在活体那一轮已经给过;
3931
+ - `tool_end` 找不到卡(收口先于开始帧到达)⇒ 合成一张已完成的卡,**不**在那里段闭合 —— 边界由那一发
3932
+ 迟到的 `tool_start` 补做。在收口这一支闭合会把**还在流的**那一段提前切成一条历史 item,每子代帽
3933
+ 配得很紧时它随即变成「最早的内容」被帽整只丢掉,而那正是用户眼前在看的字;
3934
+ - 去重射程严格是**同一次调用**:另一个 `toolCallId` 的卡照常进(子代真的调了两次工具时两张卡都要在)。
3935
+ - 迟到的 `tool_start` 先段闭合、再找卡:段闭合本身可能触发条数裁剪(从头删卡 + 顶上留痕),所以库在 flush
3936
+ **之后**才按 `toolCallId` 定位那张卡;找不到 = 刚被裁掉 ⇒ 按「新的一次」重建(留白①),绝不对着裁剪前的
3937
+ 下标/引用改名补参(否则差额记到留痕槽位上、字节账从此虚高)。
3938
+
3939
+ 📋 **如实留白(工具臂)**:
3940
+ - ①卡在不在账的判据就是 `items` 自己(不另铸卡的记号账)—— 一张卡若已被条数帽/字节帽丢掉,同 id
3941
+ 的帧按**新的一次**来处理:`tool_start` 与 `tool_end` **都还会再来**(整轮重放)时那张卡连同
3942
+ **最新那一阶段**的结果一起补回来;只剩收口帧的话补回来的卡缺参数,**只剩开始帧的话补回来的卡
3943
+ 会显示成在飞**(结果那一发已经过去了,库不留它的内容)。这是刻意的:另开一本卡的记号账会在
3944
+ 帽丢卡之后把重放永久挡住,屏上那一格从此空着且无人知道。
3945
+ - ②`toolCallId` 缺席的工具帧无从去重(库会为它合成一个位置相关的 id)—— wire 上的工具帧恒带这一位,
3946
+ 这一格只对自造帧的宿主成立。
3947
+ - ③收口**先于**开始帧到达的那种乱序里,卡的位置是收口到达的位置(库不把它挪到开始帧那一刻),
3948
+ 但它前后的散文段各自完整、各留一份。
3949
+ 📋 **如实留白**:内容闸的代价是「一条 turn 里出现两段**逐字节相同**的正文时,durable 腿上只留一段」。
3950
+ 取舍明写:每次重连把整轮正文再渲一遍是**必然**发生的用户可见损坏,而同一条 turn 说两遍一模一样的话
3951
+ 是罕见形,且活体腿上两段都在。
3952
+
3953
+ **🔴 web 端退役判据(请逐条核过再退)**:web 端自守的同款分流可以退役,当且仅当
3954
+ ①它消费的是本包 `runStream` 的输出(而不是自己直读 wire);②它没有在分流之外**另外**依赖那些聚合帧
3955
+ (例如自己拿 `text` 帧做进度统计);③它没有对子代账本做第二次写入(两条腿喂同一个账本时,吸收规则
3956
+ 只在库这一侧成立)。三条有任何一条不成立,先在板上说一声再退。
3957
+
3958
+ ---
3959
+
3960
+ ### 26c. ② 子代内容账本字节预算(additive)
3961
+
3962
+ **修的是哪一形**:这本账本此前只有**条数**帽(每条子代 ≤ 200 条 item、≤ 32 条子代),而条数帽在
3963
+ **单条无上限**的前提下不是一个预算 —— 一条工具结果里的整个内联附件、一段几 MB 的模型输出,都能让一个
3964
+ 子代槽位吃到十几 MB 常驻。
3965
+
3966
+ **签名**:
3967
+
3968
+ ```ts
3969
+ interface SubagentContentStoreConfig { maxBytesPerTask: number; maxBytesTotal: number }
3970
+ const SUBAGENT_CONTENT_STORE_DEFAULTS: Readonly<SubagentContentStoreConfig> // 2 MiB / 16 MiB
3971
+ function configureSubagentContentStore(next: Partial<SubagentContentStoreConfig>): void
3972
+ function subagentContentStoreConfig(): SubagentContentStoreConfig // 快照
3973
+ ```
3974
+
3975
+ **单位**:**UTF-8 字节**(按串长度算会让一段 CJK 的真实占用是名义值的三倍)。
3976
+
3977
+ **缺省值的来历(如实)**:对标产品的语料里**没有**对应的「子代转录字节预算」可对齐(查过),这两个数
3978
+ **不假称是 parity** —— 总帽按实测观测到的量级取 16 MiB,每子代帽取总帽的 1/8 = 2 MiB。端按自己的宿主
3979
+ 改配即可;这两个数只保证「不配时不至于无界」。
3980
+
3981
+ **缺席语义**:不调配置口 ⇒ 缺省两位生效。只配一位 ⇒ 另一位保持**现值**(不被悄悄重置成缺省)。
3982
+
3983
+ 🔴 **配置面 fail-loud**:0 / 负数 / 非有限数 / 非整数一律 `throw TypeError`,且拒绝是**原子**的
3984
+ (任何一位不合法 ⇒ 整只拒,既有配置一个字节不动)。一个被静默忽略的预算配置正是这一位要消灭的形。
3985
+
3986
+ 🔴 **配置面第二条闸**:`maxBytesPerTask` **不许高过** `maxBytesTotal`(两位各自合法、合起来自相矛盾:
3987
+ 单条子代能合法地涨到每子代帽,而总帽淘汰**不动当期那条**,于是那一条整只越过了总帽)。判的是**落值
3988
+ 之后**的那一对 —— 只配一位时另一位保持现值,矛盾同样可能是这次配出来的。相等是合法的。
3989
+
3990
+ **越限处置 + 新槽位**:从**最早**的内容开始丢(活体尾巴最后才动),并在账本最前留**恰一条**记录:
3991
+
3992
+ ```ts
3993
+ { kind: 'truncated'; droppedBytes: number; keptFrom: number } // SubagentContentItem 新臂
3994
+ { kind: 'truncated'; slot: string; droppedBytes: number; keptFrom: number } // SubagentViewSlot 新臂
3995
+ ```
3996
+
3997
+ 总帽淘汰**整条最久未用**的子代账本(不做「每条各裁一点」——半截的转录比一条整齐的缺席更难读)。
3998
+
3999
+ 🔴 **渲染端义务**:`planSubagentViewSlots` 现在可能返回 `kind:'truncated'` 槽位。**不认它 = 屏上是一段
4000
+ 无声消失的输出**(用户读到的转录比真实发生的少一截而毫无提示)。渲一行可见的省略说明即可
4001
+ (措辞归端;库只给两个数)。老宿主的 `switch` 若是穷尽形,加一条 case;若已有 `default` 分支则不会崩,
4002
+ 但那一格仍然是**看不见**的。
4003
+
4004
+ **🔴 web 端配值口**:探针那条「单槽十几 MB」的路径,启动时调一次
4005
+ `configureSubagentContentStore({ maxBytesPerTask, maxBytesTotal })` 即可;不必自己在渲染端裁 ——
4006
+ 在渲染端裁只挡得住**上屏**那一份,账本本身还是那么大。
4007
+
4008
+ ---
4009
+
4010
+ ### 26d. ③ 座位 lane 子代两位 + 逐键出身表(additive)
4011
+
4012
+ **修的是哪一形**:两位此前是**结构性缺席** —— 座位载荷是一个闭集接口,不在集里 = 「这一轮要不要看
4013
+ 子代正文 / 要不要保留子代会话」这个决定在这条 lane 上**没有位置可放**(与 `reasoningEffort` 当年同一形)。
4014
+
4015
+ **`SendMessageRequest` 新增两位**(键名与引擎请求位**同名同形**,原样转投):
4016
+
4017
+ ```ts
4018
+ forwardSubagentEvents?: boolean
4019
+ retainSubagentSessions?: boolean | { ttlMs?: number; max?: number }
4020
+ ```
4021
+
4022
+ **缺席语义**:两位缺席 = 不开(父流只有进度帧、子代会话不保留),由引擎默认接管。
4023
+
4024
+ 🔴 **能力位读法(最容易踩的一条)**:`capabilities.forwardSubagentEvents` 说的是「**这个键被受理**」而
4025
+ **不是**「转发已经开着」—— 它是一个恒真的受理位。把它读成后者的消费方会接出一个**永远空着**的子代
4026
+ 转录面,而能力面一直在回答「是」。⇒ 想要子代正文,**每一轮都得自己带这个键**。
4027
+
4028
+ 🔴 **clamp 是收紧不是拒绝**:保留窗口 `ttlMs` 与条数 `max` 过大时,引擎会**悄悄收紧**(窗口上限 24 小时、
4029
+ 条数上限 64),而不是报错。⇒ 渲染端**不许**把自己请求的值当成生效值回显 —— 那是替引擎说了一句它没说
4030
+ 的话;要显示生效值就去读引擎那一面,读不到就诚实留白。
4031
+
4032
+ 🔴 **`retainSubagentSessions` 是「子代续跑」那条路的另一半**:续跑能力位可以是真、路由可以在,调用照样
4033
+ 会被拒 —— 因为保留是**逐 run** 的、且默认关。只看能力位就渲一个「继续这个子代」的入口 = 假 affordance。
4034
+
4035
+ **同批新增导出** `SEAT_SEND_MESSAGE_KEY_ORIGINS`:座位载荷的**逐键出身**登记 —— 每个位要么写明转投哪个
4036
+ 引擎请求位(含 `text → objective` 这种**异名**转投),要么写明为什么不上 wire(`sessionId` 是**同名不同义**
4037
+ 的典型:座位那一位是本地会话句柄,引擎会话 id 由座位记录的 `engineSessionId` 承载)。端可以拿它做
4038
+ 「我这条 lane 上的这个位到底会不会到引擎」的机读对照,不必再读实现。
4039
+
4040
+ ---
4041
+
4042
+ ### 26e. ④ 出站凭证位放宽成「串或取值函数」(additive 型放宽)
4043
+
4044
+ **修的是哪一形**:凭证串是在**构造那一刻**被读走的(底层客户端把它捕获进闭包,此后每一发请求都用那
4045
+ 一份)。引擎被别的会话换代、凭证换新之后,一个长命的 client 会一直拿**旧凭证**发请求,而端唯一的出路是
4046
+ 把整个 client 连同它身上的状态重建一遍。
4047
+
4048
+ **签名**:
4049
+
4050
+ ```ts
4051
+ type WireAuthTokenSource = string | (() => string | undefined)
4052
+ // 放宽的三处(都汇到同一个构造口):
4053
+ EngineWireClientConfig.token?: WireAuthTokenSource | { mode: 'same-origin-relay' }
4054
+ EngineProbeOpts.authToken?: WireAuthTokenSource | undefined
4055
+ WireConfig.authToken?: WireAuthTokenSource // prepare 腿
4056
+ ```
4057
+
4058
+ **取值时机**:取值函数**每发一次请求读一次**;构造期**一次都不读**(构造时读一次只是把快照挪了个位置)。
4059
+
4060
+ **缺席语义**:取值函数返回 `undefined` / 空串 / **抛错**,一律按「这一刻没有凭证」走既有三态 ——
4061
+ 回环地址出**零** Authorization 头(绝不伪造),非回环出 fail-closed 的匿名身份(换一个诚实的 401)。
4062
+ 取值口抛错是 **fail-soft**:请求照发,异常不上抛(一个坏掉的取值口不该让整条 wire 不可用,而且异常里
4063
+ 可能夹带凭证材料)。
4064
+
4065
+ **串形逐字节零行为差**;同源中继声明形(`{mode:'same-origin-relay'}`)不进取值路径(它结构上就没有 token)。
4066
+
4067
+ 📋 **如实留白(同族存量两处刻意不放宽)**:①workflow 活动台账的连线身份**按凭证值比相等**来判「还是不是
4068
+ 同一条连线」,而函数没有值身份 —— 宿主每次渲染新建一个箭头函数会让台账每拍重开一条流;要放宽先给那条
4069
+ lane 一个连线身份口径。②信号路径的 cancel 兜底是**在武装那一刻快照**的,它由信号处理器同步读取,那里
4070
+ 不该调用任意宿主代码。两处都不是遗漏。
4071
+
4072
+ ---
4073
+
4074
+ ### 26f. ⑤ Esc 停止判定层补 detach 那一格(additive 入参键)
4075
+
4076
+ **修的是哪一形**:撕 SSE 的**后果**因 detach 而变。未武装 detach 的车道上,撕流 = 服务端按断连语义当场
4077
+ 收尾那条 run,所以「interrupt 那一发没落地」还有一层兜底;detach 武装之后,撕流只断开**观看**,run 会
4078
+ 一路跑到 turn 结束 —— 此时 interrupt 自身失败(传输失败/超时/未武装)= 用户按下的 Esc **一点效果都没有**,
4079
+ 而屏上没有任何东西会告诉他。
4080
+
4081
+ **签名**(`InteractiveHaltInput` 新增一位):
4082
+
4083
+ ```ts
4084
+ readonly detachArmed?: boolean | undefined
4085
+ ```
4086
+
4087
+ **新的判决词**:`InteractiveHaltReason` 加一员 `'no-verdict-detach-armed'`(闭集,机读)。
4088
+
4089
+ **分支表(七格 → 八格)**:
4090
+
4091
+ | `interruptOutcome` | `parked` | `detachArmed` | 判决 | reason |
4092
+ |---|---|---|---|---|
4093
+ | 缺席(还没打) | 任意 | 任意 | `interrupt` | `first-shot` |
4094
+ | `halted` | 任意 | 任意 | `none` | `halted` |
4095
+ | `refused` + 409 + 升级闭集码 | 任意 | 任意 | `escalate-cancel` | `engine-says-run-level` |
4096
+ | `refused`(其余) | 任意 | 任意 | `none` | `refused-no-escalation` |
4097
+ | `transport` / `unarmed` | `true` | 任意 | `escalate-cancel` | `no-verdict-on-parked-card` |
4098
+ | `transport` / `unarmed` | 其余 | `true` | `escalate-cancel` | `no-verdict-detach-armed` |
4099
+ | `transport` / `unarmed` | 其余 | 其余 | `none` | `no-verdict-not-parked` |
4100
+ | 认不得的形 | 任意 | 任意 | `none` | `unknown-outcome` |
4101
+
4102
+ **缺席语义**:缺席 / 非 `true` 一律按未武装处理(fail-closed,与 `parked` 同尺)。
4103
+ 🔴 **缺席 = 老语义零差**,而且是**证明**出来的:门里带一份上一版判定的参照实现,把全表跑两遍要求零差异。
4104
+ 🔴 `parked` **先判**,所以老宿主(只传 `parked`)的 reason 逐字不变;两者同真时仍报 `no-verdict-on-parked-card`。
4105
+ 🔴 新键**只在「没有判决」那一格**被读:引擎给了判决时判决说了算。
4106
+
4107
+ ---
4108
+
4109
+ ### 26g. ⑥ 终答与已上屏正文的补差(行为修复 + additive chrome 臂)
4110
+
4111
+ **修的是哪一形**:一个 turn 的正文正常是流着上屏的,终帧带的那份是同一段话的全文。断线前已经流过一
4112
+ 部分、重连后终帧带**完整**那份时,「这一轮已经产过 assistant 文本」为真 ⇒ 整条终答兜底被跳过,屏上永远
4113
+ 缺断线期间那一截,而且没有任何东西说少了。
4114
+
4115
+ **四格**(修前只有第一格):
4116
+
4117
+ | 已上屏 vs 终帧 `result` | 处置 |
4118
+ |---|---|
4119
+ | 一个字都还没上屏 | 终帧那份**整段渲**(修前的唯一行为,逐字保留) |
4120
+ | 已上屏是终帧的**前缀** | **只补吐后缀**那一截(相等时零发);上屏总字节 = 终帧那份 |
4121
+ | 终帧是已上屏的**前缀**(引擎侧修剪) | **不动**(屏上不缺任何东西;拿更短的覆盖更长的会抹掉用户已读到的) |
4122
+ | 两份**互相都不是对方前缀** | **零补吐** + 发一个 `result_text_diverged` 事实位 |
4123
+
4124
+ 🔴 **比较的两边必须是同一种东西**(车内异源复审逼出的定谳,端自己做同款补差时必读):终帧那一位在
4125
+ 引擎侧取的是**最后一条** assistant 消息的文本、且出厂 `.trim()` 过 —— **不是**整轮正文的拼接。拿
4126
+ 「本轮全部正文」去比,一个「散文 → 工具 → 散文」的**正常多步回答**会被判成分岔,只差首尾空白的回答
4127
+ 同理。⇒ 左边必须是**当前这条 assistant 消息**已上屏的正文(工具边界重置),相等判定先过一次 `trim()`。
4128
+
4129
+ **新 chrome 臂**:
4130
+
4131
+ ```ts
4132
+ { kind: 'result_text_diverged'; laneProof: LaneProof; committedLength: number; resultLength: number }
4133
+ ```
4134
+
4135
+ 🔴 **宿主消费义务**(可选、fail-soft):把这一拍的转录行**改口** —— 一句诚实的「这一轮的最终答复与上面
4136
+ 流式显示的内容不一致」。**不是补渲一段**:两份对不上时谁才算数只有引擎说得清,把终帧那份接在屏上那段
4137
+ 后面会拼出一段**谁都没说过**的话。不接本臂 = 用户看不到这条披露(不是报错),但**绝不许**把它渲成
4138
+ 「已完成」的正面确认。两个位是**长度**不是内容(库刻意不复述任何一份正文 —— 复述一遍等于给渲染端第三个
4139
+ 版本去挑)。
4140
+
4141
+ ---
4142
+
4143
+ ### 26h. ⑦ 呈前封长顺序同形四处收口(additive 一导出 + 行为收紧)
4144
+
4145
+ **修的是哪一形**:消毒器把每个不可见字符改写成 6 字符的可见转义,而「先按原文截长、后转义」会让承诺的
4146
+ 显示预算**膨胀六倍**(40 字符的纯控制字符原文在屏上占 240 个显示字符)。四处铸点里 READ 档位读器已是
4147
+ 对的,另外三处是反的(写保护姿态行的 `source`、它逐行的 `name`/`kind`、SQL 姿态行的三座)。
4148
+
4149
+ **签名**(共用铸点提到与消毒器同居的底座并**导出**):
4150
+
4151
+ ```ts
4152
+ function capForDisplay(raw: string, max: number): string
4153
+ ```
4154
+
4155
+ 语义:返回值恒是**已消毒**的串,长度 ≤ `max`(可能因避让短于 `max`);`max <= 0` ⇒ 空串。
4156
+ 截点避开两种断裂片段:一枚 `\uXXXX` token 的中间(留半截比整枚不渲更坏 —— 它看起来像一个完整答案的
4157
+ 开头)、一对合法代理对的中间(截断等于人为造一枚裸高位代理项)。
4158
+
4159
+ **行为面**:对**正常读数**(不含控制字符的引擎/数据库回值)四处**逐字节零变化**;只有含控制符的病态值
4160
+ 渲染形收紧。端自己有同族铸点的(先截后转义那一形),改接这一只。
4161
+
4162
+ ---
4163
+
4164
+ ### 26i. 🔴 壳与 web 端换装清单(逐件坐标)
4165
+
4166
+ | 件 | 壳(cli) | web 端 |
4167
+ |---|---|---|
4168
+ | ① | 无(自动生效);续听 durable 尾的两条路径重跑一次冒烟即可 | **可退役**自守分流(退役判据见 26b 三条) |
4169
+ | ② | 启动装配处调一次 `configureSubagentContentStore`(按 TUI 宿主的内存预算);子代查看态渲染加 `truncated` 槽位一行 | 同左;**配值口就是这一个函数**,不要在渲染端裁 |
4170
+ | ③ | 座位 lane 不是壳的主路;壳走的是请求装配口,两位已在引擎请求形上 | `sendMessage` 带 `forwardSubagentEvents: true` 才有子代正文;要子代续跑再带 `retainSubagentSessions`;**不许回显自己请求的 ttl/max** |
4171
+ | ④ | 引擎换代后的凭证换新可以从「重建 client」改成「换值」——把构造口的 `token` 改成取值函数 | 同左(同源中继形的端不受影响) |
4172
+ | ⑤ | Esc 弧调 `planInteractiveHalt` 处补传 `detachArmed`(壳自己知道这条 run 有没有武装 detach) | 同左 |
4173
+ | ⑥ | 转录自动补齐;建议接 `result_text_diverged` 臂改口 | 同左 |
4174
+ | ⑦ | 壳侧同族铸点若是先截后转义,改接 `capForDisplay` | 同左 |
4175
+
4176
+ ---
4177
+
4178
+ ### 26j. 黑盒判据(test 视角;不引用内部实现)
4179
+
4180
+ - **G-1**:开着子代正文转发跑一轮委派,中途断流、按 durable 腿续听 —— 子代说的话**不出现**在 leader 的
4181
+ 转录里,而在子代查看态里**完整**出现一次(不是两次)。
4182
+ - **G-2**:同一轮里让子代先流一段、再重放该段的聚合形 —— 子代查看态里那一段的字节数**等于**该段全文
4183
+ (不是两倍)。
4184
+ - **G-3**:leader 自己的 durable 正文(不带子代归属)照旧出现在主转录里(分流射程没有扩大)。
4185
+ - **G-4**:让一个子代产出远超每子代帽的输出 —— 查看态**仍能读到最近那一段**,且屏上**有一行**说明更早的
4186
+ 输出被丢弃了(有数);账本占用不再随输出线性增长。
4187
+ - **G-5**:给预算配一个 0 或负数 —— **响亮失败**(不是静默按缺省跑)。
4188
+ - **G-6**:座位 lane 不带 `forwardSubagentEvents` 时子代转录面为空;带上之后同一操作有内容 ——
4189
+ 且**能力面在这两次里回答相同**(证明能力位不是开关)。
4190
+ - **G-7**:请求一个超过上限的子代保留窗口 —— 调用**不报错**,而端上显示的生效值**不是**请求值
4191
+ (或诚实留白),两者不许一致。
4192
+ - **G-8**:引擎凭证在两次请求之间换代 —— **同一个** client 的下一发请求带的是新凭证(不必重建);
4193
+ 凭证在任何日志/URL 面上零出现。
4194
+ - **G-9**:武装 detach 的会话里,让 interrupt 那一发打不出去(断网/超时)后按 Esc —— run **真的停了**
4195
+ (不是跑到 turn 结束);未武装 detach 且无审批卡的同款场景**不升级**(行为与上一版逐格相同)。
4196
+ - **G-10**:断线前流过半段答复、重连后终帧带完整答复 —— 屏上最终看到的正文**逐字节等于**终帧那份
4197
+ (不缺尾、也没有重复的前半段)。
4198
+ - **G-11**:构造一个终帧与流式内容**从中途分岔**的场景 —— 屏上**不出现**拼接出来的第三种正文,且转录行
4199
+ **改口**(不是一句「已完成」)。
4200
+ - **G-12**(0.62.1 判据订正,产品字节未动):让引擎/数据库回一个含控制字符的姿态值 —— 诊断行的
4201
+ 显示长度**不超过该处的显示帽**,**行尾没有半截转义序列**,且**转义后仍在帽内的值一个字符都不截**
4202
+ (原样渲成完整的转义序列)。🔴 判据量一律是**转义后的显示长度**,不是原始字符数。
4203
+ 🔴 原文写的是「不超过**同长度良性值**那一行」,那句话在**转义后仍在帽内**的那一档不成立、而且
4204
+ 不成立是**设计**:呈前处理是**先转义、后按转义结果封长**,一个不可见字符渲成 6 个显示字符 ——
4205
+ 那一档一枚都不用截,那一行因此**必然长于**同长度的良性行(良性 1:1)。照原文去实现的唯一办法是
4206
+ 把展示行截到「原始字符数」那么长,而那会把整行诊断截没(实测:5 个控制字符 + 帽 40 ⇒ 空串)。
4207
+ 「不比同长度良性行长」只在**两条输入都长到真的被封长**时成立,那是相对判据的射程,不是承诺。
4208
+ 📐 三档一起验才不漏(帽 40 为例):5 个控制字符 ⇒ 转义后 30,**不截**;7 个控制字符 ⇒ 转义后 42
4209
+ **超帽**,截到 36(6 枚完整转义序列,原文只有 7 个字符也照截);6 个控制字符 + 33 个良性字符
4210
+ ⇒ 恰 40,行尾是完整序列后的良性字符。
4211
+ - **G-13**:一条**正常的多步回答**(散文 → 工具 → 散文)—— 转录里**不出现**任何「最终答复与显示的
4212
+ 不一致」类披露(拿整轮拼接去比会在这里假报)。
4213
+ - **G-14**:活体跑完一整轮(两段散文 + 中间一张工具卡)之后**断线重连**、按 durable 腿整轮重放 ——
4214
+ 子代查看态里每段只出现一次,**那张工具卡也只出现一次、结果只显示一次**,且顺序与活体时相同。
4215
+ 🔴 重连是**新的一条流**,所以这一格必须真的断开重连来做:在同一条流里重放测不到它。
4216
+ 反向一格(去重射程):同一轮里子代真的调了**两次不同**的工具 —— 两张卡都要在,不许被折成一张。
4217
+ 四条边界一起验:①让某张工具卡被字节预算丢掉,再断线重连**整轮**重放(开始帧与收口帧都再来)——
4218
+ 那张卡**连同最新那一阶段的结果**回来(不是空着,不是一张永远显示「在跑」的卡,也不是停在更早
4219
+ 那一阶段的结果上);②让**结果先到、开始帧后到** —— 屏上只有一张卡,且它显示的是真的工具名与
4220
+ 参数(不是占位),结果没被盖掉,这一次工具前后的两段散文各自完整、各留一份(重放之后也还是
4221
+ 一份),而**还在流的**那一段不因为收口帧到达就被提前切成历史;③让一次调用**先被门拦下、批复后
4222
+ 恢复执行**(引擎按同一个调用 id 再发一对开始/收口,批复时**改一下参数**)—— 屏上仍只有一张卡,
4223
+ 显示的是**恢复后**的真结果与**改后**的参数;④随后把被拦下那一阶段的帧原样重放、以及让它**首次
4224
+ 迟到**(乱序)—— 两种都**不许**把真结果或改后参数盖回去(「没见过」不等于「更新」)。
4225
+ - **G-15**:把每子代帽配得比总帽还大 —— **响亮失败**(一个单条子代能整只越过的总帽不是预算)。
4226
+
4227
+ ### 26k. 常驻门
4228
+
4229
+ | 门 | 守什么 |
4230
+ |---|---|
4231
+ | 🆕 `scripts/run-subagent-durable-divert-test.mjs` | ① 聚合形分流:正控/负控/不双份三格/段边界/增量恒追加/**跨工具边界重放两道闸**(事件身份 + 整段内容)/**工具两臂幂等闸**(跨流整轮重放卡恰一张 + 结果恰一次 + 异 `toolCallId` 负控 + 缺事件身份正控 + **帽淘汰后重放补回卡与结果** + **收口先到时迟来的开始帧补齐名参**) |
4232
+ | 🆕 `scripts/run-subagent-content-budget-test.mjs` | ② 字节预算:每子代帽/总帽/截尾留痕(含进渲染计划)/缺省值可触到/配置面 fail-loud 八形/未越限零行为差/**拼接边界代理对不虚账**/**每子代帽 ≤ 总帽**/**被裁过的段重放不重算统计** |
4233
+ | 🆕 `scripts/run-seat-task-request-origin-test.mjs` | ③ 座位载荷逐键出身对 sdk 型**直读**对账 + 子代两位回归钉 + 保留窗口内层两键 |
4234
+ | 🆕 `scripts/run-wire-auth-source-test.mjs` | ④ 取值时机:构造期零读 / 读取次数 = 请求次数 / 改值即生效 / 三态逐格 / 凭证只在 Authorization 头 / 抛错 fail-soft |
4235
+ | 🆕 `scripts/run-result-text-backfill-test.mjs` | ⑥ 补差四格 + 事实位形 + 跨工具边界格 + **正常多步回答零分岔** + **首尾空白零分岔**(真链路驱动,终帧按引擎真形造) |
4236
+ | 🆕 `scripts/run-display-cap-order-test.mjs` | ⑦ 封长顺序四铸点 + 共用铸点两条避让 + 反向参照 + **转义后在帽内不截 / 原文短而转义超帽照截 / 恰好抵帽**三档(G-12 订正后的判据) |
4237
+ | `scripts/run-esc-halt-plan-test.mjs` | ⑤ 新增 G6/G6b 两段(detach 格 + 缺席零差全格穷举) |
4238
+ | `scripts/run-public-surface-test.mjs` | 公面导出基线 **864 → 869**(测试钩 43 → 44) |
4239
+ | `scripts/run-integration-doc-freshness-test.mjs` | 本节新增坐标在盘 + 入库;§0a/§2/§2b 计数与基线等值 |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/client-core",
3
- "version": "0.61.0",
3
+ "version": "0.62.1",
4
4
  "description": "Client-side session runtime shared by every sema human client (TUI / web / desktop): sema wire frames (AgentEvent) -> CC session vocabulary (SDKMessage) with dual-plane output (transcript/chrome), deterministic transcript ids, lane discipline as a type, and the notification/dedup ledgers. Every CC-skin shape is collected here so the wire itself stays neutral. Renamed from @sema-agent/wire-cc-adapter (0.1.x).",
5
5
  "license": "MIT",
6
6
  "type": "module",