@sema-agent/client-core 0.37.0 → 0.38.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +159 -14
- package/README.md +3 -2
- package/dist/adapt/arms.js +40 -0
- package/dist/adapt.d.ts +1 -1
- package/dist/adapt.js +2 -0
- package/dist/adapter/activeRunSelfHeal.d.ts +1 -1
- package/dist/adapter/activeRunSelfHeal.js +6 -6
- package/dist/adapter/downstream/eventToSdkMessage.js +94 -0
- package/dist/agentSession/backgroundView.d.ts +1 -1
- package/dist/agentSession/backgroundView.js +1 -1
- package/dist/engineCapsCache.d.ts +33 -1
- package/dist/engineCapsCache.js +26 -4
- package/dist/engineErrorCodes.d.ts +20 -0
- package/dist/engineErrorCodes.js +44 -0
- package/dist/fleet/fleetLedger.d.ts +1 -1
- package/dist/fleet/fleetLedger.js +3 -3
- package/dist/fleet/fleetProjection.d.ts +34 -0
- package/dist/fleet/fleetProjection.js +40 -0
- package/dist/hitl/armedGateRegistry.d.ts +1 -1
- package/dist/hitl/armedGateRegistry.js +2 -2
- package/dist/hitl/hitlBridge.js +77 -18
- package/dist/hitl/persistedRulesWire.d.ts +1 -1
- package/dist/hitl/persistedRulesWire.js +4 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -0
- package/dist/model/modelSupplyRules.d.ts +102 -0
- package/dist/model/modelSupplyRules.js +149 -0
- package/dist/model/providerPresets.js +68 -12
- package/dist/request/taskRequest.d.ts +1 -1
- package/dist/request/taskRequest.js +8 -8
- package/dist/seam.d.ts +48 -1
- package/dist/seam.js +7 -0
- package/dist/subagent/engineSubagentResume.d.ts +20 -2
- package/dist/subagent/engineSubagentResume.js +8 -1
- package/dist/subagent/engineSubagentSteer.d.ts +1 -1
- package/dist/subagent/engineSubagentSteer.js +1 -1
- package/dist/subagent/subagentOwnerAbsence.js +1 -1
- package/dist/subagentContentStore.js +1 -1
- package/dist/wireErrorTriage.js +1 -1
- package/docs/INTEGRATION-CLIENTS.md +70 -20
- package/docs/REFACTOR-LEDGER.md +2 -2
- package/package.json +3 -3
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **本档是什么**:`@sema-agent/client-core` 作为**上游包**,对它的三个宿主端(`sema-cli` TUI /
|
|
4
4
|
> `sema-web` 聊天区 / `sema-desktop` session-host)的**正式接入文档**。按接入文档宪法(clay 08-12,
|
|
5
|
-
>
|
|
5
|
+
> 协作板 [3680])立档:消费上游先要详细全接入文档,**文档报错可直接打回**。
|
|
6
6
|
>
|
|
7
7
|
> **本档不是什么**:它**不是契约源**。契约源 = `src/**` 的实现本身 + 各文件头注/JSDoc。
|
|
8
8
|
> 本档的每一节都给**实现锚**(文件 + 符号名),读者据锚对账;对不上以真码为准,**当场改本档**。
|
|
@@ -15,22 +15,24 @@
|
|
|
15
15
|
|
|
16
16
|
## §0 版本锚与重扫纪律
|
|
17
17
|
|
|
18
|
-
### 0a. 版本锚(2026-08-
|
|
18
|
+
### 0a. 版本锚(2026-08-21)
|
|
19
19
|
|
|
20
20
|
| 项 | 值 | 真源 |
|
|
21
21
|
|---|---|---|
|
|
22
|
-
| 本包 | `@sema-agent/client-core` **0.
|
|
23
|
-
| peer:wire 契约 | `@sema-agent/sdk` **>=7.
|
|
22
|
+
| 本包 | `@sema-agent/client-core` **0.38.0** | `package.json` `version` |
|
|
23
|
+
| peer:wire 契约 | `@sema-agent/sdk` **>=7.2.0**(value-level,非 type-only) | `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
|
+
| 公开导出面 | **764** 个运行期符号(+ 39 个测试钩;= npm `0.38.0` 的值;`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
|
|
|
30
|
-
⚠️ 0.
|
|
31
|
-
端注意:0.
|
|
32
|
-
`
|
|
33
|
-
|
|
30
|
+
⚠️ 0.38.0 已于 2026-08-21 发布,本表与 npm 最新版重新对齐(工作树 = npm)。装旧版(≤0.37.0)的
|
|
31
|
+
端注意:0.38.0 新增的 **11 个 additive 导出**(`engineCapState` + `EngineCapState`、
|
|
32
|
+
`resolveEntryVision` / `computeDeleteBlockers` / `computeDeleteWarnings`、`CONFIG_DELEGATION_ENTRY_CAPS` /
|
|
33
|
+
`DELEGATION_CONCURRENCY_CAP` / `DELEGATION_SESSION_CAP` / `DELEGATION_CAP_CODES` / `isDelegationCapCode`、
|
|
34
|
+
`wireCycleSeq` / `wireRetiredBy`)在旧版上按名 import 会**在 ESM 实例化当场炸**(具名导出不存在)——
|
|
35
|
+
提货前先抬依赖。旧版对表以 `CHANGELOG.md` 对应版本段为准。
|
|
34
36
|
|
|
35
37
|
🔴 **本表里仍然手抄的数字都有门看着**(#252,2026-08-14):`scripts/run-integration-doc-freshness-test.mjs`
|
|
36
38
|
① 段把 638 / 32 / §2b 十六域名数之和 / 191 / 4 / 33 逐个对 `public-export-baseline.json` 算出来的值,
|
|
@@ -99,7 +101,7 @@ SDK 核对「本包 import 的每个值级符号仍然导出」「`TaskStats.cos
|
|
|
99
101
|
|
|
100
102
|
## §2 公共导出面地图(按域)
|
|
101
103
|
|
|
102
|
-
> 全集真源 = `scripts/public-export-baseline.json` 的 `names`(**
|
|
104
|
+
> 全集真源 = `scripts/public-export-baseline.json` 的 `names`(**764** 项)。
|
|
103
105
|
> 本节**不逐名抄**,只给「域 → 承重导出 → 用途 → 实现锚」。承重导出 = 一个端为了让这个域干活
|
|
104
106
|
> **必须**直接调到的那几个符号;其余是它们的类型、变体与辅助位。
|
|
105
107
|
> 单一入口:`import { … } from '@sema-agent/client-core'`(`exports` 只有 `.` 一个;
|
|
@@ -109,7 +111,7 @@ SDK 核对「本包 import 的每个值级符号仍然导出」「`TaskStats.cos
|
|
|
109
111
|
|
|
110
112
|
`public-export-baseline.json` 由 **`dist/index.js` 的运行期导出**生成(生成口径自述见
|
|
111
113
|
`scripts/run-client-core-typeshape-test.mjs`,双向精确集合门在 `scripts/run-public-surface-test.mjs`)。
|
|
112
|
-
实测:
|
|
114
|
+
实测:764 项 **100% 是运行期导出,零 type-only**。
|
|
113
115
|
|
|
114
116
|
**推论(端必须知道)**:
|
|
115
117
|
- barrel 导出的**类型**面比 707 大得多,且**不被这道门看守** —— `AdapterContext` / `SeamEvent` /
|
|
@@ -118,12 +120,12 @@ SDK 核对「本包 import 的每个值级符号仍然导出」「`TaskStats.cos
|
|
|
118
120
|
端依赖这些类型是合法的,但**不要**拿基线 diff 当"类型面没变"的证据。
|
|
119
121
|
- `src/agentSession/contract.ts` 对基线贡献 **0** 项(纯类型模块,`export *` 在 dist 里是空转发)。
|
|
120
122
|
|
|
121
|
-
|
|
123
|
+
764 项的内部构成(帮助端估读表大小):**225** 项是 `SCREAMING_SNAKE` 常量数据表/词汇表
|
|
122
124
|
(矩阵、键集、env 名、锚串)而非可调用物;**5** 项是 PascalCase 运行期值
|
|
123
125
|
(`ControlRouter` / `ControlSafetyError` / `HitlBridge` / `HitlSafetyError` / `DecideTransportRetryExhaustedError`);
|
|
124
126
|
**39** 项是 `*For(sessionKey, …)` 的 per-session 变体(§6;其中 `engineNamespaceKeyFor` 是命名巧合 —— 参数是 baseUrl 不是 sessionKey,见域 14)。
|
|
125
127
|
|
|
126
|
-
### 2b. 域图(16 域,逐域计数之和 =
|
|
128
|
+
### 2b. 域图(16 域,逐域计数之和 = 764)
|
|
127
129
|
|
|
128
130
|
| # | 域 | 名数 | 承重导出 | 用途 | 实现锚 |
|
|
129
131
|
|---|---|---|---|---|---|
|
|
@@ -131,18 +133,18 @@ SDK 核对「本包 import 的每个值级符号仍然导出」「`TaskStats.cos
|
|
|
131
133
|
| 2 | **seam 公共契约** | 2(其余为 type-only) | `CHROME_ARMS` · `deriveTranscriptId` | 公共词汇 + **id 确定性不变量**(同一条流重放 ⇒ 同一串 id)。`CHROME_ARMS` = 端「我要消费哪些 chrome 臂」的对照清单 | `src/seam.ts` |
|
|
132
134
|
| 3 | **HITL 决断卡链**(§4/§5 主战场) | 120 | `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` 注入) · `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`)、`parkRowBirthWait.ts`、`approvalDecisionNoteAudit.ts`、`askParkRowRouting.ts`、`resumeRunningCard.ts`(#265 上收的判定层)、`persistedRulesWire.ts`、`localAllowRule.ts`(#244 F2 规则侧) |
|
|
133
135
|
| 4 | **子代 wire + 面板侧信道台账** | 82 | `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` |
|
|
134
|
-
| 5 | **fleet 投影** |
|
|
136
|
+
| 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` |
|
|
135
137
|
| 6 | **请求装配(上行唯一构造口)** | 8 | `buildTaskRequest` · `REQUEST_FIELD_MATRIX` · `unregisteredRequestKeys` · `applyLiveRequestDefaults` · `taskNotificationToPrintFrame` | 两条车道(`interactive`/`print`)出站请求的**唯一**构造器;`unregisteredRequestKeys` 是可执行门 —— 端偷带一个未登记键上 wire 就红 | `src/request/taskRequest.ts`、`src/request/printNotification.ts` |
|
|
136
138
|
| 7 | **通知与 outstanding 台账** | 37 | `installNotificationQueuePort` · `normalizeTaskNotification` · `taskNotificationDedupKeyFromWire` · `registerOutstandingBgTask` / `registerOutstandingWorkflowRun` · `notificationQueuePortMisses` · `subscribeOutstandingWorkflows` · `outstandingDeliverableWorkflowCount` | `task_notification` 归一 + 去重 + 投递进宿主命令队列的**一把闸**;`outstandingDeliverableWorkflowCount()` 是 headless `-p` 的**退出门** | `src/notifications.ts`(11 个 module 台账) |
|
|
137
139
|
| 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` |
|
|
138
140
|
| 9 | **能力/旋钮 wire 门族** | 86 | `projectAgentsForWire` / `awaitTaskAgentsWire` · `hooksForWire` · `mcpConfigsToSpecs` · `ultracodeForRequest` · `resolveWebSearch` + `buildWebSearchEnv` · `attachmentsForRequest` · `projectEffectiveBody` | 逐功能的「引擎有没有这个能力 + 这个字段怎么上 wire」投影器,由 `buildTaskRequest` 组装。🔴 `hooksForWire()` 是 **fail-closed**:无 `SettingsPort` ⇒ 返回 `undefined`(信任门,不是取值) | 17 个 `src/*WireCaps.ts` + `src/scratchpadWireCaps.ts`、`src/hooksWireCaps.ts`、`src/goalStopHook.ts`、`src/websearch/searchProviderPresets.ts` |
|
|
139
141
|
| 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` |
|
|
140
|
-
| 11 | **模型目录与预算** |
|
|
142
|
+
| 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` |
|
|
141
143
|
| 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`) |
|
|
142
144
|
| 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**,纯类型 + 常量 + 纯谓词) |
|
|
143
145
|
| 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` |
|
|
144
|
-
| 15 | **控制面与传输** |
|
|
145
|
-
| 16 | **引擎词汇表与包自检** |
|
|
146
|
+
| 15 | **控制面与传输** | 73 | `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`(默认出路串单源)· `normalizeWirePrincipal`(A-028.10:principal 在场性 trim 原语 —— 全空白=缺席不发头,engineWireTarget 两臂/makeEngineWireClient/壳 livePrincipal 同尺)· `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` |
|
|
147
|
+
| 16 | **引擎词汇表与包自检** | 46 | `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` 识别表) | 三端分臂共用的**去字面化** `errorCode` 词表(病根正是三端各抄一份字面);编译期 verb 闭合门;搬迁补偿登记表 | `src/engineErrorCodes.ts`(39 项;A-028.11/.13 补 `DRAINING_ERROR_CODE`/`SCENARIO_NOT_ALLOWED_ERROR_CODE`/`RESUME_AT_ERROR_CODE_PREFIX`;#318 件④ 补 `delegation.*` 族四位 + `config.delegation_entry_caps`)、`src/classifierVerdictWire.ts`、`src/compensations.ts`、`src/clientSlice.ts` |
|
|
146
148
|
|
|
147
149
|
🔴 **`engineErrorCodes` 的开集纪律**(该文件头注逐字):这些 `ReadonlySet` / 前缀谓词一律是**识别表**,
|
|
148
150
|
回答的是「我认不认得这个码」,**绝不是**「合法码只有这些」。消费点 `switch` **必须留 `default`**,
|
|
@@ -264,6 +266,44 @@ lane 归属改用 id 形状 / `workflowRunId` 启发式判(`src/adapt/arms.ts`)
|
|
|
264
266
|
🔴 推论纪律:凡本包**没有**臂的帧,端**不能**指望 `switch (ev.type)` 的穷举保护;
|
|
265
267
|
要消费必须走 `unknown` + SDK 谓词窄化,并把这件事回报到 C 板(令④)。
|
|
266
268
|
|
|
269
|
+
### 3f. 🆕 `engine_notice` —— 引擎结构化通告(0.38.0 additive;server ≥7.36)
|
|
270
|
+
|
|
271
|
+
契约真源 = **server 仓**的 `ASSISTANT-WIRE-CONTRACT.md` **附录 D** + openapi `Event_engine_notice`
|
|
272
|
+
(跨仓坐标,故不写成本仓 `docs/` 形 —— 本档的坐标门只校**本仓**路径)。
|
|
273
|
+
引擎的结构化通告里,被 server 判为面向**本会话终端用户**的那一小撮,按 `sessionId` 路由到这条
|
|
274
|
+
会话的流上。**live 与 durable 两腿都有**(server 三条 run 腿都挂了口)。
|
|
275
|
+
|
|
276
|
+
**本包的投影**:`eventToSdkMessage` → 中性内部臂 `engine_notice`(已进 `INTERNAL_SDK_ARM_TYPES`)
|
|
277
|
+
→ `adapt()` → chrome 臂 `{ kind:'engine_notice', code, message, detail, sessionId?, ts?, eventId? }`。
|
|
278
|
+
**不铸 transcript 行**(通告是披露,不是转录物)。
|
|
279
|
+
|
|
280
|
+
🔴 **端的消费纪律(五条,每条都是「不许做什么」)**:
|
|
281
|
+
1. **按 `code` + `detail` 渲染,`message` 只作 fallback** —— core 明写 `memory.session_polluted` 的
|
|
282
|
+
message 随 `memoryProvenance` 模式变文,**按 message 文本匹配必碎**。
|
|
283
|
+
2. **`code` 是开集,认不得也不许丢帧** —— 认得的码渲专用呈现,认不得的码用 `message` 兜底展示。
|
|
284
|
+
**本包一个码都不硬编**(没有识别表、没有 switch):认不认得是渲染面的判断,库只负责送到。
|
|
285
|
+
3. **重放幂等** —— durable 腿按 `Last-Event-ID` 续读会**再送同一条**(与 `workspace_changed` 同纪律)。
|
|
286
|
+
幂等键**按序取**:`eventId`(core 铸的事件身份) > `eventSeq`(SDK 从 SSE `id:` stamp 的
|
|
287
|
+
per-task 序号,= `task_event.seq`) > 两者都缺才退 `code+ts`。
|
|
288
|
+
🔴 **最后那一档是有损的,别当等价物**:`ts` 是 **server 观察时刻(ms)**,同毫秒同码的两条真通告
|
|
289
|
+
会被折成一条(丢事实),跨重连的同一条又可能因观察时刻不同而重复。两个身份键**不互相顶替**
|
|
290
|
+
(全局身份 vs per-task 序号),都缺席时本包**都不 stamp** —— 端据此才知道自己只能退到有损那档。
|
|
291
|
+
4. 🔴 **`memory.harvest_quarantined` 的 `moved` 与 `escalated` 不可相减** —— 就地墓碑同时计入两者
|
|
292
|
+
(core 顶注)。两个数各自读、并列呈现;任何减法都会得出一个**不存在的量**。
|
|
293
|
+
5. 🔴 **缺席不可反推** —— server 对非白名单码 / 缺 `sessionId` 的通告**如实不投**(宁缺席不串台),
|
|
294
|
+
全族那一份只在 server 的运维日志里。「没收到通告」**不等于**「没发生」。
|
|
295
|
+
|
|
296
|
+
⚠️ **两条如实登记(2026-08-21 亲验)**:
|
|
297
|
+
- **SDK 类型面还没到货**:`engine_notice` 尚未进已发布 SDK 的 `AgentEvent` union
|
|
298
|
+
(sdk 仓 `3d6aebc` 已写,但 npm `@sema-agent/sdk@7.2.0` 的真 tarball 里 `dist/` 全树零命中)。
|
|
299
|
+
故本包按 `workflow_complete` / `human_input` 当年的先例走 **raw 预分派**,并留了自退休钉:
|
|
300
|
+
臂一进 union,`assertNeverArm` 就编译期真红,逼下一棒把它搬进 switch。
|
|
301
|
+
- **附录 D.3 的白名单表已失真**:档里仍写「起步白名单(server 7.36 三码)」,而 server main 的
|
|
302
|
+
`ENGINE_NOTICE_WIRE_CODES` 已是**六码**(core 5.47/5.48 的 `NOTICE_AUDIENCE` 到货后
|
|
303
|
+
`memory.hold_opened` / `hold_released` / `hold_disposed` 入册;v7.37.0 tag 上仍是三码 ⇒ 六码随
|
|
304
|
+
7.38 到)。**对本包与端零影响** —— 正因为消费面按开集写,白名单是 server 的投递判定,不是消费判据。
|
|
305
|
+
已按接入文档宪法回报 server。
|
|
306
|
+
|
|
267
307
|
---
|
|
268
308
|
|
|
269
309
|
## §4 回执消费义务(ack contract)—— 🔴 本档重点
|
|
@@ -502,6 +542,16 @@ durable park 腿走 `HitlBridge.decideTool(outcome, toolUseID, opts, preResolved
|
|
|
502
542
|
失败时也 resolve 且不缓存)。**端不得拿一个「可能是没读到」的 false 去销毁一个已到达的事实**
|
|
503
543
|
(帧到达本身就是那条车道活着的证据)。能力位是 **affordance 面**的门,不是「已到达帧」的判据。
|
|
504
544
|
- `engineCapString(baseUrl, key)`:**未判即 `undefined`**,调用方必须按「探不到 ⇒ 降级」处理。
|
|
545
|
+
- 🆕 **0.38.0 additive:`engineCapState(baseUrl, key)` —— 三态(实为五态)可分辨读口**
|
|
546
|
+
(`EngineCapState` = `'true' | 'false' | 'unprobed' | 'absent' | 'non_boolean'`)。
|
|
547
|
+
上一条那个「`false` 同时表示三件事」的塌缩,对**放行判据**是刻意且正确的(fail-closed),
|
|
548
|
+
但对**自检/诊断面**是谎报 —— doctor 要报的正是「这台引擎到底说了什么」。逐档语义:
|
|
549
|
+
`true`/`false` = **引擎明说**;`unprobed` = 没有已落地的 caps(没 kick / 在飞 / 探测失败 /
|
|
550
|
+
刚被 `invalidateEngineCaps` 作废 —— 四种成因**故意合并**,因为它们对调用方是同一个动作:
|
|
551
|
+
`await engineCapsSettled()` 再读,或按「不知道」呈现);`absent` = caps 已落地但**没有这个键**
|
|
552
|
+
(引擎比本包旧或改了名);`non_boolean` = 键在但值不是布尔(引擎申报了一个本口读不动的形)。
|
|
553
|
+
🔴 **放行面继续用 `engineCapTrue`**(它现在就是 `engineCapState(...) === 'true'` 的单源实现,
|
|
554
|
+
语义一字未变):拿三态去开放行分支 = 把 fail-closed 改成「按成因区别对待」,那是另一件事。
|
|
505
555
|
- 探测 kick:`kickEngineCapsProbe(baseUrl, probe)`(async 幂等,`probe` = `client.capabilities` 薄闭包)。
|
|
506
556
|
|
|
507
557
|
**实现锚**:`src/engineCapsCache.ts`。
|
|
@@ -719,13 +769,13 @@ CHANGELOG 0.29.0「已知局限」段与相应 JSDoc 都有成文。**别在读
|
|
|
719
769
|
| **P-12** | low | canonical 重呈短路臂**沿用 arm responder** ⇒ `ReopenPlanReviewOpts.deliverDecision` 注入口**不生效**(成文例外 + debug 留痕) | `src/hitl/planReviewWire.ts`(`mintFreshQuestionId:false` 分支) | 任何**包装决断投递**的端(重试/退避/上屏定序,cli 的 `decideRetry` 是参照)必须走默认 `mintFreshQuestionId` 臂 —— 否则你的包装被静默旁路,跑的是裸 `decidePlanReview` 的 fire-and-forget |
|
|
720
770
|
| **P-13** | 成文局限(不改行为) | 默认键下的 own-run 归属缺省腿是**进程级**证据,**不区分同一宿主进程内的会话代际** —— `/clear` 前登记的 run 在新会话语境下**仍判 owned**。最坏后果逐字:`用户看到自己旧会话的审批卡` | `src/hitl/parkOwnership.ts`(`ParkOwnershipDeps.isOwnRun` JSDoc);`docs/REFACTOR-LEDGER.md` 记为**驳为成文局限** | 多会话端必须**自注入**会话粒度的 `isOwnRun`;或传非默认 `sessionKey` 并接受缺省腿被整条跳过(代价 = 多一次诚实的 reopen-failed) |
|
|
721
771
|
| **P-14** | high(打包面) | `activeReopenResponders` 的**单活纪律是 module 单例**:两份实例 ⇒ 各退各的,跨份的旧卡退役不掉 —— **退化回修复前的重复活卡形** | `src/hitl/planReviewWire.ts`(`activeReopenResponders`,singleton-manifest 在册) | 见 §8-G:必须保证 bundle 里只有**一份** `@sema-agent/client-core` |
|
|
722
|
-
| **P-31** | med(0.32.0
|
|
772
|
+
| **P-31** | med(0.32.0 未发布登记;对抗复审第二/三轮抓出) | **fleet 面的会话锚整条走默认槽,不跟 ledger 的 `sessionKey`** —— 两条腿都中招:①`bg_notification` 的 `ownByRoot` 判据用 `engineSessionParam()` = `hostSessionFor(DEFAULT_SESSION_KEY)`;②更强的一条 —— `serverFailClosed`(meta 两位)**根本不读会话端口**,它信的是「这条流是替谁开的」,而本包给出的开流参数 `fleetStreamOptions()` / `fleetSnapshotOptions()` 是 **module 级函数、只认默认槽会话**,连 `sessionKey` 都拿不到。⇒ keyed 多会话宿主上,一条按默认槽开的流接到**非默认键** ledger 时,属于默认槽会话的通知会被放行并以 `evidence:'server_fail_closed'`(🔴 **生产 meta 组合下命中的正是这一格**)或 `'own_root'` 交给 hook | `src/fleet/fleetLedger.ts`(`serverFailClosed` / `ownByRoot` / `fleetStreamOptions`);`src/host.ts`(`hostSession` = 默认键) | 单会话宿主(cli 及今天的三端)**不受影响**——默认槽就是它的会话,流与 ledger 恒同会话。**keyed 多会话宿主**:①必须自己保证「流按哪个会话开、就接给哪个 ledger」;②在①落实之前,不要把 `evidence` 的前两格当作「属于本 ledger 会话」的证明去做有副作用的事(落库/翻卡/跨会话搬运)。🔴 正位解 = fleet 面**整条**换 per-key 会话锚(开流参数 + 归属判据一起动);**只改判据这一半会自相矛盾**(按默认槽开流、按 keyed 槽判定 ⇒ 反而丢自己的通知),故本批**不动行为**,登记待裁 |
|
|
723
773
|
|
|
724
774
|
### 7d. 请求面与其它在册件
|
|
725
775
|
|
|
726
776
|
| ID | 级别 | 一句话现状 | 实现锚 | 端**今天**必须怎么办 |
|
|
727
777
|
|---|---|---|---|---|
|
|
728
|
-
| **P-15** | med(6 → **5**,#292 P1 结清一条) | `REQUEST_FIELD_MATRIX` 有 **5 个字段登记为 `gap: true`**(表内 `gap:true` 的定义逐字 = 「这条差异**没有正当理由,是漏的**」),全部是 **print/headless 车道缺席**:`settings.ultracode` · `reasoningEffort` · `model` · `clientContext` · `scratchpadDir`。表内点名的后果:`clientContext` 缺席 ⇒ **引擎误标 (UTC)**;`scratchpadDir` 缺席 ⇒ **`-p` 的工具写不进 exemptDir**。**已结清**:`settings.<resolved>`(0.34.0 / #292 P1 —— 版本号与 CHANGELOG
|
|
778
|
+
| **P-15** | med(6 → **5**,#292 P1 结清一条) | `REQUEST_FIELD_MATRIX` 有 **5 个字段登记为 `gap: true`**(表内 `gap:true` 的定义逐字 = 「这条差异**没有正当理由,是漏的**」),全部是 **print/headless 车道缺席**:`settings.ultracode` · `reasoningEffort` · `model` · `clientContext` · `scratchpadDir`。表内点名的后果:`clientContext` 缺席 ⇒ **引擎误标 (UTC)**;`scratchpadDir` 缺席 ⇒ **`-p` 的工具写不进 exemptDir**。**已结清**:`settings.<resolved>`(0.34.0 / #292 P1 —— 版本号与 CHANGELOG 段**同一个**,对抗复审 [low] 抓的正是两处不一致)—— 它的缺席是**权限方向**的(`-p` 上用户 settings 的 `permissions.deny/ask` 整体不被引擎求值,cli [4208] 实测),现两车道都 stamp | `src/request/taskRequest.ts`(`REQUEST_FIELD_MATRIX` 的 `gap` 列) | headless 车道上这五项**确实不上 wire**。端不要在 print 车道假设它们在场;补齐是**行为改动**,要单独一条测试,不许端侧偷加。`settings.<resolved>` 反过来:print 车道现在**会**摊开 resolver 快照 ⇒ 端必须把值放进 `input.settings.resolved`(端不给值仍是零 stamp,不会凭空出现),且该车道 `settings` 子键走**开放集**口径:`unregisteredRequestKeys` 只放行**表外**动态子键(快照自己的 permissions/env/model/… 不可枚举),**表内但不属于本车道**的子键(今天 = print 的 `settings.ultracode`)仍会被点名 —— 端不许把它白名单化,那条红是真的;另:`settings` 子键值为**函数**(如自有 `toJSON`,能在序列化时整只改写字节)恒被点名且构造器不 stamp。🔴 **0.35.0 起快照通道对具名通道让位**:见 P-15c |
|
|
729
779
|
| **P-15b** | 🔴 权限方向 | `REQUEST_FIELD_MATRIX` 的 stamp 门对**未登记键静默丢弃** —— 表里点名的真实危险形逐字:**「用户显式排除的工具被静默放回」(权限方向回归,类型层不报)**。`excludeTools` 是真 wire 键、早在 seatContract 的 `START_SESSION_OPTION_KEYS` 里,却曾长期在矩阵外;**今天只有 desktop 在发它** | `src/request/taskRequest.ts`(`excludeTools` 行)、`src/seatContract.ts`(`START_SESSION_OPTION_KEYS`) | 端自拼 taskReq 的键**必须**先进矩阵;上 wire 前跑 `unregisteredRequestKeys(req, lane)` 并**当红对待**,别当 lint |
|
|
730
780
|
| **P-15c** | 🔴 治理方向(0.35.0 行为改动) | `settings.<resolved>` 快照是**开放集 spread**(子键即 wire 键)⇒ 它天生是一条**第二通道**。0.34.0 只剥「车道异名」子键,于是**两条车道都登记**的具名子键剥不到 —— 而它们各有治理门:`hooksForWire()` 是 fail-closed(无 `SettingsPort` / 工作区未受信 / 管理侧关停全部 hooks / 检查抛错 ⇒ 返 `undefined`),此时快照里那份**没过门**的 `hooks` 照样上 wire = 关停令等于没下。让位修前靠**合并序**(具名键覆盖快照),而合并序只在具名通道**有值**时管用,门否决时恰恰**没值**。0.35.0 改**结构剥离**:凡表里有 `settings.<sub>` 行的子键(`hooks`/`webSearch`/`ultracode`),快照一概不产;表外子键(`permissions`/`env`/`model`/…)原样摊开 | `src/request/taskRequest.ts`(`namedSettingsSubKeys` / `resolvedSnapshotForWire`) | ① 具名键**必须走具名位**:`input.settings.hooks` / `.webSearch` / `.ultracode` —— 只塞进 `input.settings.resolved` 的宿主从 0.35.0 起那两个键**不再上 wire**(两条车道对称,不是新差异面);② `input.settings.resolved` 只放**表外**的 resolver 产物;③ 该位为 `undefined`/`null` = 合法缺席(照旧降空照发),**合法载体只有对象字面量与 `null` 原型字典**;其余形(数组/原始值/boxed 包装对象/`Map`/**类实例**)抛 `TypeError`。判据锚 **prototype 层数(realm 无关 —— iframe/vm/另一渲染进程的字面量照过)不锚自报标签**:类实例与 `Symbol.toStringTag` 伪造都能自报 `[object Object]`,而摊开走 `Object.entries`(只取自有可枚举键)⇒ 权限面挂在原型 getter 上会摊出空快照、请求照发。这一位摊开的是已解析权限面,降空 = 带着被剥掉的 `deny/ask` 发出去,故 fail-closed;端别 catch 掉它当没事,那是上游产出坏了 —— 把快照**平摊成对象字面量**再传即可;④ `permissions` **子树**也递归校「序列化后还是同一份内容吗」:嵌套 `Map`/`Set`/类实例/boxed/环、**任何一层**上可 call 的 `toJSON`(自有/非枚举/原型链/数组子类)、**非有限数**(`NaN`/`±Infinity`)都 ⇒ `TypeError`,报路径如 `permissions.deny[0]`,不猜 schema;射程刻意只到 `permissions`(整体深净化 = 独立工单)。⚠️ 两条**成文边界**(各有判据钉住现状,不是漏):① 构造之后污染 `Array.prototype`(重建出来的数组必须是真数组);② `getPrototypeOf` 被 Proxy 陷阱撒谎的载体(同 realm 内无可移植的 Proxy 读法)——两者都**不新增丢失面**(发的字节 = 原生序列化那一份),要关得靠「受信 resolver 出口发烙印/已物化记录」那个结构 |
|
|
731
781
|
| **P-15d** | 已知缺口(权限方向;0.35.0 登记,**刻意未在本批闭合**) | 0.35.0 把「序列化后还是同一份内容吗」这条不变量**只**落到 `settings.resolved.permissions` 子树(校 + 就地重建)。**同一条论证对其它带权限含义的位一样成立,而它们今天没有等价强制**:`permissionMode`(字符串位;非串载体的 `toJSON` 能自己决定 wire 上那个词)· `excludeTools`(丢一个元素 = **用户显式排除的工具被放回**,与 P-15b 同一方向)· `additionalDirectories` / `additionalReadDirectories`(读写边界根)· `agents` / `hooks` / `attachments`(对象位,同款 `toJSON` 改写面)。**为什么不在本批一起做**:逐个挑两三个字段补,只会造出下一个同样任意的边界;正解是**一次**把「wire 载荷 JSON-safe 规范化」做成包级闸口(该工单自 0.34.0 起在册),覆盖所有位并同批建判据 | `src/request/taskRequest.ts`(`materializeJsonFaithful` 的射程 = `permissions`;边界本身有一条判据钉着:非权限位的同款畸形**不拦**) | 端**不要**推断「本包会替我把请求体洗干净」——今天只有 `permissions` 子树有这个保证。上 wire 的值请自己保证是 JSON 原生形(字面量 / 数组 / 字符串 / 有限数):别拿类实例、`Map`、带 `toJSON` 的包装对象、访问器对象当载体。尤其 `permissionMode` / `excludeTools`:前者决定整会话的审批姿态,后者丢一个元素就是权限变宽 |
|
|
@@ -743,7 +793,7 @@ CHANGELOG 0.29.0「已知局限」段与相应 JSDoc 都有成文。**别在读
|
|
|
743
793
|
| **P-27** | 立票设计件(web [C1] 疑点③,族A 二段票同批) | `ToolPermissionDecision` **无 note 席位**且座位宿主无 caps 缓存读口 ⇒ #229 回决备注在两座位端(web/desktop)**结构性无法供给**。修形二选一未裁:decision 形补 `note?` + 能力位随 `ToolPermissionRequest` 下发,或宿主侧统一判 | `src/seatContract.ts`(`ToolPermissionDecision`,682 行域) | 座位端今天**不要**渲 note 输入位(渲了也送不出去=假 affordance);候本条落地随提货单换 |
|
|
744
794
|
| **P-28** | 🔴 med(浏览器面) | ⚠️ 2026-08-14 补记:除下面那 10 处外,**第二个未放宽的入参面** `EngineProbeOpts.authToken`(`src/engineWireSdk.ts`)另喂 2 处探针(`agentsWireCaps.ts` 的 `engineSupportsTaskAgents` ⇒ `undefined` / `liveInitToolFace.ts` 的 `probeScenarioTools` ⇒ `null`,均属 C 档静默),合计 **12** 处 —— 见 §5d 末尾那段。 **`same-origin-relay` 只放宽了 2 个入参面,装配入口没跟**:`EngineWireClientConfig.token` 与 `LiveWorkflowConfig.authToken` 收了 `\| { mode:'same-origin-relay' }`,而 `EngineWireTarget.token` 仍是 `string` —— `installEngineWireTarget()` 恰恰是**非 Node 宿主唯一**的装配入口。经 `engineWireTarget()` 取址再构造 client 的 **10 处**(`hitl/planReviewWire.ts` ×2 · `subagent/engineSubagentTail.ts` · `engineSubagentSteer.ts` · `engineSubagentOutput.ts` · `engineCompactWire.ts` ×2 · `engineTaskHandleWire.ts` ×2 · `engineDelegatedPrompt.ts`)在同源反代部署下**没有合法凭证形可传**。⚠️ 失效形**逐点不同**(§5d 末两表:A 响亮 + **有条件**用户可见 = `decidePlanReview`,可见性取决于通知队列口装没装 / B 结构化 reason = steer 与 taskStop / C 无条件静默 = 其余七处),但**构造失败一律吞成 null、从不抛异常** | `src/engineWireTarget.ts`(`EngineWireTarget.token`)· `src/engineWireSdk.ts`(`makeEngineWireClient` 的 catch 臂)· §5d 的两表 | 浏览器同源宿主今天**只能**走 §5d 上表那两条自带入参面的路径(直调 `makeEngineWireClient` / `createLiveWorkflowSource`);走 `engineWireTarget()` 的子代与 plan-review 动词**别指望在 relay 部署下发得出去**,也**不要**把「动词没反应」读成「引擎没这个能力」(⚠️ plan-review 那条**只在通知队列口装上时**才到达用户,见 §5d 末表 —— 队列口没装就退回零用户通道,端的兜底告知别急着撤)。🔴 正位解在包侧(放宽 `EngineWireTarget.token` + 10 处透传),**不许端侧侧路补救**(跨仓缺陷源头修复);要它落地按 [C162] 令④ 回 C 板 |
|
|
745
795
|
| **P-29** | low(自检面) | **通知队列口没有存在性读口**:审批卡口有 `hasApprovalCardPort(For)`、HITL 面有 `hitlHostSurfaceFor`、宿主端口族有 `hostSettings()` 等无副作用读口(见 §5a 的 (a) 表),**唯独 `installNotificationQueuePort()` 没有对偶谓词**。而它的 `notificationQueuePortMisses()` 与同族几个 miss 计数一样**初值为 0**,只有真发生过一次「用到了但没装」才递增 ⇒ 「完全没装 + 还没有任何投递」照样是 0。拿它做**启动装配自检**必然假绿 —— §5a 此前正是这么写的(#252 复审 R3/R4 命中,已按端口拆成「存在性读口」与「回归探针」两类) | `src/notifications.ts`(`installNotificationQueuePort` 无对偶读口;`queuePortMisses` 初值与 `port()` 的 null 分支) | 队列口:按 §8-B 真调 `installNotificationQueuePort()`,miss 计数只当**跑过真流量之后**的回归探针用;其余端口按 §5a (a) 表用各自的存在性读口做启动校验。要队列口的读口按 [C162] 令④ 回 C 板提(正位解在包侧:补一个 `hasNotificationQueuePort()` 谓词) |
|
|
746
|
-
| **P-30** | med(HITL 路由面) | **durable 审批腿不按 `gateKind` 路由,且取行有「同 taskId 任意行」回落** (0.30.0 发包扫描
|
|
796
|
+
| **P-30** | med(HITL 路由面) | **durable 审批腿不按 `gateKind` 路由,且取行有「同 taskId 任意行」回落** (0.30.0 发包扫描 对抗复审 finding① 坐实,**非本窗引入**):`findPendingForTask` 在工具名谓词无命中时走 `?? rows.find(r => r.taskId === taskId)`,而 `surfaceFsApprovalAndDecide` 拿到行之后**不校 `gateKind`** ⇒ 同一 task 上同时停着 `plan_review` / `resource_limit` 行时,会弹出一张 `toolName` 为空串的**工具审批卡**。⚠️ **不会误批**(server 侧 fail-closed):本腿打的是 `POST /v1/approvals/:sessionId/decide`,非工具门在该端点上回 **409 `gate_not_tool_approval`**(SDK `dist/errors.d.ts`;⚠️ **不是** `gate_not_resumable` / `gate_not_plan_review` —— 那两个分别是 `/resume` 与 plan-review 端点的守卫,2026-08-14 对抗复审 R2 订正本条初稿的错码)。🔴 **但后果不止「一次失败的决断」**:decide 抛错 ⇒ `surfaceFsApprovalAndDecide` 折成 `{kind:'failed'}` ⇒ `parkResolver` 走 fail-soft 结束**本次客户端 turn**;而 server 侧 checkpoint 因为 fail-closed **没被消费**,run/session 仍 suspended、仍持 claim ⇒ 重试还会再撞一次。⚠️ **终帧按入口分两形,排障别只等一个码**(2026-08-14 对抗复审 R3 订正本条初稿的单一描述):① **初始 park 入口**(`done{…park…}` 经 `frameRouter.routeDone` 进来,`park.pendingDone` **在场**)⇒ 先 `led.flushHeld()` 吐出 park 期被 HOLD 的**毒化帧**(`tool_end{isError:true, output:'Operation aborted'}`,`frameRouter.ENGINE_ABORT_TOOL_RESULT`),再原样回吐那条 `done` —— **没有**合成 `failed` 终帧、**没有** `hitl_unanswered` 错误码,可观察到的失败信号只有那条 isError 的 `tool_end`。⚠️ **它与「用户真按了拒绝」可以分辨,按 `output` 分**(2026-08-14 对抗复审 R5 订正本条初稿的「同形不可分」):fail-soft 这条是 `flushHeld()` 原样吐出的**毒化帧**,`output` 逐字是 `ENGINE_ABORT_TOOL_RESULT`(`'Operation aborted'`);真 deny 走 `frameRouter` 的 `denied-call` / `deny-stamp-next` 臂,`output` 被改写成 `HITL_REJECT_MESSAGE`(CC `REJECT_MESSAGE` 逐字)。端做归因按 `output` 判,别只看 `isError`;② **续流 / durable re-attach 入口**(`suspended` 进来,无 `pendingDone`)⇒ 才合成 `failed{errorCode:'hitl_unanswered'}`。⇒ 端做告警/埋点时**不要**只锚 `hitl_unanswered`,①那条路径上它根本不出现。⚠️ 定性要分清:这条 fail-soft 链是 durable 腿**通用**的失败路径(设计如此 —— 替代方案是谎报成功,更坏),**不是**本缺口独有;本缺口的**增量**是「弹了一张 `toolName` 为空的卡 + 发了一次注定 409 的 decide + 把用户的一次表态浪费掉」 | `src/hitl/hitlBridge.ts`(`findPendingForTask` 的第二条 `rows.find`)· `src/hitl/toolApprovalWire.ts`(`surfaceFsApprovalAndDecide` 全程零 `gateKind` 读)· 常驻登记见 `scripts/run-durable-card-display-keys-test.mjs` ⑦ 段 `gateKind` 那条未投影理由 | 端**不要**把「durable 卡弹出来了」读成「这一定是个工具门」;拿到 `toolName` 为空串的卡按异常处置、别渲成可决断卡。🔴 正位解在包侧(本腿按 `gateKind` 严格路由 + 回落收窄),要同批想好 pre-`gate_kind` 历史行 `gateKind` 缺席时的降级 —— 属独立设计件,按 [C162] 令④ 回 C 板提 |
|
|
747
797
|
|
|
748
798
|
### 7e. 缺口的共同形状(值得单独说)
|
|
749
799
|
|
package/docs/REFACTOR-LEDGER.md
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
> **终态:167/167 全清**(F 族→B 族→wave1 五卡→G 族两车→wave2 六卡→收官批→A 族压轴双车)。
|
|
4
4
|
> 收官判据:21 门全绿(merge 后主树 20/20 套含 SEMA_CLI_ROOT 真差分,另 _gate-lib 加载期自测)/
|
|
5
5
|
> 导出基线 525 双向 / 六 strict 旋钮全开 / singleton manifest 双向 / typeshape 棘轮 b4=21·unknown 203(逐条登记)·裸返回 0。
|
|
6
|
-
> 收货形=refactor-pipeline-form(sonnet·opus
|
|
6
|
+
> 收货形=refactor-pipeline-form(sonnet·opus 车+双镜头 双镜头+主会话亲审亲变异);A 族压轴双车三方判决+
|
|
7
7
|
> 收货修两笔(args 快照/单读·快照·惰性三处)见 aaec8a7/c73fdf7 两 merge。残余台账=WAVE1-RESIDUALS.md+
|
|
8
8
|
> 各族「P2 裁决」段 keep-with-reason 条目。本档由 P2 工作底稿原位收卷,逐条裁决与坐标全部保留如下。
|
|
9
9
|
|
|
10
10
|
# (原)client-core 规范重构 P1→P2 裁决工作底稿
|
|
11
11
|
|
|
12
|
-
统一重排号 REF-CC-001 起连号,原镜头 id 括注保留。**裁决进度**:F 族(075-080)=GO 已落地(merge 2626369,四旋钮+20 红清零,变异实证红→绿);A 族=全 GO 但候 r2 修订稿(
|
|
12
|
+
统一重排号 REF-CC-001 起连号,原镜头 id 括注保留。**裁决进度**:F 族(075-080)=GO 已落地(merge 2626369,四旋钮+20 红清零,变异实证红→绿);A 族=全 GO 但候 r2 修订稿(对抗复审四 verdict 折入,见 A-FAMILY-ADVERSARIAL-REVIEW.md);B 族 notif-01/02/03=GO 紧急批在飞。并单已按 critic(agent5)点名 + 复核发现的同文件同行段同修法项执行(见文末统计的「并单前/后」)。P2裁决列留空,由裁决人填 GO/HOLD/REJECT + 备注。
|
|
13
13
|
|
|
14
14
|
来源镜头:agent0=dup-invariant(13) / agent1=giant-split(17) / agent2=type-shape(17) / agent3=gate-p4(38) / agent4=lexicon/域词表(21) / agent5=critic完备性(14) / sup0=midband(7) / sup1=xlate(17) / sup2=notif(14) / sup3=hitl2(17,实14条finding+3条coverage段) / sup4=fleet2(15)。
|
|
15
15
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/client-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.38.1",
|
|
4
4
|
"description": "Client-side session runtime shared by every sema human client (TUI / web / desktop): sema wire frames (AgentEvent) -> CC session vocabulary (SDKMessage) with dual-plane output (transcript/chrome), deterministic transcript ids, lane discipline as a type, and the notification/dedup ledgers. Every CC-skin shape is collected here so the wire itself stays neutral. Blackboard [1832] design axioms; [1651]/[1652]/[1653] signed seam design. Renamed from @sema-agent/wire-cc-adapter (0.1.x).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -31,12 +31,12 @@
|
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"@sema-agent/agent-types": ">=0.2.0",
|
|
34
|
-
"@sema-agent/sdk": ">=7.
|
|
34
|
+
"@sema-agent/sdk": ">=7.2.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@sema-agent/agent-types": "^0.2.0",
|
|
38
38
|
"@sema-agent/core": "^5.43.0",
|
|
39
|
-
"@sema-agent/sdk": "^7.
|
|
39
|
+
"@sema-agent/sdk": "^7.2.0",
|
|
40
40
|
"esbuild": "^0.27.4",
|
|
41
41
|
"typescript": "^6.0.2"
|
|
42
42
|
}
|