@sema-agent/client-core 0.62.1 → 0.63.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.
@@ -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
- | 公开导出面 | **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` —— **别手抄进别处,以该文件为准** |
26
+ | 公开导出面 | **905** 个运行期符号(+ 44 个测试钩;= 工作树当下的值 = **0.63.0 八件 additive、零删除**:`projectServerGateKnobs`/`postureKnobSourceOf`/`postureSourceIsOperatorPinned`/`postureKnobDetail`/`POSTURE_SOURCE_WORDS`(S-178 三根 posture 旋钮读数,详见 §27b)/ `PERSISTED_RULE_BEHAVIORS`/`PERSISTED_RULE_BEHAVIOR_UNKNOWN`/`persistedRuleBehaviorOf`/`persistedRuleBehaviorLabel`/`revokeTargetFromPersistedRule`(三态规则身份,§27c)/ `engineIdentityOf`/`engineIdentityVerdict`/`engineIdentityChanged`/`engineIdentityChangedBy`/`ENGINE_IDENTITY_ANCHORS`(S-179 代际锚,§27d)/ `GATE_DENIED_BY_WORDS`/`gateDeniedByDetail`/`ASK_ORIGIN_WORDS`/`askOriginDetail`(门词汇 +2,§27e)/ `PERMISSION_RULE_ISSUE_CODES`/`RETIRED_PERMISSION_RULE_ISSUE_CODES`/`permissionRuleIssueDetail`(规则 lint 码表,§27f)/ `projectToolRoster`/`toolRosterNames`/`toolShimFromRoster`/`applyToolRosterDelta`(L-161 工具名册,§27g)/ `ENGINE_NOTICE_CODES`/`ENGINE_NOTICE_AUDIENCE`/`noticeAudienceOf`/`engineNoticeInCatalog`/`MCP_INJECTION_DROP_REASONS`/`readMcpInjectionDrop`(L-167 通告码册,§27h)/ `AUTO_MODE_UNAVAILABLE_CAUSES`/`AUTO_MODE_BREAKER_CAUSES`/`classifierUnavailableOf`/`classifierUnavailableDetail`(件⑧ 分类器不可用事实,§27h2);`0.62.0` **869**,那批是五件 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`(**869** 项)。
114
+ > 全集真源 = `scripts/public-export-baseline.json` 的 `names`(**905** 项)。
115
115
  > 本节**不逐名抄**,只给「域 → 承重导出 → 用途 → 实现锚」。承重导出 = 一个端为了让这个域干活
116
116
  > **必须**直接调到的那几个符号;其余是它们的类型、变体与辅助位。
117
117
  > 单一入口:`import { … } from '@sema-agent/client-core'`(`exports` 只有 `.` 一个;
@@ -135,18 +135,18 @@ SDK 核对「本包 import 的每个值级符号仍然导出」「`TaskStats.cos
135
135
  `WorkflowsGateUnknownDenial` 四形**不在**基线里,`src/selfOrchestrationDenial.ts` 对基线贡献
136
136
  **4** 项运行期导出(三个函数 + `SELF_ORCHESTRATION_RETRY_WITHOUT`)。
137
137
 
138
- 869 项的内部构成(帮助端估读表大小):**258** 项是 `SCREAMING_SNAKE` 常量数据表/词汇表
138
+ 905 项的内部构成(帮助端估读表大小):**271** 项是 `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 域,逐域计数之和 = 869)
143
+ ### 2b. 域图(16 域,逐域计数之和 = 905)
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
- | 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` |
149
+ | 3 | **HITL 决断卡链**(§4/§5 主战场) | 165 | `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
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
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` |
@@ -158,8 +158,8 @@ SDK 核对「本包 import 的每个值级符号仍然导出」「`TaskStats.cos
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
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
- | 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
- | 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` |
161
+ | 15 | **控制面与传输** | 113 | `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
+ | 16 | **引擎词汇表与包自检** | 69 | `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` |
163
163
 
164
164
  🔴 **`engineErrorCodes` 的开集纪律**(该文件头注逐字):这些 `ReadonlySet` / 前缀谓词一律是**识别表**,
165
165
  回答的是「我认不认得这个码」,**绝不是**「合法码只有这些」。消费点 `switch` **必须留 `default`**,
@@ -3913,24 +3913,61 @@ text?: string // 聚合两臂的整段全文(刻意不复用 delta 位)
3913
3913
  - 但「卡在账」**不等于**「这一发 `tool_start` 处理过」—— 收口先到时库会合成一张占位卡(名 `Tool`、
3914
3914
  参数 `{}`),迟到/重放的 `tool_start` 带的才是真的 ⇒ **就地补齐名字与参数**(不覆盖已落的结果,
3915
3915
  字节按差额记账,重放多少次都不虚涨);段边界怎么给见下面那一条;
3916
- - `tool_end` **先问「卡还开着吗」**:开着就落结果 —— 一张在飞的卡若被任何一道闸拦掉收口,它就
3917
- **永远在飞**(屏上一直显示在跑),那比多渲一次结果坏得多;
3918
- - 卡在账、却已不在开着表上 = 结果**早就落过**。这一档**不是**「整帧丢弃」那么简单,因为同一次调用
3919
- 真的会有**第二对** `tool_start`/`tool_end`:门把调用 park 掉、批复之后恢复执行时,引擎复用
3920
- **同一个** `toolCallId` 再发一对,第二对带的才是真结果(批复还可能**改过参数**,两次 start 的
3921
- 参数因此可以不同)。所以判据不是「见过没见过」而是**阶段的先后**;
3922
- - **阶段水位**:库为每张卡记住它当下承载的那一阶段(已落账的 `tool_start` / `tool_end` 身份),
3923
- `eventId` 的先后判新旧 —— 比水位**旧或就是同一条帧** ⇒ 整帧丢弃(重放不重复计字节、旧一阶段
3916
+ - `tool_end` 只按 `toolCallId` **现查 `items`**:卡在账(不论开着还是已收口)走同一支、过同一道闸,
3917
+ 卡不在账才合成。**0.62.2 改**:此前「开着」那一支走的是另一张「`toolCallId` → 开着的卡下标」表、
3918
+ 且**无条件**落结果(理由写成「一张在飞的卡被拦掉收口就永远在飞」)—— 同一件事两处真源、两套规矩,
3919
+ 而下面那条真病正是从那一支进来的;
3920
+ - **闸的判据是阶段的先后,不是「见过没见过」**:同一次调用真的会有**第二对**
3921
+ `tool_start`/`tool_end` —— 门把调用 park 掉、批复之后恢复执行时,引擎复用**同一个** `toolCallId`
3922
+ 再发一对,第二对带的才是真结果(批复还可能**改过参数**,两次 start 的参数因此可以不同);
3923
+ - **阶段账**:库为每张卡记住它当下承载的那一阶段(已落账的 `tool_start` / `tool_end` 身份),
3924
+ 按 `eventId` 的先后判新旧 —— 比它**旧或就是同一条帧** ⇒ 整帧丢弃(重放不重复计字节、旧一阶段
3924
3925
  不许把屏上的真结果或改后参数盖回去);**更新** ⇒ 后到的才算数,就地替换/更新(按差额记账)。
3926
+ 🔴 **收口两半都比**(**0.62.2 改**):`tool_end` 除了跟 `end` 比,还要跟当前的 `start` 比 ——
3927
+ 两条独立的流交错时(恢复阶段的 `tool_start` 已到、旧一阶段的 `tool_end` 才姗姗来迟),只比 `end`
3928
+ 的话旧收口会落到新阶段那张**开着**的卡上并把它关掉,新参数配上旧阶段的失败结果,新流若中断这个
3929
+ 错误状态就一直留在屏上。**更新的 `start` 已经建立了新的一阶段,早于它的收口不属于它**;
3930
+ 「避免永远在飞」不构成接受一份已知过期结果的理由 —— 那张卡确实还在跑,显示在飞是**如实**的。
3931
+ ⚠️ **限定:这说的是「本来就在飞的那张卡」**。一发陈旧收口被丢弃**不会**把一张**已经落了结果**的卡
3932
+ 打回在飞 —— 丢弃是整帧不落账,卡上原有的 `output` / `isError` 一个字节不动。所以「卡会一直显示在飞」
3933
+ 只可能发生在**新一阶段的开始帧已到、它自己的收口还没到**的那一段窗口里,而那正是如实的。
3925
3934
  🔴 **可比性的前提**:`eventId` 是上游铸的**时间可排序**句柄(uuidv7);端若自己造内容帧喂进来,
3926
3935
  这一位必须同样时间可排序,否则新旧判不出。**缺席** ⇒ 判不出 ⇒ 退回「后到的落账」(重放送的是
3927
- 同一份值,落账是空操作)。水位与卡**同生共死**(卡被帽丢掉时一并清掉);
3928
- - `tool_start` 除了不铸第二张卡,还负责**段边界**:这张卡**还没落过** `tool_start`(= 它是收口先到时
3929
- 合成的占位卡)时,这一发是它的第一个段边界 **先段闭合再补齐名参**;水位已在(重放或下一阶段)
3930
- **不**段闭合,边界在活体那一轮已经给过;
3931
- - `tool_end` 找不到卡(收口先于开始帧到达)⇒ 合成一张已完成的卡,**不**在那里段闭合 —— 边界由那一发
3932
- 迟到的 `tool_start` 补做。在收口这一支闭合会把**还在流的**那一段提前切成一条历史 item,每子代帽
3933
- 配得很紧时它随即变成「最早的内容」被帽整只丢掉,而那正是用户眼前在看的字;
3936
+ 同一份值,落账是空操作)。🔴 缺席的那一发**若真的改了**卡上那一半的内容,**同拍把对应那一半的
3937
+ 记录清掉**(**0.62.2 改**):旧身份留着就是**替一份不再对应的内容背书** —— 实翻形是「先落新参数 +
3938
+ 真结果(带身份),再收一发无身份的旧参数 / 失败结果,随后正确的那一发**原样重放**被自己的记录
3939
+ 拦掉,卡永久停在旧内容上」。反过来,缺席的那一发**内容一个字节没变**(重放送来的是同一份值)
3940
+ 记录**原样保留**。⚠️ **「同值」的判据量写清楚**:比的是**规范序列化后的串**(工具名 + `JSON.stringify`
3941
+ 过的参数),**不是 wire 上的字节**。两条已知的折叠因此存在且是**有意接受**的:`JSON.stringify`
3942
+ 把 `{x: NaN}` / `{x: Infinity}` 与 `{x: null}` 折成同一份值,`undefined` 值键直接消失 ⇒ 这几形之间
3943
+ 的互换会被读成「没变」。取舍:判据量本就是**展示面**看得见的那一份(卡上渲的就是这个序列化结果),
3944
+ 一个屏上完全一样的重放不该把水位清掉;真要区分它们,得先有一个上屏可见的差别。
3945
+ 不可序列化的入参(循环引用)按空串计 —— 它进不了展示面,也就不算内容变化 —— 它仍然如实描述卡上这份内容,清掉等于顺手把「旧一阶段不许盖新一阶段」那道闸
3946
+ 一起卸了(那时旧 `start` 就能把改后参数盖回原参,而更新的结果还留在卡上)。一句话:**这一半的
3947
+ 记录永远描述卡上这一半现在这份内容,内容没动它就没理由动**。阶段账与卡**同生共死**(卡被帽丢掉时
3948
+ 一并清掉;清不掉的话卡被重建那一轮的收口会被陈旧记录整帧拦下 —— 卡建回来却永远显示在飞);
3949
+ - `tool_start` 除了不铸第二张卡,还负责**段边界**:每张卡记一位「**还欠不欠一次段闭合**」——
3950
+ 欠着(= 它是收口先到时合成的占位卡,而自它出现以来一次段闭合都没发生)⇒ 这一发**先段闭合再补齐
3951
+ 名参**;不欠了 ⇒ **不**段闭合。🔴 这一位问的是「**自这张卡出现以来发生过段闭合没有**」,不是
3952
+ 「这张卡自己切过没有」(**0.62.2**):兑现一条边界的可以是别人(另一张卡的 `tool_start`、
3953
+ `settle`),所以**任何**一次段闭合都把在场每张卡的这一位置上;只记「自己切过没有」的话,那一发
3954
+ 迟到的 `tool_start` 会在别人切完之后**再切一刀**,而这一刀切的是**当前**那条与它无关的活体尾段。
3955
+ 🔴 同样**不是**拿「`start` 身份在不在」代答:身份是可选位,上游不给 `eventId` 时它永远抬不起来 ⇒
3956
+ 每一次重放都会再切一刀。两种代答的坏结局是同一个:还在流的活体尾段被切成历史 item,随后该段的
3957
+ **聚合全文**在空缓冲上吸不到前缀、整只落回缓冲 ⇒ 前缀重复上屏;
3958
+ - `tool_end` 找不到卡(收口先于开始帧到达)⇒ 合成一张已完成的卡,**不**在那里段闭合 —— 边界由随后
3959
+ **第一次段闭合**补做(那一发迟到的 `tool_start` 只是其中一种触发者,见下)。在收口这一支闭合会把
3960
+ **还在流的**那一段提前切成一条历史 item,每子代帽配得很紧时它随即变成「最早的内容」被帽整只丢掉,
3961
+ 而那正是用户眼前在看的字。🔴 但边界的**位置**
3962
+ 在这一刻就记下来(**0.62.2 补**):此刻缓冲里的那一截是「工具之前」那一段,之后再流进来的是
3963
+ 「工具之后」那一段,迟到的 `tool_start` 按这个位置把两段**分开**收。不记的话它只能把整条缓冲当成
3964
+ 一段闭合 —— `AAA` → 收口先到 → `BBB` → 迟到的 start 这种乱序会合成 `AAABBB` 一段,整轮重放送来
3965
+ `AAA`、`BBB` 两段时两道内容闸都不命中,历史与缓冲各留一份。🔴 位置是**缓冲的**属性、**不是那张卡的
3966
+ 私产**,库因此把它记在这条子代的账本上而不是卡上:①兑现它的不一定是那一发迟到的 `tool_start`
3967
+ (别的工具起头、`settle` 同样要切),任何一次段闭合都在**所有**还没兑现的位置上一起切 —— 一条缓冲上
3968
+ 可以同时压着好几条(两张收口先到的卡交错),而闭合一发生缓冲就整只清空,没兑现的位置从此无处可切;
3969
+ ②卡被条数帽/字节帽丢掉**不等于**缓冲被丢掉(帽先丢 item、丢不够才裁缓冲),那截正文一个字都没少,
3970
+ 边界跟着卡消失就等于把「这里有过一次工具」这件事丢了;
3934
3971
  - 去重射程严格是**同一次调用**:另一个 `toolCallId` 的卡照常进(子代真的调了两次工具时两张卡都要在)。
3935
3972
  - 迟到的 `tool_start` 先段闭合、再找卡:段闭合本身可能触发条数裁剪(从头删卡 + 顶上留痕),所以库在 flush
3936
3973
  **之后**才按 `toolCallId` 定位那张卡;找不到 = 刚被裁掉 ⇒ 按「新的一次」重建(留白①),绝不对着裁剪前的
@@ -3945,7 +3982,19 @@ text?: string // 聚合两臂的整段全文(刻意不复用 delta 位)
3945
3982
  - ②`toolCallId` 缺席的工具帧无从去重(库会为它合成一个位置相关的 id)—— wire 上的工具帧恒带这一位,
3946
3983
  这一格只对自造帧的宿主成立。
3947
3984
  - ③收口**先于**开始帧到达的那种乱序里,卡的位置是收口到达的位置(库不把它挪到开始帧那一刻),
3948
- 但它前后的散文段各自完整、各留一份。
3985
+ 但它前后的散文段各自完整、各留一份 —— 包括**后一段先于迟到的 start 到达**那一形
3986
+ (`AAA` → 收口 → `BBB` → 迟到的 start):两段按收口到达那一刻的位置切开,各成一段、各进一条记号。
3987
+ - ④段边界记的是**位置**(收口到达那一刻缓冲的长度),不是缓冲的一份快照 —— 快照能有 MB 级,复制它
3988
+ 等于把那份内容再占一遍而且不进字节账。位置的维护只有三处:缓冲**头部**被字节帽裁 ⇒ 同拍左移;
3989
+ 段闭合 ⇒ 整表兑现后清空;别处缓冲只在尾部增长,前缀恒定。位置万一还是落到缓冲之外(端自造帧的
3990
+ 乱序),库夹回去、退回**整条缓冲当一段**的老形(= 0.62.1 的行为),并且绝不劈开合法代理对
3991
+ (上游把一对代理对拆成两帧送时,记边界那一刻缓冲尾部可能正停在高位代理项上;撞上就把边界退到
3992
+ 那一对之前)。两次段闭合之间最多记 **64** 条边界,满了之后再来的收口不再记(如实:那之后的边界会
3993
+ 丢、两段黏一段;一次段闭合就整表清空,所以这只在「两次闭合之间来一长串收口先到的乱序帧」时才够得着)。
3994
+ ⚠️ **「兑现前后正文逐字节相等」这句话只对非纯空白段成立**:段闭合有一条**既有的独立规则** ——
3995
+ 切出来的段 `trim()` 后为空就**丢弃、不铸空 item**(空白段上屏是噪音)。所以一条边界恰好把一段
3996
+ 纯空白切出来时,拼回去的正文会**少掉那段空白**,而那不是边界维护出的错。写黑盒判据时把语料钉成
3997
+ 非纯空白(本包常驻门 G16⑧ 就是这么做的),否则会去追一个由另一条规则解释的差异。
3949
3998
  📋 **如实留白**:内容闸的代价是「一条 turn 里出现两段**逐字节相同**的正文时,durable 腿上只留一段」。
3950
3999
  取舍明写:每次重连把整轮正文再渲一遍是**必然**发生的用户可见损坏,而同一条 turn 说两遍一模一样的话
3951
4000
  是罕见形,且活体腿上两段都在。
@@ -4218,7 +4267,10 @@ function capForDisplay(raw: string, max: number): string
4218
4267
  那张卡**连同最新那一阶段的结果**回来(不是空着,不是一张永远显示「在跑」的卡,也不是停在更早
4219
4268
  那一阶段的结果上);②让**结果先到、开始帧后到** —— 屏上只有一张卡,且它显示的是真的工具名与
4220
4269
  参数(不是占位),结果没被盖掉,这一次工具前后的两段散文各自完整、各留一份(重放之后也还是
4221
- 一份),而**还在流的**那一段不因为收口帧到达就被提前切成历史;③让一次调用**先被门拦下、批复后
4270
+ 一份),而**还在流的**那一段不因为收口帧到达就被提前切成历史;
4271
+ ⚠️ 这一格里「卡继续在飞」只针对**原本就在飞的那张卡** —— 一发被判陈旧的收口是**整帧丢弃**,
4272
+ 不会把一张**已经落了结果**的卡打回在飞;判据请对着「新一阶段开始帧已到、它自己的收口还没到」
4273
+ 那段窗口写,别对着一张已完成的卡写;③让一次调用**先被门拦下、批复后
4222
4274
  恢复执行**(引擎按同一个调用 id 再发一对开始/收口,批复时**改一下参数**)—— 屏上仍只有一张卡,
4223
4275
  显示的是**恢复后**的真结果与**改后**的参数;④随后把被拦下那一阶段的帧原样重放、以及让它**首次
4224
4276
  迟到**(乱序)—— 两种都**不许**把真结果或改后参数盖回去(「没见过」不等于「更新」)。
@@ -4237,3 +4289,430 @@ function capForDisplay(raw: string, max: number): string
4237
4289
  | `scripts/run-esc-halt-plan-test.mjs` | ⑤ 新增 G6/G6b 两段(detach 格 + 缺席零差全格穷举) |
4238
4290
  | `scripts/run-public-surface-test.mjs` | 公面导出基线 **864 → 869**(测试钩 43 → 44) |
4239
4291
  | `scripts/run-integration-doc-freshness-test.mjs` | 本节新增坐标在盘 + 入库;§0a/§2/§2b 计数与基线等值 |
4292
+
4293
+ ## §27 🆕 0.63.0 内容批八件 · sdk 8.8.0 BREAKING 迁移(posture 旋钮读数 · 三态规则身份 · 代际锚 · 门词汇 +2 · 规则 lint 码表 · 工具名册 · 通告码册 · 分类器不可用事实)
4294
+
4295
+ ### 27a. 本节速览
4296
+
4297
+ | # | 件 | 面 | 破坏性 | 端要做什么 |
4298
+ |---|---|---|---|---|
4299
+ | ① | `serverGates` 三根 posture 旋钮读数(`src/postureKnob.ts`) | 新读器 5 导出 | 🔴 **上游 BREAKING**(wire 换形),本包**新增**面 | doctor/诊断面按新读器渲三行;老 worker 的裸值靠 `legacy` 位分辨 |
4300
+ | ② | 三态规则身份 `behavior`(`hitl/persistedRulesWire.ts`) | additive 5 导出 | 🔴 **上游 BREAKING**(撤销体必填一格) | 撤销一律经 `revokeTargetFromPersistedRule()`;读不出 `behavior` 的行**藏掉撤销入口** |
4301
+ | ③ | `/health` 代际锚(`src/engineIdentity.ts`) | 新读器 5 导出 | 无 | 起 / 重连 / 定期探活时比一次,`changed` 即按换代处理 |
4302
+ | ④ | 门词汇两张表 +2(`src/gateVocabulary.ts`) | 新 4 导出 | 无 | deny 行与 ask 行的那一句改接铸点,别自拼 |
4303
+ | ⑤ | 规则 lint 拒因码表(`src/permissionRuleIssue.ts`) | 新 3 导出 | 无 | `/permissions` 的报错行改接铸点 |
4304
+ | ⑥ | 工具名册 + delta(`src/toolRoster.ts`) | 新 4 导出 | 无 | 名册在场用名册,缺席回落既有词表 |
4305
+ | ⑦ | `engine_notice` 码册 + audience(`src/engineNoticeCodes.ts`) | 新 6 导出 | 无 | 「渲不渲」改按码册判,「给谁看」按 audience 判 |
4306
+ | ⑧ | 「分类器跑不了」的事实读器(`src/autoModeUnavailable.ts`) | 新 4 导出 | 无 | 卡上那句「问你是因为分类器这次跑不了」改接铸点 |
4307
+
4308
+ 🔴 **本包侧七件全部 additive**(新导出、新模块);唯一的强制项是 `peerDependencies` 地板
4309
+ `>=8.5.0` → **`>=8.8.0`**(peer / devDep / README 地板句 / `run-sdk-floor-test.mjs` 的 `FLOOR` 四处
4310
+ 互绑同批抬)。BREAKING 在**上游 wire**,不在本包的公面 —— 本包给的正是消化那两处换形的读器。
4311
+
4312
+ **上游 BREAKING 两处与本包的对应**:
4313
+
4314
+ | 上游换形 | 迁移前 | 迁移后 |
4315
+ |---|---|---|
4316
+ | `ServerWiringGates` 三根 posture 旋钮(server ≥7.67.0 / S-178) | `gates.durableApproval`(裸布尔) | `gates.durableApproval.value`;来源读 `postureKnobSourceOf()`,别直读 `.source` |
4317
+ | `RuleRevokeRequest.behavior` 必填(server ≥7.67.0 / core 7.9.0 #625) | `rules.revoke({rule, scope})` | `rules.revoke(revokeTargetFromPersistedRule(row))`;铸不出体就**别撤** |
4318
+
4319
+ ---
4320
+
4321
+ ### 27b. ① `serverGates` 三根 posture 旋钮的读数(BREAKING 迁移①)
4322
+
4323
+ **上游为什么换形**:一个裸布尔/裸数字答不出运维真正要问的那一问 ——「这台机器**为什么**是这个值、
4324
+ 怎么钉回」。engine 7.67.0 起,单机 turnkey 部署(未设 `REQUIRE_PRINCIPAL`)在旋钮缺席时**派生**出
4325
+ 与多租户不同的默认(耐久审批开、审批窗 5 分钟 → 24 小时)。一次默认翻转必须在某个读面上看得见,
4326
+ 否则没有人会发现它。⇒ 三根旋钮换成 `{value, source, note}`。
4327
+
4328
+ **签名**
4329
+
4330
+ ```ts
4331
+ projectServerGateKnobs(wiring: unknown): ServerGateKnobsView | undefined
4332
+ postureKnobSourceOf(knob: PostureKnobView<unknown> | undefined): string | undefined
4333
+ postureSourceIsOperatorPinned(source: string): boolean
4334
+ postureKnobDetail(
4335
+ knob: PostureKnobView<boolean | number> | undefined,
4336
+ opts: { reachable: boolean; knob: 'durableApproval' | 'streamAskWindowMs' | 'sessionAutoTitle' },
4337
+ ): string
4338
+ POSTURE_SOURCE_WORDS: readonly string[] // ['env','center','posture','engine-default'],顺序即优先序
4339
+ ```
4340
+
4341
+ **输入形**:`GET /v1/diagnostics/wiring` 的**整只响应体**(读器自己进 `serverGates`)。
4342
+
4343
+ **缺席语义(逐条,别猜)**
4344
+
4345
+ - **主锚 `durableApproval` 读不出 ⇒ 整段缺席**。它是这一面上唯一**两代都在**的旋钮;没有主锚的
4346
+ 半段会被当成完整答案。
4347
+ - **旁枝坏形只丢那一根**:另两根各自独立,一根形坏不该把另外两根一起藏起来。
4348
+ - **另两根整键缺席 ⇒ 就是缺席**。🔴 **绝不铸** `300000` / `true` 的默认 —— 那会把「这台 worker
4349
+ 说不出来」渲成「它说了这个值」,正是本面存在要根治的病。
4350
+ - **老 worker(<7.67.0)的裸布尔**:折进同一只读数壳(消费端一套分支读得通)+ 立 `legacy` 位。
4351
+ 🔴 折壳可以,**冒称来源不行**:`PostureKnobView.source` 在 legacy 行上是**形上的占位**,
4352
+ **不是一次观测**。机读一律走 `postureKnobSourceOf()`(它对 legacy 行答 `undefined`);
4353
+ 措辞的 legacy 那一句**逐字不含任何来源词**。
4354
+ - 「运维显式表态过吗」只有 `env` / `center` 两词算数 —— `posture` 派生出来的 `true` **不是**
4355
+ 「有人要求过」(它是部署形状的推论,换台机器就变);表外词(开集逃生口)同样不算。
4356
+
4357
+ **措辞四态**(唯一铸点,端零自拼;四句**逐字互异**):在场 / legacy(报得出值但点明「这台 worker
4358
+ 说不出是谁定的」)/ 未观测(`reachable:false`)/ 不报(响应读到了但这一位读不出来,且**不武断咎为
4359
+ 版本**)。🔴 后两句**一个值都不报** —— 顺口说了 `off`,消费端就会把「读不出」当成「关着」。
4360
+ 数值一律按毫秒渲人话(`24h` / `5m`;除不尽就报毫秒,`1.5h` 比 `90m` 容易读错)。
4361
+
4362
+ ---
4363
+
4364
+ ### 27c. ② 三态规则身份:`behavior` 必填且不给默认(BREAKING 迁移②)
4365
+
4366
+ **上游为什么换形**:规则车道此前只有 `allow`,撤销体按 `(rule, scope)` 两元组寻址就够了。
4367
+ server 7.67.0 / core 7.9.0 起 deny/ask 也进同一个持久店 —— **同一段文本的 `deny` 与 `allow` 是两条
4368
+ 不同的行**。一次瞄准 `allow` 的撤销若被默认成同文本的 `deny`,删掉的是一条本该留着的**拒绝**规则,
4369
+ 而调用方收到的是一个 200。
4370
+
4371
+ **签名**
4372
+
4373
+ ```ts
4374
+ PERSISTED_RULE_BEHAVIORS: readonly RuleBehavior[] // ['deny','ask','allow']
4375
+ PERSISTED_RULE_BEHAVIOR_UNKNOWN: string // '(unknown)'
4376
+ persistedRuleBehaviorOf(rule: unknown): RuleBehavior | undefined
4377
+ persistedRuleBehaviorLabel(rule: unknown): string
4378
+ revokeTargetFromPersistedRule(
4379
+ rule: unknown,
4380
+ opts?: { principal?: string },
4381
+ ): RuleRevokeRequest | undefined
4382
+ ```
4383
+
4384
+ **输入形**:`GET /v1/rules` 列出来的**一行**(`PersistedRule`)。
4385
+
4386
+ **缺席语义**
4387
+
4388
+ - `persistedRuleBehaviorOf()` **按闭集读**(与本包其余开集读口刻意不同):这是**身份**的一格,
4389
+ 读不懂的身份必须判「读不出」,而不是原样当成一个能拿去撤销的态。缺席 / 表外词 ⇒ `undefined`,
4390
+ **绝不补 `allow` 默认**。
4391
+ - `persistedRuleBehaviorLabel()` 是**呈现**口,按**开集**:表外词**原样渲**(引擎加词那天治理面
4392
+ 照样看得见那个词),只有缺席 / 坏形才渲 `(unknown)` —— 它不冒充三态里的任何一个。
4393
+ - `revokeTargetFromPersistedRule()`:身份三元组**逐字回传**(拼法等价但不逐字相同的 `scope` 什么
4394
+ 都匹配不上,而且会换来一个笑呵呵的 `no-op`);只带身份三键 + 可选 `principal`,派生键
4395
+ `source` / `status` **不回传**(导入面会 400)。🔴 `behavior` 读不出 ⇒ **拒铸**。猜一态的代价是
4396
+ 不可逆地删掉另一态的行,拒铸的代价只是治理面少一个按钮 —— 这条不对称就是本次 BREAKING 的全部理由。
4397
+ - `listAllPersistedRules()` **行为一字节未变**:缺 `behavior` 的行**仍进治理清单**(丢行 = 把一条
4398
+ 活规则藏起来,正是该函数拒绝做的事),态那一列渲 `(unknown)`、撤销入口由调用方藏掉。
4399
+ - `CcImportPreview.uncovered.denyAskBuckets` 随「deny/ask 桶现在真的导入」退役;
4400
+ `readCcImportRedeemCounts()` 不读那一键,**零改**。
4401
+
4402
+ ---
4403
+
4404
+ ### 27d. ③ `/health` 的三只代际锚
4405
+
4406
+ **为什么值得一件**:`/health` 是一台 worker **唯一免凭证**的门、心跳恒绿 ⇒「另一个宿主把这台共用
4407
+ 引擎重启了」此前只能靠某个**带凭证**的请求先撞上 401 才被发现,而那条路把一次重启误读成网络故障
4408
+ (处置相反:一个要重新握手,一个只需重试)。server 7.67.0 起 `/health` 无条件带
4409
+ `startedAt`(这个**进程**自己的起点,epoch ms)。
4410
+
4411
+ **签名**
4412
+
4413
+ ```ts
4414
+ engineIdentityOf(health: unknown): { pid?: number; instanceId?: string; startedAt?: number }
4415
+ engineIdentityVerdict(a: unknown, b: unknown): 'changed' | 'same' | 'unknown'
4416
+ engineIdentityChanged(a: unknown, b: unknown): boolean
4417
+ engineIdentityChangedBy(a: unknown, b: unknown): 'startedAt' | 'instanceId' | 'pid' | undefined
4418
+ ENGINE_IDENTITY_ANCHORS: readonly ['startedAt', 'instanceId', 'pid']
4419
+ ```
4420
+
4421
+ **输入形**:`/health` 的 200 体(整只;读器自己挑三只锚)。
4422
+
4423
+ **缺席语义**
4424
+
4425
+ - `engineIdentityOf()` **恒返回一只读数对象**(读不出任何一格时是 `{}`),不返回 `undefined` ——
4426
+ 调用方问的是「哪几只锚这次答得出来」,「这次有没有响应」是它自己的探测腿知道的事。
4427
+ 三格**逐格独立**:一只形坏只丢那一格。
4428
+ - **判词是三态,不是布尔**。一个布尔把「没换」与「判不出」压进同一个 `false`,而这两件事的下一步
4429
+ 相反。`engineIdentityChanged()` 保留但语义写死:**`true` 是一句断言,`false` 不是**。
4430
+ - 🔴 **锚交集为空 ⇒ `unknown` 而不是 `same`**:老 worker 只报 `pid`、新 worker 只报 `startedAt` 时,
4431
+ 「没发现不同」是因为**没得比**,不是因为它没换。
4432
+ - 🔴 **任一**在两侧都在场的锚不同 ⇒ `changed`,**不让 `startedAt` 独裁** —— 上游声明逐字写着
4433
+ `instanceId` 与 `startedAt` **刻意不合并**(一个答「是不是同一条命」,一个还答「从何时起」);
4434
+ 只看 `startedAt` 会把「`startedAt` 同而 `instanceId` 不同」判成同一条命。`ENGINE_IDENTITY_ANCHORS`
4435
+ 的优先序**只**决定 `engineIdentityChangedBy()` 报哪一只。
4436
+ - **单侧在场的锚不是反证**:一侧有 `startedAt` 另一侧没有,只说明其中一台答不出这只锚;只要另有
4437
+ 一只锚可比且相同,判词照给 `same`。
4438
+ - **归层**:「换代之后要做什么」(丢连接、重握手、提示用户)是**宿主的状态机**,不在本包。
4439
+
4440
+ ---
4441
+
4442
+ ### 27e. ④ 门词汇两张表 +2 与唯一措辞铸点
4443
+
4444
+ core 7.9.0 各加一词:`DeniedBy` **+`persisted_rule`**(这个人自己名下的常驻 `deny` 行拒的;它是
4445
+ `org` 的**个人店同胞** —— 同属「可以否掉一次人已给过的批准」的复查层)⇒ **九词**;
4446
+ `AskOrigin` **+`rule_store_unavailable`**(规则店这次读不出来 ⇒ deny/ask 行执行不了 ⇒ fail-closed
4447
+ 转人问;`org_unavailable` 的个人店同胞)⇒ **十一词**。
4448
+
4449
+ **签名**
4450
+
4451
+ ```ts
4452
+ GATE_DENIED_BY_WORDS: readonly DeniedBy[] // 九词,顺序同源
4453
+ gateDeniedByDetail(deniedBy: unknown): string
4454
+ ASK_ORIGIN_WORDS: readonly string[] // 十一词,顺序同源
4455
+ askOriginDetail(origin: unknown): string
4456
+ ```
4457
+
4458
+ **🔴 两张表的开闭各按其出处,兜底句因此逐字分家** —— 这不是风格问题,是两条不同的事实:
4459
+
4460
+ - **`DeniedBy` 在这条 wire 上是真闭集**:引擎的 `screenGateOutcome` 把「`deniedBy` 出闭集」列为记录
4461
+ **缺陷**,而 server 对有缺陷的门记录**整条不上帧** ⇒ 一个词表外的 `deniedBy` **结构上到不了消费
4462
+ 端**,它到达的形式是 `gate` **整键缺席**。真读到一个表外词 ⇒ 那一句说的是「**这条门记录是坏的**」。
4463
+ - **`AskOrigin` 是真开集**:`tool_approval` 帧上的 `origin` 在 server 侧**只判非空串、不判成员**
4464
+ ⇒ core 加词当天一个**合法**的帧就带着表外值到达。那一句说「**这个词比这一端新,照常回答这只
4465
+ ask**」——渲成坏记录会让用户去查一个根本不存在的故障。
4466
+
4467
+ 另有三对**刻意分家**的同胞不许合并成一句:`org_unavailable` / `rule_store_unavailable`(组织店 vs
4468
+ 个人店,下一步找的人不同)、`shell_gate_tighten` / `safety_tighten`(粗粒度 `shellGate` 教条 vs
4469
+ 调用的显式事实:egress 标 / 不可逆标 / 写保护写)、`org_rule` / `ask_rule`。
4470
+
4471
+ `GateOutcome.origin` 与 `AskOrigin` 是**同一张词表的两个面**(sdk 顶注逐字),用同一只铸点。
4472
+
4473
+ ---
4474
+
4475
+ ### 27f. ⑤ 规则 lint 拒因码表
4476
+
4477
+ **上游缺口如实记**:sdk 8.8.0 对 `PermissionRuleIssue.code` **既无 spec schema 也无导出型**(sdk 自己
4478
+ 的 README 在「如实登记(本版没做的)」一节写了这一格)⇒ 本包没有可 `import` 的码表,按引擎真字节
4479
+ 铸一张。🔴 **候上游导出即整只退役**改成从 sdk 取;下游的反漂移锚:`dts-pattern` 盯 sdk 的
4480
+ `PermissionRuleIssue` 上出现 `code` 声明。
4481
+
4482
+ **签名**
4483
+
4484
+ ```ts
4485
+ PERMISSION_RULE_ISSUE_CODES: readonly string[] // 十五码
4486
+ RETIRED_PERMISSION_RULE_ISSUE_CODES: readonly string[] // 记账座,查码别查它
4487
+ permissionRuleIssueDetail(code: unknown, message: unknown): string
4488
+ ```
4489
+
4490
+ **输入形**:引擎装配规则表时对每条不接的规则出的 `{rule, code, message}` 里的后两格。
4491
+
4492
+ **措辞按前缀两族分家且逐字互斥**:`invalid.*` = 这条规则**写错了**(下一步改规则)/
4493
+ `unsupported.*` = 规则本身合法但**这条车道不接**(换写法、或落到规则店而不是策略折叠)。上游把它们
4494
+ 分成两个前缀,正是因为「以为挡住了」比没挡更危险。
4495
+
4496
+ **缺席语义**:表外码 ⇒ 那一句说「这个码比这一端新」,原样带上那个码,**并照样带上 `message`**
4497
+ (码读不懂不代表引擎那句话读不懂,而那句话往往才是能动手改的那条信息)。`message` 是**散文不是码**:
4498
+ 只渲染、呈前**先转义后封长**,绝不做等值匹配、绝不当分类位。坏码 / 坏 `message` 一律**不抛**
4499
+ (这条路径跑在「用户规则写错了」那一刻,读器再抛一次只会把一个说得清的错误换成一个说不清的崩)。
4500
+
4501
+ ---
4502
+
4503
+ ### 27g. ⑥ 工具名册与 `tool_roster_delta`
4504
+
4505
+ **病形**:「这条腿到底挂了哪些工具、每一只是什么面」此前只能猜 —— `liveInitToolFace.ts` 那三张词表是
4506
+ 一次 tap 实测按当时 pin 的引擎版本定稿的**估计值**,换引擎、换场景、换 env 门就偏。名册是引擎
4507
+ **自己说的**。
4508
+
4509
+ **签名**
4510
+
4511
+ ```ts
4512
+ projectToolRoster(manifest: unknown): ToolRosterView | undefined
4513
+ toolRosterNames(roster: ToolRosterView | undefined): readonly string[]
4514
+ toolShimFromRoster(entry: unknown): ToolShim | undefined
4515
+ applyToolRosterDelta(held: ToolRosterView | undefined, delta: unknown): ToolRosterDeltaApplication
4516
+ ```
4517
+
4518
+ **输入形**:`wiring_manifest` 帧的**整只 manifest**(读器自己进 `tools`);delta 是
4519
+ `tool_roster_delta` 帧的 `delta` 段。
4520
+
4521
+ **缺席语义**
4522
+
4523
+ - 🔴 **绝不半张名册**(照抄引擎自己的纪律):引擎的 fail-open 台账逐字写着「判不出形 ⇒ 那一段缺席 /
4524
+ 那一帧不发,绝不半张脸上 wire」,并写明**反方向更坏** ——「消费端会把读不出的行当成**这只工具
4525
+ 没挂**」。⇒ 任一行读不出、或 `count` 与真实行数对不上(core 在铸点 run-time 断言过)⇒
4526
+ **整只 `undefined`**。
4527
+ - **例外只有面**:一行的 `pathTarget` / `renderHints` 自己形坏 ⇒ 只丢那一格,行还在 —— 面不是身份。
4528
+ - `digest` 判据是**恰 16 位小写 hex**(它是重同步的锚,放宽会让一个坏锚看起来能用)。
4529
+ - **空名册是正面事实**(这条腿一只工具都没挂),照读成 0 行。词表(`source` / `effect` / `family` /
4530
+ `access`)全按**开集**读。
4531
+ - **只在 effective 半场**(live 帧)才有;诊断端点的 `static` 半场恒无 —— 那里的缺席是**结构性的**,
4532
+ 不是「这台引擎太老」。
4533
+ - `toolShimFromRoster()`:🔴 面**只从名册的行取,绝不从工具名猜**(一个叫 `Bash` 的行不一定是 shell
4534
+ 家族);🔴 轴(`egress` / `irreversibility` / `effect`)读不出时**留空**,**绝不**填 `false` /
4535
+ `'never'` —— 那会把「不知道」渲成「安全」,是这条面上最贵的一种谎。
4536
+ - 🔴 `applyToolRosterDelta()`:**`fromDigest` 对不上不是拒绝**(契约里唯一的硬话)—— 携带的整只名册
4537
+ **无论如何都是新状态**,只有 `summary` 变得不可用。⇒ 名册**照换** + 立 `skew` 位 + 丢 `summary`。
4538
+ 拒绝换会让消费端永远抱着一份过期名册。手上没册(首帧即 delta)同样算 `skew`(比不出来 ≠ 对上了);
4539
+ 帧判不出形 ⇒ **什么都没发生**、原样交回入参(清空会把一次读不懂的帧变成「所有工具都没挂」)。
4540
+ `summary` 三张名单必须**互不相交**;三张全空是**正面事实**(字节相同的 re-mint 也会发帧)。
4541
+ - **本批不删** `liveInitToolFace.ts` 那三张表(名册只在 effective 半场铸、且 ≥7.9.0 才真推,回落还得
4542
+ 留着)。退役条款登记在那个文件的头注:**名册恒在场的引擎版本成为支持底线即删表**,并连
4543
+ `runnerFaceForEnv` 的 env 门一起删。
4544
+
4545
+ ---
4546
+
4547
+ ### 27h. ⑦ `engine_notice` 码册与 audience 表
4548
+
4549
+ **判据不在这一端**:一条引擎通告该不该占用户的注意力,正确判据**不是**「这一端的渲染表有没有那一
4550
+ 格」(那会随各端施工进度晃),而是**上游有没有把这个码铸进它的成文码册**。上游导出码册的理由逐字
4551
+ 写着「a downstream table with a row missing … is a mechanically detectable disagreement rather than
4552
+ an argument」。audience 答另一问:这条事实**说给谁听**。
4553
+
4554
+ **签名**
4555
+
4556
+ ```ts
4557
+ ENGINE_NOTICE_CODES: readonly string[] // 四十八码,顺序同源
4558
+ ENGINE_NOTICE_AUDIENCE: Readonly<Record<string, 'user' | 'operator'>>
4559
+ noticeAudienceOf(code: unknown): 'user' | 'operator'
4560
+ engineNoticeInCatalog(code: unknown): boolean
4561
+ MCP_INJECTION_DROP_REASONS: readonly string[] // 四个原因词
4562
+ readMcpInjectionDrop(notice: unknown): McpInjectionDropFactsView | undefined
4563
+ ```
4564
+
4565
+ **输入形**:`engine_notice` 帧(`{code, message, detail}`)。
4566
+
4567
+ **缺席语义**
4568
+
4569
+ - 码册**内**的码 = 引擎作为治理事实铸出来的,即使这一端还没有专属文案也该让人看见(通用行);
4570
+ 码册**外**的码 = 转发进同一个水槽的宿主/适配器噪声,呈现面 fail-closed(只落 debug)。
4571
+ - audience **两值而已**(`user` / `operator`)—— 上游刻意不设第三值:「这条通告能不能路由到某个
4572
+ 会话」不是 audience,而是 `sessionId` 在不在场(**逐次发射**的事实)。
4573
+ - 🔴 表外码**保守判 `operator`**:把一个读不懂的码推给终端用户是最坏的猜法(他既看不懂也无从
4574
+ 下手),漏给运维看一条只是让他晚一点知道。
4575
+ - 🔴 `engineNoticeInCatalog()` **认原始值** —— 消毒器会把一整类字符换掉,拿清洗值查表等于让
4576
+ `memory\nsession_polluted` 这种脏码冒充册内码。消毒只进文案,不进判据。
4577
+ - `mcp.injection_dropped` 是 **user** 行:掉的是用户自己的 `.mcp.json` 条目,补救(改名 / 修字段 /
4578
+ 删条目)也在他手上,operator 通道够不到他。
4579
+ - `readMcpInjectionDrop()` **只认自己那一个码**;`sessionId` / `server` / `reason` 三格缺一 ⇒ 整只
4580
+ 缺席(core 在**铸点**对同样三格抛 `TypeError`,本包在**读点**判缺席而**不抛** —— 读点抛只会把一条
4581
+ 读不懂的通告升级成一次崩);`reason` **按闭集读**(它是消费端分支的键)。
4582
+ - 🔴 **单铸律**:上游对这条通告立了 `settlement.single_mint` —— 宿主只供事实,**core composes** 码字、
4583
+ audience 行与句子。⇒ 本包**一句措辞都不抄**,转录**直接用引擎给的 `message`**。想要自己的措辞就
4584
+ 键在 `detail.reason` 上,**绝不**对 `message` 做等值匹配。
4585
+
4586
+ ---
4587
+
4588
+ ### 27h2. ⑧ 「这只 ask 是因为分类器跑不了才问人」的事实(core 7.10.0 #616)
4589
+
4590
+ auto 模式下门会就一只 ask 去咨询分类器;**没跑成**时引擎在这只 ask 上盖一格
4591
+ `classifierUnavailable: { cause }` —— 卡上那句「问你是因为分类器这次跑不了(超时)」的事实来源。
4592
+
4593
+ **签名**
4594
+
4595
+ ```ts
4596
+ AUTO_MODE_UNAVAILABLE_CAUSES: readonly string[] // ['error','timeout','breaker_open']
4597
+ AUTO_MODE_BREAKER_CAUSES: readonly string[] // ['error','timeout','parse_error']
4598
+ classifierUnavailableOf(ask: unknown): { cause: string } | undefined
4599
+ classifierUnavailableDetail(cause: unknown): string
4600
+ ```
4601
+
4602
+ **输入形**:一只 `AskRequest`,**或**一条 durable park 行的 `PendingAction.tool_approval` 载荷 ——
4603
+ 两处**键路同形**,所以是**同一只读器**(各写一份就是两份台账各漂各的)。
4604
+
4605
+ **缺席语义**
4606
+
4607
+ - 🔴 **它是显示元数据,不是裁决位**:没有任何东西读它来决定放行与否(引擎在门上已经判完了)。
4608
+ 消费端只拿它渲一句话,**绝不**据它翻转任何裁决。
4609
+ - 🔴 **缺席 ≠「分类器跑成了」**:绝大多数 ask 根本没咨询过分类器(部署没武装 auto、或这只 ask 走的
4610
+ 是别的门)。缺席只意味着「这只 ask 上没有这条事实」。
4611
+ - 🔴 **两条 cause 轴,不是一张表**:`AUTO_MODE_UNAVAILABLE_CAUSES` 答「这一轮分类**为什么没跑成**」
4612
+ (这是 `classifierUnavailable.cause` 的值域);`AUTO_MODE_BREAKER_CAUSES` 答「**熔断闩为什么合上**」。
4613
+ 两集交于 `error`/`timeout`,各有一个独占成员 —— 合成一张表就把两条轴的差别扔了。
4614
+ - 🔴 **`parse_error` 只在熔断轴上**:上游顶注逐字「`parse_error` stamps nothing」—— 分类器
4615
+ **跑了并且答了**,只是答在契约之外,那是**另一句话**。读器按 unavailable 那一集收窄,读到
4616
+ `parse_error` 判**缺席**;把它读成一个「没跑成」的成因,就是替引擎编一件它明说没发生的事。
4617
+ 措辞表仍为它备了一句(给**渲熔断面**的消费端用同一个铸点),那一句逐字说的是「答在契约之外」。
4618
+ - 表外词 / 坏值 ⇒ 一句**兜底**:仍告诉用户这只 ask 是分类器那条腿引出来的,但不冒充四句里的任何
4619
+ 一句,并原样带上那个词供运维追问上游。
4620
+
4621
+ **为什么是镜像**:这两张表与那一格在 sdk 8.8.0 与 agent-types 上**都还没有**(亲验两棵树全树零命中),
4622
+ 唯一出处是 `@sema-agent/core`;而 core **不是本包消费者的依赖** ⇒ 与 ⑦ 同一条处置(镜像 + 门对实装
4623
+ core 双向对账)。**候上游导出即整只退役改成从 sdk 取**。
4624
+
4625
+ ---
4626
+
4627
+ ### 27h3. `WorkflowRunStatus` 的 `parked`:**零改**,原词透出
4628
+
4629
+ core 7.10.0 给 workflow run 的状态开放联合加了 `parked`。本包**一个字节都不用改**:
4630
+ `src/workflow.ts` 的两处读法本来就把 `status` 当**开集字符串**原样透出(`typeof === 'string' &&
4631
+ length > 0` 即收),从不窄读成枚举 —— 所以 `parked` 自动逐字到达端上,端按自己的行渲染即可。
4632
+ 📋 **如实记一笔已知形**(不是本批引入,也不在本批射程):`readWorkflowRunLine` 在 `status` 读不出时
4633
+ 回落 `'completed'` —— 那是把「说不出」渲成「完成了」。它先于本批存在,登记在此供后续独立处置。
4634
+
4635
+ ---
4636
+
4637
+ ### 27i. 🔴 壳与 web 端换装清单(逐件坐标)
4638
+
4639
+ | 件 | 壳(cli 1.0.106) | web 端 |
4640
+ |---|---|---|
4641
+ | ① | `doctor` 的 Permissions 段加**三行**:`postureKnobDetail(view.durableApproval, {reachable, knob:'durableApproval'})` / 同形的 `streamAskWindowMs` / `sessionAutoTitle`;`reachable` 由壳自己那次 operator 探测的结果给。🔴 分类读一律走 `postureKnobSourceOf()`,**别直读** `.source` | 诊断面同左;无 operator 权限的部署上这一面整段不渲(端点本身 operator-gated) |
4642
+ | ② | `/permissions` 治理表加「态」一列 = `persistedRuleBehaviorLabel(row)`;撤销按钮改经 `revokeTargetFromPersistedRule(row)` —— **铸不出体就把那一行的撤销入口藏掉**(别退化成两元组撤销) | 同左 |
4643
+ | ③ | 三条 uplift 锚 `*-startedat-*`:①引擎 spawn 后的首探记下 `engineIdentityOf(health)`;②重连 / 定期心跳时 `engineIdentityVerdict(prev, now)`,`changed` ⇒ 走既有的 `engineReplacedGate`;③`unknown` **不许**走「没换」那条臂(它要么再探一次、要么按最坏情况重握手) | 同左;web 端没有 spawn 半场,只有 ②③ |
4644
+ | ④ | 门记录那一行:`gateDeniedByDetail(gateDeniedBy(g))`(九词);审批卡的出身行:`askOriginDetail(frame.origin)`(十一词)。壳侧同族自拼句子一律退役 | 同左 |
4645
+ | ⑤ | `/permissions` 的规则报错行:`permissionRuleIssueDetail(issue.code, issue.message)` | 同左 |
4646
+ | ⑥ | `wiring_manifest` 帧到达时 `projectToolRoster(manifest)`;**在场**则自报面与工具卡 shim 走 `toolRosterNames()` / `toolShimFromRoster()`,**缺席**才回落 `liveInitToolFace` 三表。`tool_roster_delta` 帧走 `applyToolRosterDelta()`,`skew` 时**记一次偏斜并按快照重同步**(别拿 summary 算位置) | 同左 |
4647
+ | ⑦ | `engine_notice` 呈现闸改按 `engineNoticeInCatalog(code)`;投递面按 `noticeAudienceOf(code)` 分流(`user` 上屏 / `operator` 落运维面)。壳自己的 `CORE_ENGINE_NOTICE_CODES` / `PENDING_ENGINE_NOTICE_CODES` 两表**可以退役成一张「明拒表」**(本包这张是上游码册的机械镜像,壳只需保留它**刻意不渲**的那几个码)。🔴 `mcp.injection_dropped` 的转录**直接用 `message`**,别自拼 | 同左 |
4648
+
4649
+ | ⑧ | 审批卡上「为什么问我」那一行:`classifierUnavailableDetail(classifierUnavailableOf(ask)?.cause)`;durable park 行的卡走**同一只**读器(键路同形)。🔴 这一格是**显示元数据**,别据它翻转任何裁决;缺席**不许**渲成「分类器跑成了」 | 同左 |
4650
+
4651
+ ---
4652
+
4653
+ ### 27j. 黑盒判据(test 视角;不引用内部实现)
4654
+
4655
+ - **G-1**:对一台 **≥7.67.0** 的 worker 打开诊断面 —— 三根旋钮各渲**一行**,每行同时有**值**、
4656
+ **来源词**与一句**指路话**;把机器 env 里的 `DURABLE_APPROVAL` 钉成 `false` 重启后再看,那一行的
4657
+ 来源词变成 `env`(部署主权恒赢)。
4658
+ - **G-2**:对一台 **<7.67.0** 的 worker 打开同一面 —— 耐久审批那一行**报得出值**,但**不出现任何
4659
+ 来源词**,且逐字说明这台 worker 说不出是谁定的;另两行**不出现**(不是渲成 5 分钟 / 开着)。
4660
+ - **G-3**:让诊断请求**打不通**(断网 / 403)—— 三行都渲「未观测」,**一个值都不报**;这一句与
4661
+ G-2 那一句**逐字不同**。
4662
+ - **G-4**:在单机 turnkey 部署上**什么都不设** —— 审批窗那一行渲 `24h`(不是 `86400000ms`),
4663
+ 来源词是 `posture`;而「运维显式表态过吗」这一问的答案是**否**。
4664
+ - **G-5**:同一段规则文本同时存在 `deny` 与 `allow` 两行 —— 治理面**两行都在**、态那一列各显其态;
4665
+ 撤销其中一行之后**另一行仍在**(用 `GET /v1/rules` 对账)。
4666
+ - **G-6**:构造一台**只**报 `allow` 行、不报 `behavior` 的旧 worker —— 那一行**仍出现在治理清单里**
4667
+ (不许消失),态那列显示的**不是三态里的任何一个**,且**没有可用的撤销入口**。
4668
+ - **G-7**:引擎在同一端口上重启 —— 客户端在下一次心跳内报出「换了一条命」,**不需要**先发生一次
4669
+ 失败的带凭证请求。
4670
+ - **G-8**:把客户端指向一台 **<7.67.0** 的 worker 并重启它,而两次 `/health` 的 `pid` 恰好相同 ——
4671
+ 客户端**不许**报「同一条命」;它要么报「判不出」,要么按最坏情况重握手。
4672
+ - **G-9**:让一次调用被一条**这个人自己的**常驻 `deny` 规则拒 —— 屏上那一句点名「一条常驻规则」,
4673
+ 与「组织规则拒」「部署策略拒」**逐字不同**。
4674
+ - **G-10**:让规则店在一次调用期间读不出来 —— 那次调用**转成问人**(不是静默放行),而屏上那一句
4675
+ 与「组织策略读不出来」**逐字不同**。
4676
+ - **G-11**:给部署一条 `Edit(src/**)` 的 **allow** 规则 —— 启动**响亮拒绝**(不是静默丢规则),
4677
+ 而屏上那一句说的是「这条车道不接」而**不是**「这条规则写错了」;再给一条括号写坏的规则 ——
4678
+ 那一句反过来。
4679
+ - **G-12**:对一台 **≥7.9.0** 的 worker 跑一轮 —— 自报的工具名单与引擎真发给模型的名单**逐名相等**
4680
+ (不是三张估计词表的并集);把部署的 `SCHEDULER_ENABLED` 关掉再跑,名单**跟着变**。
4681
+ - **G-13**:运行期让名册变一次(挂/卸一只 MCP server)—— 客户端手上的名册**换成新的**;把
4682
+ `fromDigest` 改成一个对不上的值重放同一帧 —— 名册**仍然换成新的**,而客户端**不据那份 summary
4683
+ 算位置**(它记一次偏斜并重同步)。
4684
+ - **G-14**:把名册里的一行改坏(去掉 `name`)—— 客户端**不显示一份少了一行的名单**;它要么整只
4685
+ 不用名册、回落到旧行为,要么显性说「这次读不出名册」。**绝不**把那一行渲成「这只工具没挂」。
4686
+ - **G-15**:在请求里带一个坏的 `.mcp.json` 条目 —— 用户**看得见**一条通告,里面**引了他写的那个
4687
+ server 名**并说清补救办法;同一次里再带一条 audience 为 `operator` 的通告 —— 它**不上**用户屏。
4688
+ - **G-16**:让宿主往同一个水槽转发一个**不在码册里**的码 —— 它**不上**用户屏(只落诊断),
4689
+ 而码册内的码即使这一端没有专属文案也**上屏**。
4690
+ - **G-17**:构造一个码含控制字符、清洗后恰好等于某个册内码的通告 —— 它**不上**屏(在册判据认原始值)。
4691
+ - **G-18**:武装 auto 模式后让分类器那条腿**超时**一次 —— 卡上出现一句点名「分类器这次跑不了(超时)」
4692
+ 的话;让它**熔断**之后再来一只 ask —— 那一句**逐字不同**(点名闩已经合上、这一轮压根没发出去)。
4693
+ 同一只 ask 走 durable park 再恢复 —— 卡上那句话**一字不差**(两处同一只读器)。
4694
+ - **G-19**:让分类器**答了但答在契约之外**(parse_error)—— 卡上**不出现**「分类器跑不了」那一族的
4695
+ 任何一句(引擎在这一形上根本不盖那一格);而**不武装** auto 模式跑同样的调用 —— 卡上同样没有那一行
4696
+ (缺席 ≠「分类器跑成了」)。
4697
+ - **G-20**:让一条 workflow run 进入 `parked` —— 端上那一行显示的状态词**逐字是 `parked`**
4698
+ (不是被折成 `running` / `completed` / 或一个 `?`)。
4699
+
4700
+ ---
4701
+
4702
+ ### 27k. 常驻门
4703
+
4704
+ | 门 | 守什么 |
4705
+ |---|---|
4706
+ | 🆕 `scripts/run-posture-knob-projection-test.mjs` | ① 三旋钮窄读 / 裸值折壳但来源不冒称 / 主锚坏整段缺席·旁枝坏只丢一根 / 四句逐字互异 + 毫秒人话 / 转义后封长 / sdk spec + d.ts 真字节(含「本面数值旋钮恰一根」的措辞前提钉) |
4707
+ | 🆕 `scripts/run-engine-identity-test.mjs` | ③ 三锚逐格窄读 / 三态判词 + 优先序 / **锚交集为空 ⇒ unknown 而非 same** / 布尔口只对 changed 说 true / sdk `health.d.ts` 真字节含缺席语义注 |
4708
+ | 🆕 `scripts/run-gate-vocabulary-test.mjs` | ④ 两张表对 sdk 联合成员**双向等值**(缺一词红、多一词红、乱序红)/ 措辞逐词覆盖 + 句句互异 / **两张表的开闭兜底逐字分家** |
4709
+ | 🆕 `scripts/run-permission-rule-issue-codes-test.mjs` | ⑤ 表 ⟷ core 真铸码双向等值 / 表 ∪ 退役表 ⟷ core 声明联合(显形改名遗留)/ `invalid`·`unsupported` 两族措辞逐字互斥 / 散文消毒 + 转义后封长 / 可选 `SEMA_FENCE_ENGINE_DIR` 第二见证 |
4710
+ | 🆕 `scripts/run-tool-roster-projection-test.mjs` | ⑥ **绝不半张名册** / digest 恰 16 位小写 hex / `pathTarget` 三新键可选且缺席不铸默认 / shim 只从名册取(轴缺席不填 false·never)/ **skew 不是拒绝** / 坏帧什么都不发生 / 纯函数不改入参 |
4711
+ | 🆕 `scripts/run-engine-notice-catalog-test.mjs` | ⑦ 码册与 audience 表对实装 core 双向等值 + 两表逐键 lockstep / 表外码保守判 operator / 在册判据认原始值 / 掉落事实窄读 / **single_mint 反向钉:本包无第二份措辞** |
4712
+ | `scripts/run-rules-side-test.mjs` | ② 新增 G9 段:闭三词镜像 / 读不出不补默认 / 缺这一格的行仍进清单 / 标签 `(unknown)` 不冒充三态 / 撤销体身份三元组逐字回传且 `behavior` 读不出即拒铸 / sdk `rules.d.ts` 真字节 |
4713
+ | `scripts/run-sdk-floor-test.mjs` | `FLOOR` 8.5.0 → **8.8.0**,抬版理由逐条记账(编译期对账钉 / 撤销体铸点 / `startedAt` / `pathTarget` 三键 / 两张闭集加员) |
4714
+ | `scripts/run-client-core-portability-test.mjs` | index 闭包上限 148 → **154**(六件新读器叶,逐件登记;`EXPECTED_PACKAGES_INDEX` 一字未动) |
4715
+ | `scripts/run-public-surface-test.mjs` | 公面导出基线 **869 → 901** |
4716
+ | `scripts/run-integration-doc-freshness-test.mjs` | 本节新增坐标在盘 + 入库;§0a/§2/§2b 计数与基线等值 |
4717
+
4718
+ ---