@sema-agent/client-core 0.53.0 → 0.55.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 +168 -1
- package/README.md +2 -1
- package/dist/adapt/arms.js +40 -4
- package/dist/adapter/downstream/eventToSdkMessage.js +12 -0
- package/dist/hitl/editedRuleTextPrecheck.d.ts +25 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +7 -0
- package/dist/peerFrames.d.ts +117 -0
- package/dist/peerFrames.js +369 -0
- package/dist/retryStatus.d.ts +112 -16
- package/dist/retryStatus.js +46 -2
- package/dist/toolResult.js +8 -0
- package/docs/INTEGRATION-CLIENTS.md +216 -16
- package/package.json +2 -2
package/dist/toolResult.js
CHANGED
|
@@ -117,6 +117,14 @@ export const STRUCTURED_DETAIL_TYPES = new Set([
|
|
|
117
117
|
// 病根仍是对账物代际差:门锚本仓 devDep core,devDep 停 5.20.0 时该词还没铸 ⇒ 门恒绿。
|
|
118
118
|
// 修 = devDep 升 ^5.43.0(门当场翻红显出漂移)+ 补词,与 A-004.5 那批同一条路。
|
|
119
119
|
'read_path_denied',
|
|
120
|
+
// ── core 7.3.0 跟车一词(B-003 批,2026-09-05;engine-vocab ⑤ 段对 7.3.1 实装物直证)──────────
|
|
121
|
+
// 🔴 **不是本批点名的件,是抬对账物时族扫捞出来的存量漏词**([same-shape-residue-constitution]):
|
|
122
|
+
// 本批为了让 G2-b 对 `waiting_first_token` 有判别力,把 devDep core 从 ^7.1.0 抬到 ~7.3.0;
|
|
123
|
+
// 门当场翻红,红文逐字:「core 7.3.1:43 项 / 本包 42 项;漏:list-agents」。
|
|
124
|
+
// `list-agents` = ListAgents 工具的结构化卡(core `dist/agents/list-agents-tool.js` 铸点,
|
|
125
|
+
// 并已登记在 `dist/core/runner/tool-output-projection.js:67` 的 `CC_DETAIL_TYPES` 里)。
|
|
126
|
+
// 病根与 5.20 / 5.43 两次逐字同族:词漏了不会响,只会让那一类卡**永远退回正则解模型面文本**。
|
|
127
|
+
'list-agents',
|
|
120
128
|
]);
|
|
121
129
|
// 🔴 同批**删三词**(core 5.10.0 BREAKING「幽灵卡」清仓):`multiedit` / `memory-saved` /
|
|
122
130
|
// `memory-recall` —— 全树零铸点(MultiEdit/批量重放铸的是 `type:"edit"` 带 `edits[]`;core
|
|
@@ -15,15 +15,15 @@
|
|
|
15
15
|
|
|
16
16
|
## §0 版本锚与重扫纪律
|
|
17
17
|
|
|
18
|
-
### 0a. 版本锚(2026-09-
|
|
18
|
+
### 0a. 版本锚(2026-09-05)
|
|
19
19
|
|
|
20
20
|
| 项 | 值 | 真源 |
|
|
21
21
|
|---|---|---|
|
|
22
|
-
| 本包 | `@sema-agent/client-core` **0.
|
|
22
|
+
| 本包 | `@sema-agent/client-core` **0.55.0**(工作树**未发**;npm 最新 = **0.54.0**。B-003 `waiting_first_token` 那一批进 `CHANGELOG.md` 的 `## 0.55.0(2026-09-05)` 段,冻结账已按两阶段协议插 `pending` 行) | `package.json` `version` |
|
|
23
23
|
| peer:wire 契约 | `@sema-agent/sdk` **>=7.4.0**(value-level,非 type-only;0.48.0 抬版,四条硬理由见 `CHANGELOG.md` 0.48.0 段末的地板影响面账) | `package.json` `peerDependencies` |
|
|
24
24
|
| peer:会话词汇表 | `@sema-agent/agent-types` **>=0.2.0**(type-only,零运行时) | 同上 |
|
|
25
25
|
| runtime dep | `diff` ^9.0.0(**唯一**一条;portability 门按**等值**钉死) | `package.json` `dependencies` |
|
|
26
|
-
| 公开导出面 | **
|
|
26
|
+
| 公开导出面 | **815** 个运行期符号(+ 41 个测试钩;= 工作树当下的值 = 已发 `0.54.0`(design/385 十件 additive 含 `AUTHORITY_ENVELOPE_TAGS`);`0.52.0` 是 **805**(L-69⑨ 两件);`0.51.0` 是 **803**;`0.50.0` 是 **800**(S-81 五件);`0.49.0` 是 **795**;`0.48.0` 是 **794**;npm `0.47.0` 是 **790**,`0.46.0` 是 **787**,`0.44.0` 是 **783**,`0.43.1`/`0.43.0` 是 **776**,`0.42.0` 是 **771**,`0.41.0` 是 **767**,`0.39.0` 是 **766**,`0.38.0` 是 **764**;`0.37.0` 是 **753**,见 `CHANGELOG.md`) | `scripts/public-export-baseline.json` 的 `count` / `testHookCount` —— **别手抄进别处,以该文件为准** |
|
|
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
|
|
|
@@ -31,13 +31,11 @@
|
|
|
31
31
|
(`readSessionMemoryStatus` / `classifyMemoryStatusFailure` / `readCaptureOptOut` / `readLastCapture`)
|
|
32
32
|
在旧版上按名 import 会**在 ESM 实例化当场炸**(具名导出不存在)—— 提货前先抬依赖。
|
|
33
33
|
装 ≤`0.48.0` 的端同理:`0.49.0` 的 `projectCrashConverged`(L-38,§12)在旧版上不存在。
|
|
34
|
-
|
|
35
|
-
`
|
|
36
|
-
`
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
在此之前按名 import 会在 ESM 实例化当场炸 —— 提货前先抬依赖。
|
|
40
|
-
(L-38 的 `projectCrashConverged` 已随 `0.49.0` 发出,不再是未发件。)
|
|
34
|
+
装 ≤`0.49.0` 的端同理:`0.50.0` 的 S-81 五件(`classifySelfOrchestrationRefusal` / `stripSelfOrchestrationIntent` /
|
|
35
|
+
`projectWorkflowsGate` / `SELF_ORCHESTRATION_RETRY_WITHOUT` / `CAPABILITY_SELF_ORCHESTRATION_REQUIRED`
|
|
36
|
+
+ **四个** type-only 形 `SelfOrchestrationRefusal` / `SelfOrchestrationDenialReason` / `WorkflowsGateProjection` / `WorkflowsGateUnknownDenial`,§13)、
|
|
37
|
+
装 ≤`0.53.0` 的端同理:`0.54.0` 的 design/385 十件(§17)—— 在旧版上按名 import 会在 ESM 实例化当场炸,提货前先抬依赖。
|
|
38
|
+
(「未发」告示按「发出即退场」约定退场:本档只列**已发版本**的下限,不再预告工作树里的未发件。)
|
|
41
39
|
🔴 **0.48.0 还抬了 peer 地板**(`@sema-agent/sdk >=7.4.0`),这是本版**唯一**的非 additive 面:
|
|
42
40
|
端装 <7.4.0 的 SDK 会看到 peer 警告(运行期不因此变化)。同一条对 0.47.0 那 **3 个 additive 导出**
|
|
43
41
|
成立(`planInteractiveHalt` / `RUN_LEVEL_STOP_ERROR_CODES` / `readDecideCurrentPending`)。同一条对 0.38.0 那 11 个
|
|
@@ -106,14 +104,14 @@ SDK 核对「本包 import 的每个值级符号仍然导出」「`TaskStats.cos
|
|
|
106
104
|
`--platform=browser` **真打包**看守,不是靠约定。
|
|
107
105
|
- 宿主能力一律**经端口注入**(`installHost({...})`,见 §5),库自己**绝不** `require('fs')`、
|
|
108
106
|
绝不 `process.env` 直读(env 走 `hostEnv()`)、绝不全局 `fetch`(目录线上腿走注入的 `CatalogFetchJson`)。
|
|
109
|
-
- 闭包棘轮(零松量,逐块记账在各上限常量头注):内核 7 文件 / A 层
|
|
107
|
+
- 闭包棘轮(零松量,逐块记账在各上限常量头注):内核 7 文件 / A 层 24 / index 142(0.54.0 `peerFrames.ts` 入 A 层与 index 各 +1)。
|
|
110
108
|
- **实现锚**:`scripts/run-client-core-portability-test.mjs`、`src/hostEnv.ts`、`src/host.ts`。
|
|
111
109
|
|
|
112
110
|
---
|
|
113
111
|
|
|
114
112
|
## §2 公共导出面地图(按域)
|
|
115
113
|
|
|
116
|
-
> 全集真源 = `scripts/public-export-baseline.json` 的 `names`(**
|
|
114
|
+
> 全集真源 = `scripts/public-export-baseline.json` 的 `names`(**815** 项)。
|
|
117
115
|
> 本节**不逐名抄**,只给「域 → 承重导出 → 用途 → 实现锚」。承重导出 = 一个端为了让这个域干活
|
|
118
116
|
> **必须**直接调到的那几个符号;其余是它们的类型、变体与辅助位。
|
|
119
117
|
> 单一入口:`import { … } from '@sema-agent/client-core'`(`exports` 只有 `.` 一个;
|
|
@@ -123,7 +121,7 @@ SDK 核对「本包 import 的每个值级符号仍然导出」「`TaskStats.cos
|
|
|
123
121
|
|
|
124
122
|
`public-export-baseline.json` 由 **`dist/index.js` 的运行期导出**生成(生成口径自述见
|
|
125
123
|
`scripts/run-client-core-typeshape-test.mjs`,双向精确集合门在 `scripts/run-public-surface-test.mjs`)。
|
|
126
|
-
实测:
|
|
124
|
+
实测:815 项 **100% 是运行期导出,零 type-only**。
|
|
127
125
|
|
|
128
126
|
**推论(端必须知道)**:
|
|
129
127
|
- barrel 导出的**类型**面比 707 大得多,且**不被这道门看守** —— `AdapterContext` / `SeamEvent` /
|
|
@@ -137,12 +135,12 @@ SDK 核对「本包 import 的每个值级符号仍然导出」「`TaskStats.cos
|
|
|
137
135
|
`WorkflowsGateUnknownDenial` 四形**不在**基线里,`src/selfOrchestrationDenial.ts` 对基线贡献
|
|
138
136
|
**4** 项运行期导出(三个函数 + `SELF_ORCHESTRATION_RETRY_WITHOUT`)。
|
|
139
137
|
|
|
140
|
-
|
|
138
|
+
815 项的内部构成(帮助端估读表大小):**244** 项是 `SCREAMING_SNAKE` 常量数据表/词汇表
|
|
141
139
|
(矩阵、键集、env 名、锚串)而非可调用物;**5** 项是 PascalCase 运行期值
|
|
142
140
|
(`ControlRouter` / `ControlSafetyError` / `HitlBridge` / `HitlSafetyError` / `DecideTransportRetryExhaustedError`);
|
|
143
141
|
**41** 项是 `*For(sessionKey, …)` 的 per-session 变体(§6;其中 `engineNamespaceKeyFor` 是命名巧合 —— 参数是 baseUrl 不是 sessionKey,见域 14)。
|
|
144
142
|
|
|
145
|
-
### 2b. 域图(16 域,逐域计数之和 =
|
|
143
|
+
### 2b. 域图(16 域,逐域计数之和 = 815)
|
|
146
144
|
|
|
147
145
|
| # | 域 | 名数 | 承重导出 | 用途 | 实现锚 |
|
|
148
146
|
|---|---|---|---|---|---|
|
|
@@ -152,7 +150,7 @@ SDK 核对「本包 import 的每个值级符号仍然导出」「`TaskStats.cos
|
|
|
152
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` |
|
|
153
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` |
|
|
154
152
|
| 6 | **请求装配(上行唯一构造口)** | 8 | `buildTaskRequest` · `REQUEST_FIELD_MATRIX` · `unregisteredRequestKeys` · `applyLiveRequestDefaults` · `taskNotificationToPrintFrame` | 两条车道(`interactive`/`print`)出站请求的**唯一**构造器;`unregisteredRequestKeys` 是可执行门 —— 端偷带一个未登记键上 wire 就红 | `src/request/taskRequest.ts`、`src/request/printNotification.ts` |
|
|
155
|
-
| 7 | **通知与 outstanding 台账** |
|
|
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 台账) |
|
|
156
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` |
|
|
157
155
|
| 9 | **能力/旋钮 wire 门族** | 91 | `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) | 逐功能的「引擎有没有这个能力 + 这个字段怎么上 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 拒绝判定层) |
|
|
158
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` |
|
|
@@ -2592,3 +2590,205 @@ interface ActiveRunSelfHealDeps {
|
|
|
2592
2590
|
`offerStaleParkChoice` / `staleParkArm` / `cancelAndConfirmRelease` / 五个新 kind 的文案臂)。
|
|
2593
2591
|
**常驻门**:`scripts/run-selfheal-reopen-test.mjs`(G11 段,含「该位缺席 ⇒ 逐字旧结局且零 cancel」负控)、
|
|
2594
2592
|
`scripts/run-terminal-identity-copy-test.mjs`(G2 段:新 kind 的注入形与处置分类)。
|
|
2593
|
+
|
|
2594
|
+
|
|
2595
|
+
## §17 🆕 design/385 三条引擎注入车道的类型化投影(0.54.0;cli L-61② / L-87 5a①5a②)
|
|
2596
|
+
|
|
2597
|
+
### 17a. 修的是哪一形
|
|
2598
|
+
|
|
2599
|
+
引擎把三类**根本不是同一种东西**的载荷塞进同一条 `task_notification` 车道,而 core 对这三类
|
|
2600
|
+
**不套** `<task-notification>` 壳(`core/task-notification.ts::renderTaskNotificationXml` 头三个分支
|
|
2601
|
+
按这个顺序判):
|
|
2602
|
+
|
|
2603
|
+
| 载体键(`TaskNotificationPayload` 上) | 出处 | 模型面 |
|
|
2604
|
+
|---|---|---|
|
|
2605
|
+
| `agentMessage: {from, body}` | §1.4 d1 —— 同进程子代 `SendMessage("main")` 的 uplink | `<agent-message from="…">` + peer 纪律块 |
|
|
2606
|
+
| `crossSessionMessage: CrossSessionEnvelopeFields & {body}` | §4.1 —— 另一个会话的消息,从本会话自己的信箱 drain 出来 | `<cross-session-message from="…" from-session="…" from-name="…" from-mode="…">` + 跨会话纪律块 |
|
|
2607
|
+
| `crossSessionNotice: {kind, text}` | §4.4/§5.2 —— **本会话自己发出去**那条消息的回执 / `notify_when_idle` 的 idle 通知 | 一行纯文本(`[Cross-session delivery notice] …` / `[Cross-session idle notice] …`) |
|
|
2608
|
+
|
|
2609
|
+
端此前照 `task_notification` 泛化卡渲 ⇒ 屏上是「后台任务完成」,而模型读到的是一条同事发来的话。
|
|
2610
|
+
|
|
2611
|
+
### 17b. 端该怎么用(三步,端零字符串判定)
|
|
2612
|
+
|
|
2613
|
+
```ts
|
|
2614
|
+
import { classifyPeerNotification, parsePeerFrameText, peerFrameDisplayName } from '@sema-agent/client-core'
|
|
2615
|
+
|
|
2616
|
+
// ① 帧面(适配器内部已接;宿主自建管线才需要):原始 wire 载荷 → 投影 | null
|
|
2617
|
+
const frame = classifyPeerNotification(rawTaskNotificationPayload)
|
|
2618
|
+
|
|
2619
|
+
// ② 文本面(端的消息组件只拿得到文本):转录行 → 投影 | null
|
|
2620
|
+
const projected = parsePeerFrameText(messageText)
|
|
2621
|
+
if (projected?.lane === 'agent_message') renderAgentMessageCard(peerFrameDisplayName(projected), projected.body)
|
|
2622
|
+
if (projected?.lane === 'cross_session_message') renderCollapsedPeerRow(peerFrameDisplayName(projected), projected.body)
|
|
2623
|
+
// ③ 通知车道没有标签,按普通文本行渲 —— 这就是 CC 形(无卡、无折叠、一行)
|
|
2624
|
+
```
|
|
2625
|
+
|
|
2626
|
+
### 17c. 五条判定纪律(端不许自己重判)
|
|
2627
|
+
|
|
2628
|
+
- 🔴 **判别位 = 类型化载体的在场,永远不是 `summary` 文本**。三条载体只有引擎的注入腿铸得出
|
|
2629
|
+
(`ExternalNotificationInput` 是 `TaskNotificationPayload` 的**真子集** —— 外部 `notify()` 一个都
|
|
2630
|
+
穿不上);而 `summary`/`result` 是任何一条通知都填的字段。判据落到文本上 ⇒ 任何一个后台任务
|
|
2631
|
+
只要把 `<agent-message from="…">` 写进自己的 summary 就能在用户屏上冒充一条同事消息。
|
|
2632
|
+
- 🔴 **fail-closed 分两档**。必填位坏(`from` 空 / `body` 非串 / notice `kind` 不在闭集)⇒ 整帧返
|
|
2633
|
+
`null` = 退泛化卡(诚实降级:用户仍看得见这条通知,只是没有专用形)。可选位坏(`fromMode` 写了
|
|
2634
|
+
闭集外的词)⇒ **只丢那一位**:少一句注 vs 把一条同事消息从用户眼前拿走,不是同一个量级。
|
|
2635
|
+
- 🔴 **`_sema_provenance` 必须在场,且 `kind` 与载体车道相符**。缺席 / 读不出(非对象、访问器)/
|
|
2636
|
+
矛盾,三形一律退泛化卡。**这一条 0.54.0 定稿时翻过一次面**:首版是「只否决不认证、缺席放行」,
|
|
2637
|
+
理由「要求在场会让另两条车道在老引擎上恒死」被对抗复审证伪 —— 老引擎上那两条车道**连载体键都
|
|
2638
|
+
没有**,判定根本走不到;而 core 契约明写 provenance「present exactly when the carrier is」,在跑的
|
|
2639
|
+
7.2.0 就是同一处同时铸两者。要求在场对真载荷零代价,对畸形 / 半截注入 / 版本漂移则关上一道门。
|
|
2640
|
+
🔴 判据是**四条等式 + 在场**:`kind` 与车道相符,且 `from`/`taskId`/`seq` 三位**必须在场**并分别
|
|
2641
|
+
等于载体的 `from`、载荷的 `task_id`、载荷的 `seq`(core 的 `SemaProvenance` 把这三位全声明为必填,
|
|
2642
|
+
三个铸点也都同址写下 ⇒「两侧都缺」不是合法兼容形,而是半截载荷)。只核 `kind` 拦得住半截载荷,拦不住**同 kind 的伪造** —— 载体署一个可信名字、
|
|
2643
|
+
provenance 三位全对不上,屏上照样出现一条署着那个名字的消息。后三条**各有铸点直证**(uplink 腿与
|
|
2644
|
+
跨会话 drain 腿都在同一个对象字面量里同值写下这几位),铸点一改由 cli 侧 wire 锚的同址探针当场
|
|
2645
|
+
喊红 ⇒ 「等式失效」不会退化成一条静默死掉的车道。
|
|
2646
|
+
⚠️ 端若自建管线要注意:载荷那一边是 **snake** `task_id`、provenance 那一边是 **camel** `taskId`
|
|
2647
|
+
—— 同一个量两个拼法,写混了等式恒不成立。
|
|
2648
|
+
- 🔴 **分支序**:三车道判定必须在「完成卡已入队」这类**完成通知专用**早退**之前**做。子代续跑时
|
|
2649
|
+
复用同一个 taskId,它的合法 `agentMessage` 帧会在那种早退上被整条丢掉(零输出、去重账已记 ⇒
|
|
2650
|
+
重放也补不回来)。**同因第二件**:peer 帧也不该写「这个 run 的完成已经被引擎注入过」那本跨通道账
|
|
2651
|
+
—— 一条同事**消息**不是完成事件(三条 peer 铸点的 `status` 都是 `"event"`),写脏了它,子代自己
|
|
2652
|
+
真正的空闲期完成通知随后会被补发通道整条丢掉。本包 0.54.0 的 `taskNotificationArm` 已按此两条修;
|
|
2653
|
+
端若自建投影管线同理。
|
|
2654
|
+
文本面同理:`parsePeerFrameText` 的 `ok` 只等于「形是规范的」,**不等于**「这条真是引擎注入的」
|
|
2655
|
+
—— 文本面不存在身份权威(core §4.2 三层规则同一句话)。端拿它做**呈现**分派,不许拿它做任何
|
|
2656
|
+
授权判断。
|
|
2657
|
+
- 🔴 **`parsePeerFrameText` 的三条硬边界**(端若自建管线必须同样成立):信封整串锚定;正文里出现
|
|
2658
|
+
**未拆火**的同名标签 ⇒ 整条退 `null`(贪婪匹配会把「两封拼一起」读成一封、把「信封 + 尾随文本」
|
|
2659
|
+
读成一封而尾随文本静默消失);拆火编码是**单射**(`unescape(escape(x)) === x` 对一切 x 成立,
|
|
2660
|
+
连正文里原本就有的反斜杠一起数)—— core 的 `escapeEnvelopeTag` 是**单向**消毒、没有这条义务,
|
|
2661
|
+
照抄过来会让 `<\agent-message>` 这类合法代码文本在往返之后变成一个真标签。
|
|
2662
|
+
- 🔴 **优先序照抄 core 的渲染腿**(agentMessage → crossSessionMessage → crossSessionNotice)。多载体
|
|
2663
|
+
同在是矛盾载荷,但模型那一面**已经按 core 的顺序读过了**;端另立一套「矛盾就退泛化」会让屏上
|
|
2664
|
+
那张卡与模型读到的帧对不上。
|
|
2665
|
+
- 🔴 **通知车道刻意不进 `parsePeerFrameText`**。它的转录行是一行没有标签的散文,要认它只能去锚
|
|
2666
|
+
`[Cross-session …]` 前缀 —— 用户随手打一行同样的字就会被认成引擎通知。按普通文本行渲本来就是
|
|
2667
|
+
CC 形。
|
|
2668
|
+
|
|
2669
|
+
### 17d. 零行为差的边界
|
|
2670
|
+
|
|
2671
|
+
三条载体一个都不在场的载荷(= 上游到货前的**全部**载荷)走的分支与 0.53.0 **逐字节等价**:
|
|
2672
|
+
`taskNotificationArm` 只换了转录行那一行的**文本来源**,去重键 / `bgshell_settle` / 面板 settle /
|
|
2673
|
+
module 台账一个都没动(它们判的是「哪个 task 的哪个状态」,与这条通知在模型面穿哪件外衣无关,
|
|
2674
|
+
跟着换会把三条独立的账搅成一本)。
|
|
2675
|
+
|
|
2676
|
+
🔴 **边界的另一半(发包扫描 [high] 纠偏)**:载体**在场**时,`taskNotificationArm` 对**所有**宿主(含没接 `parsePeerFrameText`
|
|
2677
|
+
的老宿主 desktop/web-client)都把转录行文本换成信封形(`<agent-message …>…</agent-message>` / `<cross-session-message …>` /
|
|
2678
|
+
通知纯行),不再是 `<task-notification>` XML。这是**有意的**——那一行本来就是模型读到的帧,老宿主把它当普通 user 文本行渲
|
|
2679
|
+
正是 CC 形(CC 的 `<agent-message>` 就是一条 user 文本)。所以「零行为差」只对**载体缺席**的载荷成立;载体在场的老宿主差异=
|
|
2680
|
+
「泛化完成卡 → 一行原文」,不是回退。
|
|
2681
|
+
|
|
2682
|
+
### 17e. 上游供给的诚实边界(本版新增的已知局限)
|
|
2683
|
+
|
|
2684
|
+
`crossSessionMessage` / `crossSessionNotice` 两条载体在 **core 7.2.0** —— 即 cli `ENGINE_PIN 7.57.0`
|
|
2685
|
+
内嵌的那一版 —— 上**零铸点**(直证:`node_modules/@sema-agent/core/dist/**` 两词零命中)。本批是
|
|
2686
|
+
**消费半场先落地**,上游到货前这两条车道恒不触发。`agentMessage` 一条今天就到得了
|
|
2687
|
+
(core 7.2.0 `dist/agents/send-message-tool.js` 的 uplink 腿直证 `_sema_provenance: { kind: "agent_message" …`)。
|
|
2688
|
+
两个标签字面量已由 cli 侧的 wire 锚契约门(登记表里的 A-K24 条)对 core 真字节看着。
|
|
2689
|
+
|
|
2690
|
+
**cli / web / desktop 认领**:cli 侧接点(消息组件三形)在本批同车;web / desktop **无需动作**
|
|
2691
|
+
(不调新导出 ⇒ 逐字节零行为差,只是这三条车道在它们那儿仍渲泛化卡)。
|
|
2692
|
+
**实现锚**:`src/peerFrames.ts`(`classifyPeerNotification` / `renderPeerFrameTranscriptText` /
|
|
2693
|
+
`parsePeerFrameText` / `peerFrameDisplayName` / 三张闭集表)、`src/adapt/arms.ts` 的通知臂。
|
|
2694
|
+
**常驻门**:`scripts/run-peer-frame-projection-test.mjs`(71 checks,含适配器级的两条台账回归)。
|
|
2695
|
+
|
|
2696
|
+
## §18 🆕 `waiting_first_token` —— 等第一个 token 的相(0.55.0;core 7.3.0 #530 / server ≥7.58;cli B-003 / L-108①)
|
|
2697
|
+
|
|
2698
|
+
### 18a. 修的是哪一形
|
|
2699
|
+
|
|
2700
|
+
`BrainStatusPhase` 是**闭集**,而消费端对它有 `switch`。core 7.3.0 加了第七相
|
|
2701
|
+
`waiting_first_token`(首 token 看门狗已武装、第一个字还没到;等满 `WAITING_FIRST_TOKEN_AFTER_MS`
|
|
2702
|
+
= 30s 起每 30s 一帧,携 `elapsedMs` / `timeoutMs`;铸点 `core/dist/brain/stream-engine.js`
|
|
2703
|
+
的 `emitWaitFrame`)。0.54.0 及以前本包不认得它 ⇒ 落 `mapBrainStatusToRetry` 的 `default:` 臂
|
|
2704
|
+
被投成 `{kind:'error'}`。用户端两个面:
|
|
2705
|
+
|
|
2706
|
+
| 引擎 | 用户看到 | 事实 |
|
|
2707
|
+
|---|---|---|
|
|
2708
|
+
| 7.57.0(core 7.2.0) | 什么都没有 | 相不存在 |
|
|
2709
|
+
| 7.58.0(core 7.3.1) | 一条**错误色**的 `API error` 行 | 模型已连上,正在生成前的静默;**一次尝试都没失败过** |
|
|
2710
|
+
|
|
2711
|
+
病形 = 「上游闭集加员落 default 臂 ⇒ 新状态被渲成**最像的旧状态**」,与 #3004 修的 `recovered`
|
|
2712
|
+
(重试**成功**被渲成 `API error`)逐字同族。
|
|
2713
|
+
|
|
2714
|
+
### 18b. 端该怎么用
|
|
2715
|
+
|
|
2716
|
+
```ts
|
|
2717
|
+
import { mapBrainStatusToRetry, type RetryStatus } from '@sema-agent/client-core'
|
|
2718
|
+
|
|
2719
|
+
const st = mapBrainStatusToRetry(brainStatusPayload, Date.now())
|
|
2720
|
+
if (st === null) return null // recovered:摘掉覆盖层
|
|
2721
|
+
if (st.kind === 'waiting') { // 🆕 0.55.0
|
|
2722
|
+
// 非错误色。elapsedMs 缺席 ⇒ 不渲秒数(绝不折 0);timeoutMs 缺席 ⇒ 不渲「/ Ns」。
|
|
2723
|
+
return renderWaitingRow({ elapsedMs: st.elapsedMs, timeoutMs: st.timeoutMs })
|
|
2724
|
+
}
|
|
2725
|
+
if (st.kind === 'stalled') return renderStalledRow(st) // reconnecting:网络面
|
|
2726
|
+
return renderErrorRow(st) // 其余:重试/限流/熔断/放弃
|
|
2727
|
+
```
|
|
2728
|
+
|
|
2729
|
+
三条**必须**照做的纪律:
|
|
2730
|
+
|
|
2731
|
+
1. **不得拿 `deadline` 渲倒计时**。本相不带 `retryIn*`(core:它不宣告等待,它**观测**等待),
|
|
2732
|
+
`deadline` 恒 = 映射时刻的 `nowMs`,是**兼容位**(见 18c),不是一个真的截止点。
|
|
2733
|
+
2. **不得拿 `timeoutMs` 当倒计时源**。它是这次等待的**天花板**(渲「45s of 120s」的分母),
|
|
2734
|
+
拿它当「还要等多久」会渲出一个从不递减的数。
|
|
2735
|
+
3. **缺席禁折 0**。两位都可能缺席(旧 server 不发 / 未来相位铺开),缺席 = 不知道,不渲。
|
|
2736
|
+
|
|
2737
|
+
### 18c. 🔴 老宿主:**结构形免动,穷尽形必须加一条 case**
|
|
2738
|
+
|
|
2739
|
+
`waiting` 臂刻意是 `error` 臂的**结构超集**:它带 `deadline` / `attempt` / `maxRetries` /
|
|
2740
|
+
`errClass` / `retryAtMs` / `errorStatus` / `error{formatted:''}`,`terminal` 恒缺席。于是一个还没加
|
|
2741
|
+
`kind === 'waiting'` 分支的端,其 `if (kind==='stalled') … else …` 的 else 臂读到的一切与 0.54.0
|
|
2742
|
+
**逐位相同**,渲出的行也逐字节相同。
|
|
2743
|
+
|
|
2744
|
+
**为什么不用「更干净」的瘦臂**:少了 `error` 这一位,结构形老宿主的 `status.error.rateLimits` 就是
|
|
2745
|
+
一次 TypeError —— 渲染路径裸抛 = 整屏崩,把「一行渲错」升级成「一屏全没」。加员制造崩渲是不可接受
|
|
2746
|
+
的代价;让未跟车的端**保持原样**才是加员的诚实形。
|
|
2747
|
+
|
|
2748
|
+
🔴 **但结构超集救不了穷尽形消费者,这一条必须说清楚,不许含混成「无需动作」**(异源对抗复审
|
|
2749
|
+
[medium] 采纳):`switch (status.kind) { case 'stalled': …; case 'error': …; default: assertNever(status) }`
|
|
2750
|
+
这一形破的是**判别值**本身,不是形状 —— 补多少结构位都没用。后果两面:重新编译时报
|
|
2751
|
+
「`waiting` 不可赋给 `never`」(编译错,响亮,好);**已经编译好**的老产物拿到新 kind 会在
|
|
2752
|
+
default 臂**抛**(运行期,安静得多)。⇒ 对这一类消费方,0.55.0 是 **BREAKING**,发布帖必须点名。
|
|
2753
|
+
两形的事实各自钉在门里:`run-client-core-pure-test.mjs` G6-e ①(结构形零差异)/ ②(穷尽形真抛)
|
|
2754
|
+
/ ③(反空转:加员**前**同一个穷尽形消费者**不抛** ⇒ ② 的抛精确归因于多了一个判别值)。
|
|
2755
|
+
|
|
2756
|
+
🔴 **第三形:不读 `kind` 的宿主**(0.55.0 发包扫描 [medium] 采,按实际消费者取来的分类):web-client 现状是把
|
|
2757
|
+
`retryStatus` 非空一律渲成既有错误行「Request failed · retrying (n/m)」,根本不看 `kind`。它在 waiting 相上
|
|
2758
|
+
渲的仍是那一行 —— 与 0.54.0 **逐字节相同**,所以不是回归;但用户面拿不到进度行,要接 §18b(读 `kind==='waiting'`
|
|
2759
|
+
渲 headline + `elapsedMs`/`timeoutMs`)才有真话。三形对照:结构形零差异 / 穷尽形 BREAKING / 不读 kind 形=旧行不变。
|
|
2760
|
+
|
|
2761
|
+
判据不是一句承诺,是两道门(拿**本函数自己的 `default:` 臂**当活对照 —— 一个未知相走的正是加员前
|
|
2762
|
+
第七相走的那条臂 —— 两边在老宿主读得到的键上逐位相等才绿):
|
|
2763
|
+
`scripts/run-client-core-pure-test.mjs` G6-d(单元)/ `scripts/run-additive-key-passthrough-test.mjs`
|
|
2764
|
+
G5d(全链两段)。
|
|
2765
|
+
|
|
2766
|
+
### 18d. `BrainStatus` 九键 → 十一键
|
|
2767
|
+
|
|
2768
|
+
新增 `elapsedMs`(已经等了多久,ms)与 `timeoutMs`(这次等待的天花板,ms)。两位**进 `extra` 统一
|
|
2769
|
+
透传**,不按相位写特判 —— core 的不变式(「只在本相在场」)由 core 说,本层复述一遍就等于上游哪天
|
|
2770
|
+
把它铺到别的相时本层单方面剥键(`task_progress.model` / `retryAtMs` 两次存量的病根)。
|
|
2771
|
+
|
|
2772
|
+
🔴 **族扫落点两处**:`adapter/downstream/eventToSdkMessage.ts` 的 `case 'status'`(wire → 内部臂)
|
|
2773
|
+
与 `adapt/arms.ts` 的 `retryStatusArm`(内部臂 → chrome 事件)。漏任一层,新相到端只剩空壳
|
|
2774
|
+
(kind 对了、秒数永远缺席)。
|
|
2775
|
+
|
|
2776
|
+
### 18e. 端的措辞(sema 超集,非 CC parity)
|
|
2777
|
+
|
|
2778
|
+
CC 2.1.250 语料**无同形提示行** —— `first token` / `firstToken` / `first_token` 的全部命中都是
|
|
2779
|
+
账号首 token 日期与流式计时**遥测**,无渲染面;同族里唯一的用户可见行是 `Waiting for API response`,
|
|
2780
|
+
而那是 `reconnecting` 相的既有行(「在等应答」≠「已连上、在等第一个字」)。⇒ 措辞按 [6386] / L-79②
|
|
2781
|
+
既定形 `Waiting for the model's first token · Ns [/ Ns]`,各端登记 divergence。
|
|
2782
|
+
|
|
2783
|
+
**cli / web / desktop 认领**:cli 侧渲染臂在本批同车。web / desktop 按 18c **分两形**:
|
|
2784
|
+
消费形是**结构形**(`if (kind==='stalled') … else …`)⇒ 免动,逐字节零差异;
|
|
2785
|
+
消费形是**穷尽形**(`switch (kind)` + `default: assertNever`)⇒ **必须加一条 `waiting` case**,
|
|
2786
|
+
否则重新编译报「waiting 不可赋给 never」、已编译的老产物在 default 臂**抛**。
|
|
2787
|
+
跟车的正形都是加一条 `kind === 'waiting'` 臂,渲成**非错误色**的进度行。
|
|
2788
|
+
|
|
2789
|
+
**实现锚**:`src/retryStatus.ts`(`RetryStatus` 的 `waiting` 臂 / `BRAIN_STATUS_PHASES` 七相 /
|
|
2790
|
+
`BRAIN_STATUS_PAYLOAD_KEYS` 十一键 / `mapBrainStatusToRetry` 的 `case 'waiting_first_token'`)、
|
|
2791
|
+
`src/adapt/arms.ts`、`src/adapter/downstream/eventToSdkMessage.ts`。
|
|
2792
|
+
**常驻门**:`scripts/run-client-core-pure-test.mjs` G6 组(相位→kind 全矩阵 / 缺席位不捏造 /
|
|
2793
|
+
老宿主零差异)、`scripts/run-additive-key-passthrough-test.mjs` G5 组(全链两段)、
|
|
2794
|
+
`scripts/run-engine-vocab-floor-test.mjs` G2-b/G2-c(对**实装 core** 逐词逐键等值)。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/client-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.55.0",
|
|
4
4
|
"description": "Client-side session runtime shared by every sema human client (TUI / web / desktop): sema wire frames (AgentEvent) -> CC session vocabulary (SDKMessage) with dual-plane output (transcript/chrome), deterministic transcript ids, lane discipline as a type, and the notification/dedup ledgers. Every CC-skin shape is collected here so the wire itself stays neutral. Renamed from @sema-agent/wire-cc-adapter (0.1.x).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@sema-agent/agent-types": "^0.2.0",
|
|
38
|
-
"@sema-agent/core": "
|
|
38
|
+
"@sema-agent/core": "~7.3.0",
|
|
39
39
|
"@sema-agent/sdk": "^7.4.0",
|
|
40
40
|
"esbuild": "^0.27.4",
|
|
41
41
|
"typescript": "^6.0.2"
|