@sema-agent/client-core 0.59.2 → 0.61.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.
- package/CHANGELOG.md +245 -0
- package/README.md +4 -2
- package/dist/adapt/toolCards.d.ts +2 -1
- package/dist/adapter/downstream/eventToSdkMessage.d.ts +24 -4
- package/dist/adapter/downstream/eventToSdkMessage.js +42 -70
- package/dist/adapter/downstream/terminalToSdkResult.d.ts +27 -22
- package/dist/adapter/downstream/terminalToSdkResult.js +47 -30
- package/dist/adapter/runStream.js +27 -11
- package/dist/engineCapsCache.d.ts +27 -0
- package/dist/engineCapsCache.js +29 -0
- package/dist/engineErrorCodes.d.ts +12 -0
- package/dist/engineErrorCodes.js +13 -0
- package/dist/gateOutcome.d.ts +157 -0
- package/dist/gateOutcome.js +181 -0
- package/dist/headlessReconnectWire.js +9 -2
- package/dist/hitl/askParkRowRouting.d.ts +2 -0
- package/dist/hitl/askParkRowRouting.js +17 -2
- package/dist/hitl/frameRouter.d.ts +2 -2
- package/dist/hitl/frameRouter.js +49 -35
- package/dist/hitl/hitlBridge.d.ts +49 -2
- package/dist/hitl/hitlBridge.js +22 -1
- package/dist/hitl/parkResolver.d.ts +3 -1
- package/dist/hitl/parkResolver.js +15 -5
- package/dist/hitl/planReviewWire.d.ts +8 -1
- package/dist/hitl/planReviewWire.js +18 -7
- package/dist/hitl/toolApprovalWire.d.ts +37 -3
- package/dist/hitl/toolApprovalWire.js +36 -9
- package/dist/index.d.ts +4 -0
- package/dist/index.js +12 -0
- package/dist/printToolResultFrame.d.ts +34 -49
- package/dist/readFacePosture.d.ts +53 -0
- package/dist/readFacePosture.js +120 -0
- package/dist/resumeRefusalCopy.d.ts +12 -4
- package/dist/resumeRefusalCopy.js +15 -5
- package/dist/runTerminal.d.ts +194 -0
- package/dist/runTerminal.js +189 -0
- package/dist/sqlEngineCapability.d.ts +11 -1
- package/dist/sqlEngineCapability.js +13 -1
- package/dist/writeProtectionCapability.d.ts +142 -0
- package/dist/writeProtectionCapability.js +224 -0
- package/docs/INTEGRATION-CLIENTS.md +433 -11
- package/package.json +3 -3
|
@@ -19,11 +19,11 @@
|
|
|
19
19
|
|
|
20
20
|
| 项 | 值 | 真源 |
|
|
21
21
|
|---|---|---|
|
|
22
|
-
| 本包 | `@sema-agent/client-core` **0.
|
|
23
|
-
| peer:wire 契约 | `@sema-agent/sdk` **>=8.
|
|
22
|
+
| 本包 | `@sema-agent/client-core` **0.59.2**(= npm 最新;`package.json` `version` 未动)。**工作树另有一批未发内容** = 0.60.0(sdk 8.4.0 / engine 7.64.0 提货:终局因由 + 门记录两只读器 / B-042 身份闸 / B-037 候包两件 / `Capabilities` 93→94),进 `CHANGELOG.md` 的 `## 0.60.0(未发布)` 段;bump 与冻结账两阶段由发包批做,本内容批**不 bump** | `package.json` `version` |
|
|
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
|
-
| 公开导出面 | **
|
|
26
|
+
| 公开导出面 | **864** 个运行期符号(+ 43 个测试钩;= 工作树当下的值 = 0.61.0 三件 additive、**零删除**:`projectReadFacePosture`/`readFacePostureDetail`/`readFaceDisagreement`(S-167 READ 容纳面 operator 读器,详见 §25);`0.60.0` 是 **861**,那批是二十五件 additive、**零删除**:`readRunTerminal`/`runTerminalCode`/`runTerminalGateKind`/`runTerminalGateToolName`/`isReviewPark`/`REVIEW_PARK_GATE_KINDS` 六件终局因由读器 + `gateOutcomeOf`/`isDeniedGate`/`gateDeniedBy`/`gateApprover`/`isApprovalWindowExpiredGate`/`isDenialLimitAutoDeniedGate`/`isParkSlaExpiredGate`/`isHumanSettledGate` 八件门记录读器 + `projectWriteProtectionCapability`/`noteEngineCapsForWriteProtection`/`observedWriteProtection`/`forgetWriteProtectionReading`/`writeProtectionDoctorDetail`/`projectWriteProtectionPosture`/`writeProtectionPostureDetail` 七件写保护读面(测试钩 `__resetWriteProtectionReadingsForTests` +1)+ `readWireErrorCode` + 真机边界①的两件 `isGateParkedToolEnd`/`GATE_PARKED_ERROR_CODE` + 异源复审逼出的 `engineCapsGeneration`;`0.59.0` 是 **836**,那批是八件 additive:`RULE_OFFERS_ABSENCE_REASONS`/`DENIAL_LIMIT_KINDS` 两张闭词表再导出 + `engineCapValue` 能力位四态通用读口 + `projectSqlEngineCapability`/`observedSqlEngine`/`noteEngineCapsForSqlEngine`/`forgetSqlEngineReading`/`sqlEngineDoctorDetail` 五件 SQL 姿态读面(测试钩 `__resetSqlEngineReadingsForTests` +1);`0.58.0` 是 **828**,那批是三件 additive:`RULE_OFFER_MATCHES`/`RULE_OFFER_BATCH_MEMBER_KINDS`/`RULE_OFFER_UNCOVERED_REASONS` 三张闭词表再导出 + `RESUME_REFUSAL_CODES`/`RESUME_PLACEMENT_MISMATCH`/`resumeRefusalFromError`/`resumeRefusalContent` 四件文案铸点;`0.57.0` 是 **821**;`0.56.0`/`0.55.0` 是 **815**;已发 `0.54.0`(design/385 十件 additive 含 `AUTHORITY_ENVELOPE_TAGS`);`0.52.0` 是 **805**(L-69⑨ 两件);`0.51.0` 是 **803**;`0.50.0` 是 **800**(S-81 五件);`0.49.0` 是 **795**;`0.48.0` 是 **794**;npm `0.47.0` 是 **790**,`0.46.0` 是 **787**,`0.44.0` 是 **783**,`0.43.1`/`0.43.0` 是 **776**,`0.42.0` 是 **771**,`0.41.0` 是 **767**,`0.39.0` 是 **766**,`0.38.0` 是 **764**;`0.37.0` 是 **753**,见 `CHANGELOG.md`) | `scripts/public-export-baseline.json` 的 `count` / `testHookCount` —— **别手抄进别处,以该文件为准** |
|
|
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`(**
|
|
114
|
+
> 全集真源 = `scripts/public-export-baseline.json` 的 `names`(**864** 项)。
|
|
115
115
|
> 本节**不逐名抄**,只给「域 → 承重导出 → 用途 → 实现锚」。承重导出 = 一个端为了让这个域干活
|
|
116
116
|
> **必须**直接调到的那几个符号;其余是它们的类型、变体与辅助位。
|
|
117
117
|
> 单一入口:`import { … } from '@sema-agent/client-core'`(`exports` 只有 `.` 一个;
|
|
@@ -135,31 +135,31 @@ SDK 核对「本包 import 的每个值级符号仍然导出」「`TaskStats.cos
|
|
|
135
135
|
`WorkflowsGateUnknownDenial` 四形**不在**基线里,`src/selfOrchestrationDenial.ts` 对基线贡献
|
|
136
136
|
**4** 项运行期导出(三个函数 + `SELF_ORCHESTRATION_RETRY_WITHOUT`)。
|
|
137
137
|
|
|
138
|
-
|
|
138
|
+
864 项的内部构成(帮助端估读表大小):**256** 项是 `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 域,逐域计数之和 =
|
|
143
|
+
### 2b. 域图(16 域,逐域计数之和 = 864)
|
|
144
144
|
|
|
145
145
|
| # | 域 | 名数 | 承重导出 | 用途 | 实现锚 |
|
|
146
146
|
|---|---|---|---|---|---|
|
|
147
|
-
| 1 | **适配内核(下行主链)** |
|
|
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 主战场) |
|
|
149
|
+
| 3 | **HITL 决断卡链**(§4/§5 主战场) | 153 | `makeHitlCanUseTool` · `HitlBridge` · `findPendingForTask` · `HitlSafetyError` · `bridgeAskUserQuestionGates` · `surfaceToolApprovalFrameAndRespond` / `surfaceFsApprovalAndDecide` · `readToolApprovalRespondAck` · `installApprovalCardPort(For)` · `installHitlHostSurface(For)` · `armPlanReviewApproval` · `reopenPlanReviewCard` · `decidePlanReview` · `startApprovalsFeed` · `pendingRowIsOwnedByThisSession` · `approvalCallKey`/`liveFrameCallKey`/`planReviewQuestionId` · `registerArmedGateFor`/`wasGateArmedFor`/`clearArmedGateFor` · `waitForGateArmed(For)`/`onGateArmed(For)`/`gateArmedWaitMs`(#244 F1 呈现回执事件源) · `planReviewArmedKey(For)`/`notePlanReviewAnswered(For)`/`notePlanReviewAnsweredIfDecisive(For)`(A-024.4 plan 呈现分代) · `toolEndOutputText` · `isAskTool` · `waitForParkRowBirth` · `classifyAskParkRows` / `askParkRowArm` / `classifyAskParkChainFailure` · `readDecisionNoteAudit` / `decisionNoteAuditLine` · `resumeRunningOptions` / `resumeChoiceFromLabels` · `persistedRulesLaneAvailable`/`persistedRulesGovernanceAvailable` · `classifyRulesFailure` · `listAllPersistedRules` · `classifySkippedReason` · `readRulePersistOutcome`(#244 F2:persist-ack 读口与 `readToolApprovalRespondAck` 合成一处) · `parseLocalAllowRule`(durable 腿本地落规则窄化骨架,谓词经 `LocalAllowRuleDeps` 注入) · `readToolApprovalRespondRefusal`(#225 件5,0.42.0:respond 抛错的结构化原文读口 —— 三位各自防御读、各自缺席不铸、**三位皆缺席时整只返 `undefined`**;原样交还零加工,UNTRUSTED-for-display)· `installEditedRuleTextPrechecker` / `hasEditedRuleTextPrechecker` / `precheckEditedRuleText`([5076] 转出口,0.42.0:core 5.57.0 `precheckEditedRuleText` 的**端口注入形** —— 类型面 + 注入口 + 诚实缺席读口。🔴 **不是** value 级 re-export,理由见 §7 缺口 **P-34**;未装 ⇒ 返 `undefined`,绝不编一个 `{ok:true}`)· `surfaceRuleArmNotSent` / `RULE_NOT_SENT_WARN_TEXT` + `surfaceRuleArmRejected` / `RULE_NOT_SENT_REJECTED_WARN_TEXT`(#334,0.43.0:人在卡上按下的「不再询问」被整条丢弃时的诚实告知——**两条刻意分开**:前者=**引擎能力位未确认**(换台引擎/等探测就好),后者=**这次选择没过包内表核/互斥核**(表外文本/坏下标/两臂同场,换引擎也不会变) —— 编辑臂 `respondFreeFormRules` 与批臂 `respondBatchRuleOffers` 两条同形存量共用一条,与 `surfaceRememberNotApplied` 同族纪律:决断照送、只是规则没存,静默丢掉用户明确意图 = 让人以为功能坏了)· `projectCrashConverged`(L-38,0.49.0:`/v1/approvals` additive 键 `crashConverged` 的分桶投影 —— 崩溃收敛的孤儿审批读面,**缺席 ≠ 空数组**、分桶恰一个合取、坏行丢弃并计数,详见 §12;同批把 `ApprovalsResourceLike.list()` 的返回位 additive 放宽成 `ApprovalsListEnvelope`,老形 `{pending}` 仍可赋值)· `readRuleOffers` / `readRuleOfferSupply`(L-103/[C228],0.57.0 additive 导出:「不再询问」候选两代 wire 键的窄读器**公面化** —— 此前只经卡端口出包,不走卡端口架构的宿主只能自己重铸一把,而它承载的是兑付安全判据(原始下标不前移/逐条丢坏/闭集 kind/两代取舍序),详见 §20)· `RULE_OFFERS_ABSENCE_REASONS` / `DENIAL_LIMIT_KINDS`(0.59.0 / sdk 8.3.0 提货:`ruleOffersAbsence` 三词与 `denialLimitFallback.limit` 二词的**运行期**闭词表再导出 —— 与 0.58.0 那三张同一条纪律:端拿它做判定别再手抄字面量。⚠️ **本包自己不拿这两张表做窄读判定**(两处都按开集透传,词表属主是 core、server 已按闭集拒过),再导出只是给端的 `switch` 一份可数的表,端仍必须带 `default` 臂,详见 §23a)· `DecideTransportRetryExhaustedError`(Inkglow-1085 P0a:decide 出站瞬断重试耗尽的 typed 判别 —— HitlBridge 内建单次退避重试,耗尽走重呈臂不判死 turn;端一般只消费行为,不需要 instanceof) · `gateOutcomeOf` / `isDeniedGate` / `gateDeniedBy` / `gateApprover` / `isApprovalWindowExpiredGate` / `isDenialLimitAutoDeniedGate` / `isParkSlaExpiredGate` / `isHumanSettledGate`(0.60.0 / engine ≥7.64.0:`tool_end.gate` 整条门记录的窄读器 + 判定谓词 —— 取代退役的四个正交词 `settledBy`/`resolution`/`autoDenied`/`approver`。🔴 **判定归包**:「是不是审批窗自己走完的拒绝」旧形上是端各自拼的两键合取,而三条窗词现在分得开、合并读回去等于把刚修好的判别力扔掉;⚠️ 公面型一律带 `View` 后缀 —— 本文件的 `GateOutcome`(决断腿结局)与 sdk 的 `GateOutcome`(引擎门记录)**同名不同义**,详见 §24c)· `isGateParkedToolEnd`(「这次调用没在这里结算」的**第二形**谓词:gate 缺席 + `errorCode:"gate.parked"`;端判这件事必须与 `isApprovalWindowExpiredGate` **两形合读**) · `readWireErrorCode`(B-037 候包件②:decide/取件失败上引擎 wire 机器码的单源读口,结构视图读不 `instanceof`;与本包铸的 `code` 是两个命名空间,详见 §24g)| suspended→decide→resume 环。🔴 **D-1 两元组 verbatim 回显**是字节级断言的安全不变量,端**不许重实现它的任何一段**。🔴 键空间边界(web [C1] d3 拦截):`gateIdentity` 四常量两函数只覆盖 HITL questionId/callKey 空间;seat 的 `TOOL_PERMISSION_REQUEST_ID_DOMAINS`(`plan:` 等)是另一键空间,**两者绝不合并**(合并=座位校验器静默拒全部 plan-review 卡) | `src/hitl/hitlBridge.ts`、`toolApprovalWire.ts`、`askGateWire.ts`、`planReviewWire.ts`、`hitlHostSurface.ts`、`gateIdentity.ts`、`armedGateRegistry.ts`、`parkOwnership.ts`、`parkResolver.ts`、`approvalsFeed.ts`、`frameRouter.ts`(**只挑名导出** `toolEndOutputText`/`ENGINE_ABORT_TOOL_RESULT`/`isAskTool`/`HITL_REJECT_MESSAGE`/`HITL_INTERRUPT_MESSAGE_FOR_TOOL_USE`)、`parkRowBirthWait.ts`、`approvalDecisionNoteAudit.ts`、`askParkRowRouting.ts`、`resumeRunningCard.ts`(#265 上收的判定层)、`persistedRulesWire.ts`、`localAllowRule.ts`(#244 F2 规则侧)、`crashConverged.ts`(L-38 崩溃收敛读面)、`src/gateOutcome.ts` |
|
|
150
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` |
|
|
153
153
|
| 7 | **通知与 outstanding 台账** | 51 | `installNotificationQueuePort` · `normalizeTaskNotification` · `taskNotificationDedupKeyFromWire` · `registerOutstandingBgTask` / `registerOutstandingWorkflowRun` · `notificationQueuePortMisses` · `subscribeOutstandingWorkflows` · `outstandingDeliverableWorkflowCount` | `task_notification` 归一 + 去重 + 投递进宿主命令队列的**一把闸**;`outstandingDeliverableWorkflowCount()` 是 headless `-p` 的**退出门** · `classifyPeerNotification` / `renderPeerFrameTranscriptText` / `parsePeerFrameText` / `peerFrameDisplayName` + 三张闭集表(design/385,0.54.0:同一条 `task_notification` 车道上三条**引擎注入帧**的类型化投影 —— 判别位=载体在场而非 summary 文本,详见 §17) | `src/notifications.ts`(11 个 module 台账) |
|
|
154
154
|
| 8 | **工具结果卡** | 25 | `structuredToToolUseResult` · `readAsyncLaunchedAgentReceipt` · `wireOutputToBody` · `parseModelFacingBash` · `getPatchFromContents` · `toolEndResultToUserFrame` · `flattenToolOutput` | 铸端要渲的 `tool_result` 卡体,含客户端 diff hunk(唯一 runtime dep 的用处) | `src/toolResult.ts`、`src/printToolResultFrame.ts`、`src/diff/patch.ts` |
|
|
155
|
-
| 9 | **能力/旋钮 wire 门族** |
|
|
155
|
+
| 9 | **能力/旋钮 wire 门族** | 92 | `projectAgentsForWire` / `awaitTaskAgentsWire` · `hooksForWire` · `mcpConfigsToSpecs` · `ultracodeForRequest` · `resolveWebSearch` + `buildWebSearchEnv` · `attachmentsForRequest` · `projectEffectiveBody` · `classifySelfOrchestrationRefusal` / `stripSelfOrchestrationIntent` / `projectWorkflowsGate` / `SELF_ORCHESTRATION_RETRY_WITHOUT`(S-81,server 7.57.0:上面两条 stamp 腿的**背面** —— 半配置多租户形态下 server 把带 `selfOrchestration` / `settings.ultracode` 的提交 501 拒掉,判型/去键/caps 闸三处都是判定不是文案,详见 §13) · `engineCapsGeneration`(0.60.0,异源对抗复审 [medium] 逼出:caps 探测的**当代号**读口 —— 代际闸在 probe **返回之后**才跑,而宿主的读面 tee 是在 probe **内部**落地的 ⇒ 一次慢的旧探测能把上一台引擎的读数写回来。宿主在**起探测那一刻**抓号、tee 时交回 `{generation}`,陈旧那一发整只丢掉;不传 = 逐字节同行为,详见 §24e)| 逐功能的「引擎有没有这个能力 + 这个字段怎么上 wire」投影器,由 `buildTaskRequest` 组装。🔴 `hooksForWire()` 是 **fail-closed**:无 `SettingsPort` ⇒ 返回 `undefined`(信任门,不是取值) | 17 个 `src/*WireCaps.ts` + `src/scratchpadWireCaps.ts`、`src/hooksWireCaps.ts`、`src/goalStopHook.ts`、`src/websearch/searchProviderPresets.ts`、`src/selfOrchestrationDenial.ts`(S-81 拒绝判定层) |
|
|
156
156
|
| 10 | **headless / 部署旋钮 wire** | 61 | `parseSandboxArgv` / `sandboxRequestFields` · `parseLimitsArgv` / `limitsForPrint` · `resolveHeadlessFinalVerify` · `resolveHeadlessPermissionMode` · `resolveHeadlessInteractiveTools` · `armDetachCancel` + `detachCancelArm` + `isDetachArmed` · `withHeadlessR1Reconnect` | `-p`/headless 车道的 env+argv 旋钮。🔴 `detachWire` 是**拆**的补偿:判定与 cancel-arm 台账在库,信号路径的裸 fetch 留宿主(`detachCancelArm()` 是取件口) | `src/sandboxWire.ts`、`scenarioWire.ts`、`finalVerifyWire.ts`、`limitsWire.ts`、`interactiveToolsWire.ts`、`headlessPermissionModeWire.ts`、`headlessReconnectWire.ts`、`detachWire.ts` |
|
|
157
157
|
| 11 | **模型目录与预算** | 67 | `resolveModelCatalog` · `loadCatalogWithSources` · `PROVIDER_PRESETS` / `MODEL_FAMILIES` · `defaultMaxTokensFor` · `getLiveModelCatalog` / `setLiveModelCatalogRefresher` · `providerAuthMethods` / `beginDeviceCodeAuth` · `providerCatalogRows` / `providerCatalogRowDetail` / `providerPresetById`(#244 F4 族D A-028.17:46 家表的规范折表层 —— 全表不重排、诚实缺席「model id typed in」,cli 目录/web 向导同一份折表)· `TIER_ORDER` / `CC_TIER_ALIASES` / `isTier` / `resolveTierBinding`(A-028.18:档位词表+校验+fail-open 降档派生单源;settings 存储归宿主)· `resolveEntryVision` / `computeDeleteBlockers` / `computeDeleteWarnings`(0.38.0 #318 件③ 上收:Model Hub 供给面的三端公共判定 —— vision 生效值+来源三态、删除断链核(拒删+指路)、删除降级后果(照删但必说)。**零 IO**,读盘那半场留各端;`doc === null` 的两义在调用方分流) | 三层 provider 目录解析(线上 URL → 包内预设 → 用户覆盖)+ per-model `maxTokens` 封顶。线上腿需注入 `CatalogFetchJson`,缺席 ⇒ 整条不启用(`online.reason='no-fetch-port'`);缓存落盘经 `CatalogCachePort` | `src/model/{catalog,catalogLoader,providerAuth,providerPresets,providerCatalog,tierVocabulary}.ts`、`src/liveModelCatalog.ts`、`src/modelBudgetRule.ts`、`src/sessionModelLatch.ts`、`src/effortWire.ts` |
|
|
158
158
|
| 12 | **workflow 与后台工作视图** | 19 | `projectWorkflowRun` · `createLiveWorkflowSource` · `ensureWorkflowActivityLedger` · `readWorkflowActivityLedger` · `stopWorkflowActivityLedger` · `resetWorkflowActivityLedgers` · `createBackgroundView` · `projectBackgroundView` · `recordWorkflowAgentTaskId` · `agentDisplayStatus` | 活过一个 turn 的长任务读面:workflow run + 跨 session 后台任务归一表(`assistant.tasks` 与 fleet SSE **两源独立降级**) | `src/workflow.ts`、`src/workflowClient.ts`、`src/workflowMonitor.ts`、`src/agentSession/backgroundView.ts`(+ 纯类型 `src/agentSession/contract.ts`) |
|
|
159
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 | **控制面与传输** |
|
|
162
|
-
| 16 | **引擎词汇表与包自检** |
|
|
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` |
|
|
163
163
|
|
|
164
164
|
🔴 **`engineErrorCodes` 的开集纪律**(该文件头注逐字):这些 `ReadonlySet` / 前缀谓词一律是**识别表**,
|
|
165
165
|
回答的是「我认不认得这个码」,**绝不是**「合法码只有这些」。消费点 `switch` **必须留 `default`**,
|
|
@@ -3430,3 +3430,425 @@ older mint or an external derivation")—— `mcp: []` 是「这条腿一台 MCP
|
|
|
3430
3430
|
| `scripts/run-hitl-gate-honesty-test.mjs` | 段 F15 新增:三个新位**到不到得了卡口** · `origin` 缺席不折 `"policy"` · `ruleOffersAbsence` 与 `ruleOffers` 同时在场时两者都上卡(包不替上游湮灭矛盾)· `denialLimitFallback` 四成员缺一整只丢 / `0` 是合法读数 / `limit` 开集 · 老引擎三键全不在场 |
|
|
3431
3431
|
| `scripts/run-sdk-floor-test.mjs` | 地板 `8.3.0` 与 peer 声明**三位逐位等值**互绑 |
|
|
3432
3432
|
| `scripts/run-public-surface-test.mjs` | peer 地板 / README 三处逐字核 + 公面导出基线 **828 → 836**(测试钩 41 → 42) |
|
|
3433
|
+
|
|
3434
|
+
## §24 🆕 sdk 8.4.0 / engine 7.64.0 提货 · 终局因由与门记录两只读器 · B-042 身份闸 · B-037 候包两件(0.60.0)
|
|
3435
|
+
|
|
3436
|
+
本节四件同一批。**共同的形**与 §23 那一批不同:那一批修的是「上游把事实摆在 wire 上、包边界空转」,
|
|
3437
|
+
这一批修的是**上游把并列的可选键换成了形状**——退役的键从型面**整删**,而 `tsc` 对本包**零报错**
|
|
3438
|
+
(所有消费点都是 `unknown` 宽读),所以编译器一个字都不会说。⇒ 这一批的判据必须落在**门**上,
|
|
3439
|
+
落在**投影出来的产物**上,而不是编译期。
|
|
3440
|
+
|
|
3441
|
+
### 24a. peer 地板 `>=8.3.0` → `>=8.4.0`(本版唯一的非 additive 面)
|
|
3442
|
+
|
|
3443
|
+
| 面 | 0.59.x | 0.60.0 | 端要做什么 |
|
|
3444
|
+
|---|---|---|---|
|
|
3445
|
+
| peer 地板 | `@sema-agent/sdk >=8.3.0` | **`>=8.4.0`** | 提货前先抬自己的 sdk 依赖 |
|
|
3446
|
+
| 支持窗 | engine ≥7.60.0 | **engine ≥7.64.0** | 🔴 **整链缺一不换**:sdk 8.4.0 与 engine 7.63.0 及以前的 wire **不同窗**,端不能先换 sdk 后等引擎 |
|
|
3447
|
+
| `Capabilities` | 93 键 | **94** 键(+`writeProtection`) | 见 24e |
|
|
3448
|
+
| `TaskResult` | 八个平面键 | **一条 `terminal` 因由** | 见 24b |
|
|
3449
|
+
| `tool_end` 门面 | 四个正交词 | **一条 `gate` 记录** | 见 24c |
|
|
3450
|
+
|
|
3451
|
+
🔴 **地板抬升的四条硬理由**逐条写在 `scripts/run-sdk-floor-test.mjs` 的 `FLOOR` 注里
|
|
3452
|
+
(`TerminalCause`/`PausedCause` / `GateOutcome` 四型 / `WriteProtectionCapability` /
|
|
3453
|
+
`McpDelivered`·`McpFailureKind`,在 <8.4.0 上都不存在)。
|
|
3454
|
+
|
|
3455
|
+
🔴 **本批对退役键零兼容读**。这句话有一个**必须说清的例外**,它不是「旧引擎兼容」:
|
|
3456
|
+
`readRunTerminal` 的第二代臂认的是**同一台 ≥7.64.0 引擎**真会送来的扁平字节,两个来源都有出处 ——
|
|
3457
|
+
① **读盘面**(`GET /v1/runs/:id` 与 durable 账本回放对存储 blob **逐字透传、不补 `terminal`**,
|
|
3458
|
+
所以跨 7.64.0 升级过的部署上,升级**之前**落盘的行读出来仍是平面形);② **server 自己的 409 拒绝
|
|
3459
|
+
信封**(它从来就是扁平的,这次形变没动它)。判别位 = 上游钉的那一个:**`"terminal" in result`**。
|
|
3460
|
+
|
|
3461
|
+
### 24b. 终局:`TaskResult.terminal` 四臂(读器 `readRunTerminal`)
|
|
3462
|
+
|
|
3463
|
+
#### 读器签名与形
|
|
3464
|
+
|
|
3465
|
+
```ts
|
|
3466
|
+
readRunTerminal(result: unknown): RunTerminalRead | null
|
|
3467
|
+
|
|
3468
|
+
type RunTerminalPlane = 'cause' | 'flat'
|
|
3469
|
+
interface RunTerminalPause { gate?: CheckpointGate; checkpointId?: string; restoreMode?: string }
|
|
3470
|
+
interface RunTerminalPausedRead extends RunTerminalPause { kind: 'paused'; plane: RunTerminalPlane; code?: string }
|
|
3471
|
+
type RunTerminalRead =
|
|
3472
|
+
| { kind: 'completed'; plane: RunTerminalPlane }
|
|
3473
|
+
| { kind: 'failed'; plane: RunTerminalPlane; code?: string; message?: string; nestedPause?: RunTerminalPause }
|
|
3474
|
+
| { kind: 'blocked'; plane: RunTerminalPlane; reason?: string; code?: string }
|
|
3475
|
+
| RunTerminalPausedRead
|
|
3476
|
+
| { kind: 'unknown'; plane: RunTerminalPlane; word: string; code?: string; message?: string }
|
|
3477
|
+
|
|
3478
|
+
runTerminalCode(read): string | undefined // 这次终局的机器码(completed 上恒 undefined)
|
|
3479
|
+
runTerminalGateKind(read): string | undefined // paused 停在哪一种门上(开集)
|
|
3480
|
+
runTerminalGateToolName(read): string | undefined // paused 扣着哪个工具(tool-less park 上缺席)
|
|
3481
|
+
isReviewPark(read): boolean // 这次暂停在等一次「复核」而不是一次「交互作答」
|
|
3482
|
+
// 🔴 **两个来源合读**:因由形的门种 ∪ 扁平形的 `flatStatus === 'needs_review'`
|
|
3483
|
+
// (同步 park 体上连门都没有,只读门种必然答错)
|
|
3484
|
+
REVIEW_PARK_GATE_KINDS: readonly string[] // 上一行的词表(措辞轴;不是路由表)
|
|
3485
|
+
```
|
|
3486
|
+
|
|
3487
|
+
#### 输入形与缺席语义
|
|
3488
|
+
|
|
3489
|
+
| 输入 | 读数 | 端要知道的 |
|
|
3490
|
+
|---|---|---|
|
|
3491
|
+
| `result.terminal` 在场 | 对应臂 + `plane: 'cause'` | 现行形。**平面键即使同时在场也一个都不读** —— 留一条兼容读 = 同一事实两个真源 |
|
|
3492
|
+
| 无 `terminal` 但有 `status` | 对应臂 + `plane: 'flat'` | **三个来源同形不可分**,所以共用一个标:①读盘回放的历史行;②server 的 409 拒绝信封;③🆕 **同步提交腿的 park 体**(下一行单列)。拿 `stats` 在场性去分是假判据(本包的抢救腿合成的 done 帧本来就可能没有 `stats`) |
|
|
3493
|
+
| 🆕 **同步提交腿的 park 体** `{taskId, sessionId, status:"suspended"\|"needs_review"}` | `paused` + `plane:'flat'` + `flatStatus` | 🔴 **它不是 `TaskResult`**(server 回帖确认:server **自造**的一只形,既没有退役的八个平面键、也没有 `terminal`、**连门都不带**)。⇒ 「这是复核还是交互作答」在这一形上**只有那个 status 词**这一个来源,读器把它原样带回 `flatStatus`,`isReviewPark` 两个来源合读。**绝不折成 `completed` / `failed`**:它说的是一句正面事实 —— 这条 run park 了 |
|
|
3494
|
+
| **非终态**词(`running` / 未来的排队词) | `unknown` + `word` 逐字 | 🔴 「这不是一个我认得的终局」—— **不是**成功,也不是一个失败码 |
|
|
3495
|
+
| 暂停中的 run 在 `GET /v1/runs/:id` 上 | 连 `result` 载体都没有 ⇒ `null` | 端据此**继续等**,别把它读成任何终局 |
|
|
3496
|
+
| 四臂之外的词 | `kind: 'unknown'` + `word` 逐字 | 🔴 **绝不折成成功**。端渲「非成功终态」并把那个词原样报出来 |
|
|
3497
|
+
| 连终态词都没有 | `null` | **唯一**返回 `null` 的那一格(mock 车道 / 不带 status 的旧合成帧)。它**不是**「成功」也**不是**「未知」—— 消费点对这一格的既有处置由消费点自己保留 |
|
|
3498
|
+
| `completed` | — | 🔴 **结构上写不出码**:`runTerminalCode` 恒 `undefined` |
|
|
3499
|
+
| `blocked` / `paused` 上的 `code` | 只可能来自 `plane: 'flat'` | 因由形下这两臂写不出码;扁平形回放的历史 park 行仍可能带 `review.pending` |
|
|
3500
|
+
| `paused.restoreMode` | `'snapshot'` / `'park_only'` / 缺席 | **缺席 = 进程内暂停**(没有远端工作区被捕获)。🔴 把「paused ⇒ 空闲且免费」当不变量的调度器必须看得见 `park_only`(机器继续跑、继续花钱) |
|
|
3501
|
+
| `failed.nestedPause` | 嵌套硬边界(verify / cascade) | 嵌套腿停在一道耐久门上而编排器驱动不了 resume;顶层调用方据 `gate`/`checkpointId` 仍能恢复。🔴 wire 上**不带 token** |
|
|
3502
|
+
|
|
3503
|
+
🔴 **`token` 席位永不上 wire**:恢复凭据(token-as-auth)永不离开服务边界,读器一个字节都不碰它、
|
|
3504
|
+
也不给它留位。要动一次暂停:走 `POST /v1/approvals/{sessionId}/decide`,凭 `checkpointId` 对账。
|
|
3505
|
+
|
|
3506
|
+
🔴 **本包不提供「因由 → 五词 `status`」的兼容投影**:提供一条就等于同一个事实有两个真源,消费端会
|
|
3507
|
+
分成「读因由的」和「读投影的」两派,下一次形变时后者全部无声地错(sdk 顶注同规)。
|
|
3508
|
+
|
|
3509
|
+
#### 包内换装点(端不必动,但要知道行为差)
|
|
3510
|
+
|
|
3511
|
+
| 消费点 | 修前判据 | 修后判据 |
|
|
3512
|
+
|---|---|---|
|
|
3513
|
+
| `doneToSdkResult`(`-p` 终帧投影) | `result.status` 五词 + `errorCode`/`errorMessage`/`blockedReason`/`checkpointGate` | `readRunTerminal` 五臂 |
|
|
3514
|
+
| `runStream` 的可见性行 + plan-review chrome 事件 | 同上 | 同上;复核 park 的判据换成 `isReviewPark`(**两个来源合读**)。⚠️ 那一处对 `status === 'needs_review'` 的读**保留**、不是旧八键残留 —— 它读的是**同步提交腿的 park 体**(server 自造形),因由形那一半覆盖 done 帧的引擎结果,两半各管一条腿 |
|
|
3515
|
+
| `frameRouter` 两条 park 臂 | 两个谓词各自强转 + 各判一次 `status` | **一次**读取 → `parkGateOf` 分 ask / fs 两臂 |
|
|
3516
|
+
| `frameRouter` 的用户中断证据 | `result.errorCode === 'cancelled'` | `runTerminalCode(read) === 'cancelled'`(两代归一) |
|
|
3517
|
+
| `isPlanReviewPark` | `status === 'needs_review' && checkpointGate.kind === 'plan_review'` | `kind === 'paused' && 门种 === 'plan_review'`(**逐字只认这一个词**:本臂驱动的是那一个 resume verb) |
|
|
3518
|
+
| `headlessReconnectWire.terminalEventFromRunRow` | 原样透传 `row.result` | **不变**(这是读盘面:补一个 `terminal` 出来就是替停产的写者重新裁定形状,server 自己也刻意不做);run 行**列** `status` 7.64.0 未动 |
|
|
3519
|
+
| `workflowClient.coerceRunStatus` | run 行**列** `status` | **不变**(复核后只登记不改码:那是 server 自己的列,不是引擎结果) |
|
|
3520
|
+
|
|
3521
|
+
🔴 **可观察行为变化(端必须知道)**:`-p` 投影里 **park / blocked 终帧的 additive `errorCode` 位从
|
|
3522
|
+
0.60.0 起恒缺席**(此前 `review.pending` 骑在 park 帧的平面 `errorCode` 上)。扁平形回放的历史行仍
|
|
3523
|
+
带得动它 —— 端**不许**据这一位的在场性推断「是不是 park」(那件事读 `terminal.kind`)。
|
|
3524
|
+
|
|
3525
|
+
### 24c. 门记录:`tool_end.gate: GateOutcome`(读器 `gateOutcomeOf`)
|
|
3526
|
+
|
|
3527
|
+
#### 读器签名与形
|
|
3528
|
+
|
|
3529
|
+
```ts
|
|
3530
|
+
gateOutcomeOf(frame: unknown): GateOutcomeView | undefined // 入参是**整只帧**,不是 frame.gate
|
|
3531
|
+
|
|
3532
|
+
type GateDispositionView = { kind: 'allowed' } | { kind: 'denied'; deniedBy: string }
|
|
3533
|
+
interface SettlementWhoView { party: string; approver?: string; window?: string }
|
|
3534
|
+
interface SettlementView { kind: string; who: SettlementWhoView; when?: number; note?: string }
|
|
3535
|
+
interface GateOutcomeView { disposition: GateDispositionView; settlement?: SettlementView; origin?: string }
|
|
3536
|
+
|
|
3537
|
+
isDeniedGate(g): boolean // 这次门是不是拒
|
|
3538
|
+
gateDeniedBy(g): string | undefined // 拒的是哪一**层**(policy/hook/org/classifier/plan_mode/compliance/write_protection/ask_resolution)
|
|
3539
|
+
gateApprover(g): string | undefined // 通道自报的身份串(UNTRUSTED-for-display)
|
|
3540
|
+
isApprovalWindowExpiredGate(g): boolean // ← 端此前自拼的 settledBy==='timeout' && resolution==='window_expired'
|
|
3541
|
+
isDenialLimitAutoDeniedGate(g): boolean // ← 端此前读的 tool_end.autoDenied === true
|
|
3542
|
+
isParkSlaExpiredGate(g): boolean // 耐久 park 的 SLA 到期被宿主的扫判成拒
|
|
3543
|
+
isHumanSettledGate(g): boolean // human_allowed / human_refused
|
|
3544
|
+
isGateParkedToolEnd(frame): boolean // 🆕「这次调用没在这里结算」的**第二形**:gate 缺席 + errorCode:"gate.parked"
|
|
3545
|
+
GATE_PARKED_ERROR_CODE: string // 上一行那个码的单源常量(frameRouter 的连坐判据读同一个)
|
|
3546
|
+
```
|
|
3547
|
+
|
|
3548
|
+
🔴 **「这次调用没在这里结算」要两形合读**(engine 7.64.0 真机黑盒直证,端最容易只写一半):
|
|
3549
|
+
`isApprovalWindowExpiredGate(gateOutcomeOf(f)) || isGateParkedToolEnd(f)`。两形语义**不同**、不许互相顶替 —— 前者是「窗走完了,**拒了**」,后者是「**park 了**,还没人决」,端要说的话不一样。
|
|
3550
|
+
|
|
3551
|
+
#### 缺席与坏形
|
|
3552
|
+
|
|
3553
|
+
| 情形 | 读数 | 端要知道的 |
|
|
3554
|
+
|---|---|---|
|
|
3555
|
+
| `gate` 整键缺席 | `undefined` | 🔴 **缺席不是关于这次调用的事实**。上游列了**四种**在 wire 上不可分辨的成因:门根本没看见这次调用(未武装的门)/ 延后重发 / reconcile 捡回的孤儿 / 那条记录过不了引擎的不变量筛而 server **整条不上帧**。⇒ 一律退回 `isError` + 文案 |
|
|
3556
|
+
| 🆕 `gate` 缺席 **且** `errorCode:"gate.parked"` | `undefined`,但 `isGateParkedToolEnd(frame) === true` | 真机黑盒补的**第五种成因**,而且它有一个**可读的正面标记**:审批窗走完(默认 300s)未决而走 **park** 路径时,帧上没有 `gate`,只有这个码。那不是「记录坏了」,是「这次调用根本没结算过什么」⇒ 端渲「已转后台候批」 |
|
|
3557
|
+
| `disposition` 读不出 | `undefined` | 它是**唯一的承重座**:一条只有 `settlement` 而说不出「放行还是拒了」的记录,端能从它得到的只有误导 |
|
|
3558
|
+
| `denied` 却没有 `deniedBy` | `undefined` | 上游那一形**写不出来**(两臂在类型上就分了家)⇒ 到得了这里的是坏记录 |
|
|
3559
|
+
| `settlement` 缺 `kind` 或 `who` | **整只结算缺席**,门记录仍在 | 半只会被读成「结算过了只是没说是谁」 |
|
|
3560
|
+
| `when` 非有限数 | 那一位缺席 | 绝不铸一个 `0` 时刻 |
|
|
3561
|
+
| 三处词表的陌生词 | **原样透传** | 🔴 词表属主是 core;端的 `switch` **必须带 `default`** |
|
|
3562
|
+
|
|
3563
|
+
🔴 **三条窗词刻意不合并读**:旧形上 `approval_window_expired` / `denial_limit_window_expired` /
|
|
3564
|
+
`park_sla_expired` 共用一个 `settledBy:"timeout"`,分不出来正是当年要补 `resolution` 第二个键的原因;
|
|
3565
|
+
形变把它们分成了三个词,**合并读回去等于把刚修好的判别力扔掉**。三条各有各的出路:再决一次 /
|
|
3566
|
+
「引擎替你拒了」/ 重新提这一步。
|
|
3567
|
+
|
|
3568
|
+
🔴 **不校上游的 I1–I4 四条不变量**(`settlement` ⇔ `origin` 同在、`ask_resolution` ⇒ 有拒绝类结算、
|
|
3569
|
+
`human_allowed` + `denied` = 复查否决、`allowed` ⇒ 结算缺席或恰是 `human_allowed`):筛子的属主是引擎,
|
|
3570
|
+
server 对过不了筛的记录**整条不投**。包再校一遍只会在上游合法演进的当天把一条真记录判没。
|
|
3571
|
+
⚠️ 端要读 **I3**(人批了之后被复查否决)时:`isHumanSettledGate(g) && isDeniedGate(g)` —— 那时
|
|
3572
|
+
`deniedBy` 指的是**复查那一层**,approver 留在结算上,**不是** approver 的拒。
|
|
3573
|
+
|
|
3574
|
+
⚠️ **同名不同义**:本包 `hitl/parkResolver.ts` 另有一个导出型也叫 `GateOutcome`(本包**决断腿的结局**:
|
|
3575
|
+
`decided`/`aborted`/`failed`),与 sdk 的 `GateOutcome`(**引擎的门记录**)是两个语义面 ⇒ 本模块的公面型
|
|
3576
|
+
一律带 `View` 后缀,两者在任何一处都不许互相赋值。
|
|
3577
|
+
|
|
3578
|
+
#### 内部臂(`tool_end_result`)的键变化
|
|
3579
|
+
|
|
3580
|
+
| 键 | 0.59.x | 0.60.0 |
|
|
3581
|
+
|---|---|---|
|
|
3582
|
+
| `settledBy` / `resolution` / `autoDenied` / `approver` | 在场 | **四键全删,无 alias** |
|
|
3583
|
+
| `gate` | — | 🆕 整条门记录(不成形不铸键) |
|
|
3584
|
+
| `delivered` | — | 🆕 MCP 投递判词(`yes`/`no`/`unknown`,开集读;与 `errorCode` **合读**) |
|
|
3585
|
+
| `gatedCallId` | — | 🆕 耐久 park 正扣着哪个 call(tool-less park 上整键缺席) |
|
|
3586
|
+
| 其余(`errorCode`/`output`/`structured`/`truncated`/`label`/`_sema_collateral_abort`…) | 在场 | 一字未动 |
|
|
3587
|
+
|
|
3588
|
+
### 24d. `wiring_manifest.mcp[]` 的 S6-B 两位
|
|
3589
|
+
|
|
3590
|
+
`WiringManifestMcpEntry` additive 两位:`delivered?: string`(投递判词)、`httpStatus?: number`
|
|
3591
|
+
(**只**与 `errorCode === "http_status"` 同行)。与既有五位同律:逐键挑、开集读、缺席不铸。
|
|
3592
|
+
🔴 `httpStatus` **缺席而不是 `0`**(编出来的 `0` 会被读成「服务器答了状态码 0」)。
|
|
3593
|
+
🔴 `errorCode` 的码族换代到 `McpFailureKind`(退役的 `network` / `http_<status>` **两形一并作废**),
|
|
3594
|
+
本包**照旧只判非空串**(词表属主在 core;抄一份会把新词吞成缺席)。
|
|
3595
|
+
⚠️ 本层**不校**「`httpStatus` 只与 `http_status` 同行」那条配对:它是 core 的段内规矩,server 已按它铸。
|
|
3596
|
+
|
|
3597
|
+
### 24e. `capabilities.writeProtection` 与 operator 面(S-138)
|
|
3598
|
+
|
|
3599
|
+
```ts
|
|
3600
|
+
projectWriteProtectionCapability(caps: unknown): WriteProtectionReading | undefined
|
|
3601
|
+
type WriteProtectionReading =
|
|
3602
|
+
| { kind: 'unobserved' } | { kind: 'not_reported' } | { kind: 'none' }
|
|
3603
|
+
| { kind: 'present'; view: { armed: boolean; rows: number; replaced: boolean } }
|
|
3604
|
+
writeProtectionDoctorDetail(reading): string // 唯一措辞铸点(三端共用一句话)
|
|
3605
|
+
noteEngineCapsForWriteProtection(baseUrl, caps) // 宿主 caps probe 的 tee 落点
|
|
3606
|
+
observedWriteProtection(baseUrl?): WriteProtectionReading
|
|
3607
|
+
forgetWriteProtectionReading(baseUrl?) // 引擎温切成功后的换代失效口
|
|
3608
|
+
|
|
3609
|
+
projectWriteProtectionPosture(wiring: unknown): WriteProtectionPostureView | undefined // operator 面(带行内容)
|
|
3610
|
+
writeProtectionPostureDetail(posture): string
|
|
3611
|
+
```
|
|
3612
|
+
|
|
3613
|
+
与 `sqlEngineCapability` **同构同纪律**(四态词汇 / tee + 换代失效口 / 唯一措辞铸点 / 不按 principal 分域)。
|
|
3614
|
+
本格独有的三条:
|
|
3615
|
+
|
|
3616
|
+
- 🔴 **三位刻意不合成一个布尔**:合成之后「引擎缺省表在岗」与「运维换了一张自己的表」同为 `true`,
|
|
3617
|
+
而两者对运维说的是两件事。`replaced` 把「整表替换成**空**表」也算 `true`(报 `false` 会把
|
|
3618
|
+
「运维亲手关掉了表」说成「缺省表没被动过」)。
|
|
3619
|
+
- 🔴 `not_reported` **不是**「这台部署没有写保护表」—— 引擎侧**座位缺席时缺省表在岗**(按键名 grep
|
|
3620
|
+
推断「没装」会推错,上游把这条写进了本位顶注)。四句话互不相同、也互不蕴含。
|
|
3621
|
+
- 🔴 **最小披露**:逐行 name/kind **不上租户面**(部署自定义行可能含内部路径名,列出来等于告诉想
|
|
3622
|
+
绕过的人「哪些名字不受保护」)。行内容只在 operator 面,而那个端点本身是 operator-gated。
|
|
3623
|
+
🔴 与**模式型** write deny(`SENSITIVE_WRITE_PATTERNS`)是**并列机制**,两个读面分开报、绝不合成
|
|
3624
|
+
一位(两者的解法不同:一套改模式,一套改名表)。
|
|
3625
|
+
- operator 面:`rows: []` 是**正面事实**(显式无表),照铸 `[]`;坏行只丢那一行;`source` 读不出 ⇒
|
|
3626
|
+
**整段缺席**(没有来源词的行表答不了「这是缺省表还是你换过的表」)。行名是 UNTRUSTED 自由文本 ⇒
|
|
3627
|
+
呈前逐条消毒 + 封长。
|
|
3628
|
+
|
|
3629
|
+
### 24e-bis. 🔴 异源对抗复审逼出的四条 —— 同批修根,各带正反控
|
|
3630
|
+
|
|
3631
|
+
这四条**不是推演**,是复现出来的反例(红先绿后的红都在车内留了日志)。列在这里是因为端要知道
|
|
3632
|
+
其中两条改变了可观察行为:
|
|
3633
|
+
|
|
3634
|
+
| # | 病 | 修 | 端要知道的 |
|
|
3635
|
+
|---|---|---|---|
|
|
3636
|
+
| ① | **旧 caps 探测覆盖新写保护读数**:代际闸在 probe **返回之后**才跑,而读面 tee 是在 probe **内部**落地的 ⇒ 一次慢的旧探测能在 `invalidateEngineCaps` + 新探测都走完之后把**上一台引擎**的读数写回来(屏上是一句肯定句:「这台部署有 12 行写保护」) | 两个 tee(`noteEngineCapsForWriteProtection` / `noteEngineCapsForSqlEngine`,**同批改齐**)加 additive 第三参 `{ generation }`;新读口 `engineCapsGeneration(baseUrl)` 让宿主在**起探测那一刻**抓号 | 🔴 **端要跟**:见 24h 第 19 行。不传 = 逐字节同行为(additive 供给,不是强制门);🔴 **tee 自己抓号是假判据**(陈旧那一发在 tee 时读到的是新代号) |
|
|
3637
|
+
| ② | **因由座在场却读不出被洗成成功**:`terminal: null` / `{}` / `[]` / `{kind:7}`(混合载体、schema 漂移)此前返 `null`,消费点当成「连终态词都没有」而落成功臂 —— 一条明确 park 着的 run 投影成空成功 | 这几形改落 `unknown` 臂(`word` **缺席** = 那个词本身读不出);措辞说「读不出」而不是编一个词 | 🔴 `RunTerminalRead.unknown.word` 从必填变成**可选**:缺席 = 因由座在场但整个读不出,**不是**「没有终局」(那一格仍是 `null`) |
|
|
3638
|
+
| ③ | **ask 工具身份被门种压过**:`gateKind:"irreversible_ask"` + `toolName:"AskUserQuestion"` 在门种早退里被判 tool-gate ⇒ 工具臂弹一张**没有问句**的卡、送出**不带 answer 的 approve**(一次静默的错动作:用户从没被问过,引擎却收到「批准」) | `askParkRowArm` 把「toolName 明说是 ask 工具 ⇒ 问答臂」提到**最前**,门种一律不再压过它 —— 与 `frameRouter` 那条 park 分流**同一个次序** | 行为变化:`tool_approval` / `irreversible_ask` 门下的 **ask 工具名**行从工具臂改到问答臂。非 ask 工具名一格未动(B-042 的修根不受影响) |
|
|
3639
|
+
| ④ | **失败四类仍不结构可分**:两条决断腿的 `failed` 把 typed 错拍平成一个 `reason` 串,「取件没有可决行 / 卡口没装 / 引擎语义拒绝 / 出站瞬断耗尽」在消费点同形,分流只能正则匹配文案 | `FsApprovalOutcome.failed` 与 `parkResolver.GateOutcome.failed` **同批**加**必填** `stage`(`fetch`/`input`/`card`/`decide`/`orchestration`);fs 腿另加 `safetyCode`(本包的安全停闭五词,ask 腿早有对应位) | 🔴 **型面 BREAKING**(两条 failed 臂多一个必填位);🔴 **只有 `stage === 'decide'` 代表「引擎那一头知道这次决断」**——安全停的第一问就是它,前三段一个字节都没送出去。三个码位是**三个命名空间**:`code`(取件闭集)/ `safetyCode`(本地安全停闭五词)/ `errorCode`(引擎 wire 码,开集),绝不合并 |
|
|
3640
|
+
|
|
3641
|
+
### 24f. B-042 —— park 重开取件的身份闸与行路由**同源**
|
|
3642
|
+
|
|
3643
|
+
**病形** = `[paired-mechanisms-must-share-premise]`。三端把一行**路由**进工具门臂用的是
|
|
3644
|
+
`askParkRowArm`(kind ∈ ask 门族 ∧ 不是 ask 工具 ⇒ `tool-gate`),而 `surfaceFsApprovalAndDecide`
|
|
3645
|
+
取件时的**身份闸**此前是 `isToolApprovalGate`,只有两条腿(一等 `kind === 'tool_approval'` 放行任意
|
|
3646
|
+
toolName,或 fs 写 / shell 的**名字腿**)。于是:
|
|
3647
|
+
|
|
3648
|
+
| 行 | 路由说 | 修前身份闸说 | 结果 |
|
|
3649
|
+
|---|---|---|---|
|
|
3650
|
+
| `gateKind:"human"` + `Write`/`Bash` | tool-gate | ✅(名字腿) | 正常 |
|
|
3651
|
+
| `gateKind:"tool_approval"`(无 toolName) | tool-gate | ✅(一等 kind) | 正常 |
|
|
3652
|
+
| `gateKind:"irreversible_ask"` + `mcp__srv__tool` | tool-gate | ❌ 两腿都不中 | 🔴 身份逐字命中却被族闸判出局 ⇒ 候选池收紧到空 ⇒ `no_pending` ⇒ 卡永远重开不了 |
|
|
3653
|
+
|
|
3654
|
+
**同形族** = 凡 `irreversible_ask` / `human` / `policy_ask` 门下、工具名不在 fs 写三件 + Bash 里的行
|
|
3655
|
+
(MCP 远端工具、WebFetch、NotebookEdit、agent 工具…)全中招;内建 fs/Bash 行只是**恰好**走了名字腿。
|
|
3656
|
+
|
|
3657
|
+
**修** = 身份闸读**同一只**谓词:`askParkRowArm(row) === 'tool-gate'`。名字腿只留给 leg 2
|
|
3658
|
+
「同族第一行」的 `matches`。外族门种(plan_review / resource_limit / 未来新词)由
|
|
3659
|
+
`findPendingForTask` 上游的 `askParkForeignGateKind` 整族先滤 —— 两道闸串联的语义与修前逐字相同,
|
|
3660
|
+
变的只是「工具臂认不认得非 fs/Bash 的工具名」。
|
|
3661
|
+
|
|
3662
|
+
🔴 **端零改**(壳侧不补丁,跨仓源头修律)。卡入参对未知工具名走**通用卡形**(壳的
|
|
3663
|
+
`FallbackPermissionRequest` 已渲得出)。
|
|
3664
|
+
|
|
3665
|
+
### 24g. B-037 候包两件
|
|
3666
|
+
|
|
3667
|
+
- **① 无窗 `preflight_rejected` 第三句改自足措辞**。旧句「The engine line **above** says whether this
|
|
3668
|
+
token can still be redeemed」把答案**外包给屏上另一行**,而那一行只有 **turn 级**报错路径才有;
|
|
3669
|
+
同一份文案在**决断路径**(审批卡 decide 的 409)上也照发,那条路上引擎原文按凭证纪律根本不上屏
|
|
3670
|
+
⇒ 用户读到一句指向不存在的东西的指路话。新句:
|
|
3671
|
+
`The engine supplied no wait, so this refusal does not say whether the token stays redeemable`。
|
|
3672
|
+
🔴 门的判据锚在**性质**(每一句都自足)而不是「不等于那一个旧串」—— 后者对任何一句**新的**外包话
|
|
3673
|
+
都为真;门枚举了一族外包措辞(` above` / `previous line` / `the line before` / …)并带判别力自证。
|
|
3674
|
+
**端要做的**:如果端复述过旧句,同批换掉;走包面 `resumeRefusalContent` 的端零改。
|
|
3675
|
+
- **② `FsApprovalOutcome.failed` 补 `errorCode`**(与 0.47.0 补 `currentPending` **同形**)。本类型把
|
|
3676
|
+
typed 错**拍平成 `reason` 串**,于是只读 outcome 形的消费者对机读码全盲,要分辨拒绝族只能去正则
|
|
3677
|
+
匹配那句拍平文案(里面嵌的是 `String(e)`,上游改一个词就静默空转)。新单源读口 `readWireErrorCode`
|
|
3678
|
+
(结构视图读,不 `instanceof` —— 抛出来的是不是 SDK 的错误类由**宿主注入的传输层**决定);
|
|
3679
|
+
ask 腿的 `parkResolver.GateOutcome.failed` **同批同形**改齐(同形存量清剿)。
|
|
3680
|
+
🔴 与本包铸的 `code`(闭集取件判别位,今天只有 `no_pending`)是**两个命名空间,绝不合并**。
|
|
3681
|
+
🔴 **只搬码不搬正文**:错误正文可能含凭据/内部路径,照旧只进 `reason` 的既有拍平位。
|
|
3682
|
+
|
|
3683
|
+
### 24h. 🔴 壳换装清单(cli 1.0.104 照单做;web / desktop 同形)
|
|
3684
|
+
|
|
3685
|
+
| # | 壳侧坐标 | 修前读的 | 换成 | 备注 |
|
|
3686
|
+
|---|---|---|---|---|
|
|
3687
|
+
| 1 | `sema-cli/src/sema/upstreamBridge.ts` 的 `timeoutWindowExpiredCallIdOf` | `f.settledBy === 'timeout' && f.resolution === 'window_expired'` | `isApprovalWindowExpiredGate(gateOutcomeOf(frame))` | 🔴 **别自拼**:三条窗词现在分得开,自拼合取会把 `denial_limit` / `park_sla` 两条误收 |
|
|
3688
|
+
| 2 | `sema-cli/src/sema/askParkExpiry.ts` 的 `readToolEndAutoDenied` | `rec.autoDenied === true` | `isDenialLimitAutoDeniedGate(gateOutcomeOf(frame))` | 语义逐字不变(引擎的限额回落窗自己拒的);缺席纪律不变 |
|
|
3689
|
+
| 3 | 同上,迟到决断行/审计面读结算方身份 | `f.approver` | `gateApprover(gateOutcomeOf(frame))` | UNTRUSTED-for-display 不变 |
|
|
3690
|
+
| 4 | 转录审批账本行的**出身**词 | `f.origin`(卡面)/ 无(帧面) | 帧面改读 `gateOutcomeOf(frame)?.origin` | 卡面那一位(`ApprovalCardRequest.origin`)**没变**,别混 |
|
|
3691
|
+
| 5 | 拒绝**层次**的呈现(如果端要渲「谁拒的」) | 无(旧形答不了) | `gateDeniedBy(...)` 八词,**带 `default` 臂** | 新能力,不是换装 |
|
|
3692
|
+
| 6 | `sema-cli/src/commands/doctor/permissionsPostureRow.ts:70` 的 `writeProtection: string \| null` | 恒 `null`(留位) | `writeProtectionDoctorDetail(observedWriteProtection())` | 留位注里点名的「候 client-core 0.60.0 投影」到货 |
|
|
3693
|
+
| 7 | `sema-cli/src/commands/doctor/axesRows.ts:234` 的 `writeProtection: null` | 恒 `null`(留位) | 同上 | 🔴 **绝不在壳里自铸一份写保护表**(留位注自己写的红线) |
|
|
3694
|
+
| 8 | doctor 的 caps 单飞探测口(`doctorEngineCapsProbe`) | 只 tee 给 `sql` / `pricingConfigured` | **同批** tee 给 `noteEngineCapsForWriteProtection(baseUrl, caps)` | 不 tee ⇒ 第 6/7 两行恒 `not observed`,而那句话会被读成「这台部署没有写保护」 |
|
|
3695
|
+
| 9 | 引擎温切臂(已调 `forgetSqlEngineReading`) | — | **同批**调孪生口 `forgetWriteProtectionReading(baseUrl)` | 漏它 ⇒ 上一台引擎的行数被当成当代事实答出去 |
|
|
3696
|
+
| 10 | `sema-cli/src/sema/liveClient.ts:127/348` 的 caps tee | 现有两处 | 复核是否与第 8 行同一个 anchor;不是就各自补 | 两条 tee 分叉 = 一条腿有读数、另一条恒 unobserved |
|
|
3697
|
+
| 11 | 读 `done` 帧终局的**任何**壳侧点 | `result.status` / `errorCode` / `blockedReason` / `checkpointGate` | `readRunTerminal(result)` 五臂 | 🔴 壳对 done 帧的消费**必须**经本包(sdk-isolation 门公约) |
|
|
3698
|
+
| 12 | `-p` 消费面对 park 帧 `errorCode` 的读取 | `review.pending` 在场 | **改读 `terminal.kind === 'paused'`** | 见 24b 末的「可观察行为变化」:那一位从 0.60.0 起恒缺席 |
|
|
3699
|
+
| 13 | wire 锚登记表(`run-wire-anchor-contract-test`) | `settledBy` / `resolution` / `autoDenied` 三条文案锚 | 改**机读锚**(`gate.settlement.kind` / `gate.disposition.deniedBy`) | 🔴 提货批**必跑**锚契约门;DRIFT 恒机读锚优先 |
|
|
3700
|
+
| 14 | `_ask-park-reopen-live-pty` | 只有 fs/Bash 腿 | 加 **MCP fixture 工具腿** | B-042 的黑盒半场:换装后应转绿 |
|
|
3701
|
+
| 15 | `package.json` | `@sema-agent/sdk ^8.3.0` + `client-core ^0.59.x` | `^8.4.0` + `^0.60.0` | 🔴 整链缺一不换(server 7.64.0 / sdk 8.4.0 / client-core 0.60.0 / settings-schema 1.8.0) |
|
|
3702
|
+
| 16 | 壳侧任何回答「这次调用 settle 了没有」的点 | 单锚 `settledBy`/`resolution` 合取 | `isApprovalWindowExpiredGate(gateOutcomeOf(f)) \|\| isGateParkedToolEnd(f)` **两形合读** | 🔴 真机黑盒:park 路径的 `tool_end` 上**根本没有 `gate`**,只写一半 = 那一格永远读不到 |
|
|
3703
|
+
| 17 | 壳侧 `/decide` 请求体 | (本包与壳都没带过) | **不许**新增 `settledBy` / `hostDecision` 两键 | 真机黑盒:server **都不读**(结算方从认证主体算);带了是死字节。结算方身份只从**响应/帧**的 `gate.settlement` 读 |
|
|
3704
|
+
| 18 | 壳侧 `TaskSpec.checkpointStore` | 无 | **保持无** | server 未接线(部署侧配置决定);包侧只登记不投影发送 |
|
|
3705
|
+
|
|
3706
|
+
| 19 | 壳侧 caps 探测闭包(第 8 行那一处) | `noteEngineCapsFor*(baseUrl, caps)` | 起探测那一刻 `const gen = engineCapsGeneration(baseUrl)`,tee 时传 `{ generation: gen }` | 🔴 不传只是维持现状(不会变坏),传了才真正关掉「旧探测覆盖新读数」那条竞态;两个 tee(sql / writeProtection)**一起**传,只传一个 = 另一格照旧会被旧响应写脏 |
|
|
3707
|
+
|
|
3708
|
+
**端可以不动的**(复核后登记,不是遗漏):run 行**列** `status` / `errorCode`、任务列表行 `status`、
|
|
3709
|
+
`/decide` 200 体、fleet 终态词 —— engine 7.64.0 **没动**它们(只是数据源改成从因由投影一次)。
|
|
3710
|
+
|
|
3711
|
+
### 24i. 黑盒判据(test 视角;不引用内部实现)
|
|
3712
|
+
|
|
3713
|
+
| # | 构造 | 期望 |
|
|
3714
|
+
|---|---|---|
|
|
3715
|
+
| G1 | 对 engine 7.64.0 跑一个正常 turn | `-p --output-format json` 的 result 帧 `subtype:"success"` / `is_error:false`;`SEMA_DEBUG` 留痕的 done 帧含 `terminal.kind`,**不含** `status` |
|
|
3716
|
+
| G2 | `--max-turns 1` 触发轮数到限 | result 帧 `subtype:"error_max_turns"`,additive `errorCode` = `limits.max_turns_exceeded` |
|
|
3717
|
+
| G3 | `-p` 下模型调 AskUserQuestion(无 overlay) | result 帧 `is_error:true` 且文案含「an interactive answer (AskUserQuestion)」;🔴 **exit code ≠ 0**(绝不空成功) |
|
|
3718
|
+
| G4 | `-p` 下 plan-mode ExitPlanMode | 文案含「a plan review」,与 G3 那句**逐字不同** |
|
|
3719
|
+
| G5 | 同上两形 | result 帧上 additive `errorCode` **整键缺席**(0.60.0 行为变化;修前是 `review.pending`) |
|
|
3720
|
+
| G6 | 引擎发一个本客户端不认识的终局因由词 | result 帧 `is_error:true` 且 errors[] 里**逐字**出现那个词;绝不 `subtype:"success"` |
|
|
3721
|
+
| G7 | 跨 7.64.0 升级过的部署,读一条**升级前**的历史 run | `GET /v1/runs/:id` 回体仍是平面形,而客户端投影出的终帧 **kind 语义相同**(park 仍是 park、failed 仍是 failed);不要求字节级同形——历史平面行的 additive `errorCode` 残留可与新行不同(test 双轨实测的窄形差异,记账不修) |
|
|
3722
|
+
| G8 | 同一 session 已有活跃 run 时再提交 | 409 拒绝信封照旧被投影成「This session is locked by an earlier run…」那一行(拒绝信封没换形) |
|
|
3723
|
+
| G9 | auto 模式下分类器限额回落窗走完自动拒一次 Bash | 转录/审计面渲「自动拒(限额回落)」那一句,**不是**「已转后台候批」 |
|
|
3724
|
+
| G10 | 普通审批窗 TTL 到期 | 渲「窗到期」那一句;与 G9 **逐字不同**(三条窗词分得开) |
|
|
3725
|
+
| G11 | 人拒一次并写了理由 | 卡/审计面同时出现拒绝人身份与那句话;`deniedBy` 渲 `ask_resolution` |
|
|
3726
|
+
| G12 | 一台 MCP 服务器 401 | `wiring_manifest` 面渲出机器码 `http_status` + 状态 401 + 投递判词;**没有**远端返回的自由文本 |
|
|
3727
|
+
| G13 | `sema doctor` 对 7.64.0 引擎 | Permissions 行的写保护段渲三位(是否武装 / 几行 / 缺省表还在不在岗),与 `GET /v1/capabilities.writeProtection` 逐键一致 |
|
|
3728
|
+
| G14 | `sema doctor` 一次性进程(引擎不可达) | 写保护段说「未观测」,**不说**「这台部署没有写保护」;与 G13 那句逐字不同 |
|
|
3729
|
+
| G15 | MCP 工具触发 `irreversible_ask` park → `kill -9` 壳 → `--resume` | 审批卡**重开**(通用卡形 + 上方 park 说明行);修前两次均渲「could not put that card back on screen here」 |
|
|
3730
|
+
| G16 | 决断路径上撞 409 `resume.preflight_rejected`(无窗) | 屏上那句第三分句**自足**:不出现「the engine line above」这类指向另一行的话 |
|
|
3731
|
+
| G17 | 审批窗走完(默认 300s)未决走 park 路径 | `tool_end` 帧上**没有** `gate`、只有 `errorCode:"gate.parked"`;端仍渲得出「已转后台候批」那一句(读的是第二形谓词),**不**渲成「窗走完拒了」 |
|
|
3732
|
+
| G18 | 同步提交直接撞上审批暂停 | 200 回体是 `{taskId, sessionId, status:"suspended"}`(既无八键也无 `terminal`,**连门都没有**);客户端投影成 park(is_error true),**绝不**是 success 或 failed;`GET /v1/runs/:id` 此时读不到 `result`,端继续等而不报终局 |
|
|
3733
|
+
| G18b | 同步提交直接撞上 **plan 复核** | 同上但 `status:"needs_review"`;客户端**仍弹 plan-review 卡**、终帧措辞说「a plan review」而不是「an interactive answer」(那一形上只有这个词能分出两者) |
|
|
3734
|
+
| G19 | 三臂 decide(不带 / 带 `settledBy` / 带 `hostDecision`) | 三臂全 200 且 `gate.settlement` 一致;客户端**推荐路径 `HitlBridge.decideTool`** 的发送面零这两个键(抓包直证);🔴 判据只覆盖这条路径——`makeEngineWireClient(...).approvals.decide` 是 SDK 透传层,调用方显式塞进去的键会原样上 wire(server 不读,属死字节);端**不许**拿裸 wire client 发 decide 带这两键 |
|
|
3735
|
+
|
|
3736
|
+
### 24j. 常驻门
|
|
3737
|
+
|
|
3738
|
+
| 门 | 守什么 |
|
|
3739
|
+
|---|---|
|
|
3740
|
+
| 🆕 `scripts/run-terminal-cause-projection-test.mjs` | **B9 真机三边界各一正控一负控**(park 形无 `gate` / 同步 `suspended` 无 `terminal` / decide 产物零死字节);A 终局四臂 + `unknown` 防御臂 + 缺席格 + 两代字节判别位 + 「因由在场时平面键一个都不读」;B `tool_end.gate` 三键、坏形五种、开集读、**三条窗词的判别力矩阵**、退役四键不再上臂;C `mcp[]` 两位;D `writeProtection` 四态 + tee/失效口 + operator 面;E **B-042 锚在 `surfaceFsApprovalAndDecide` 真调用点**(不是门自己传进去的谓词);F `readWireErrorCode` |
|
|
3741
|
+
| `scripts/run-sdk-floor-test.mjs` | 地板 `8.4.0` 与 peer 声明**三位逐位等值**互绑;四条硬理由逐条成文 |
|
|
3742
|
+
| `scripts/run-engine-caps-ledger-test.mjs` | `Capabilities` **94** 键逐键处置台账(新增 `writeProtection` = `read`);台账 ↔ SDK 键集双向等值 |
|
|
3743
|
+
| `scripts/run-resume-refusal-copy-test.mjs` | ⑤ 三句逐字 + 🆕 ⑤b **自足性**(一族外包措辞逐条枚举,带判别力自证) |
|
|
3744
|
+
| `scripts/run-public-surface-test.mjs` | peer 地板 / README 三处逐字核 + 公面导出基线 **836 → 861**(测试钩 42 → 43) |
|
|
3745
|
+
| `scripts/run-client-core-typeshape-test.mjs` | 棘轮 unknown 出境 **277 → 282**(净 +5,逐格记账 + 同批退役 4 格);B4 与裸 unknown 返回**未动** |
|
|
3746
|
+
| `scripts/run-client-core-singleton-test.mjs` | 清单 282 → **285** 条;dupRisk=high 上限 **95 → 96**(`writeProtectionCapability.readingByBase`,与 SQL 那一格同族) |
|
|
3747
|
+
|
|
3748
|
+
|
|
3749
|
+
## §25 🆕 sdk 8.5.0 提货 · READ 容纳面档位 + 来源的 operator 面窄读器(S-167,0.61.0)
|
|
3750
|
+
|
|
3751
|
+
本节一件:operator 面 `GET /v1/diagnostics/wiring` 新增 `readFace: ReadFacePosture`
|
|
3752
|
+
(server ≥7.65.0)—— 这台部署 READ 容纳面此刻的**生效档**,**+ 这一档是谁定的**。与 §24e 的
|
|
3753
|
+
`writeProtection` 半场同构同纪律(防御读 / 唯一措辞铸点 / UNTRUSTED-for-display),规模小得多:
|
|
3754
|
+
**纯 additive**,无退役、无形变,老引擎(<7.65.0)上整键缺席。
|
|
3755
|
+
|
|
3756
|
+
### 25a. peer 地板 `>=8.4.0` → `>=8.5.0`
|
|
3757
|
+
|
|
3758
|
+
| 面 | 0.60.x | 0.61.0 | 端要做什么 |
|
|
3759
|
+
|---|---|---|---|
|
|
3760
|
+
| peer 地板 | `@sema-agent/sdk >=8.4.0` | **`>=8.5.0`** | 提货前先抬自己的 sdk 依赖 |
|
|
3761
|
+
| 支持窗(本读器) | — | engine ≥7.65.0 | 老引擎上整键缺席,读器按缺席处置——**不是** BREAKING,提交面/其余读面零影响 |
|
|
3762
|
+
| `Capabilities` | 94 键 | 94 键(未动) | 型面本身未动,只加了语义注;`run-engine-caps-ledger-test` 台账零改 |
|
|
3763
|
+
|
|
3764
|
+
硬理由见 `scripts/run-sdk-floor-test.mjs` 的 `FLOOR` 注:本包新模块
|
|
3765
|
+
`src/readFacePosture.ts` type-only import `ReadFacePosture` 并用它钉编译期对账
|
|
3766
|
+
(`_readFacePostureShapePin`)。<8.5.0 上这个名字根本不存在,`tsc` 报「没有导出成员」。
|
|
3767
|
+
|
|
3768
|
+
### 25b. 读器签名与形
|
|
3769
|
+
|
|
3770
|
+
```ts
|
|
3771
|
+
projectReadFacePosture(wiring: unknown): ReadFacePostureView | undefined
|
|
3772
|
+
|
|
3773
|
+
interface ReadFacePostureView {
|
|
3774
|
+
face: 'open' | 'roots' | null // 这台部署此刻的生效档;null = 没钉,引擎默认接管
|
|
3775
|
+
source: string // 这一档是谁定的(server 闭四词,按开集读)
|
|
3776
|
+
note: string // 给运维的指路句;UNTRUSTED-for-display
|
|
3777
|
+
}
|
|
3778
|
+
|
|
3779
|
+
readFacePostureDetail(view: ReadFacePostureView | undefined, opts: { reachable: boolean }): string
|
|
3780
|
+
readFaceDisagreement(capFace: unknown, posture: ReadFacePostureView | undefined): string | undefined
|
|
3781
|
+
```
|
|
3782
|
+
|
|
3783
|
+
#### 输入形与缺席语义
|
|
3784
|
+
|
|
3785
|
+
| 输入 | 读数 | 端要知道的 |
|
|
3786
|
+
|---|---|---|
|
|
3787
|
+
| `wiring.readFace` 整键缺席 | `undefined` | 老 worker(server <7.65.0)。**不是**「这台部署没有 READ 档」——那句话只有 `face: null` 才说得出口 |
|
|
3788
|
+
| 在场但 `face` 不在三态里 / `source` 非串或空串 / `note` 非串 | `undefined`,**不抛出** | 防御读:回体形不对时整只判畸形,不留半个对象。整键缺席与形坏同判 `undefined`——「为什么答不出来」不是这一位该回答的,见 `opts.reachable` |
|
|
3789
|
+
| `face: null` | `{ face: null, source, note }`(present) | 🔴 **这才是**「没有钉档」的正面事实;与上面两条「答不出来」不是一回事 |
|
|
3790
|
+
| `source` 在 server 闭四词(`env`/`center`/`posture`/`engine-default`)之外 | 原样透传 | 开集读——server 闭集之外还有 `(string & {})` 逃生口,窄读成枚举会在 server 加词当天把一个合法读数判没 |
|
|
3791
|
+
| `note` 为空串 | `note: ''` | 空文本本身也是一句读数,不是「读不出」;投影层不截长(截长是显示层的事,见 25c) |
|
|
3792
|
+
|
|
3793
|
+
### 25c. 三态措辞(黑盒锚,`readFacePostureDetail` 唯一铸点)
|
|
3794
|
+
|
|
3795
|
+
| # | 条件 | 句子(逐字) | 端要知道的 |
|
|
3796
|
+
|---|---|---|---|
|
|
3797
|
+
| ① | `view` 在场 | `read face <open\|roots\|not pinned> (source: <source> — <note>)`(`note`/`source` 呈前**先转义、后按转义结果封长** ≤256/≤40,不劈开转义 token 或合法代理对;空 `note` 省去 `— note` 半句) | 唯一会出现「not pinned」字样的分支是 `face === null`,不是①②两种「未观测/不报」 |
|
|
3798
|
+
| ② | `view` 缺席 且 `opts.reachable === false` | `read face not observed (this end could not read the engine's diagnostics)` | 这次进程压根没读到 operator 响应;与「没有 READ 档」不是同一件事 |
|
|
3799
|
+
| ③ | `view` 缺席 且 `opts.reachable === true` | `read face not reported by this engine (an engine below 7.65.0, or a response this end could not parse)` | 响应读到了,但这一位读不出来——**不武断咎为版本**:整键缺席(老引擎)与在场但形坏(≥7.65.0 引擎送出本读器解不出来的响应)在 `projectReadFacePosture` 折成同一个 `undefined`,句子同时点出两种成因,不擅自替调用方选一种 |
|
|
3800
|
+
|
|
3801
|
+
①②③ 对运维是三条不同的下一步,句子逐字互异;`opts.reachable` **只能由调用方提供**——本读器
|
|
3802
|
+
不持有连接状态,不猜。
|
|
3803
|
+
|
|
3804
|
+
### 25d. 与租户面 `capabilities.readFace` 的关系(刻意不合流)
|
|
3805
|
+
|
|
3806
|
+
两位是**同一份 boot 产物**的两个投影:租户面(`Capabilities.readFace: 'open'|'roots'|(string&{})|null|undefined`)
|
|
3807
|
+
只报生效档;operator 面多出来源与指路句。两条读面各自独立在场/缺席(operator 面这一位到得更晚,
|
|
3808
|
+
且那个端点本身是 operator-gated)——本包**不**把它们合成一位。
|
|
3809
|
+
|
|
3810
|
+
`readFaceDisagreement(capFace, posture)` 只答**分歧**(两者确实不同才开口;一致不是新闻,不铸
|
|
3811
|
+
正面「相符」句):
|
|
3812
|
+
|
|
3813
|
+
| capFace | posture | 结果 |
|
|
3814
|
+
|---|---|---|
|
|
3815
|
+
| `'roots'` | `{face:'open', …}` | `capabilities says roots, wiring says open` |
|
|
3816
|
+
| `null` | `{face:'open', …}` | `capabilities says not pinned, wiring says open` |
|
|
3817
|
+
| `'open'` | `{face:'open', …}` | `undefined`(一致,不开口) |
|
|
3818
|
+
| 任意值 | `undefined`(operator 面读不到) | `undefined`(诚实缺席,不是「没有分歧」) |
|
|
3819
|
+
| `undefined` / 非字符串非 `null` 的畸形值 | 任意 | `undefined`(判不了,不猜) |
|
|
3820
|
+
|
|
3821
|
+
渲染归端:doctor 两位都渲时,以 **operator 面(`posture`)为准**,不一致时额外说一句
|
|
3822
|
+
`readFaceDisagreement` 给出的话。
|
|
3823
|
+
|
|
3824
|
+
### 25e. 🔴 壳换装清单(cli 1.0.105 待做;本批不改 cli 仓,只登记坐标)
|
|
3825
|
+
|
|
3826
|
+
| # | 壳侧坐标 | 现状 | 待做 |
|
|
3827
|
+
|---|---|---|---|
|
|
3828
|
+
| 1 | `sema-cli/src/commands/doctor/permissionsPostureRow.ts` 的 `PermissionsPostureReading`(`writeProtection: string \| null` 那一位旁) | 只有 `writeProtection` 段(0.60.0 已接线) | 同构加一位 `readFace: string \| null`,渲 `readFacePostureDetail(observedReadFacePosture(), { reachable })`(读口/tee 姿势与 `observedWriteProtection` 同形——本批**未**提供 tee/换代失效口,壳侧若要常驻观测需自行仿 `writeProtectionCapability.ts` 那一套或改为一次性直读 `GET /v1/diagnostics/wiring`) |
|
|
3829
|
+
| 2 | 同文件的行装配(渲 `write-protection ${…}` 那一行旁) | — | 同构加一行 `read-face ${row.readFace}`,措辞不再自拼(唯一铸点在包) |
|
|
3830
|
+
| 3 | `sema-cli/src/commands/doctor/axesRows.ts`(第 226 行附近,`writeProtection` 的动态 import 段) | 动态 `import('../../sema/writeProtectionCapability.js')` 取 `writeProtectionDoctorDetail`/`observedWriteProtection` | 同构动态 import `readFacePosture.js` 取 `readFacePostureDetail`;🔴 **绝不在壳里自铸这句话**——留位注自己写的红线,与 write protection 那一位同律 |
|
|
3831
|
+
| 4 | doctor 的 caps/wiring 单飞探测口 | 已 tee 给 write protection(第 8 行,§24h) | operator 面 `GET /v1/diagnostics/wiring` 是**独立请求**(不是 caps 探测的一部分)——壳需要另起一次 fetch 或复用既有 wiring 诊断腿,把响应体喂 `projectReadFacePosture` |
|
|
3832
|
+
| 5 | doctor 渲染层(两位都渲的场合) | — | 同批可选:调 `readFaceDisagreement(capabilities.readFace, posture)`,不一致时追加一行(25d) |
|
|
3833
|
+
|
|
3834
|
+
**端可以不动的**:`GET /v1/capabilities.readFace`(租户面)的现有消费点——本批未改它的型或语义,
|
|
3835
|
+
只是新增了 operator 面的第二个投影。
|
|
3836
|
+
|
|
3837
|
+
### 25f. 黑盒判据(test 视角;不引用内部实现)
|
|
3838
|
+
|
|
3839
|
+
| # | 构造 | 期望 |
|
|
3840
|
+
|---|---|---|
|
|
3841
|
+
| G-r1 | 单机零配置部署,`sema doctor` 对 engine ≥7.65.0 | operator 面读到 `read face open (source: posture — …)` 或 `(source: env — …)`(取决于是否设了 `READ_FACE` env),**不是**「not observed」/「not reported」 |
|
|
3842
|
+
| G-r2 | 用户设 `READ_FACE=roots` 后起引擎,`sema doctor` | `read face roots (source: env — …)`;`source` 恒为 `env`(env 赢过其余来源) |
|
|
3843
|
+
| G-r3 | `sema doctor` 一次性进程,引擎不可达 | 渲 `read face not observed (this end could not read the engine's diagnostics)`,**不说**「这台部署没有 READ 档」 |
|
|
3844
|
+
| G-r4 | `sema doctor` 对 engine <7.65.0(整键缺席)**或** engine ≥7.65.0 送出本读器解不出来的响应(在场但形坏) | 渲 `read face not reported by this engine (an engine below 7.65.0, or a response this end could not parse)`;与 G-r3 那句逐字不同;句子不武断咎为版本(两种输入投影层折成同一个 `undefined`,措辞不许替其中一种撒谎) |
|
|
3845
|
+
| G-r5 | 部署把租户面与 operator 面钉成不同档(如 center 面已切但进程未重启) | doctor 额外渲 `capabilities says <X>, wiring says <Y>` 一行;两位相符时**不**渲这一行 |
|
|
3846
|
+
|
|
3847
|
+
### 25g. 常驻门
|
|
3848
|
+
|
|
3849
|
+
| 门 | 守什么 |
|
|
3850
|
+
|---|---|
|
|
3851
|
+
| 🆕 `scripts/run-read-face-posture-projection-test.mjs` | 三态措辞逐字互异 + 四 source 开集读 + `face:null` 正面事实 + 坏形不抛 + `note` 截长/消毒 + `readFaceDisagreement` 正反两格 + sdk `openapi.yaml` 的 `ReadFacePosture` 组件真字节(必填三键 + `additionalProperties:false`) |
|
|
3852
|
+
| `scripts/run-sdk-floor-test.mjs` | 地板 `8.5.0` 与 peer 声明**三位逐位等值**互绑 |
|
|
3853
|
+
| `scripts/run-public-surface-test.mjs` | peer 地板 / README 三处逐字核 + 公面导出基线 **861 → 864**(测试钩数未动) |
|
|
3854
|
+
| `scripts/run-integration-doc-freshness-test.mjs` | 本节新增坐标在盘 + 入库;§0a/§2/§2b 计数与基线等值 |
|