@sema-agent/client-core 0.67.1 → 0.68.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 +211 -0
- package/README.md +67 -3
- package/dist/adapt/arms.js +27 -2
- package/dist/adapt/turnFlags.d.ts +14 -0
- package/dist/adapt/turnFlags.js +4 -1
- package/dist/adapter/activeRunSelfHeal.d.ts +53 -6
- package/dist/adapter/activeRunSelfHeal.js +79 -8
- package/dist/adapter/downstream/eventToSdkMessage.d.ts +18 -1
- package/dist/adapter/downstream/eventToSdkMessage.js +33 -2
- package/dist/adapter/downstream/terminalToSdkResult.d.ts +75 -6
- package/dist/adapter/downstream/terminalToSdkResult.js +144 -44
- package/dist/adapter/runStream.d.ts +22 -2
- package/dist/adapter/runStream.js +190 -52
- package/dist/adapter/types.d.ts +4 -28
- package/dist/autoModeUnavailable.d.ts +17 -9
- package/dist/autoModeUnavailable.js +26 -8
- package/dist/classifierStatus.d.ts +32 -4
- package/dist/classifierStatus.js +5 -3
- package/dist/engineErrorCodes.d.ts +52 -0
- package/dist/engineErrorCodes.js +117 -0
- package/dist/engineNoticeCodes.d.ts +95 -1
- package/dist/engineNoticeCodes.js +124 -1
- package/dist/gateVocabulary.d.ts +18 -7
- package/dist/gateVocabulary.js +21 -8
- package/dist/hitl/parkResolver.d.ts +0 -14
- package/dist/hitl/parkResolver.js +22 -9
- package/dist/hitl/toolApprovalWire.d.ts +2 -1
- package/dist/hitl/toolApprovalWire.js +1 -0
- package/dist/ownKey.d.ts +34 -0
- package/dist/ownKey.js +36 -0
- package/dist/retryStatus.d.ts +13 -2
- package/dist/retryStatus.js +4 -1
- package/dist/runTerminal.d.ts +87 -14
- package/dist/runTerminal.js +89 -15
- package/dist/toolResult.js +8 -0
- package/dist/workflowClient.d.ts +22 -0
- package/dist/workflowClient.js +37 -0
- package/docs/INTEGRATION-CLIENTS.md +469 -10
- package/package.json +2 -2
|
@@ -173,6 +173,53 @@ function readStatus(record) {
|
|
|
173
173
|
const st = record?.status;
|
|
174
174
|
return typeof st === 'string' && st.length > 0 ? st : null;
|
|
175
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* 「这条 run 还占着会话吗」的**直接读数**(0.68.0 / L-230;server 7.73.0 S-122 P-45:
|
|
178
|
+
* `GET /v1/runs/:id` 上的 `heldBy`)。
|
|
179
|
+
*
|
|
180
|
+
* 三态,**刻意不是布尔**:
|
|
181
|
+
* · `'held'` —— `heldBy` 是一个非空串 = **有一条 run 名字在上面**,它确实还占着;
|
|
182
|
+
* · `'released'` —— `heldBy` 显式是 `null` = 一句**正面事实**:会话已经交出来了;
|
|
183
|
+
* · `'unknown'` —— 这一位**整键缺席**(老引擎 / 不发这一位的部署),或者形不对
|
|
184
|
+
* (非串非 null)⇒ 读不出,**绝不**当成 released。
|
|
185
|
+
*
|
|
186
|
+
* 🔴 `null` 与「键不在」必须分开,这是本读器存在的全部理由:
|
|
187
|
+
* 把两者都读成「释放了」,老引擎上每一次探测都会当场答「已释放」并立刻重发那条消息 ——
|
|
188
|
+
* 而它可能一头撞回一个还锁着的会话(再吃一个 409,正是这条腿存在的理由);
|
|
189
|
+
* 把两者都读成「还占着」,新引擎上那句正面事实白给了,窗要一直等到点。
|
|
190
|
+
* 🔴 判据用 `Object.hasOwn`:`{heldBy: undefined}` 与 `{}` 在这里是**同一件事**(都读不出),
|
|
191
|
+
* 而 `null` 必须与它们分开 —— 靠 `?? ` / `!= null` 一类写法分不出来。
|
|
192
|
+
*/
|
|
193
|
+
function readClaimHolder(record) {
|
|
194
|
+
if (record === null || typeof record !== 'object')
|
|
195
|
+
return 'unknown';
|
|
196
|
+
if (!Object.hasOwn(record, 'heldBy'))
|
|
197
|
+
return 'unknown';
|
|
198
|
+
const v = record.heldBy;
|
|
199
|
+
if (v === null)
|
|
200
|
+
return 'released';
|
|
201
|
+
if (typeof v === 'string' && v.length > 0)
|
|
202
|
+
return 'held';
|
|
203
|
+
// 空串 / 数 / 对象:形不对 ⇒ 读不出(失效方向在保守那一侧:不宣告释放)。
|
|
204
|
+
return 'unknown';
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* cancel 回执(202 体)/ run 行上的 `cancelRequested`(0.68.0 / L-230;server 7.73.0 S-122 P-45)。
|
|
208
|
+
*
|
|
209
|
+
* 🔴 **三态**同上:`true` / `false` 都是真读数,**读不出 ⇒ `undefined`**(老引擎不发这一位)。
|
|
210
|
+
* 绝不折 `false` —— 「没请求过取消」与「不知道有没有请求过」对下一步的含义不同:前者可以放心
|
|
211
|
+
* 再发一次 cancel,后者再发就可能是第二枪。
|
|
212
|
+
* 🔴 它**不是**「已经取消了」:上游逐字是 *requested*,一次**已受理的请求**,不是终局。
|
|
213
|
+
* 会话有没有交出来仍然只由 {@link readClaimHolder} 与 {@link CLAIM_RELEASED_STATES} 回答。
|
|
214
|
+
*/
|
|
215
|
+
export function readCancelRequested(body) {
|
|
216
|
+
if (typeof body !== 'object' || body === null || Array.isArray(body))
|
|
217
|
+
return undefined;
|
|
218
|
+
if (!Object.hasOwn(body, 'cancelRequested'))
|
|
219
|
+
return undefined;
|
|
220
|
+
const v = body.cancelRequested;
|
|
221
|
+
return typeof v === 'boolean' ? v : undefined;
|
|
222
|
+
}
|
|
176
223
|
/** 重开判决 → 结局(真呈现回执消费点:`presented === false` 时 reopened 断言不成立)。 */
|
|
177
224
|
function reopenDelivered(verdict) {
|
|
178
225
|
return verdict.reopened === true && verdict.presented !== false;
|
|
@@ -339,28 +386,33 @@ export async function waitForClaimRelease(taskId, deps) {
|
|
|
339
386
|
const isAborted = () => deps.signal?.aborted === true;
|
|
340
387
|
let delay = CANCEL_POLL_START_MS;
|
|
341
388
|
let lastStatus = null;
|
|
389
|
+
// 🔴 0.68.0 / L-230:`heldBy` 的三态与 `lastStatus` **同律** —— 只记最近一次**有回答**的探测。
|
|
390
|
+
let lastHolder = 'unknown';
|
|
342
391
|
for (;;) {
|
|
343
392
|
if (isAborted())
|
|
344
|
-
return { released: false, waitedMs: waited(), aborted: true, lastStatus };
|
|
393
|
+
return { released: false, waitedMs: waited(), aborted: true, lastStatus, lastHolder };
|
|
345
394
|
const remaining = deadline - now();
|
|
346
395
|
if (remaining <= 0)
|
|
347
|
-
return { released: false, waitedMs: waited(), aborted: false, lastStatus };
|
|
396
|
+
return { released: false, waitedMs: waited(), aborted: false, lastStatus, lastHolder };
|
|
348
397
|
await sleep(Math.min(delay, remaining), deps.signal);
|
|
349
398
|
if (isAborted())
|
|
350
|
-
return { released: false, waitedMs: waited(), aborted: true, lastStatus };
|
|
399
|
+
return { released: false, waitedMs: waited(), aborted: true, lastStatus, lastHolder };
|
|
351
400
|
delay = Math.min(Math.round(delay * 1.5), CANCEL_POLL_MAX_MS);
|
|
352
401
|
let status;
|
|
402
|
+
let holder;
|
|
353
403
|
const lease = claimProbeLease(deps.signal, deadline - now());
|
|
354
404
|
try {
|
|
355
|
-
|
|
405
|
+
const record = await deps.get(taskId, {
|
|
356
406
|
signal: lease.signal,
|
|
357
407
|
...(deps.session !== undefined ? { session: deps.session } : {}),
|
|
358
|
-
})
|
|
408
|
+
});
|
|
409
|
+
status = readStatus(record);
|
|
410
|
+
holder = readClaimHolder(record);
|
|
359
411
|
}
|
|
360
412
|
catch {
|
|
361
413
|
lease.release();
|
|
362
414
|
if (isAborted())
|
|
363
|
-
return { released: false, waitedMs: waited(), aborted: true, lastStatus };
|
|
415
|
+
return { released: false, waitedMs: waited(), aborted: true, lastStatus, lastHolder };
|
|
364
416
|
// 🔴 本发是被**窗自己的截止**掐掉的(lease 到点;caller 未中止)⇒ 没有读到任何回答 ——
|
|
365
417
|
// 既不是释放证据,也不是「读不出来」的新证据,上一发**完成了的**读数保持在座(#244 F1
|
|
366
418
|
// 换装批 G-c 尾竞态定谳:R2 [medium] 的「只记最近一次」指最近一次**有回答**的探测 ——
|
|
@@ -371,6 +423,7 @@ export async function waitForClaimRelease(taskId, deps) {
|
|
|
371
423
|
// 这一发**有回答但读不出**(404/传输错)⇒ 最新证据是「不知道」,陈旧的成功读数当场作废
|
|
372
424
|
// (见 lastStatus 头注)。
|
|
373
425
|
lastStatus = null;
|
|
426
|
+
lastHolder = 'unknown';
|
|
374
427
|
// 🔴 **404 也只是「读不到」,不是「释放了」**(二次评审 [high] 处置,2026-08-14):这个读口的
|
|
375
428
|
// 404 在 SDK 契约上是 `not_found.run` —— 「这条 run 不存在」与「它不是你的」**共用同一个码,
|
|
376
429
|
// 且是刻意设计的不可分辨**(跨租户不给存在性预言机)。凭它断言「那条 run 已经不占着会话」,
|
|
@@ -381,10 +434,22 @@ export async function waitForClaimRelease(taskId, deps) {
|
|
|
381
434
|
}
|
|
382
435
|
lease.release(); // 🔴 定时器必清:这只是**普通**定时器(见 claimProbeLease),不清就把进程多拖活一拍
|
|
383
436
|
lastStatus = status; // 奇形记录(读不出 status)同样把陈旧读数清掉 —— 最新证据仍是「不知道」
|
|
437
|
+
lastHolder = holder;
|
|
438
|
+
// ── 🔴 0.68.0 / L-230(server 7.73.0 S-122 P-45 读面消费)—— **两条释放证据,取并** ────────
|
|
439
|
+
// ① `heldBy === null` —— 引擎**直说**会话交出来了。这是本批新接的那条,而且它比 ② 强:
|
|
440
|
+
// ② 是拿「run 走到了某个终态词」去**推断**锁没了,而锁与 run 的生命周期不是同一件事
|
|
441
|
+
// (park 态保留 claim 正是这条推断会踩的坑,也是 `CLAIM_RELEASED_STATES` 是白名单而不是
|
|
442
|
+
// 「running 之外的一切」的原因)。有直接证据就别再推断。
|
|
443
|
+
// ② `status ∈ CLAIM_RELEASED_STATES` —— 老引擎不发 `heldBy`(读出 `'unknown'`)时的**既有路**,
|
|
444
|
+
// 一字未动。⇒ 本件对老引擎**零行为变化**;对新引擎多了一条更早、更硬的收口证据。
|
|
445
|
+
// 🔴 `'unknown'` 绝不当成释放:缺席不是证据(与本文件 404 那一格逐字同一条纪律)。
|
|
446
|
+
if (holder === 'released') {
|
|
447
|
+
return { released: true, waitedMs: waited(), aborted: false, lastStatus: status, lastHolder: holder };
|
|
448
|
+
}
|
|
384
449
|
// status 读不出(奇形记录)/ 还在跑 / park / 没想过的新词 ⇒ 都不算释放,继续等到点(保守方向:
|
|
385
450
|
// 宁可不重发)。判据见 {@link CLAIM_RELEASED_STATES}。
|
|
386
451
|
if (status !== null && CLAIM_RELEASED_STATES.includes(status)) {
|
|
387
|
-
return { released: true, waitedMs: waited(), aborted: false, lastStatus: status };
|
|
452
|
+
return { released: true, waitedMs: waited(), aborted: false, lastStatus: status, lastHolder: holder };
|
|
388
453
|
}
|
|
389
454
|
}
|
|
390
455
|
}
|
|
@@ -800,7 +865,13 @@ async function cancelAndConfirmRelease(taskId, durable, durableGet, deps) {
|
|
|
800
865
|
outcome: 'timeout',
|
|
801
866
|
waitedMs: verdict.waitedMs,
|
|
802
867
|
aborted: verdict.aborted,
|
|
803
|
-
|
|
868
|
+
// 🔴 0.68.0 / L-230:**两条持锁证据取并**,直接证据优先 ——
|
|
869
|
+
// ① `heldBy` 读出 `'held'` = 引擎**直说**那条 run 还占着(server 7.73.0 S-122 P-45 读面);
|
|
870
|
+
// ② 老引擎不发那一位(`'unknown'`)⇒ 回落既有的按状态词推断({@link CLAIM_HELD_STATES})。
|
|
871
|
+
// 🔴 `'released'` 在这里到不了(released 的判决在上面就 return 了),`'unknown'` 不算证据 ——
|
|
872
|
+
// 两条都答不出时仍然收口成 `false` = 「确认不了」,文案绝不说「它还占着」。
|
|
873
|
+
confirmedHeld: verdict.lastHolder === 'held' ||
|
|
874
|
+
(verdict.lastStatus !== null && CLAIM_HELD_STATES.includes(verdict.lastStatus)),
|
|
804
875
|
};
|
|
805
876
|
}
|
|
806
877
|
/**
|
|
@@ -235,7 +235,24 @@ export interface WiringManifestMcpEntry {
|
|
|
235
235
|
* CS-7 §2.7 — turn_end usage → CC `ModelUsage` (pinned name mapping;
|
|
236
236
|
* costMicroUsd/1e6 → costUSD). Surfaced separately because the slice has no
|
|
237
237
|
* standalone usage SDKMessage arm; the run driver folds it into the footer /
|
|
238
|
-
* terminal rollup.
|
|
238
|
+
* terminal rollup.
|
|
239
|
+
*
|
|
240
|
+
* ── 🔴 0.68.0 BREAKING(core 7.17.0 #711;异源对抗复审轮三实抓的**第五处**)──────────────────
|
|
241
|
+
* 返回 `undefined` 的含义从「`usage` 这一格缺席」收窄成 **「这一轮的账不知道」**,两种入形都答它:
|
|
242
|
+
* ① `ev.usage` 整个缺席 —— #711 之后这**不再是一条合法形**(契约违约;`runStream` 那一层另有
|
|
243
|
+
* 响亮留痕,本读器只如实答「不知道」);
|
|
244
|
+
* ② `usageMissing: true` **且六格全是 0** —— 那正是 core 的**占位**
|
|
245
|
+
* (`rs.turn.turnUsage ?? {六个 0}`,`run-harness-handlers.js:289-293`)。
|
|
246
|
+
* 🔴 为什么必须改:#711 之前,「这一轮没量出账」这件事在 wire 上的形是**不发 usage** ⇒ 本函数答
|
|
247
|
+
* `undefined`;#711 之后同一件事的形变成六个 0,而本函数是**公面导出** —— 不改的话,同一个真实
|
|
248
|
+
* 情形在引擎升级前后由同一个公开读器给出两个相反的答案(「不知道」变成「这一轮恰好花了 0」),
|
|
249
|
+
* 而调用方**一个字都没改**。收窄之后跨引擎升级的答案**逐字不变**。
|
|
250
|
+
* 🔴 **非零照交**:`usageMissing` 并不保证六格是零(core 在同一轮里可能已攒到真数字而另一次调用
|
|
251
|
+
* 报了缺账 ⇒ 判别位与真数字同帧并存)。占位恒为全零,所以**任何一格非零**都说明这一轮真的量到过
|
|
252
|
+
* ⇒ 照交镜像(那些数字是**下界**,而「是不是下界」由帧上的 `usageMissing` / 终帧的
|
|
253
|
+
* `_sema_usage_lower_bound` 回答,不由本函数回答)。
|
|
254
|
+
* ⚠️ 要**原样**的 wire 镜像(不做任何判断)请直接调 {@link turnUsageToModelUsage} —— 那一只是
|
|
255
|
+
* 纯映射,本函数是**带缺席语义的读器**,两者刻意分开。
|
|
239
256
|
*/
|
|
240
257
|
export declare function turnEndUsage(ev: Extract<AgentEvent, {
|
|
241
258
|
type: 'turn_end';
|
|
@@ -1198,10 +1198,41 @@ function humanInputProjection(ev, ctx) {
|
|
|
1198
1198
|
* CS-7 §2.7 — turn_end usage → CC `ModelUsage` (pinned name mapping;
|
|
1199
1199
|
* costMicroUsd/1e6 → costUSD). Surfaced separately because the slice has no
|
|
1200
1200
|
* standalone usage SDKMessage arm; the run driver folds it into the footer /
|
|
1201
|
-
* terminal rollup.
|
|
1201
|
+
* terminal rollup.
|
|
1202
|
+
*
|
|
1203
|
+
* ── 🔴 0.68.0 BREAKING(core 7.17.0 #711;异源对抗复审轮三实抓的**第五处**)──────────────────
|
|
1204
|
+
* 返回 `undefined` 的含义从「`usage` 这一格缺席」收窄成 **「这一轮的账不知道」**,两种入形都答它:
|
|
1205
|
+
* ① `ev.usage` 整个缺席 —— #711 之后这**不再是一条合法形**(契约违约;`runStream` 那一层另有
|
|
1206
|
+
* 响亮留痕,本读器只如实答「不知道」);
|
|
1207
|
+
* ② `usageMissing: true` **且六格全是 0** —— 那正是 core 的**占位**
|
|
1208
|
+
* (`rs.turn.turnUsage ?? {六个 0}`,`run-harness-handlers.js:289-293`)。
|
|
1209
|
+
* 🔴 为什么必须改:#711 之前,「这一轮没量出账」这件事在 wire 上的形是**不发 usage** ⇒ 本函数答
|
|
1210
|
+
* `undefined`;#711 之后同一件事的形变成六个 0,而本函数是**公面导出** —— 不改的话,同一个真实
|
|
1211
|
+
* 情形在引擎升级前后由同一个公开读器给出两个相反的答案(「不知道」变成「这一轮恰好花了 0」),
|
|
1212
|
+
* 而调用方**一个字都没改**。收窄之后跨引擎升级的答案**逐字不变**。
|
|
1213
|
+
* 🔴 **非零照交**:`usageMissing` 并不保证六格是零(core 在同一轮里可能已攒到真数字而另一次调用
|
|
1214
|
+
* 报了缺账 ⇒ 判别位与真数字同帧并存)。占位恒为全零,所以**任何一格非零**都说明这一轮真的量到过
|
|
1215
|
+
* ⇒ 照交镜像(那些数字是**下界**,而「是不是下界」由帧上的 `usageMissing` / 终帧的
|
|
1216
|
+
* `_sema_usage_lower_bound` 回答,不由本函数回答)。
|
|
1217
|
+
* ⚠️ 要**原样**的 wire 镜像(不做任何判断)请直接调 {@link turnUsageToModelUsage} —— 那一只是
|
|
1218
|
+
* 纯映射,本函数是**带缺席语义的读器**,两者刻意分开。
|
|
1202
1219
|
*/
|
|
1203
1220
|
export function turnEndUsage(ev) {
|
|
1204
|
-
|
|
1221
|
+
// 🔴 **成形判据不是 `!raw`**(异源复审轮四同族):wire 是 JSON、SSE 解析原样透传 ⇒ `usage: null` /
|
|
1222
|
+
// `usage: 7` 这类形真到得了这里。`null` 会让映射在读字段时抛;标量更坏 —— 它会被映射成一份
|
|
1223
|
+
// **看起来已测量**的全零账(`toCcModelUsage(7)` 逐格读不出、逐格折 0)。坏形与缺席同义:
|
|
1224
|
+
// **这一轮的账不知道**。
|
|
1225
|
+
const rawValue = ev.usage;
|
|
1226
|
+
if (typeof rawValue !== 'object' || rawValue === null || Array.isArray(rawValue))
|
|
1227
|
+
return undefined;
|
|
1228
|
+
const raw = rawValue;
|
|
1229
|
+
const unknown = ev.usageMissing === true;
|
|
1230
|
+
// 占位判据 = **六格全零**(不是「有 usageMissing 就算占位」)。逐格按值判,非有限值当 0 看待
|
|
1231
|
+
// (那一格本来就读不出,它不构成「量到过」的证据)。
|
|
1232
|
+
const placeholder = unknown &&
|
|
1233
|
+
[raw.inputTokens, raw.totalInputTokens, raw.outputTokens, raw.cacheReadTokens, raw.cacheWriteTokens, raw.costMicroUsd]
|
|
1234
|
+
.every((v) => !(typeof v === 'number' && Number.isFinite(v) && v !== 0));
|
|
1235
|
+
return placeholder ? undefined : turnUsageToModelUsage(raw);
|
|
1205
1236
|
}
|
|
1206
1237
|
/**
|
|
1207
1238
|
* Defensive open-set read of a `suspended` gate kind for the run driver's HITL
|
|
@@ -238,13 +238,15 @@ export interface RunCostFacts {
|
|
|
238
238
|
* 🔴 `stats` 不是可读对象(409 拒绝信封 / park 体 / `failed` 事件帧)⇒ 返 `undefined` =
|
|
239
239
|
* **这条帧没有账**,调用方据此「不说话」(不发臂、不铸键),而不是发一条全缺席的空账。
|
|
240
240
|
*
|
|
241
|
-
* 🔴 **第二参 `
|
|
241
|
+
* 🔴 **第二参 `observed`(0.67.1 / B-091,additive)**:**这一条流**的流内观测快照。给了它,对账段上的
|
|
242
242
|
* {@link RunCostReconcile.usageLowerBound} 就是**取并后**的读数(见 {@link usageLowerBoundOf});
|
|
243
243
|
* 不给(旧签名)⇒ 只读 `stats` 那一半,既有端逐位不变。
|
|
244
244
|
* ⚠️ 包内的两个调用点(`costFactParts` 与 `runStream` 的 `run_cost_reconciled` 铸点)**都必须**
|
|
245
245
|
* 传它 —— 少传一处就是把本件修的那条不对称原样种回去。
|
|
246
|
+
* 🔴 0.67.2 / 车 I 件 I-1:它是**按次调用的入参**,不再是 `EmitContext` 上的一格 —— 共享 ctx 被复用
|
|
247
|
+
* 给多条流时,那一格会把别的流的缺口串进这一条(见 {@link usageLowerBoundOf} 的第三段 🔴)。
|
|
246
248
|
*/
|
|
247
|
-
export declare function readRunCostFacts(stats: TaskStats | undefined,
|
|
249
|
+
export declare function readRunCostFacts(stats: TaskStats | undefined, observed?: {
|
|
248
250
|
readonly usageMissingObserved?: boolean;
|
|
249
251
|
}): RunCostFacts | undefined;
|
|
250
252
|
/**
|
|
@@ -267,9 +269,24 @@ export interface SemaSubagentUsageRow {
|
|
|
267
269
|
readonly cacheReadTokens?: number;
|
|
268
270
|
/** `true` ⇒ 这只子任务**至少有一轮**没报 usage,本行三个数是**下界**。never false。 */
|
|
269
271
|
readonly usageMissing?: true;
|
|
272
|
+
/**
|
|
273
|
+
* 0.67.2(车 I 件 I-2)—— `true` ⇒ **这一行是两个身份命名空间的同字面碰撞合并出来的**:
|
|
274
|
+
* 一半轮次的行键来自真身份 `sourceTaskId`、另一半来自回落的 `parentToolCallId`,而两者的**字面相等**。
|
|
275
|
+
* 本层没有任何读数能把它们拆回去(core 的合同不保证两个命名空间互斥),所以这一行的数字是**几只
|
|
276
|
+
* 子任务混在一起**的和。⇒ 这一位在场时**必然**伴随 `_sema_nested_usage_by_task_partial`,消费方
|
|
277
|
+
* 不许把本行当成某一只子代的账去渲。**never false**(缺席 = 这一行的每一轮都来自同一个命名空间)。
|
|
278
|
+
*/
|
|
279
|
+
readonly keyCollision?: true;
|
|
270
280
|
}
|
|
271
281
|
/** {@link SemaSubagentUsageRow} 的累加中间态(runStream 持有;`readonly` 在收口那一拍才加)。 */
|
|
272
282
|
export interface MutableSubagentUsageRow {
|
|
283
|
+
/**
|
|
284
|
+
* 0.67.2(车 I 件 I-2)—— **交付面的裸 id**(`sourceTaskId`,缺席时是回落的 `parentToolCallId`)。
|
|
285
|
+
* 🔴 累加表的**键**从 0.67.2 起带出身前缀(`s:` / `p:`,见 `runStream` 的 `rowKey`),因为两个身份
|
|
286
|
+
* 命名空间的字面可以相等、并在累加那一层就把两只子任务并掉(并掉之后出身位只剩一种,「出身混合」
|
|
287
|
+
* 那道闸读不出混合);交付面的键仍然必须是**裸 id**(端零改),所以原值存在行上,前缀不出本层。
|
|
288
|
+
*/
|
|
289
|
+
taskId: string;
|
|
273
290
|
turns: number;
|
|
274
291
|
inputTokens: number;
|
|
275
292
|
outputTokens: number;
|
|
@@ -287,6 +304,35 @@ export interface MutableSubagentUsageRow {
|
|
|
287
304
|
*/
|
|
288
305
|
keyFromParentFallback?: true;
|
|
289
306
|
}
|
|
307
|
+
/**
|
|
308
|
+
* 子代用量面的**下界判别位键名**(`_sema_subagent_usage_partial`)。
|
|
309
|
+
*
|
|
310
|
+
* 🔴 它与终帧的 `_sema_usage_lower_bound` **刻意不同名**(见上段):两者答的是两个问题,
|
|
311
|
+
* 一个消费面同时拿到两者时必须分得出来。
|
|
312
|
+
* 🔴 本包**不在任何 wire 帧上铸它** —— 它是**渲染面**的位(端把一行子代用量交给自己的视图时用)。
|
|
313
|
+
* 包给名与判据,是为了三端零自拼(同 `gateIdentity` 的三条身份键字面同一条纪律)。
|
|
314
|
+
*/
|
|
315
|
+
export declare const SEMA_SUBAGENT_USAGE_PARTIAL_KEY = "_sema_subagent_usage_partial";
|
|
316
|
+
/**
|
|
317
|
+
* 一行子代用量的数字**是不是下界**({@link SEMA_SUBAGENT_USAGE_PARTIAL_KEY} 该不该立)。
|
|
318
|
+
*
|
|
319
|
+
* 三个来源**取并**,每一条都能独立让这一行的数字不是最终数:
|
|
320
|
+
* · `row.usageMissing` —— 这只子代**至少有一轮**引擎没报账(数字照累加,但它是下界);
|
|
321
|
+
* · `row.keyCollision` —— 两个命名空间的 id 撞了字面,这一行是**几只子代的账并起来的**;
|
|
322
|
+
* · `opts.tablePartial` —— 整张表对不上引擎的权威合计(`_sema_nested_usage_by_task_partial`)⇒
|
|
323
|
+
* 表里**每一行**都不可证完整。
|
|
324
|
+
*
|
|
325
|
+
* 🔴 **`false` 不是「这一行是最终数」的证据**:它只说「本端没有任何一条理由认为它是下界」。
|
|
326
|
+
* 行本身还没收口(端自己的 store 知道,包不知道)时,端要自己把那一条并进来 —— 所以这只谓词
|
|
327
|
+
* 收一个 `opts`,而不是假装它掌握全部真相。
|
|
328
|
+
* 🔴 非对象 / 缺席入参 ⇒ `false`(答不出,不是断言);坏形位(非 `true` 的值)不当真。
|
|
329
|
+
*/
|
|
330
|
+
export declare function subagentUsageIsPartial(row: {
|
|
331
|
+
readonly usageMissing?: unknown;
|
|
332
|
+
readonly keyCollision?: unknown;
|
|
333
|
+
} | null | undefined, opts?: {
|
|
334
|
+
readonly tablePartial?: boolean;
|
|
335
|
+
}): boolean;
|
|
290
336
|
/** 终帧两个超集键的产物形(见 {@link nestedUsageByTaskParts})。 */
|
|
291
337
|
export interface SemaNestedUsageByTask {
|
|
292
338
|
readonly rows: Readonly<Record<string, SemaSubagentUsageRow>>;
|
|
@@ -317,14 +363,37 @@ export interface SemaTerminalModelUsage extends SemaModelUsage {
|
|
|
317
363
|
/** `done` → SDKResultSuccess (contract 02 §2.10 / 08 CS-10). */
|
|
318
364
|
export declare function doneToSdkResult(ev: Extract<AgentEvent, {
|
|
319
365
|
type: 'done';
|
|
320
|
-
}>, ctx: EmitContext
|
|
366
|
+
}>, ctx: EmitContext,
|
|
367
|
+
/**
|
|
368
|
+
* 0.67.2:**这一条流**的收口快照 —— usage 缺口观测(见 {@link usageLowerBoundOf})与 per-subagent
|
|
369
|
+
* 累加表(见 {@link nestedUsageByTaskParts})。两格都是 per-stream 的事实,缺席 ⇒ 这次投影没有流内面
|
|
370
|
+
* (只读 `stats` 那一半、两个分表键都不铸),**绝不**回头去读调用方对象上可能残留的上一条流。
|
|
371
|
+
*/
|
|
372
|
+
observed?: {
|
|
373
|
+
readonly usageMissingObserved?: boolean;
|
|
374
|
+
readonly nestedUsageByTask?: ReadonlyMap<string, MutableSubagentUsageRow>;
|
|
375
|
+
}): SDKMessage;
|
|
321
376
|
/** `failed` → SDKResultError (contract 02 §2.11 / 08 CS-11). */
|
|
322
377
|
export declare function failedToSdkResult(ev: Extract<AgentEvent, {
|
|
323
378
|
type: 'failed';
|
|
324
|
-
}>, ctx: EmitContext
|
|
325
|
-
/**
|
|
379
|
+
}>, ctx: EmitContext,
|
|
380
|
+
/** 0.67.2:同 {@link doneToSdkResult} 的第三参。 */
|
|
381
|
+
observed?: {
|
|
382
|
+
readonly usageMissingObserved?: boolean;
|
|
383
|
+
readonly nestedUsageByTask?: ReadonlyMap<string, MutableSubagentUsageRow>;
|
|
384
|
+
}): SDKMessage;
|
|
385
|
+
/**
|
|
386
|
+
* Dispatch a terminal AgentEvent to its SDKResult arm.
|
|
387
|
+
*
|
|
388
|
+
* 🔴 第三参(0.67.2 / 车 I 件 I-1):**这一条流**的 usage 观测快照。`runStream` 在终帧那一拍**按值**
|
|
389
|
+
* 交给它与 `run_cost_reconciled` 铸臂 —— 两个投影口读的是**同一份本流快照**,而不是一个可能被别的流
|
|
390
|
+
* 写过的共享位。缺席(端直调)⇒ 与 0.67.1 的旧签名逐位同行为。
|
|
391
|
+
*/
|
|
326
392
|
export declare function terminalToSdkResult(ev: Extract<AgentEvent, {
|
|
327
393
|
type: 'done';
|
|
328
394
|
} | {
|
|
329
395
|
type: 'failed';
|
|
330
|
-
}>, ctx: EmitContext
|
|
396
|
+
}>, ctx: EmitContext, observed?: {
|
|
397
|
+
readonly usageMissingObserved?: boolean;
|
|
398
|
+
readonly nestedUsageByTask?: ReadonlyMap<string, MutableSubagentUsageRow>;
|
|
399
|
+
}): SDKMessage;
|