@sema-agent/client-core 0.11.7 → 0.11.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.
@@ -49,9 +49,11 @@
49
49
  * timeout salvage:写出窗兑现的 r.result(非空)骑 additive `result` 字段——CC error 帧无
50
50
  * result,但把引擎已救回的最终文本静默丢弃对 -p 消费者是净损失。
51
51
  *
52
- * costUsd === 0 is AMBIGUOUS (free vs. no MODEL_COST_* configured): disambiguate
53
- * upstream via Capabilities.pricingConfigured (contract 02 §2.10 VERIFY); this
54
- * projector passes the number through verbatim.
52
+ * 成本取数 = `TaskStats.costMicroUsd`(整数 micro-USD 单轨,SDK 1.0.0 / server 1.319.0 /
53
+ * core 2.0.0 起;legacy float `costUsd` 已删,见 costOrNull 头注)。零成本 is AMBIGUOUS
54
+ * (free vs. no MODEL_COST_* configured): disambiguate upstream via
55
+ * Capabilities.pricingConfigured (contract 02 §2.10 VERIFY); this projector converts
56
+ * the integer to USD (/1e6) and passes it through verbatim otherwise.
55
57
  */
56
58
  import type { AgentEvent } from '@sema-agent/sdk';
57
59
  import { type SDKMessage, type EmitContext } from '../types.js';
@@ -21,14 +21,41 @@ function elapsedMs(ctx) {
21
21
  return typeof ctx.startedAtMs === 'number' ? Math.max(0, Date.now() - ctx.startedAtMs) : 0;
22
22
  }
23
23
  /**
24
- * P1-5 `total_cost_usd` truthfulness: `costUsd === 0` is AMBIGUOUS (free vs. no MODEL_COST_* configured
24
+ * micro-USD USD。引擎账面的成本单位是 **micro-USD**(百万分之一美元,整数轨:1e6 micro = 1 USD;
25
+ * 整数是为了不吃 FP 漂移)。CC 的输出面字段 `total_cost_usd` / `costUSD` 单位是 **USD**(字段名即
26
+ * 单位),所以出口一律 `/1e6`。非有限值(NaN/Infinity)不上账 —— Infinity 比 undefined 更糟,它会被
27
+ * 当成真数字参与求和,把一条离谱账悄悄摊进总计(与 SDK `taskCostMicroUsd()` 的规约逐条同义;
28
+ * `mapModelUsage()` 里的 `costMicroUsd / 1e6` 是同一换算的既有用法,两处此后共用本函数)。
29
+ *
30
+ * 为什么不直接调 SDK 的 `taskCostMicroUsd()`:本文件在 `runStream` **可移植闭包**内,portability 门
31
+ * 对该闭包断言「值级外部包 = 空集」(EXPECTED_PACKAGES_KERNEL),值级 import SDK 会当场红。故就地
32
+ * 复刻规约,单一真源以 SDK 那份注释为准。
33
+ */
34
+ function microUsdToUsd(micro) {
35
+ return typeof micro === 'number' && Number.isFinite(micro) ? micro / 1e6 : null;
36
+ }
37
+ /**
38
+ * P1-5 — `total_cost_usd` truthfulness: 成本 `0` is AMBIGUOUS (free vs. no MODEL_COST_* configured
25
39
  * on the worker — the local TOC engine ships unconfigured, so `-p --output-format json` was reporting a
26
40
  * fake `0`). Emit the number only when it is a REAL positive cost; otherwise `null` (JSON consumers see
27
41
  * "unknown", never a fabricated zero). CC's schema field is numeric — number-guarded readers
28
42
  * (replEntry cost fold) already treat non-number as absent.
43
+ *
44
+ * 🔴 键迁移([2006] cli 自查最重命中,2026-07-29):取数源从 legacy float `TaskStats.costUsd` 换成
45
+ * 整数 `TaskStats.costMicroUsd`。`costUsd` 已被 **core 2.0.0 / server 1.319.0 / SDK 1.0.0 同批删除**
46
+ * (发端、适配、类型三层),对 core 2.0.0+ 引擎该键恒 undefined ⇒ 本函数恒 null ⇒ `sema -p
47
+ * --output-format json` 的 `total_cost_usd` **恒 null**,而账其实一直在 `costMicroUsd` 上
48
+ * (本文件 `mapModelUsage()` 早就在读它 = 两代键并存的实锤)。devDep 此前钉 SDK `^0.1.7`(0.x 的 caret 是精确锁)
49
+ * ⇒ 类型层看到的还是「两轨都在」的旧声明,对 1.0.0 才报 TS2345 —— 校旧物的门抓不住这类断链,故
50
+ * 另有 `run-sdk-floor-test.mjs` 断言实装 SDK 地板 ≥ 1.1。
51
+ *
52
+ * 老引擎只发 float `costUsd` 时**不做兼容换算**:单轨之后读到 undefined(当场可见「不知道」)好过
53
+ * 把一笔来源可疑的账静默吸收成看起来正常的数字(SDK normalize.ts 的 BREAKING 留档同款裁定;
54
+ * 本仓 [clean-cut-no-legacy-compat])。
29
55
  */
30
- function costOrNull(costUsd) {
31
- return typeof costUsd === 'number' && costUsd > 0 ? costUsd : null;
56
+ function costOrNull(stats) {
57
+ const usd = microUsdToUsd(stats?.costMicroUsd);
58
+ return usd !== null && usd > 0 ? usd : null;
32
59
  }
33
60
  // 件2a(中断事故修复批 G,2026-07-15,症状2 第一环):参数放宽 TaskStats | undefined + 全链守卫。
34
61
  // 409 active-run 拒绝时引擎的 done 帧本体是 {status:'failed', errorMessage, activeTaskId} —— NO stats
@@ -51,7 +78,7 @@ function mapModelUsage(stats) {
51
78
  cacheReadInputTokens: num(m.cacheReadTokens),
52
79
  cacheCreationInputTokens: num(m.cacheWriteTokens),
53
80
  webSearchRequests: 0,
54
- costUSD: num(m.costMicroUsd) / 1e6,
81
+ costUSD: microUsdToUsd(m.costMicroUsd) ?? 0, // ⇐ 与 costOrNull 同一个换算口(旧写法 num(...)/1e6 逐值等价)
55
82
  contextWindow: 0,
56
83
  maxOutputTokens: 0,
57
84
  };
@@ -79,7 +106,11 @@ export function doneToSdkResult(ev, ctx) {
79
106
  modelUsage = {
80
107
  [r.model]: {
81
108
  ...usage,
82
- costUSD: typeof stats?.costUsd === 'number' ? stats.costUsd : 0,
109
+ // 键迁移(见 costOrNull 头注):legacy float `costUsd` 整数 `costMicroUsd`(/1e6 USD)。
110
+ // ⚠️ 这一处**类型门抓不到**:TaskStats 带 `[key: string]: unknown` 开集索引,`typeof
111
+ // stats?.costUsd === 'number'` 的旧写法在 SDK 1.x 下照样编译得过,只是恒假 ⇒ 合成行的
112
+ // costUSD 静默恒 0(比报错更隐蔽)。tsc 只红了 6 处直传,这处靠人核。
113
+ costUSD: microUsdToUsd(stats?.costMicroUsd) ?? 0,
83
114
  contextWindow: 0,
84
115
  maxOutputTokens: 0,
85
116
  },
@@ -129,7 +160,7 @@ export function doneToSdkResult(ev, ctx) {
129
160
  is_error: true,
130
161
  num_turns: stats?.turns ?? 0,
131
162
  stop_reason: null,
132
- total_cost_usd: costOrNull(stats?.costUsd), // P1-5: null when unknown, never a fake 0
163
+ total_cost_usd: costOrNull(stats), // P1-5: null when unknown, never a fake 0
133
164
  usage,
134
165
  modelUsage,
135
166
  permission_denials: [],
@@ -178,7 +209,7 @@ export function doneToSdkResult(ev, ctx) {
178
209
  is_error: true,
179
210
  num_turns: stats?.turns ?? 0,
180
211
  stop_reason: null,
181
- total_cost_usd: costOrNull(stats?.costUsd),
212
+ total_cost_usd: costOrNull(stats),
182
213
  usage,
183
214
  modelUsage,
184
215
  permission_denials: [],
@@ -207,7 +238,7 @@ export function doneToSdkResult(ev, ctx) {
207
238
  is_error: true,
208
239
  num_turns: stats?.turns ?? 0,
209
240
  stop_reason: null,
210
- total_cost_usd: costOrNull(stats?.costUsd),
241
+ total_cost_usd: costOrNull(stats),
211
242
  usage,
212
243
  modelUsage,
213
244
  permission_denials: [],
@@ -256,7 +287,7 @@ export function doneToSdkResult(ev, ctx) {
256
287
  is_error: true,
257
288
  num_turns: stats?.turns ?? 0, // 件2a 同款守卫 —— park 帧多数不带 stats,不炸
258
289
  stop_reason: null,
259
- total_cost_usd: costOrNull(stats?.costUsd),
290
+ total_cost_usd: costOrNull(stats),
260
291
  usage,
261
292
  modelUsage,
262
293
  permission_denials: [],
@@ -280,7 +311,7 @@ export function doneToSdkResult(ev, ctx) {
280
311
  is_error: true,
281
312
  num_turns: stats?.turns ?? 0,
282
313
  stop_reason: null,
283
- total_cost_usd: costOrNull(stats?.costUsd),
314
+ total_cost_usd: costOrNull(stats),
284
315
  usage,
285
316
  modelUsage,
286
317
  permission_denials: [],
@@ -308,7 +339,7 @@ export function doneToSdkResult(ev, ctx) {
308
339
  })(),
309
340
  ...degradedField,
310
341
  stop_reason: null, // dropped (contract 02 §2.10)
311
- total_cost_usd: costOrNull(stats?.costUsd), // P1-5: costUsd===0 is "unconfigured pricing" → null, never a fake 0
342
+ total_cost_usd: costOrNull(stats), // P1-5: 零成本 = "unconfigured pricing" → null, never a fake 0
312
343
  usage,
313
344
  // MF-25/P1-5 — the per-model usage split (wire `stats.modelUsage`, or the synthesized current-model row).
314
345
  modelUsage,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/client-core",
3
- "version": "0.11.7",
3
+ "version": "0.11.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,7 +31,7 @@
31
31
  },
32
32
  "devDependencies": {
33
33
  "@sema-agent/agent-types": "^0.2.0",
34
- "@sema-agent/sdk": "^0.1.7",
34
+ "@sema-agent/sdk": "^1.1.0",
35
35
  "esbuild": "^0.27.4",
36
36
  "typescript": "^6.0.2"
37
37
  }