@sema-agent/client-core 0.70.0 → 0.70.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 +13 -0
- package/README.md +1 -1
- package/dist/adapt/textStream.js +40 -0
- package/docs/INTEGRATION-CLIENTS.md +32 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -49,6 +49,19 @@
|
|
|
49
49
|
> 挡住 ⇒ 本批把它机械化——④a0 对 `pending` 行**要求段头已是日期形**(`(未发布)` 直接红),阶段一
|
|
50
50
|
> commit 漏转在发布前就红,不再靠人记。
|
|
51
51
|
|
|
52
|
+
## 0.70.1(2026-09-16)
|
|
53
|
+
|
|
54
|
+
> 主题:**CC-17 推理/文本面安全修复** —— `handOffOnly` 形(段跨包侧边界 ∧ 已提交前缀自身过期)同步两本补差账 + 清未提交尾段。接入面 §40。
|
|
55
|
+
|
|
56
|
+
### Fixed
|
|
57
|
+
|
|
58
|
+
- 🔴 **CC-17 `handOffOnly` 形不再漏账**(接入方两形回执,整链实测复现)。段跨过包侧边界(工具卡 / 消息划界)且已提交前缀自身也过期时,`replaceAnswerSegment` 旧码只发三键信号就返回,两本补差账一行不动:
|
|
59
|
+
- **形一(卡后有增量)**:`answerSegment` 里那截**原始未脱敏**尾段照常被 `takeAnswerSegment` 提交上屏落盘 —— 它属于一个已作废的段,宿主换的是**另一条**消息,没人会来清它。
|
|
60
|
+
- **形二(卡后零增量)**:`lastCommittedAnswerText` 回退到上一条消息的原始正文,`result` 臂第④臂据此把尾段当「屏上缺的后缀」再吐一遍(终帧未脱敏时连字节都残留)。
|
|
61
|
+
修后:前缀落在哪本账就把哪本的 `livePrefix` 换成 `content`(`endsWith` 守卫,与非跨界形逐字同律),`answer` 同步,并清掉本段还没交出去的那一截(含它押在活体缓冲里的部分;上一段的 `carry` 原样留)。**仍然一个字节都不新交**,不铸任何消息。
|
|
62
|
+
🔴 **收窄(实测逼出)**:两本账是**按消息**记的,而 `livePrefix` 可能跨**多条**消息(多个工具卡 ⇒ 段身份覆盖多行)。那一形下宿主自己也换不了(归属歧义,端按「证明不了就不动手」整发放弃),包若照样清账清尾段,补差基线会回退到中间那条,`result` 反而从第⑤臂(只报分岔)掉进第④臂(补吐一整段)。⇒ 账对不上就退回既有行为:只发信号、一个字节不动;多行那一形的正解是把整段归属交全(`text_segment_end.committedUuids[]`,排下一版),不是包单方面清。
|
|
63
|
+
`run-text-segment-authority-test` Y 段九格 + D14b 按更强判据改口(旧格钉的「与 0.68.0 基线逐字相同」正是这个病);232 → 247;变异三发(去收窄 / 不清尾段 / 不同步账)各红。
|
|
64
|
+
|
|
52
65
|
## 0.70.0(2026-09-16)
|
|
53
66
|
|
|
54
67
|
> 主题:**CC-04 技能帽退役改预算式**(server 7.78.1 + core 7.19.0 同窗:`SKILL_CAPS` 三键退役、零条数丢弃、清单裁切整只归引擎 + `readSkillsListingTruncated` 读器)+ 通告码 +3 + CC-15 形 (a) 透传 `committedUuids: []`。接入面 §39。
|
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ Renamed from **`@sema-agent/wire-cc-adapter`** (0.1.x, deprecated — see *Migra
|
|
|
35
35
|
|
|
36
36
|
## Scope
|
|
37
37
|
|
|
38
|
-
**Version:** 0.70.
|
|
38
|
+
**Version:** 0.70.1
|
|
39
39
|
|
|
40
40
|
- **Today** — the adapter seam, the whole `adapt()` pipeline (all 14 A-layer arms plus the
|
|
41
41
|
B/D/E tool-card layers), the notification/caps/model families, the adapter kernel (stream driver
|
package/dist/adapt/textStream.js
CHANGED
|
@@ -367,6 +367,46 @@ export function createTextStream(ctx, idOf) {
|
|
|
367
367
|
*/
|
|
368
368
|
const handOffOnly = committedPrefixDiverged && previousSegmentCommitted.length > 0;
|
|
369
369
|
if (handOffOnly) {
|
|
370
|
+
// ── CC-17(0.70.1,cli 接入回执两形;整链实测复现)────────────────────────────────────
|
|
371
|
+
// 🔴 「只发信号」曾被写成「两本账一行都不动」,那是**漏账**不是克制:权威全文这一刻已经
|
|
372
|
+
// 交给宿主去换了,包却还按「屏上只有那截原始前缀」继续算,于是
|
|
373
|
+
// 形一(卡后有增量):`answerSegment` 里那截**原始未脱敏**尾段照常被 `takeAnswerSegment`
|
|
374
|
+
// 提交上屏落盘 —— 它属于一个已经作废的段,宿主换的是**另一条**消息,没人会来清它;
|
|
375
|
+
// 形二(卡后零增量):`lastCommittedAnswerText` 回退到上一条消息的原始正文,`result` 臂
|
|
376
|
+
// 的第④臂据此把尾段当「屏上缺的后缀」再吐一遍(终帧未脱敏时连字节都残留)。
|
|
377
|
+
// ⇒ 同步两本补差账(前缀落在哪本就换哪本,`endsWith` 守卫不匹配就不动,与非跨界形逐字同律)
|
|
378
|
+
// + 清掉本段还没交出去的那一截。**仍然一个字节都不新交**(不铸任何消息):那些字节在别的
|
|
379
|
+
// 消息里,宿主按三键换;本分支改的只是「包自己以为屏上有什么」。
|
|
380
|
+
// 🔴 旧注释(异源复审轮五 finding②)说「清未提交尾段会让 `lastCommittedAnswerText` 回退 ⇒
|
|
381
|
+
// 第④臂补吐一整段」—— 那个后果的**前提正是账没同步**;上面一行同步之后前提消失。这句话
|
|
382
|
+
// 不是推理,是判据:`run-text-segment-authority-test.mjs` 的 Y4 就跑那条时序。
|
|
383
|
+
// 🔴 **只在「宿主确定会把某一整条换成 `content`」时才认账**(收窄,实测逼出):这两本账是
|
|
384
|
+
// **按消息**记的,而 `livePrefix` 可能跨了**多条**消息(两个工具卡 ⇒ 段身份覆盖多行)。
|
|
385
|
+
// 那一形下宿主自己也换不了(段身份对上多行 = 归属歧义,端按「证明不了就不动手」整发放弃),
|
|
386
|
+
// 包若照样清账清尾段,`lastCommittedAnswerText` 会回退到中间那条,`result` 臂反而从第⑤臂
|
|
387
|
+
// (只报分岔)掉进第④臂(补吐一整段)—— 那正是轮五 finding② 抓到的**新增重复转录路径**,
|
|
388
|
+
// 实测确认过(D35/D36 两格钉的就是它)。⇒ 对不上就退回 0.68.0 行为:只发信号,一个字节不动。
|
|
389
|
+
// 多行跨界那一形的正解是把整段归属交全(`committedUuids[]`,CC-16),不是包单方面清。
|
|
390
|
+
const syncedBook = committedText.length > 0 && committedText.endsWith(livePrefix) ? 'current'
|
|
391
|
+
: previousCommittedText.endsWith(livePrefix) ? 'previous'
|
|
392
|
+
: null;
|
|
393
|
+
if (syncedBook === 'current') {
|
|
394
|
+
committedText = committedText.slice(0, committedText.length - livePrefix.length) + content;
|
|
395
|
+
}
|
|
396
|
+
else if (syncedBook === 'previous') {
|
|
397
|
+
previousCommittedText =
|
|
398
|
+
previousCommittedText.slice(0, previousCommittedText.length - livePrefix.length) + content;
|
|
399
|
+
}
|
|
400
|
+
if (syncedBook !== null && answer.endsWith(liveSegment)) {
|
|
401
|
+
answer = answer.slice(0, answer.length - liveSegment.length) + content;
|
|
402
|
+
}
|
|
403
|
+
if (syncedBook !== null) {
|
|
404
|
+
// 活体面:本段还押着没吐的那一截也作废(`textPending` 是段缓冲的后缀,前面那截 `carry`
|
|
405
|
+
// 属于上一段,与本次替换无关 ⇒ 原样留)。
|
|
406
|
+
const staleTailLen = Math.min(textPending.length, prevSegment.length);
|
|
407
|
+
textPending = textPending.slice(0, textPending.length - staleTailLen);
|
|
408
|
+
answerSegment = '';
|
|
409
|
+
}
|
|
370
410
|
segmentCommitted = '';
|
|
371
411
|
previousSegmentCommitted = '';
|
|
372
412
|
idleFlushUsedInSegment = false;
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
| 项 | 值 | 真源 |
|
|
21
21
|
|---|---|---|
|
|
22
|
-
| 本包 | `@sema-agent/client-core` **0.70.
|
|
22
|
+
| 本包 | `@sema-agent/client-core` **0.70.1**(本批发布版 = patch,零型面变化:🔴 CC-17 `handOffOnly` 形(段跨包侧边界 ∧ 已提交前缀自身过期)同步两本补差账 + 清未提交尾段 —— 修前形一卡后原始未脱敏尾段照常上屏落盘、形二 `result` 臂把它当缺的后缀补吐;账对不上(前缀跨多条消息)则退回既有行为;§40 / 40y 两条)|
|
|
23
23
|
| peer:wire 契约 | `@sema-agent/sdk` **>=8.4.0**(value-level,非 type-only;**0.60.0 抬版**,四条硬理由见 §24a 与 `scripts/run-sdk-floor-test.mjs` 的 `FLOOR` 注;上一次是 0.59.0 的 `>=8.3.0`)。🔴 支持窗同批收到 **engine ≥7.64.0**:sdk 8.4.0 与 7.63.0 及以前的 wire **不同窗** | `package.json` `peerDependencies` |
|
|
24
24
|
| peer:会话词汇表 | `@sema-agent/agent-types` **>=0.2.0**(type-only,零运行时) | 同上 |
|
|
25
25
|
| runtime dep | `diff` ^9.0.0(**唯一**一条;portability 门按**等值**钉死) | `package.json` `dependencies` |
|
|
@@ -7736,6 +7736,8 @@ import { AgentClient, isApprovalRequestFrameV1, type AgentEvent } from '@sema-ag
|
|
|
7736
7736
|
|
|
7737
7737
|
**语义一句话**:一个身份覆盖「上一次段收口 → 本次段收口」之间过境的**全部** committed assistant 文本行。流开场一个初值;idle-flush 提交的半段与终态提交的同段**共享**一个身份(归属正要这个);`text_segment_end` 帧带的是轮换**前**的那个(与它盖过的行同值);臂处理完之后轮换;思考→回答边界**不**轮换;子流(带 `parentToolCallId`)的段边界不轮换。
|
|
7738
7738
|
|
|
7739
|
+
🔧 **0.70.0 订正(cli 1.0.116 归层清账真机实撞,一句打回)**:与 `text_segment_end.segmentId` 同值的**只有段边界之前**提交的行(idle-flush 半段、或收口前已提交的整段)。**常态段**(`text_end` 先到、定稿行在边界**之后**才提交)的定稿行按包语义属于**下一**窗口 —— 它提交时正文已经是权威文本(缓冲在臂里换过),端**不用也不该**拿它对账;拿它去认「这一段交出去的那一行」会锚错行。上面「idle-flush 提交的半段与终态提交的同段共享一个身份」只对**两者都在边界前提交**那一形成立。
|
|
7740
|
+
|
|
7739
7741
|
**形**:派生自窗口首帧锚 + 窗口序号(`idOf(anchor, 'segment<n>')`)⇒ **同流重放同身份**(与行 `uuid` 同一条确定性纪律);锚缺席那一形(durable 整条消息先于任何增量到达)退到 `ctx.uuid()` 并**缓存一次**。
|
|
7740
7742
|
|
|
7741
7743
|
**铸点唯一** = `adapt()` 的出口(`TextStream.stampSegmentIdentity`):durable 整条消息(`assistant` 臂)与流式分段(M1 提交口)两条腿都从那里过。🔴 **只盖尾块是 `text` 的 assistant 行**:思考块、只带 `tool_use` 的行、chrome 事件一个字节不动;就地盖不复制(台账/宿主持有的引用仍是同一条)。
|
|
@@ -7926,7 +7928,7 @@ installCoreValuePorts({ resolveAutonomousLoopPrompt, loosenReasons, combinePolic
|
|
|
7926
7928
|
|
|
7927
7929
|
| 件 | 一句话 | 端要做什么 |
|
|
7928
7930
|
|---|---|---|
|
|
7929
|
-
| S-1 🔴 **已知边界** |
|
|
7931
|
+
| S-1 🔴 **已知边界**(0.70.0 改口:test [7335] B-125 实证 cli **两条车道**都经 `runStream`,「交互车道 `adapt()` 不经去重不受影响」是包视角的车道假设)| **凡经 `runStream` 的消费端(cli 交互 + `-p` 皆是;只有直喂 `adapt()` 的自建管线不在内)在 server <7.78.1 上收不到 `reasoning_end`**:server 7.77.0 给它的 SSE `id:` 复用该段首枚 `reasoning_delta` 的 id,`runStream` 的 durable seq 去重把它当重放丢;0.69.1 起**丢了必留痕**(`ctx.onDroppedFrame({ type:'reasoning_end', why:'duplicate_seq' })`)。**交互车道 `adapt()` 不经这只去重**,`thinking_segment_end` 照到 | 装 `onDroppedFrame` 的端把 `duplicate_seq` 当运维面留痕渲(不是用户面);`-p` 端在 server <7.78.1 上**如实披露**「推理面权威段不到」,不许壳侧绕过去重(去重律是契约) |
|
|
7930
7932
|
| S-2 🔴 **安全面** | **段末帧子流断闸改按身份三键**(CC-10):`text_end` / `reasoning_end` 内部臂新带 `sourceTaskId?` / `bgAgentId?`(与 `parentToolCallId` 同律原样透传),两臂任一在场即断;只带 `sourceTaskId` 的子代段末帧不再被当 leader 投上来 | 自建管线的端(print 车道 / 座位层)断子流按**三键**判,别只看 `parentToolCallId` |
|
|
7931
7933
|
| S-3 🔴 **安全面** | **跨工具卡的推理段归属交全**(CC-11):`thinking_segment_end` 新带 `committedUuids[]`(段内全部已提交思考行,按序;`committedUuid` = 末条兼容位);`committedPrefixLen` = 全部之和;新增 (b1) 形(前缀对得上 ⇒ 只换还押着的后段) | **宿主义务改口**:`committedPrefixDiverged` ⇒ `committedUuids` 首行就地换成 `content`,其余行**删除或清空其推理正文**(宿主按自身转录结构选;不再只换 `committedUuid` 那一条);缺席 ⇒ 行照旧 |
|
|
7932
7934
|
| S-4 | **park 两新键进 `WorkflowRunState`**(CC-12):`parks?` / `resumeAdmissionIncomplete?`(additive,由两读器铸) | 渲 resume 候选 / park 行的端改读视图上这两位;cli 的「到货锚」可翻 consumed |
|
|
@@ -8015,4 +8017,31 @@ installCoreValuePorts({ resolveAutonomousLoopPrompt, loosenReasons, combinePolic
|
|
|
8015
8017
|
2. **清单裁切只从 `skills.listing_truncated` 读**:`readSkillsListingTruncated(notice)` 给八格事实 + `laneClamped?`;运维面据此分「调 `skillListingBudgetFraction` 有用(部署比例)」与「调了也没用(车道 clamp)」;别自己数条数。
|
|
8016
8018
|
3. **CC-15 读法改口**:`thinking_segment_end.committedUuids` 在场即以它为准(`[]` = 本段零已提交行),整键缺席才是「包说不出」;为形 (a) 自建的过境行计数臂删。
|
|
8017
8019
|
|
|
8018
|
-
|
|
8020
|
+
**到期复核(0.70.0 发后当拍记)**:① core 7.20.0 预告([7334] ③a)删 `skills.listing_truncated.detail.requestedBudgetBytes`(8 KiB 天花板退役后请求预算即实际预算)⇒ `readSkillsListingTruncated` 该键可选、`laneClamped` 只在它在场时派生,到货零改即缺席;随 7.20.0 提货批把 `laneClamped` 派生位整只删。② §37y ③ 「交互车道 `adapt()` 不经去重不受影响」是包视角的车道假设:cli 交互车道实际也经 `runStream`(durable-seq 去重在那里),server 7.77.0 的 `reasoning_end` id 复用在交互车道**同样**把权威段当重放丢(test [7335] B-125 实证);包侧去重律不变、零改,根因修在 server 7.78.1(唯一 id)。**已知边界改口**:凡经 `runStream` 的车道(cli 两车道皆是)在 server <7.78.1 上都收不到 `reasoning_end`。
|
|
8021
|
+
|
|
8022
|
+
**包侧缺口:** ① `readRootCandidate` 候 sdk 出键;② CC-08 三段(`tool_progress` / `tool_disclosure` / `wiring_manifest`)顺延 0.71;③ `laneClamped` 派生位随 core 7.20.0 删;④ 其余同 §38 包侧缺口。
|
|
8023
|
+
|
|
8024
|
+
## §40 🆕 0.70.1 安全修复(CC-17:`handOffOnly` 形同步两本补差账 + 清未提交尾段)
|
|
8025
|
+
|
|
8026
|
+
### 40a. 本节速览
|
|
8027
|
+
|
|
8028
|
+
| 件 | 一句话 | 端要做什么 |
|
|
8029
|
+
|---|---|---|
|
|
8030
|
+
| S-1 🔴 **安全面** | **`handOffOnly` 形不再漏账**:段跨包侧边界(工具卡 / 消息划界)∧ 已提交前缀自身过期时,旧码只发三键信号、两本补差账一行不动 ⇒ 形一 卡后**原始未脱敏**尾段照常上屏落盘;形二 `result` 臂第④臂把它当「屏上缺的后缀」补吐一遍。修后账同步 + 清未交尾段,仍一个字节不新交 | **零动作**(端义务不变:按三键把那一行换成 `content`)。为这两形自建的壳侧补偿可退役;「归属证明不了就不动手」的闸留着当反钉 |
|
|
8031
|
+
| S-2 | **收窄:账对不上就不动**(`livePrefix` 跨多条消息 ⇒ 宿主自己也换不了)| 多行跨界那一形**照旧**是既有行为(原始行在场);它的正解是整段归属交全(`committedUuids[]`,排下一版),端在那之前对多行段保持「整发放弃」是对的 |
|
|
8032
|
+
|
|
8033
|
+
### 40z. 逐键处置表(0.70.1)
|
|
8034
|
+
|
|
8035
|
+
| # | 键 / 面 | 处置 | 坐标 / 理由 |
|
|
8036
|
+
|---|---|---|---|
|
|
8037
|
+
| 1 | `handOffOnly` 分支的两本补差账(`committedText` / `previousCommittedText`) | **consumed(修复)** | `src/adapt/textStream.ts` 的 `#replaceAnswerSegment`;`endsWith` 守卫按前缀落在哪本换哪本 |
|
|
8038
|
+
| 2 | 本段未提交尾段(`answerSegment` + 其 `textPending` 那截) | **consumed(修复)** | 账同步成功才清;`carry`(上一段的)原样留 |
|
|
8039
|
+
| 3 | 多行跨界形(`livePrefix` 跨多条消息) | **declined(退回既有行为)** | 清了会把补差基线搞回退 ⇒ `result` 从第⑤臂掉进第④臂补吐;正解是 `committedUuids[]`(下一版) |
|
|
8040
|
+
| 4 | 三键信号(`diverged` / `committedPrefixLen` / `committedPrefixDiverged`) | **不变** | 本批只改「包自己以为屏上有什么」,信号面逐字未动 |
|
|
8041
|
+
|
|
8042
|
+
### 40y. 🔴 三端升级必读(固定段式,[C295];本批**两条**)
|
|
8043
|
+
|
|
8044
|
+
1. 🔴 **端义务不变**,但包不再交那截未脱敏尾段:为这两形自建的壳侧补偿(记住原始尾段再比对、或整发放弃后手工清)**可退役**;通用的「归属证明不了就不动手」闸留着。
|
|
8045
|
+
2. **多行跨界段仍按老规矩**:一个 `segmentId` 对上多行时端照旧整发放弃(包这一版不单方面清);整段归属交全在下一版(`text_segment_end.committedUuids[]`)。
|
|
8046
|
+
|
|
8047
|
+
**包侧缺口:** ① 多行跨界形候 `committedUuids[]`(下一版);② 其余同 §39 包侧缺口。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/client-core",
|
|
3
|
-
"version": "0.70.
|
|
3
|
+
"version": "0.70.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. Renamed from @sema-agent/wire-cc-adapter (0.1.x).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|