@sema-agent/client-core 0.30.7 → 0.30.8

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 CHANGED
@@ -16,6 +16,67 @@
16
16
  > 🔴 **互链**(web [C166]⑦):各版「已知局限」段只记**该版新增**;接入面已知局限的完整台账在
17
17
  > `docs/INTEGRATION-CLIENTS.md` §6e/§7 —— **只读其一会漏**,两处都过。
18
18
 
19
+ ## 0.30.8(2026-08-15)
20
+
21
+ **server 7.23.0 / core 5.35.0 三键透传批(#281 件4b + #263 半场,[4037] 认领件的 client-core 半场)
22
+ ——公开面 **additive**,零 BREAKING;唯一非 additive 的一行是 SDK peer 地板(见末段):**
23
+
24
+ - **`tool_end` 的 `settledBy` / `approver` 到得了宿主了**(#263 半场)。`eventToSdkMessage` 的
25
+ `case 'tool_end'` 臂此前是**闭形白名单**,两键在包边界上被整段剥掉 ⇒ 三端无论怎么写都读不到一条
26
+ **已经在 wire 上**的事实(`toolCallId` [1947] / `governanceForced` [2942] 同款病形第三例)。
27
+ - `settledBy` = 这次调用**是怎么结算的**(core ≥5.18.1 #187:`human`/`timeout`/`aborted`);
28
+ `approver` = **是谁结算的**(core ≥5.35.0 design/252 G-7 / server ≥7.23.0:审批通道自报的身份串)。
29
+ - **两键合读**:`settledBy` 说哪一种收场,`approver` 说是谁的 —— `settledBy:"timeout"` 带 approver
30
+ 时,名字指的是**超时的那条队列**,不是拒绝的人。
31
+ - 🔴 **开集读**:`settledBy` 的类型面虽是三词闭集,SDK 头注逐字要求按开集读(词表属主是引擎)
32
+ —— 消费方分支已知值、**永远带 default**,绝不写无 default 的穷举 switch;`approver` 本就无词表。
33
+ - 🔴 **缺席不带语义,不许反推**:`settledBy` 缺席既不等于 `human` 也不等于「没走审批」(旧引擎 /
34
+ 姿态臂故意不填,两源同形);`approver` 缺席永不意味「没人批」也永不意味「有人批」。⇒ 本包
35
+ **缺席就不铸键**(绝不折成 null/空串/false)。判运行命运请读 run 行 / checkpoint 面。
36
+ - 🔴 `approver` 是 **UNTRUSTED-for-display**:**转录不是认证**(core 只筛形 ≤256 + 拒控制符/双向
37
+ 覆盖符,不鉴别不比对不回读;server 过境再打一遍密钥形码)。只渲染,绝不回喂模型/工具入参,
38
+ 更不许当鉴权判据。本层**零加工**(不 trim 不截断不改写),宿主看到的就是通道报的那个字节串。
39
+ - 读面类型:`ToolEndResultArmLike` 补 `settledBy?: unknown` / `approver?: unknown`(该形整体就是
40
+ 内部臂的**防御性读形**)。**`ToolEndPayload` 刻意不动** —— 它是关卡切片不是臂镜像,加一个没有
41
+ 落点的键 = 一句「关卡会用到它」的假话(同 `errorCode` 自 core 5.9.0 起的既有处置)。
42
+ - **`tool_approval.ruleEvidence` 帧 → 卡透传**(#281 件4b;server ≥7.23.0 / core 5.35.0 design/252
43
+ G-2,`"tool_approval"` only)。`ToolApprovalFrame` / `ApprovalCardRequest` 双侧补键 + 活卡帧腿
44
+ re-mint 处 stamp,壳的 `ruleEvidenceNote` 窄读器(已在位)自此有真值可读。
45
+ - 语义:这只 ask 背后的**规则出处证据** —— 组织策略快照 `orgRevision`、命中的组织规则原文
46
+ `orgRule`、被越级的个人规则 add dots `personalRuleDots`(**构造上就是数组**,按数据渲染/对账,
47
+ 绝不 join 成标量 id)。每个成员各自是「值 **⊕** 具名缺席」(`…Absent`;上游五词 `not_wired` /
48
+ `not_adjudicated` / `unavailable` / `no_match` / `not_reported`,**按开集读** —— 词表属主是引擎)。
49
+ - 🔴 **消费方必看**:具名缺席 **≠** 省略键,而且 live 实测**无治理车道**部署下引擎对**每一只**
50
+ ask 都 stamp 全具名缺席(`{"orgRevisionAbsent":"not_wired",…}`)——[4051] 定性为**契约行为非
51
+ 缺陷**。⇒ 「在场即渲」的天真消费形会给每张审批卡糊三行 not wired。呈现必须带**谓词**:成员
52
+ **有值**、或缺席词**不在良性白名单**(`unavailable` = 审计链断,恒可见;未知词可见)才占卡面
53
+ 一行。该形由 [4051] 背书为正解,web/desktop 渲此键照此抄。
54
+ - 🔴 **窄读域只许等于或宽于铸点域,绝不更窄**(随批立的消费纪律):整只拒读语义下「更窄」会**连坐
55
+ 吞掉**同记录里兄弟成员的合法证据。
56
+ - 🔴 **包只判载体形**(非 null 非数组对象)+ 原样搬运(类型 = `unknown` 开集,与 `probeCause` 同裁);
57
+ 成员级窄读与呈现谓词**单源在端**。两键共用同一把载体判词(`isProbeCauseCarrier` 更名
58
+ `isWireRecordCarrier`,私有函数,公开面零变化)。
59
+ - 🔴 **耐久路今天无对偶**:sdk 7.1.0 只在 `ToolApprovalFrame` 上声明该键,`PendingCheckpoint` /
60
+ `riskDescriptor` 两处都没有 ⇒ durable 行 → 卡那条腿**刻意零 stamp**(不去猜一个载体名:猜对了
61
+ 是恒 undefined 的白写,猜错了是拿另一个量冒充同一位),已登记进 durable 门的未投影账。上游补位
62
+ 后按 `probeCause` 的双源合流形跟批。
63
+ - **键镜像追平**:`TOOL_APPROVAL_FRAME_KEYS_MIRROR` 16 → **17 键**(补 `ruleEvidence`)。sdk 7.1.0 的
64
+ 运行期锚**已含** `probeCause` 与 `ruleEvidence` 两键 ⇒ 0.30.4 立的 `probeCause` **领先登记
65
+ (AHEAD_OF_ANCHOR)按它自己的退出条件同批删除**,领先表**回空**=镜像与锚逐元素相等、零豁免。
66
+
67
+ **🔴 唯一非 additive 的一行 —— SDK peer 地板 `>=6.17.2` → `>=7.1.0`(跨 major)**:
68
+
69
+ - **本包代码面对 6.x 消费者零破坏**(三键全是开集防御读,不依赖 7.x 的任何运行期符号);抬地板是
70
+ **两道门的合取结论**,不是版本洁癖:①`run-approval-frame-keys-test.mjs` 是**等值**门,而
71
+ `ruleEvidence` 只在 sdk ≥7.1.0 的运行期锚上出现,<7.1.0 的实装物上本包镜像会被判「多键」;
72
+ ②`run-public-surface-test.mjs` ⑤ 要求**声明地板不低于见证到的最低实装版本** —— devDep 与 sema-cli
73
+ 树同批上 7.1.0 后,盘上再无 6.17.2 那条线的实装物,继续声明 `>=6.17.2` 就是承诺一条从没有物料
74
+ 证明跑过的下线(README 同段逐字:*"A floor nobody ever ran is a promise, not a contract"*)。
75
+ - **消费侧影响**([4050] 已点名):cli / web 已提 7.1.0;**desktop 仍在 6.16.0**,在它跟提之前装本包
76
+ 会看到 peer 警告(不是安装失败,也不是运行期错误)。
77
+ - sdk 7.0.0 的 BREAKING 面(images/bakes 输入收窄 + `ImageSelectResult` 形变)对本包**零受迫**:
78
+ `src/**` 对 `images.` / `bakes` / `ImageSelect` / `.select(` 全树零调用点(本批亲跑直证)。
79
+
19
80
  ## 0.30.7(2026-08-15)
20
81
 
21
82
  **#242 批 2 subagent 归属单表 + resume 腿归位(A-028.6/.7 收编)——公开面 additive,一处行为面加强:**
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.30.6
38
+ **Version:** 0.30.8
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
@@ -65,7 +65,7 @@ Renamed from **`@sema-agent/wire-cc-adapter`** (0.1.x, deprecated — see *Migra
65
65
  `SseIdleError`, `probeHealth`, `APIError` and `TaskStopConflictError` are imported as values in
66
66
  five modules, and the browser bundle really bundles the SDK through (the portability guard would
67
67
  exit 3 rather than quietly mark it external).
68
- - The declared floor is `>=6.17.2`, and it is *witnessed*: the guard checks that an actually
68
+ - The declared floor is `>=7.1.0`, and it is *witnessed*: the guard checks that an actually
69
69
  installed SDK at that line still exports every value-level symbol this package imports and still
70
70
  declares `TaskStats.costMicroUsd` (the key `costOrNull` reads). A floor nobody ever ran is a
71
71
  promise, not a contract.
@@ -32,7 +32,17 @@ export interface PendingToolUse {
32
32
  eventId?: string;
33
33
  parentToolCallId?: string;
34
34
  }
35
- /** `tool_end_result` 帧上与关卡有关的那几位(原 `buildToolResult` 的内联入参形,具名化)。 */
35
+ /**
36
+ * `tool_end_result` 帧上**与关卡有关的那几位**(原 `buildToolResult` 的内联入参形,具名化)。
37
+ *
38
+ * 🔴 本形**不是** `tool_end_result` 臂的镜像,是它的**关卡切片** —— 只收「铸这张 tool_result 卡真正
39
+ * 要读的位」。臂上另有若干**开集观察位**按设计**不进本形**:`errorCode`(core 5.9.0 W3)、
40
+ * `settledBy`(core 5.18.1 #187)、`approver`(core 5.35.0 / server 7.23.0 #263)。它们由
41
+ * `eventToSdkMessage` 的 `case 'tool_end'` 臂原样上臂、由**宿主/桥**直接从臂上读(壳 upstreamBridge、
42
+ * print 车道 {@link import('../printToolResultFrame.js').ToolEndResultArmLike}),卡台账一位都不消费。
43
+ * 往本形加一个没有落点的键 = 一句「关卡会用到它」的假话(声明与产物对不上,且没有任何门会响);
44
+ * 真要让转录卡带上结算归属,那是一件**新的呈现设计**(需定 `_sema_` 超集键 + 台账登记),不是透传。
45
+ */
36
46
  export interface ToolEndPayload {
37
47
  output?: unknown;
38
48
  structured?: unknown;
@@ -21,8 +21,11 @@
21
21
  * ② 审批/提问门(human / irreversible_ask / tool_approval)⇒ **重开那张卡**(把决定权还给
22
22
  * 用户;cancel+自动重发臂已整退役 —— 实证它是「新消息→cancel→新 run 同 ask 再 park」的
23
23
  * 循环病根;重开失败也只如实告知,绝不回退 cancel)。
24
- * ③ `running`(或任何非 park 态)**什么都不做**。持有 claim 的可能是用户背景化的 durable
25
- * run,正在替他干活;见 409 cancel = 销毁他明确要求的工作。
24
+ * ③ `running` ⇒ **呈 steer/cancel/wait 三选卡**(runningChoiceArm;offerRunningChoice 供给
25
+ * 缺席即回落 not-parked 现状行不动)。持有 claim 的可能是用户背景化的 durable run,正在替
26
+ * 他干活——绝不见 409 就自动 cancel,决定权给用户。新结局五个:running-steered /
27
+ * running-steer-failed / running-cancelled / running-cancel-timeout / running-cancel-failed;
28
+ * cancel 后走有界等 claim 释放腿(waitForClaimRelease,CANCEL_RELEASE_WAIT_MS 封顶)。
26
29
  * ④ kind/status 两级都判不出(含今天还不存在的新门/新状态)⇒ 也什么都不做,如实告知。
27
30
  * 不确定时不做破坏性动作;新词到货时最坏结果是保守,而不是拿没想过的词做破坏性动作。
28
31
  *
@@ -205,6 +205,26 @@ export function eventToSdkMessage(ev, ctx) {
205
205
  // 绝不按识别表过滤(过滤 = 替引擎决定哪些原因配得上被看见)。判别用
206
206
  // `engineErrorCodes.isInterruptedToolEndCode`,不要在消费点重新写字面比较。
207
207
  const toolEndErrorCode = ev.errorCode;
208
+ // ── #263 半场(0.30.8):`settledBy`(core ≥5.18.1,#187)/ `approver`(core ≥5.35.0
209
+ // design/252 G-7,server ≥7.23.0)—— 这次调用**是怎么结算的**与**是谁结算的**。
210
+ // 两键在 SDK 头注里明说要**合读**:`settledBy` 说这次收场是哪一种,`approver` 说是谁的
211
+ // (`settledBy:"timeout"` 带 approver 时,名字指的是**超时的那条队列**,不是拒绝的人)。
212
+ // 此前本臂是闭形白名单,两键在包边界上被整段剥掉 ⇒ 宿主(壳/web/desktop)无论怎么写都读不到
213
+ // 一条**已经在 wire 上**的事实 —— [1947] `toolCallId` / [2942] `governanceForced` 同款病形第三例。
214
+ // 🔴 **开集防御读**(与上面 `errorCode` 同姿势):`settledBy` 的类型面虽是三词闭集,SDK 头注却
215
+ // 逐字要求「按**开集**读、分支已知值、忽略其余」—— 词表属主是引擎,窄读成枚举会在加员当天把
216
+ // 一个**合法**值判没;`approver` 本就是无词表的开集身份串。两键一律「非空串即原样透传」,
217
+ // 绝不按识别表过滤,也绝不 trim/截断/改写(core 只筛形 ≤256 且拒控制符/双向覆盖符,server
218
+ // 过境再打一遍密钥形码;本层任何加工都会让宿主看到的名字不再是通道报的那一个)。
219
+ // 🔴 **缺席不带语义,不许反推**(SDK 头注逐字):`settledBy` 缺席至少两源(旧引擎;姿态臂如
220
+ // headless auto-deny / 整体 `onAsk` 放行 —— 那些结局里没有「结算来源」这回事),既不等于
221
+ // `human` 也不等于「没走审批」;`approver` 缺席 = 通道没报名字,永不意味「没人批」也永不
222
+ // 意味「有人批」。⇒ 缺席就**不铸键**(绝不折成 null / 空串 / false)。判这次运行的命运读
223
+ // run 行 / checkpoint 面,别拿这两键当判据。
224
+ // 🔴 **UNTRUSTED-for-display**:`approver` 是部署侧审批通道**自报**的身份串,转录不是认证
225
+ // (core 不鉴别、不比对、不回读)—— 只渲染,绝不回喂模型/工具入参,更不许当鉴权判据。
226
+ const toolEndSettledBy = ev.settledBy;
227
+ const toolEndApprover = ev.approver;
208
228
  return projected(stamp(ctx, armBody({
209
229
  type: 'tool_end_result',
210
230
  toolCallId: ev.toolCallId,
@@ -220,6 +240,13 @@ export function eventToSdkMessage(ev, ctx) {
220
240
  ...(ev.output !== undefined ? { output: ev.output } : {}),
221
241
  ...(structured !== undefined ? { structured } : {}),
222
242
  ...(ev.truncated !== undefined ? { truncated: ev.truncated } : {}),
243
+ // #263 半场:结算种类 + 结算方身份(开集、缺席不铸、在场原样;顶部大段头注是判据本体)。
244
+ ...(typeof toolEndSettledBy === 'string' && toolEndSettledBy.length > 0
245
+ ? { settledBy: toolEndSettledBy }
246
+ : {}),
247
+ ...(typeof toolEndApprover === 'string' && toolEndApprover.length > 0
248
+ ? { approver: toolEndApprover }
249
+ : {}),
223
250
  ...(ev.eventId !== undefined ? { eventId: ev.eventId } : {}),
224
251
  ...(ev.parentToolCallId !== undefined
225
252
  ? { parentToolCallId: ev.parentToolCallId }
@@ -250,6 +250,24 @@ export interface ApprovalCardRequest {
250
250
  * 卡形字节不变。UNTRUSTED-for-display:code/roots 是工具作文面,只渲染绝不回喂模型/工具入参。
251
251
  */
252
252
  probeCause?: unknown;
253
+ /**
254
+ * 规则出处证据(#281 件4b,0.30.8;server ≥7.23.0 / core 5.35.0 design/252 G-2)——原样来自
255
+ * {@link ToolApprovalFrame.ruleEvidence} 的**载体形合形值**(单源:活卡帧腿;durable 行腿今天
256
+ * 无对偶,见帧上同名键的 JSDoc 末段)。缺席 = 老 server / 坏载体形(两者同形,不猜),卡形与
257
+ * 0.30.7 字节不变。
258
+ *
259
+ * 🔴 **开集(unknown),包只搬运**:成员级窄读(具名缺席词、矛盾形丢成员、整只拒读)与**呈现
260
+ * 谓词**全部归端。呈现谓词不是可选项 —— 无治理车道部署下引擎对**每一只** ask 都 stamp 全具名
261
+ * 缺席([4050] live 实测 / [4051] 定性为契约行为),「在场即渲」会给每张卡糊三行 not wired。
262
+ * 端侧形照 [4051] 背书的正解:成员**有值**、或缺席词**不在良性白名单**(`unavailable` = 审计链
263
+ * 断,恒可见;未知词可见)才占卡面一行;窄读侧仍如实留全部成员。
264
+ * 🔴 **展示/对账元数据,永不是裁决输入**:它不参与也不改变本次决断的任何字节。
265
+ * 🔴 **不是身份通道**(与 {@link persistedRuleShadowed} 同族警示):`orgRule` 是脱敏后的展示文本、
266
+ * `personalRuleDots` 是耐久身份**数组**——下游删除/撤销规则面不许拿 `orgRule` 当规则身份,也不许
267
+ * 把 dots join 成一个标量 id;要操作规则走规则店自己的枚举与标识。
268
+ * UNTRUSTED-for-display:只渲染,绝不回喂模型/工具入参。
269
+ */
270
+ ruleEvidence?: unknown;
253
271
  }
254
272
  /**
255
273
  * 🔴 **拆缝口** —— 弹「三选卡」并等人的决断。壳 = vendored CC `PermissionRequest`;
@@ -386,6 +404,35 @@ export interface ToolApprovalFrame {
386
404
  * UNTRUSTED-for-display:code/roots 是工具作文面(server 已 redact),端呈前消毒,只渲染绝不回喂。
387
405
  */
388
406
  probeCause?: unknown;
407
+ /**
408
+ * server ≥7.23.0 / core 5.35.0 design/252 G-2(#281 件4b,0.30.8;**ADDITIVE**,`"tool_approval"`
409
+ * only。运行期锚直证 = sdk 7.1.0 `TOOL_APPROVAL_FRAME_KEYS` 已含本键)——这只 ask 背后的
410
+ * **规则出处证据**:组织策略快照 `orgRevision`、命中的组织规则原文 `orgRule`、被越级的个人规则
411
+ * add dots `personalRuleDots`(规则的耐久身份,**构造上就是数组**——按数据渲染/对账,绝不 join
412
+ * 成一个标量 id)。
413
+ *
414
+ * 🔴 **每个成员各自是「值 或 具名缺席」**(`…Absent`,上游五词 + 开集读:词表属主是引擎)。
415
+ * `not_wired` 没有这条车道 / `not_adjudicated` 车道在但这次没问 / `unavailable` **问了而源读不出**
416
+ * (唯一意味「审计链断了」而不是「空」的那个词——手上这只 ask 正是随后的 fail-closed 收紧) /
417
+ * `no_match` 问了没人说话 / `not_reported` 缝答了但没带身份。
418
+ * 🔴 **具名缺席 ≠ 省略键**,而且 [4050] live 实测:**无治理车道**部署下 7.23.0 对**每一只** ask 都
419
+ * stamp 本键为全具名缺席(`{"orgRevisionAbsent":"not_wired",…}`)——[4051] 定性为**契约行为非缺陷**
420
+ * (把「没接治理」从裸 undefined 里拆出来正是该键的设计承重)。⇒ 「在场即渲」的天真消费形会给每张
421
+ * 审批卡糊三行 not wired;呈现必须带**谓词**(成员有值、或缺席词不在良性白名单 —— `unavailable`
422
+ * 恒可见、未知词可见),那一层归端(壳 `ruleEvidenceNote` 单源,[4051] 背书为正解形)。
423
+ * 🔴 **展示/对账元数据,永不是裁决输入**;server 只转录引擎盖的记录,自己绝不铸缺席词。
424
+ * 🔴 **本包刻意不拥有这个形**(类型 = unknown 开集,与 {@link probeCause} 同裁):载体形判
425
+ * (非 null 非数组对象)之外零校验零改写。成员级窄读**单源在端**——包内校严一档,就会把上游
426
+ * additive 成员挡在边界外,还让「谁把成员窄没的」在排障时说不清;而端侧窄读器有一条更硬的纪律:
427
+ * **窄读域只许等于或宽于铸点域,绝不更窄**([4050] 随批立)——整只拒读语义下「更窄」会连坐吞掉
428
+ * 同记录里兄弟成员的合法证据。
429
+ * 🔴 **缺席 ≠「没有治理」**:帧上无本键 = 老 server,或窄读丢了一条畸形记录 —— 两者同形,不猜。
430
+ * 耐久路今天**无对偶**(sdk 7.1.0 的 `PendingCheckpoint` / `riskDescriptor` 均未声明本键),故
431
+ * durable 行 → 卡那条腿不 stamp;上游补位后按 {@link probeCause} 的双源合流形跟批。
432
+ * UNTRUSTED-for-display:`orgRule` 是策略作文面(core `inlineUntrusted` + server redact 后上帧),
433
+ * 端呈前消毒,只渲染绝不回喂模型/工具入参。
434
+ */
435
+ ruleEvidence?: unknown;
389
436
  }
390
437
  /** {@link ToolApprovalFrame.delegation} 的形(命名形,不用内联匿名 —— typeshape 门 B4 棘轮口径)。 */
391
438
  export interface ToolApprovalDelegation {
@@ -401,7 +448,7 @@ export interface ToolApprovalDelegation {
401
448
  * `TOOL_APPROVAL_FRAME_KEYS` 比对——SDK additive 增键时对账当天红,不再人肉追平。
402
449
  * 下面两个类型钉保证镜像与 interface 本身不可能漂移(少键/多键都是编译错)。
403
450
  */
404
- export declare const TOOL_APPROVAL_FRAME_KEYS_MIRROR: readonly ["type", "approvalId", "toolCallId", "toolName", "sourceTaskId", "fromSubagent", "sourceAgentName", "message", "args", "argsOmitted", "governanceForced", "ruleSuggestions", "persistedRuleShadowed", "probeCause", "delegation", "outcome"];
451
+ export declare const TOOL_APPROVAL_FRAME_KEYS_MIRROR: readonly ["type", "approvalId", "toolCallId", "toolName", "sourceTaskId", "fromSubagent", "sourceAgentName", "message", "args", "argsOmitted", "governanceForced", "ruleSuggestions", "persistedRuleShadowed", "probeCause", "ruleEvidence", "delegation", "outcome"];
405
452
  /** 子代帧判别:显式键 fromSubagent(core 1.378 RB-39②)优先;缺席退 sourceTaskId 在场性权宜式
406
453
  * (server 1.258 [1549]①3,旧代际兼容)。 */
407
454
  export declare function isFromSubagent(frame: ToolApprovalFrame): boolean;
@@ -258,7 +258,13 @@ export async function surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signa
258
258
  // #280 件1(0.30.4):durable 行腿的探针因由载体 = `riskDescriptor.probeCause`(server 对
259
259
  // riskDescriptor 整体透传,与活卡帧同值)。SDK 6.17.2 的 riskDescriptor 声明尚无此键 ⇒ 结构
260
260
  // 视图读(与 caps 防御读同款姿势),类型半场候 SDK 班车;载体形不合 ⇒ 不铸键(内部结构不校,
261
- // 归呈卡端窄读器 —— isProbeCauseCarrier 顶注)。
261
+ // 归呈卡端窄读器 —— isWireRecordCarrier 顶注)。
262
+ // 🔴 **`ruleEvidence` 在本腿刻意零 stamp**(#281 件4b,0.30.8):sdk 7.1.0 的 `PendingCheckpoint`
263
+ // 与 `riskDescriptor` 都**没有**这一位 —— 上游今天只在活卡帧上铸它,耐久路无对偶。凭一个
264
+ // 「大概也叫这个名字」的猜测去读 `riskDescriptor.ruleEvidence`,读到的恒是 undefined(白写),
265
+ // 而万一上游选了别的载体名/别的语义,那就是拿一个不同的量冒充同一位。⇒ 诚实零投影 + 登记
266
+ // (run-durable-card-display-keys-test.mjs 的 NOT_PROJECTED 账),上游补位后按 probeCause 的
267
+ // 双源合流形跟批。
262
268
  const durableProbeCause = pending.riskDescriptor?.probeCause;
263
269
  const card = await surfaceApprovalCard({
264
270
  toolName,
@@ -267,7 +273,7 @@ export async function surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signa
267
273
  ...(signal ? { signal } : {}),
268
274
  ...(pending.governanceForced === true ? { governanceForced: true } : {}),
269
275
  ...(ruleSuggestionsReadOnly !== undefined ? { ruleSuggestionsReadOnly } : {}),
270
- ...(isProbeCauseCarrier(durableProbeCause) ? { probeCause: durableProbeCause } : {}),
276
+ ...(isWireRecordCarrier(durableProbeCause) ? { probeCause: durableProbeCause } : {}),
271
277
  });
272
278
  switch (card.kind) {
273
279
  case 'failed':
@@ -354,10 +360,16 @@ export const TOOL_APPROVAL_FRAME_KEYS_MIRROR = [
354
360
  // 领先登记(AHEAD_OF_ANCHOR):SDK 锚一旦补上,那条登记当场红,逼人删登记而不是让豁免长住。
355
361
  'persistedRuleShadowed',
356
362
  // #280 件1(client-core 0.30.4):server 7.21.0-rc(#253)起真发 `probeCause`(直证 = engine
357
- // 7.22.0 fixture 的 `@sema-agent/server/dist/tool-approval.d.ts`)。⚠️ 本键**领先** SDK 运行期锚
358
- // 一代(sdk 6.17.2 尚无)⇒ 对账门里有一条带退出条件的领先登记(AHEAD_OF_ANCHOR),SDK 锚一旦
359
- // 补上,那条登记当场红,逼人删登记回到逐元素相等 —— #144 persistedRuleShadowed 同形先例。
363
+ // 7.22.0 fixture 的 `@sema-agent/server/dist/tool-approval.d.ts`)。当时本键**领先** SDK 运行期锚
364
+ // 一代(sdk 6.17.2 尚无)⇒ 对账门里挂着一条带退出条件的领先登记(AHEAD_OF_ANCHOR)
365
+ // **锚已追平**(0.30.8 提货,sdk 7.1.0 的 `TOOL_APPROVAL_FRAME_KEYS` 已含本键)⇒ 那条登记按它
366
+ // 自己的退出条件当场翻红、同批删除,回到逐元素相等 —— #229 那批删 `persistedRuleShadowed`
367
+ // 登记时走的是同一条路。领先表回空 = 健康态。
360
368
  'probeCause',
369
+ // #281 件4b(client-core 0.30.8):server 7.23.0 / core 5.35.0 design/252 G-2 起真发 `ruleEvidence`。
370
+ // ⚠️ 与上面两例**不同形**:sdk 7.1.0 的运行期锚**已经含**本键(node 直读实证)⇒ 这是一次**追平**,
371
+ // 不是领先,故**不**进 AHEAD_OF_ANCHOR(往那张表里塞一个锚已有的键,它的第二条退出条件当场红)。
372
+ 'ruleEvidence',
361
373
  'delegation',
362
374
  'outcome',
363
375
  ];
@@ -470,13 +482,18 @@ function pathFromGateMessage(message) {
470
482
  * 掩盖注入,整体降缺席)。
471
483
  */
472
484
  /**
473
- * `probeCause` 的**载体形**判(#280 件1):非 null 非数组对象才搬运(wire 契约的载体就是对象,
474
- * 串/数/数组/null 是坏载体不是「另一种因由」)。刻意只判到这一层 —— 内部结构(code/roots/further)
475
- * 校验归呈卡端窄读器(server `readProbeCause` 的消费端对偶,壳 `probeCauseNote.readProbeCauseKey`),
476
- * 包内校严一档就会把上游 additive 内键挡在边界外,还让「谁把键窄没的」在排障时多一层嫌疑。
477
- * 两条决断腿(活帧/durable 行)共用这一把,判词绝不各写各的。
485
+ * **开集记录键的载体形判**(#280 件1 `probeCause` 用;#281 件4b 起 `ruleEvidence` 同用):
486
+ * null 非数组对象才搬运 —— 这两键的 wire 契约载体都是**记录**,串/数/数组/null 是坏载体,
487
+ * 不是「另一种因由 / 另一种证据」。
488
+ *
489
+ * 🔴 刻意**只判到载体这一层**:内部成员的校验(probeCause 的 code/roots/further;ruleEvidence 的
490
+ * 值 ⊕ 具名缺席词、矛盾形、整只拒读)归**呈卡端窄读器**(壳 `probeCauseNote.readProbeCauseKey` /
491
+ * `ruleEvidenceNote.readRuleEvidenceKey`,各自与 server 铸点 `readProbeCause` / `readRuleEvidence`
492
+ * 同向)。包内校严一档就会把上游 additive 内键挡在边界外,还让「谁把键窄没的」在排障时多一层嫌疑。
493
+ * 🔴 **一把判词服务多键**是设计而非省事:两条决断腿(活帧 / durable 行)× 多个开集记录键共用同一个
494
+ * 载体判据,判词绝不各写各的(各写一份 = 同一个问题有了两个属主,分叉那天没人发现)。
478
495
  */
479
- function isProbeCauseCarrier(v) {
496
+ function isWireRecordCarrier(v) {
480
497
  return typeof v === 'object' && v !== null && !Array.isArray(v);
481
498
  }
482
499
  function readRuleSuggestions(v) {
@@ -592,8 +609,14 @@ export async function surfaceToolApprovalFrameAndRespond(frame, respond, streamA
592
609
  ? { persistedRuleShadowed: frame.persistedRuleShadowed }
593
610
  : {}),
594
611
  // #280 件1(0.30.4):探针因由随卡透传 —— 载体形判之外零校验零改写(内部结构归呈卡端窄读器,
595
- // isProbeCauseCarrier 顶注);缺席/坏载体 ⇒ 键不 stamp,卡形字节不变。
596
- ...(isProbeCauseCarrier(frame.probeCause) ? { probeCause: frame.probeCause } : {}),
612
+ // isWireRecordCarrier 顶注);缺席/坏载体 ⇒ 键不 stamp,卡形字节不变。
613
+ ...(isWireRecordCarrier(frame.probeCause) ? { probeCause: frame.probeCause } : {}),
614
+ // #281 件4b(0.30.8):规则出处证据随卡透传 —— 同上,只判载体形、**成员级一概不校**
615
+ // (值 ⊕ 具名缺席词、未知缺席词、矛盾形一律原样上卡:判决权单源在端侧窄读器,
616
+ // 且端侧窄读域只许 ≥ 铸点域)。缺席/坏载体 ⇒ 键不 stamp,卡形与 0.30.7 字节不变。
617
+ // 🔴 呈现谓词归端(无治理部署下每只 ask 都带全具名缺席,[4050] live 实测 / [4051] 定性);
618
+ // 本层若替端做「全 not_wired 就别给了」的过滤,就是替引擎把一条真事实湮灭掉。
619
+ ...(isWireRecordCarrier(frame.ruleEvidence) ? { ruleEvidence: frame.ruleEvidence } : {}),
597
620
  });
598
621
  const decision = card.kind === 'allow' ? (card.allowSession ? 'allow_session' : 'allow') : 'deny';
599
622
  if (card.kind === 'failed') {
@@ -42,9 +42,9 @@
42
42
  * value (integer domain below; a mis-typed budget bound must not quietly run unbounded).
43
43
  * b) settings env lane (settings.json `env` block → 1a-envseed → process.env):
44
44
  * `SEMA_HEADLESS_DEADLINE_SEC` / `SEMA_HEADLESS_MAX_TURNS` / `SEMA_HEADLESS_MAX_TOKENS`
45
- * → SILENT degrade on an invalid value (spec'd 静默降级 a settings typo must never brick every
46
- * headless run; unlike scenarioWire we don't even warn, per the batch-E triage shape. SEMA_DEBUG
47
- * surfaces the drop for diagnosis).
45
+ * → a) FAIL-LOUD on an invalid value(#132 / test [2508] 2026-08-04 推翻旧 batch-E
46
+ * 「静默降级」形;见 headlessLimitsFromEnv JSDoc——非法值 {ok:false} 上抛,caller exit 1,
47
+ * 常驻门 run-limits-env-failloud-test.mjs 守此形)
48
48
  * c) neither → NO stamp = today's behaviour, verbatim.
49
49
  *
50
50
  * DOMAINS (integer, inclusive): deadline 30..86400 **秒**(→ 30_000..86_400_000 ms 上 wire)·
@@ -42,9 +42,9 @@
42
42
  * value (integer domain below; a mis-typed budget bound must not quietly run unbounded).
43
43
  * b) settings env lane (settings.json `env` block → 1a-envseed → process.env):
44
44
  * `SEMA_HEADLESS_DEADLINE_SEC` / `SEMA_HEADLESS_MAX_TURNS` / `SEMA_HEADLESS_MAX_TOKENS`
45
- * → SILENT degrade on an invalid value (spec'd 静默降级 a settings typo must never brick every
46
- * headless run; unlike scenarioWire we don't even warn, per the batch-E triage shape. SEMA_DEBUG
47
- * surfaces the drop for diagnosis).
45
+ * → a) FAIL-LOUD on an invalid value(#132 / test [2508] 2026-08-04 推翻旧 batch-E
46
+ * 「静默降级」形;见 headlessLimitsFromEnv JSDoc——非法值 {ok:false} 上抛,caller exit 1,
47
+ * 常驻门 run-limits-env-failloud-test.mjs 守此形)
48
48
  * c) neither → NO stamp = today's behaviour, verbatim.
49
49
  *
50
50
  * DOMAINS (integer, inclusive): deadline 30..86400 **秒**(→ 30_000..86_400_000 ms 上 wire)·
@@ -58,6 +58,25 @@ export interface ToolEndResultArmLike {
58
58
  /** 引擎 CC-shaped details(core 1.199+ AgentToolResult.details;Bash 时带 exitCode)。 */
59
59
  structured?: unknown;
60
60
  truncated?: unknown;
61
+ /**
62
+ * 这次调用**是怎么结算的**(core ≥5.18.1 #187;`human`/`timeout`/`aborted` + 开集读)与
63
+ * **是谁结算的**(core ≥5.35.0 design/252 G-7 / server ≥7.23.0 #263;审批通道自报的身份串,
64
+ * 无词表)。#263 半场(0.30.8)起由 `eventToSdkMessage` 的 `case 'tool_end'` 臂原样透传到本臂,
65
+ * 宿主据此把结算归属渲到工具卡/审计面。
66
+ *
67
+ * 🔴 两键**合读**:`settledBy` 说这次收场是哪一种,`approver` 说是谁的 —— `settledBy:"timeout"`
68
+ * 带 approver 时,名字指的是**超时的那条队列**,不是拒绝的人。
69
+ * 🔴 **缺席不带语义,不许反推**:`settledBy` 缺席既不等于 `human` 也不等于「没走审批」(旧引擎 /
70
+ * 姿态臂故意不填,两源同形);`approver` 缺席 = 通道没报名字,永不意味「没人批」也永不意味
71
+ * 「有人批」。判这次运行的命运读 run 行 / checkpoint 面。
72
+ * 🔴 `approver` 是 **UNTRUSTED-for-display**:转录不是认证(core 只筛形不鉴别不比对不回读),
73
+ * 可信度 = 那条审批通道的可信度;只渲染,绝不回喂模型/工具入参,更不许当鉴权判据。
74
+ * 🔴 本形是**防御性读形**(全员 `unknown`):`settledBy` 的类型面虽是三词闭集,但词表属主是引擎,
75
+ * 消费方必须**分支已知值 + 永远带 default**,绝不写无 default 的穷举 switch。
76
+ */
77
+ settledBy?: unknown;
78
+ /** 见 {@link settledBy} —— 两键同批同源(#263 半场,0.30.8),合读、缺席不反推、转录不是认证。 */
79
+ approver?: unknown;
61
80
  parentToolCallId?: unknown;
62
81
  uuid?: unknown;
63
82
  session_id?: unknown;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/client-core",
3
- "version": "0.30.7",
3
+ "version": "0.30.8",
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": ">=6.17.2"
34
+ "@sema-agent/sdk": ">=7.1.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@sema-agent/agent-types": "^0.2.0",
38
38
  "@sema-agent/core": "^5.20.0",
39
- "@sema-agent/sdk": "^6.17.2",
39
+ "@sema-agent/sdk": "^7.1.0",
40
40
  "esbuild": "^0.27.4",
41
41
  "typescript": "^6.0.2"
42
42
  }