@sema-agent/client-core 0.55.0 → 0.57.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` **>=7.4.0**(value-level,非 type-only;0.48.0 抬版,四条硬理由见 `CHANGELOG.md` 0.48.0 段末的地板影响面账) | `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
- | 公开导出面 | **815** 个运行期符号(+ 41 个测试钩;= 工作树当下的值 = 已发 `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
+ | 公开导出面 | **821** 个运行期符号(+ 41 个测试钩;= 工作树当下的值 = 0.57.0 内容批 D 三件 additive;`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`(**815** 项)。
114
+ > 全集真源 = `scripts/public-export-baseline.json` 的 `names`(**821** 项)。
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
- 815 项的内部构成(帮助端估读表大小):**244** 项是 `SCREAMING_SNAKE` 常量数据表/词汇表
138
+ 821 项的内部构成(帮助端估读表大小):**247** 项是 `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 域,逐域计数之和 = 815)
143
+ ### 2b. 域图(16 域,逐域计数之和 = 821)
144
144
 
145
145
  | # | 域 | 名数 | 承重导出 | 用途 | 实现锚 |
146
146
  |---|---|---|---|---|---|
147
147
  | 1 | **适配内核(下行主链)** | 33 | `adapt` · `createWireToCcAdapter` · `runStream` · `eventToSdkMessage` · `terminalToSdkResult` · `turnUsageToModelUsage` · `isRunStreamActive` · `ADAPTER_DIVERGENCES` | 引擎 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` |
148
148
  | 2 | **seam 公共契约** | 2(其余为 type-only) | `CHROME_ARMS` · `deriveTranscriptId` | 公共词汇 + **id 确定性不变量**(同一条流重放 ⇒ 同一串 id)。`CHROME_ARMS` = 端「我要消费哪些 chrome 臂」的对照清单 | `src/seam.ts` |
149
- | 3 | **HITL 决断卡链**(§4/§5 主战场) | 136 | `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}` 仍可赋值)· `DecideTransportRetryExhaustedError`(Inkglow-1085 P0a:decide 出站瞬断重试耗尽的 typed 判别 —— HitlBridge 内建单次退避重试,耗尽走重呈臂不判死 turn;端一般只消费行为,不需要 instanceof) | 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 崩溃收敛读面) |
149
+ | 3 | **HITL 决断卡链**(§4/§5 主战场) | 138 | `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)· `DecideTransportRetryExhaustedError`(Inkglow-1085 P0a:decide 出站瞬断重试耗尽的 typed 判别 —— HitlBridge 内建单次退避重试,耗尽走重呈臂不判死 turn;端一般只消费行为,不需要 instanceof) | 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 崩溃收敛读面) |
150
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
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` |
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 契约** | 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 桌面壳里的符号只会零命中,而零命中在这里**既不证真也不证伪**。复核这些断言**不得**拿本仓语料当反证 |
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 | **控制面与传输** | 82 | `ControlRouter`(+ `ControlSafetyError`)· `makeEngineWireClient` + `resolveWireAuth` · `installEngineWireTarget` · `diagnoseSseIdleTear` / `isSseIdleError` · `attemptActiveRunSelfHeal` + `activeRunBusySignal` + `activeRunSelfHealRow` / `activeRunBusyHeadlessRow` · `kickEngineCapsProbe` / `engineCapTrue` / `invalidateEngineCaps(baseUrl, probe?)`(#307 S25:引擎温切后的 caps 生产失效口 —— kick 自带幂等闸,同 baseUrl 重启后不显式失效就永远读到旧引擎那一版能力位;调用方 = 壳的 respawn/restartEngine。🔴 **推荐两参形**:第二参给替代探测则「推进代际 + 注册新探测」在同一同步块内完成,失效与下一次 kick 之间那个「等待者读到未判」的窗按构造不存在;单参形保留给「只丢缓存、这一刻没有替代探测」的调用方,那种情形下读到未判是诚实结局) · `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 错误分型判定半场,人话文案与渲染归端) | 上行通道的**监管**半场(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` |
162
- | 16 | **引擎词汇表与包自检** | 51 | `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) | 三端分臂共用的**去字面化** `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` 一位)、`src/classifierVerdictWire.ts`、`src/compensations.ts`、`src/clientSlice.ts` |
161
+ | 15 | **控制面与传输** | 83 | `ControlRouter`(+ `ControlSafetyError`)· `makeEngineWireClient` + `resolveWireAuth` · `installEngineWireTarget` · `diagnoseSseIdleTear` / `isSseIdleError` · `attemptActiveRunSelfHeal` + `activeRunBusySignal` + `activeRunSelfHealRow` / `activeRunBusyHeadlessRow` · `kickEngineCapsProbe` / `engineCapTrue` / `invalidateEngineCaps(baseUrl, probe?)`(#307 S25:引擎温切后的 caps 生产失效口 —— kick 自带幂等闸,同 baseUrl 重启后不显式失效就永远读到旧引擎那一版能力位;调用方 = 壳的 respawn/restartEngine。🔴 **推荐两参形**:第二参给替代探测则「推进代际 + 注册新探测」在同一同步块内完成,失效与下一次 kick 之间那个「等待者读到未判」的窗按构造不存在;单参形保留给「只丢缓存、这一刻没有替代探测」的调用方,那种情形下读到未判是诚实结局) · `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) | 上行通道的**监管**半场(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` |
162
+ | 16 | **引擎词汇表与包自检** | 54 | `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) | 三端分臂共用的**去字面化** `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`**,
@@ -235,12 +235,18 @@ type EventProjection =
235
235
  `context_usage` / `config_assembled` / `message_committed`(引擎可观测/审计面,CC transcript 无对位物)·
236
236
  `needs_review`(壳消费的是 `done{status:'needs_review'}` 终帧,事件形重复且更早)·
237
237
  `compaction_outcome`(压缩**非 compacted 结局**报告;CC transcript 无对位物,见 §7 缺口 **P-6**)·
238
- `wiring_manifest`(引擎接线自述)· `tool_approval` / `tool_approval_complete`(**不是丢帧**:它们走
238
+ `tool_approval` / `tool_approval_complete`(**不是丢帧**:它们走
239
239
  `hitl/toolApprovalWire.isToolApprovalFrame` + `hitl/frameRouter` 那条审批卡链;在这里投一条 transcript 行
240
240
  只会让同一只 ask 出现两次)· **`error`**(见下,单列)。
241
241
 
242
242
  <!-- /ARM-SET:not_in_slice -->
243
243
 
244
+ 📌 **`wiring_manifest` 自 0.56.0 起离开本表,但它只被接了两段**(L-70 / L-108②③)——
245
+ `modelGate` 与 `autoMode` 投成内部臂 + chrome 臂(§19),整份 manifest 的**其余十一段**
246
+ (leg / ask / question / interaction / elicit / parkLane / session / fleet / permissionRules /
247
+ governance / configFingerprint)仍然一件没接。两段都不成形的那些帧照旧回 `none/not_in_slice`,
248
+ **不铸空臂**。⇒ 别把「wiring_manifest 不在这张表里了」读成「manifest 接上了」。
249
+
244
250
  🔴 **`error` 单列点名**(端最容易据 §3a 的规则推错的一条):它的名字骗人 —— 那是流的 **15 分钟帽帧**
245
251
  (server `sse-log.ts`,已知 `errorCode` = `STREAM_MAX_DURATION`),帧自己就说 **run 仍然活着**;
246
252
  终态臂只有 `done` / `failed`。本切片对它无对位渲染物,所以走 `nothing('not_in_slice')` ——
@@ -1521,6 +1527,8 @@ CHANGELOG 0.29.0「已知局限」段与相应 JSDoc 都有成文。**别在读
1521
1527
 
1522
1528
  | **P-43** | med(治理面)@cli @web @desktop | 🆕 **`hooksForWire()` 只守住了本地执行器四条治理腿里的三条**(0.52.0 L-67④ 补了第三条,第四条**在册未修**):cli 仓那份本地执行器(`utils/hooks/hooksConfigSnapshot.ts` 的 `getHooksFromAllowedSources`,**cli 仓坐标不是本仓坐标**)的第四条腿 = `disableAllHooks` 出现在**非** managed 来源(user/project/local)时按 CC 语义降级成「只跑 managed hooks」。本包今天**只读** `policySettings.disableAllHooks` ⇒ 该形在引擎腿上**不成立**(用户把自己的 hooks 关了,引擎照投照跑)。**为什么不做单边近似**:cli 那条腿读的是**合并后**的标量(四源按 policy→user→project→local 后写覆盖前写),而本包 `SettingsPort` 只有 per-source 读口 —— 拿「任一来源为 true」去近似会在「user 写 `true`、local 写 `false`」这一形上**判反**(cli 那边是**不**限制)。忠实复刻要给 `SettingsPort` 加一个合并读口 = **公面改动**(端要跟车实现),属另一批 | `src/hooksWireCaps.ts`(头注「在册缺口」段);§15b/§15d | 端**不要**假定「用户 settings 里的 `disableAllHooks` 会挡住引擎腿的 hooks」——今天只有 **managed(policySettings)** 那一份挡得住。要它落地按 [C162] 令④ 回 C 板提(正位解在包侧:`SettingsPort` 补合并读口 + 本函数补第四条腿) |
1523
1529
 
1530
+ | **P-44** | low(型面)@sdk @cli @web @desktop | 🆕 **SDK 8.1.0 的 `ToolApprovalsResource.respond` 体型只锚到 `persistRule: { rule: string }`,server ≥7.46.0 已发的两位没跟**([C233]①,0.57.0 登记):①`persistRule.edited?: true`(编辑臂,server #340 / [5071]);②`persistRule.batchOfferIndex?: number`(批臂,server ≥7.46.0 / design/377)。同一族锚滞后还有三处:`ToolApprovalFrame.ruleOffers` 与 `PendingCheckpoint.ruleOffers`(server ≥7.46.0 的判别联合;SDK 8.1.0 全树 `ruleOffers`/`RuleOffer` **零命中**,亲验)、`ToolApprovalRespondAck` 的 `persistedRule`/`persistedRules`/`persistedRuleAnchors`(server ≥7.44 / ≥7.46.0 / ≥7.48.0)。🔴 **本包侧零 cast、也不需要 cast**:本包**不铸 respond 的 wire 体** —— `RespondToolApprovalFn` 是**宿主注入**的函数型,包只把扁平兄弟位(`persistRule: string` / `persistRuleEdited?: true` / `persistRuleBatchOfferIndex?: number`)交给注入面,由注入面按 `RespondToolApprovalOpts.persistRuleBatchOfferIndex` 头注写的那段优先序合成嵌套体。所以**吃这个型面缺口的是三端的注入面**(它们直接调 `client.toolApprovals.respond`),不是本包。帧侧那一半本包自铸归一形 `RuleOffer` 并在 `scripts/run-approval-frame-keys-test.mjs` 的 `AHEAD_OF_ANCHOR` 带退出条件登记(SDK 补上当天自红逼删),0.57.0 起窄读器 additive 上公面(§20) | `src/hitl/toolApprovalWire.ts`(`RespondToolApprovalOpts.persistRuleBatchOfferIndex` / `ToolApprovalRespondAckView` 两处头注已写清「候 sdk 补锚」)· `scripts/run-approval-frame-keys-test.mjs` 的 `AHEAD_OF_ANCHOR` 登记 | 端的注入面今天要合成那个嵌套体:**用宽形声明自己的 respond 包装**(或就地 `as`),并在**改动处写清锚到哪个 sdk 版本**、SDK 补锚当天回收。🔴 **别为了绕过型面缺口去改本包的扁平位**(那是既有 `persistRule: string` 消费者的 BREAKING,而这一批的纲领是 additive);也别在端侧自铸一个同名 `RuleOffer` 形 —— 归一形与它的伴生类型自 0.57.0 起都在本包公面上。正位解在 sdk(补型),按 [C162] 令④ 回 C 板提 |
1531
+
1524
1532
  ### 7e. 缺口的共同形状(值得单独说)
1525
1533
 
1526
1534
  **P-1 / P-2 / P-3 / P-4 / P-5 / P-6 / P-7 是同一类**:上游(server / SDK)已经把材料铸到 wire 上了,
@@ -2792,3 +2800,241 @@ CC 2.1.250 语料**无同形提示行** —— `first token` / `firstToken` / `f
2792
2800
  **常驻门**:`scripts/run-client-core-pure-test.mjs` G6 组(相位→kind 全矩阵 / 缺席位不捏造 /
2793
2801
  老宿主零差异)、`scripts/run-additive-key-passthrough-test.mjs` G5 组(全链两段)、
2794
2802
  `scripts/run-engine-vocab-floor-test.mjs` G2-b/G2-c(对**实装 core** 逐词逐键等值)。
2803
+
2804
+
2805
+ ## §19 🆕 `wiring_manifest` 的两段用户面事实(0.56.0;core #524 + core 147③ / server ≥7.58.0;cli L-70 / L-108②③)
2806
+
2807
+ ### 19a. 修的是哪一形
2808
+
2809
+ `wiring_manifest` 此前整帧走 `nothing('not_in_slice')`。那对整份 manifest 的**其余十一段**今天仍然
2810
+ 成立(它们是 operator / 诊断面读物,CC transcript 无对位物)。问题只在两段:
2811
+
2812
+ | 段 | 它回答用户的哪个问题 | 不接的后果 |
2813
+ |---|---|---|
2814
+ | `modelGate` | 「这一 run 我为什么少了几个工具?」 | 工具**静默消失**,用户以为模型不听话 |
2815
+ | `autoMode` | 「auto 到底武装了没有?为什么?」 | 只能靠 `/v1/capabilities` 猜,而那套词表**同名不同义** |
2816
+
2817
+ 两段都**没有第二条**到达消费端的路,留在 `not_in_slice` 里就是静默蒸发。
2818
+
2819
+ ### 19b. 端该怎么用(三步,端零字符串判定)
2820
+
2821
+ 1. 接 chrome 臂 `kind: 'wiring_manifest'`(`WiringManifestChromeEvent`)——`CHROME_ARM_TABLE` 里
2822
+ `required:false`:不接 = 这两条披露在该宿主上看不见,不是报错。
2823
+ 2. `modelGate` 在场 ⇒ 渲一行:类别 `class`、被卸工具 `removed[]`、**逐字**的 `restore`。
2824
+ 3. `autoMode` 在场 ⇒ 渲武装位与 `reason` 原文。
2825
+
2826
+ 形已经在包里校验过(见 19c),端**不许再自抄一套形判定**。
2827
+
2828
+ ### 19c. 🔴 五条读法(端不许自己重判,也不许自己放宽)
2829
+
2830
+ 1. **帧是 FLAT 的**:`ev.modelGate` / `ev.autoMode`,**不是** `ev.manifest.*`。6.7.0 契约声明过的
2831
+ 那层 `manifest:{...}` wire 上从未有过(SDK `events.d.ts` 该臂头注 2026-08-07 按真 capture 更正);
2832
+ server 把 core `TaskEvent` 的 `ev.manifest` 过白名单 builder 后 **spread 进帧**,三条腿
2833
+ (live SSE / durable 账本行 / resume 重放)同源。
2834
+ 2. **缺席不可反推**。`modelGate` 缺席 = 本 run 没有门卸(core 只在真卸时铸段);`autoMode` 缺席 =
2835
+ 老 mint / 外部 derive **没报**。两者都**不许**被渲成肯定句(「没有工具被卸掉」/「auto 未武装」)。
2836
+ 两段都不成形时本臂**根本不铸** ⇒ 端见到本臂,至少有一段是真读数。
2837
+ 3. **`autoMode.reason` 绝不映射** `/v1/capabilities.permissionModeAuto.reason` 的六词:两套词表
2838
+ **同名不同义**(`settings_denied` 在 capabilities 那边折 `no_intent` 不折 `denied`)。两面都要说
2839
+ 就两面各自读、各自渲。
2840
+ 4. **`modelGate.restore` 原样呈现**:它是 core 铸的**逐字**恢复办法。端自己拼一句「试试关掉某某开关」
2841
+ 等于替引擎编了一条它没说过的出口。
2842
+ 5. **幂等**:durable 腿重放会再送同一帧(与 `workspace_changed` / `engine_notice` 同纪律),按 run/leg
2843
+ 去重,别按到达次数计数。
2844
+
2845
+ ### 19d. 形校验(包侧已做,两段各自独立)
2846
+
2847
+ - `modelGate`:`class` / `restore` 非空串 ∧ `removed` 是全串数组 ∧ 过滤空串后**非空** —— 三件缺一
2848
+ 整段不收。空 `removed` 与「没卸」在屏面上不可区分,而 core 契约是「有门卸才铸段」⇒ 它是坏值。
2849
+ - `autoMode`:`armed` 是 boolean ∧ `reason` 非空串 ∧ **`armed === (reason === 'armed')`**(server 侧
2850
+ 同一条互证式)。照抄它是因为**非投影口**(宿主自建管线 / 重放存量转录)喂进来的帧不过 server。
2851
+ - 子流断闸按「**键在不在**」判、不按「是不是串」判:本臂决定的是「这份自述算谁的」,一个坏
2852
+ `parentToolCallId` 会把子代的门卸清单擦成 leader 的,用户会以为**自己这一 turn**少了工具。
2853
+
2854
+ ### 19e. 上游供给的诚实边界(本版新增的已知局限)
2855
+
2856
+ - **超集键名是临时形**:`modelGate` / `autoMode` 今天**不在**已发布 SDK(8.1.0)的 `AgentEvent`
2857
+ wiring_manifest 臂里(全树零命中,亲验)⇒ 本包内部臂用 `_sema_modelGate` / `_sema_autoMode`
2858
+ 前缀,不自铸 SDK 同名形。SDK 声明该臂之后改名是 **BREAKING**,另立一批。
2859
+ - **纯投影口刻意不导出**:`wiringManifestSupersetBody` 的入参是未校验 wire 帧,唯一诚实的声明形
2860
+ 是 `Record<string, unknown>` —— 推上公面等于在包公面开一个无类型边界(typeshape 棘轮正是为此
2861
+ 存在)。端要消费走**已定型**的 chrome 臂;两个具名形 `WiringManifestModelGate` /
2862
+ `WiringManifestAutoMode` 照常导出。
2863
+ - **`autoMode` 只在有 run 的时候来**:它骑在 per-leg 的 live 帧上。**一次性诊断进程**(没有任何
2864
+ 引擎腿)读不到它 —— 那种面上只能诚实报「本进程未观测到」,**不许**折成 `armed:false`。
2865
+
2866
+ **实现锚**:`src/adapter/downstream/eventToSdkMessage.ts`(`case 'wiring_manifest'` +
2867
+ `projectModelGateSection` / `projectAutoModeSection` / `wiringManifestSupersetBody`)、
2868
+ `src/adapt/arms.ts`(`wiringManifestArm`)、`src/seam.ts`(`WiringManifestChromeEvent` +
2869
+ `CHROME_ARM_TABLE` 加员)。
2870
+ **常驻门**:`scripts/run-type-superset-ledger-test.mjs`(两个 `_sema_` 键登记)、
2871
+ `scripts/run-integration-doc-freshness-test.mjs` ③ §3c(臂表双向对账)。
2872
+
2873
+ ---
2874
+
2875
+ ## §20 🆕 「不再询问」候选窄读器的公面(0.57.0;L-103 / [C228]、[C233]②)
2876
+
2877
+ ### 20a. 修的是哪一形
2878
+
2879
+ 两代 wire 键(`ruleOffers`,server ≥7.46.0 / 退役键 `ruleSuggestions`,≤7.45)在 0.56.0 及更早
2880
+ **只经卡端口出包**:归一形 `RuleOffer[]` 唯一的公面落点是 `ApprovalCardRequest.ruleOffers`
2881
+ (活卡帧腿)与 `ApprovalCardRequest.ruleOffersReadOnly`(durable 行腿)。窄读器本身
2882
+ (`readRuleOffers` / `readRuleOfferSupply`)是**模块内私有函数**。
2883
+
2884
+ 后果:**不走卡端口架构的宿主**(浏览器端自己拿帧渲,没有 Ink 三选卡)只能在自己那边重铸一遍
2885
+ 同一把窄读器。而这把读器承载的不是格式化,是**兑付安全**判据:
2886
+
2887
+ | 判据 | 重铸错了会怎样 |
2888
+ |---|---|
2889
+ | 逐条丢坏、**原始 wire 下标不前移** | 人点的第 k 个与服务端兑的第 k 个指向**两条不同规则** |
2890
+ | `kind` 是闭集判别位 | 不认识的臂被猜成 `single`,渲出一个按下去必被拒的选项 |
2891
+ | 两代取舍序(新键在场即定局 / `null` ≡ 缺席) | 拿旧键素材冒充这次 ask 的候选;或把中转层的 `null` 判成坏形,整段打掉 ≤7.45 引擎的「不再询问」档 |
2892
+
2893
+ 0.57.0 把两个口 **additive 推上公面**,语义与字节一字未改。
2894
+
2895
+ ### 20b. 端该怎么用(两步)
2896
+
2897
+ 1. **手里是一整帧/一整行** ⇒ 调 `readRuleOfferSupply(frame.ruleOffers, frame.ruleSuggestions)`
2898
+ —— 这是三端唯一该调的那个口,两代键的取舍序在它里面。
2899
+ 2. **手里只有新键** ⇒ 调 `readRuleOffers(offers)`。
2900
+
2901
+ 返回 `RuleOffer[]`(至少一条)或 `undefined`(= 这张卡没有可渲的「不再询问」档)。
2902
+ 🔴 **`undefined` 不是空数组**:空数组会被渲成「有这个档、里面一条都没有」,而真相是「没有档」。
2903
+
2904
+ ### 20c. 🔴 五条读法(端不许自己重判,也不许自己放宽)
2905
+
2906
+ 1. **`offerIndex` 的定义域随腿不同,函数不知道你在哪条腿上**:
2907
+ · **活卡帧腿**(`tool_approval` 帧的 `ruleOffers`)—— server 同步腿是纯前缀截、零逐条丢弃 ⇒
2908
+ 它恒等于引擎侧的 offer index,**是**合法选择键,批臂按它回兑
2909
+ (respond 体 `persistRule.batchOfferIndex`);
2910
+ · **durable 行腿**(`/v1/approvals` 行的 `ruleOffers`)—— server 已逐条丢弃并压紧过一次 ⇒
2911
+ 行上的下标本就不是引擎的 offer index,而这条腿**根本没有兑付口**(`/decide` 体无规则位)。
2912
+ 那一位只是展示/对账座,**禁**当选择键。
2913
+ 2. **逐条丢坏,不整只拒**:一条坏 offer 不该让另一条真 offer 消失(与 server 侧同向)。
2914
+ 但**留下来的每一条都带原始下标**——压紧下标是本节表里第一行那条失效形。
2915
+ 3. **batch 的成员不逐条丢**:合取批是「对全体成员一次说是」,任一成员不合形 ⇒ **整条 batch 丢**
2916
+ (兄弟 offer 不连坐)。渲一个缺了成员的批 = 人点的与引擎兑的不是同一批规则。
2917
+ 4. **`uncoveredSegments` 是诚实余量披露,不是装饰位**:非「有限非负整数」⇒ 整条 batch 丢。
2918
+ 渲一个编出来的 `0` 等于对用户说「兑完这批就全覆盖了」,而那句话没有证据。
2919
+ `0` 本身是**合法读数**。
2920
+ 5. **超帽整只降缺席、不截断**:条数 > 8(server 执法帽是 4,包侧刻意留宽)⇒ 整只缺席。
2921
+ 截了不留痕会掩盖注入。
2922
+
2923
+ ### 20d. 形校验(包侧已做,端不必重做)
2924
+
2925
+ - 一条 offer / 一个 batch 成员共用三位窄化:`rule` 非空串 ∧ `match` ∈ {`exact`,`prefix`} ∧
2926
+ `command` 非空串;batch 成员另加 `segment` 非空串(渲染座,**永不参与裁决**)。
2927
+ - `rule` / `command` / `segment` 三位都是 **UNTRUSTED-for-display**(原始 post-rewrite 命令字节)——
2928
+ 包不消毒(消毒单源在端的展示层),端渲之前照自己那套控制字符/长度纪律处理。
2929
+ - 空数组 / 非数组 / `null` / 全条坏形 / 超帽 ⇒ `undefined`。
2930
+
2931
+ ### 20e. 与卡端口的关系(不是二选一,是同一把读器的两个出口)
2932
+
2933
+ 已经走卡端口的宿主**什么都不用改**:`ApprovalCardRequest.ruleOffers` /
2934
+ `.ruleOffersReadOnly` 仍是同一把读器的产物,字节零变化。公面出口是给**没有卡端口**的宿主准备的
2935
+ 第二个观察面。常驻门 ⑦ 段把两者对同一份素材逐字对账 —— 哪天有人在其中一条腿上就地改窄读,当场红。
2936
+
2937
+ **实现锚**:`src/hitl/toolApprovalWire.ts`(`readRuleOffers` / `readRuleOfferSupply` 两个 `export`;
2938
+ 归一形 `RuleOffer` / `RuleOfferMatch` / `RuleOfferBatchMember` 与入参形 `WireRuleOffer` 照旧导出)。
2939
+ **常驻门**:`scripts/run-rule-offers-reader-test.mjs`(公面在场 + 两代正控 + 下标不前移 +
2940
+ batch 语义 + 坏形处置 + 取舍序 + 与两条卡端口腿的单一铸点对账)。
2941
+
2942
+ ---
2943
+
2944
+ ## §21 🆕 resume 族「时间性拒绝」二码的判型(0.57.0;L-102,server ≥7.47.0 / ≥7.51.0,SDK 8.1.0)
2945
+
2946
+ ### 21a. 修的是哪一形
2947
+
2948
+ `resume.*` 这一族(409 合同拒绝族)今天有九个成员。下面两个是这一族里**唯一带得出「等多久」**
2949
+ (`retryAfterSec`)的两个 —— 这是它们被收成一个闭集的**全部理由**:
2950
+
2951
+ | 码 | 语义 | server 起版 |
2952
+ |---|---|---|
2953
+ | `resume.usage_window_exhausted` | 这一行的账本键上,本部署的**治理窗**满了。什么都没消费、什么都没解钉 ⇒ 同一个 token 带同一个决议在窗放开后**可直兑** | ≥7.47.0 |
2954
+ | `resume.preflight_rejected` | 部署自己的 resume 预检拒了这次(显式拒 / 抛 / 超时 / 读不动,四臂一律 fail-closed)。它是提交前的**最后一档**,所以什么都没被消费 | ≥7.51.0 |
2955
+
2956
+ 两码的 409 体 additive 携 `retryAfterSec`(秒,`ceil`,**下限 1**;server 的铸键判据闭集恰是
2957
+ 「本码 ∧ 有限正数」,两码之外恒缺席)。SDK 8.1.0 起按这两个具名码铸 `ResumeRetryLaterError`。
2958
+
2959
+ 🔴 **本闭集不是「哪些码可以等」的名单**(端最容易读错的一句):族内有**明确的可等反例** ——
2960
+ `resume.row_recycling`(core 铸文逐字 "this clears on its own; send again in a moment",本包既有
2961
+ 处置 `row-contended`),它可等,只是 server 给不出秒数,所以它**不在**这个闭集里。
2962
+ ⇒ `resumeRetryLaterFromError()` 返回 `null` 只意味着**没命中这两码**,**不意味着「等也没用」**;
2963
+ 闭集外的码照旧按既有分类结果处置。把 `null` 读成「不可等」会把一个自清的瞬时争用说成「去重开一个
2964
+ 新 agent」——正是本包 0.38.0 收 `row_recycling`/`row_gone` 那批要根治的病换了个方向。
2965
+
2966
+ 修前:本包的 resume 失败分类器把两码双双落进开集兜底 `error` ⇒ server 明明给了等待窗,到端只剩
2967
+ 一句泛泛失败;而壳侧自己手接了一份判型 —— 判定长在端里,三端各写一遍必然各错一遍。
2968
+
2969
+ ### 21b. 端该怎么用(两条路,按你消费哪一层选)
2970
+
2971
+ 1. **只要判型**(自己组织重试/文案,或者你消费的不是子代复活腿而是别的 resume 面)⇒
2972
+ `resumeRetryLaterFromError(err)` → `{ code, waitable, retryAfterSec? } | null`。
2973
+ 🔴 **命中 ≠ 可等**:处置位是 `waitable`,不是「返回了非 null」。
2974
+ 2. **走子代复活腿** ⇒ 照旧读 `resumeSettledSubagent()` 的结局:失败臂新增**两格**
2975
+ `reason: 'retry-later'` / `'refused-preflight'`,并 additive 携 `code?` 与 `retryAfterSec?`。
2976
+
2977
+ **两格怎么分**(判据在包里,端零重判):
2978
+
2979
+ | `reason` | 什么时候 | 端能说什么 |
2980
+ |---|---|---|
2981
+ | `retry-later` | `resume.usage_window_exhausted`(该码本身即证据)**或** `resume.preflight_rejected` **且 server 给了窗** | 「等一会儿(有窗就说等多久)再试」 |
2982
+ | `refused-preflight` | `resume.preflight_rejected` **且没有窗** | 「这一拒发生在提交之前,你的决定没被消费」+ 把「还能不能再赎」交给引擎那行原文。**不许**渲成「稍后重试」,**也不许**渲成终局 |
2983
+
2984
+ ### 21c. 🔴 四条读法
2985
+
2986
+ 1. **`retryAfterSec` 缺席 ≠ 0**:缺席 = 服务端没给窗(老引擎 / 老 SDK 映射把本码塌进无字段的族
2987
+ 基类 / 该腿不发 `Retry-After` 头)。**绝不渲一个编出来的倒计时**。
2988
+ 🔴 **缺席时该说什么由 `reason` 决定,不由这一位决定**:`retry-later` 且窗缺席 ⇒ 说「稍后重试」
2989
+ 但不给数字;`refused-preflight` ⇒ **不许**说「稍后重试」(见上表)。把两者说成同一句,
2990
+ 就是本节要修的病换了个方向。
2991
+ 2. **「可以再试」是说给人听的,不是自动重试腿的授权**:resume 是 **AT-MOST-ONCE** 的有副作用动作
2992
+ (叫醒 = 真跑一轮),本包在这一格上照旧**一次都不重试**。端要做自动重试请自己承担幂等论证。
2993
+ 3. **`preflight_rejected` 的两条上游臂在客户端分辨不出来**(`retry_later` / `terminal` 的判别位在
2994
+ message 散文里)。本包**不按文案分臂** —— 按文案分支正是上游改一个词就静默空转的形。
2995
+ ⇒ 正解是**按正向证据分格**:有窗 ⇒ `retry-later`;没窗 ⇒ `refused-preflight` = **不可判**。
2996
+ 证据成立是**直证**(core 7.3.1 `dist/core/runner/runtask.js` 的 `resumePreflight` 拒绝段直读):
2997
+ `terminal` 臂抛的错**一个 detail 都不带**(它走的是单发 `expire` CAS,文末逐字 "the token is not
2998
+ redeemable")⇒ 结构上不可能有 `retryAfterMs` ⇒ server 无从铸 `retryAfterSec`;而 `retry_later` 臂
2999
+ 只在部署真给了等待建议时带它。⇒ **有窗是 retry_later 的充分证据,没窗两臂都可能**。
3000
+ 🔴 `refused-preflight` **不是**「不可重试」,它是「我不知道还能不能再赎」——两者渲成同一句话
3001
+ 就又回到了本条要修的病(一个把终局说成暂时,一个把暂时说成终局)。
3002
+ 4. **别把它与 429 的 `usage.window_exhausted` 合并判**:同一本账、**不同门、不同码** —— 那一个是
3003
+ 提交面的准入拒,这两个是 resume/decide 腿的**提交前**拒。
3004
+ 🔴 **「卡仍 pending、token 仍可兑」只对 `retry-later` 那一格成立**:`resume.usage_window_exhausted`
3005
+ 有 core 的不变量背书(什么都没消费、什么都没解钉);而 `refused-preflight` 那一格**不能**这么说 ——
3006
+ `terminal` 臂恰恰已经把行用单发 `expire` CAS 结清了。端在无窗预检拒上只许陈述**这一次拒绝本身**
3007
+ (发生在提交之前、这一拒没有消费你的决定),**不许**顺带承诺卡还在、token 还能兑。
3008
+
3009
+ ### 21d. 形校验(包侧已做)
3010
+
3011
+ - 判型是**闭集**的(恰这两个码),**绝不放宽成 `resume.` 前缀判**:放宽会把 `retain_off` /
3012
+ `evicted` / `row_gone` 这些「等也没用」的码一起说成「过会儿再试」——一半用户白等、另一半白重开。
3013
+ 上游哪天真在新码上铸 `retryAfterSec`,加成员必须同批带判据。
3014
+ - `retryAfterSec` 的窄读域 = **server 的铸键域**(整数 ∧ ≥1),不更宽也不更窄:`0` / 负数 / 小数 /
3015
+ `NaN` / `Infinity` / 数字串一律**降缺席**(不降 0、不取绝对值)。放行 `0` 就是对消费端说
3016
+ 「立刻重试」,而上面第 2 条说了那意味着什么。
3017
+ - **结构读不 `instanceof`**:客户端是宿主注入的,跨 bundle 的同名类是两个类;键位只认 `errorCode`
3018
+ (退役 `code` 槽不做兼容)。null 原型的 plain object(desktop IPC / web 转投的真形)照样命中。
3019
+ - **闭集是 `Object.freeze` 的只读数组,不是 `ReadonlySet`**:后者只在类型面只读,运行期就是普通
3020
+ `Set`,而判定查的是**同一个实例** ⇒ 端一行 `.add('resume.row_gone')` 就能把一条「等也没用」的
3021
+ 拒绝翻成带窗的 `retry-later`。端**可以**读它(对表/留痕),**改不动**它(ESM strict 下 `push` /
3022
+ 下标赋值直接抛)—— 要加成员请回上游,不是在消费侧撑开闭集。
3023
+
3024
+ ### 21e. 🔴 老宿主:穷尽形必须加一条 case
3025
+
3026
+ `SubagentResumeFailureKind` 从八员变**十**员(`retry-later` + `refused-preflight`)。
3027
+ **结构形免动**(读 `reason`/`detail` 的宿主一字不用改),
3028
+ 但对该联合做**穷尽 switch**(TS `never` 兜底)的宿主会在提货时编译红 —— 那是设计:一个新的处置
3029
+ 落进 `default` 而无人处理,就是把「等一会儿就好」渲成「失败了」。同 §18c 的老宿主纪律。
3030
+
3031
+ ### 21f. 上游供给的诚实边界(本版新增的已知局限)
3032
+
3033
+ - **本包不铸 `ResumeRetryLaterError`,也不 `instanceof` 它**:该类是 SDK 8.1.0 的,而本包 peer 地板
3034
+ 仍是 `>=7.4.0`。⇒ 挂在 ≤8.0 SDK 上的宿主,这两码走的是无字段的族基类,`retryAfterSec` 在错误对象上
3035
+ **根本不存在** ⇒ 本包如实报缺席。要拿到窗,宿主得把 SDK 抬到 8.1.0(或自己从 409 体里读出来,
3036
+ 按同一条窄读域喂进来)。
3037
+ - **本包不替端排重试节拍**:窗只是 server 给的**建议下界**。
3038
+ - **`refused-preflight` 这一格今天在多数部署上结构性不可达**:`resume.preflight_rejected` 要有
3039
+ 部署自己的 resume 预检座才发得出来。它是**预置臂**,不是当版可触发的行为面 —— 端接上去是为了
3040
+ 「码到货那天不至于只会印一行裸码」,不要据此推断本部署已经在用预检。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/client-core",
3
- "version": "0.55.0",
3
+ "version": "0.57.0",
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",