@sema-agent/client-core 0.66.0 → 0.67.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 +118 -0
- package/README.md +3 -2
- package/dist/adapter/activeRunSelfHeal.js +3 -2
- package/dist/adapter/downstream/eventToSdkMessage.d.ts +14 -0
- package/dist/adapter/downstream/eventToSdkMessage.js +13 -1
- package/dist/adapter/downstream/terminalToSdkResult.d.ts +67 -1
- package/dist/adapter/downstream/terminalToSdkResult.js +150 -9
- package/dist/adapter/runStream.js +129 -4
- package/dist/adapter/types.d.ts +27 -0
- package/dist/autoModeUnavailable.d.ts +95 -70
- package/dist/autoModeUnavailable.js +130 -97
- package/dist/classifierStatus.d.ts +7 -5
- package/dist/classifierStatus.js +62 -20
- package/dist/engineNoticeCodes.d.ts +19 -2
- package/dist/engineNoticeCodes.js +25 -3
- package/dist/gateOutcome.d.ts +14 -0
- package/dist/gateOutcome.js +3 -1
- package/dist/gateVocabulary.d.ts +37 -0
- package/dist/gateVocabulary.js +69 -8
- package/dist/hitl/parkResolver.js +9 -2
- package/dist/hitl/toolApprovalWire.d.ts +44 -43
- package/dist/hitl/toolApprovalWire.js +47 -59
- package/dist/seam.d.ts +70 -0
- package/dist/seam.js +15 -0
- package/docs/INTEGRATION-CLIENTS.md +637 -12
- package/package.json +2 -2
|
@@ -310,6 +310,15 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
310
310
|
// projector can emit a REAL `duration_ms` (the wire carries no duration; the old hardcoded 0 was fake).
|
|
311
311
|
if (ctx.startedAtMs === undefined)
|
|
312
312
|
ctx.startedAtMs = Date.now();
|
|
313
|
+
// ── L-228(0.67.0):per-subagent turn usage 的**流内**累加表 ─────────────────────────────────
|
|
314
|
+
// 🔴 **每条流一张**(局部量,不是模块级):两条并发的流各自攒各自看见的账;做成模块级单例会让
|
|
315
|
+
// A 流的子代用量落进 B 流的终帧(本仓在册的「共享 store 跨流污染」病形)。
|
|
316
|
+
// 🔴 它**只在终帧那一拍**挂到 `ctx` 上(见下面 `done`/`failed` 分支),不是开流时就挂 ——
|
|
317
|
+
// 终帧的铸点在 `terminalToSdkResult`(单一 mint 点,见该文件顶注:给信封加一个位要改六处
|
|
318
|
+
// 正是它收编掉的病),而 `ctx` 是**调用方**的对象:宿主若把同一个 ctx 复用给两条并发的流,
|
|
319
|
+
// 开流时挂等于让后开的那条把先开的那条的表顶掉,先开的终帧于是报出别人的账。
|
|
320
|
+
// 挂在终帧那一拍 + 与 `terminalToSdkResult(...)` 在**同一个同步步**里,那个窗按构造不存在。
|
|
321
|
+
const nestedUsageByTask = new Map();
|
|
313
322
|
for await (const ev of events) {
|
|
314
323
|
// event-id idempotency — drop a re-seen durable seq (contract 02 §1.1).
|
|
315
324
|
const seq = eventSeq(ev);
|
|
@@ -379,14 +388,38 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
379
388
|
// `usage` **可以同帧**)。把它剥掉,本批新开的这条 usage 通道就会把「不知道」渲成一笔
|
|
380
389
|
// 全零的已知账 —— 与本批要根治的病(B-073 的成本 0/缺席)逐字同形,只是换了个量。
|
|
381
390
|
const usageMissing = ev.usageMissing === true;
|
|
391
|
+
// 🔴 L-215③ 的**终局对偶**(异源对抗复审 [medium] 实抓):这条流上**只要有一轮**报过
|
|
392
|
+
// 「这一轮没有 usage」,终帧上那些数字就是**下界**。`stats.usageMissing` 只在带得出
|
|
393
|
+
// `TaskResult` 的终帧上有,而 `failed` 事件帧 / 409 拒绝信封 / park 体**根本没有 stats**
|
|
394
|
+
// ⇒ 只读 stats 的话,一条已经观测到缺口的 run 会在终帧上被读成「每一轮都报了 usage」
|
|
395
|
+
// (而 `usage` 那几格恰好是 `flattenUsage(undefined)` 的全零)——「不知道」渲成了精确零。
|
|
396
|
+
// ⇒ 流内观测到就记下来,终帧那一拍与 stats 的读数**取并**(见 costFactParts)。
|
|
397
|
+
if (usageMissing)
|
|
398
|
+
ctx.usageMissingObserved = true;
|
|
382
399
|
const stopReasonRaw = ev.stopReason;
|
|
383
400
|
const stopWord = typeof stopReasonRaw === 'string' && stopReasonRaw.length > 0 ? stopReasonRaw : undefined;
|
|
384
401
|
const usage = turnEndUsage(ev);
|
|
385
402
|
// §E2 identity (service 1.78) — a SUB-FLOW's turn_end (orchestration/subagent round, carries
|
|
386
|
-
//
|
|
387
|
-
// child's, and reconciling the leader's responseLength against them is the token-jump bug
|
|
403
|
+
// the identity envelope) must NOT drive the leader's C1a `end` reconcile: its outputTokens are
|
|
404
|
+
// the child's, and reconciling the leader's responseLength against them is the token-jump bug
|
|
388
405
|
// (blackboard dogfood #2 / upstreamBridge TODO #9). Fold its usage into the rollup only.
|
|
389
|
-
|
|
406
|
+
// ── 🔴 0.67.1 / B-090(下游验收轨实抓)—— **身份键盲区** ────────────────────────────────
|
|
407
|
+
// 修前这条判据只认 `parentToolCallId`,而 §32g「归属钥匙」段自己写的读法是「`sourceTaskId`
|
|
408
|
+
// 优先,wire 缺席时回落 `parentToolCallId`」—— 那句话隐含「只带 `sourceTaskId`」是一条合法
|
|
409
|
+
// 组合。于是那一形的子代轮被判成**主流**,后果双重且都是本件当初要防的病:
|
|
410
|
+
// ① `!isSubFlow` 为真 ⇒ 它的 usage 去驱动 `last_turn_usage` chrome 臂与 `turn_usage` 消息臂
|
|
411
|
+
// —— §E2 断闸要防的 token-jump 在这条路上原样重现(子代的窗口占用落进主会话 Context);
|
|
412
|
+
// ② `isSubFlow` 为假 ⇒ 整条不进分表(下面那条 `if (isSubFlow)` 腿)—— 那只子代的账消失。
|
|
413
|
+
// 🔴 判据锚在 core 真字节上(`task-event.d.ts` `TaskEventIdentity`,不是本包的口味):
|
|
414
|
+
// `sourceTaskId` 逐字「stamped ONLY on the content events of a task running AS A SUB-AGENT
|
|
415
|
+
// (same condition as `parentToolCallId`)」,且「A TOP-LEVEL task's own events carry NO
|
|
416
|
+
// `sourceTaskId`」⇒ **两键同条件盖章、顶层一个都不带** ⇒ 任一在场 = 这条帧属于子流。
|
|
417
|
+
// 🔴 判据是**键在不在**(`!== undefined`),不是「是不是非空串」:空串 / 坏形也算信封在场,
|
|
418
|
+
// 于是按子流走 —— 失效方向是安全的那一侧(宁可少发一拍主会话的 usage,也绝不把子代的账
|
|
419
|
+
// 混进主会话;反过来那一侧就是 token-jump)。**行键**那一层才做非空串窄化(见下面的
|
|
420
|
+
// `parent` / `sourceTaskId`),两层问的不是同一个问题:信封在不在 vs 这一行归到谁名下。
|
|
421
|
+
const isSubFlow = ev.parentToolCallId !== undefined ||
|
|
422
|
+
ev.sourceTaskId !== undefined;
|
|
390
423
|
if (usage) {
|
|
391
424
|
handle.latestUsage = usage;
|
|
392
425
|
// [2295] 裁 ② 逐字通道:与镜像同拍存一份引擎原形(六键含 totalInputTokens)。
|
|
@@ -419,6 +452,88 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
419
452
|
}
|
|
420
453
|
}
|
|
421
454
|
}
|
|
455
|
+
// ── L-228(0.67.0):子流那条腿 —— **additive 第三条腿**,主臂两处 `!isSubFlow` 断闸不动 ────
|
|
456
|
+
// 病(车 E 件⑥ 实抓):core 终局只有合计 `stats.nested`(**无 per-task 分项**),而流里每条
|
|
457
|
+
// 子流 turn_end 都带着它自己那一轮的 usage —— 本包此前在 §E2 断闸处**折而未读**,于是壳的
|
|
458
|
+
// 子代详情面只渲得出 `_sema_usage_absent`。⇒ 分表只能由流内累加得出,这就是那条通道。
|
|
459
|
+
// 🔴 **归属钥匙**:`sourceTaskId` 优先,wire 缺席时回落 `parentToolCallId`(同一只子代的每一轮
|
|
460
|
+
// 至少归得到同一行)。两个都读不出 ⇒ **整条不入表**(编一个 `"unknown"` 行就是把几只子代的
|
|
461
|
+
// 账混成一只)。身份位为什么**不是** `taskId`,见下面那段 🔴。
|
|
462
|
+
// 🔴 **`usageMissing` 与数字同帧并存**:core 明说那一轮的 usage 是 UNKNOWN 不是 0 ⇒ 数字照
|
|
463
|
+
// 累加(它是**下界**),判别位在行上立起来;把那一轮整个丢掉会让下界更假。
|
|
464
|
+
if (isSubFlow) {
|
|
465
|
+
const parentToolCallId = ev.parentToolCallId;
|
|
466
|
+
// 🔴 **身份键读 `sourceTaskId`,不是 `taskId`**(异源对抗复审 [high] 实抓,core 真字节直证):
|
|
467
|
+
// core `TaskEventIdentity` 顶注逐字「the `WorkflowRun.sourceTaskId` family, **NOT a second
|
|
468
|
+
// `taskId`** — that field already exists on `task_progress` and a duplicate would bite
|
|
469
|
+
// consumers」⇒ 子代内容事件上**根本没有** `taskId` 这一位,读它恒缺席、恒回落到父调用 id,
|
|
470
|
+
// 于是**同一个父调用下的多只子任务会并成一行**(sdk 的 `turn_end` 臂也没有声明任何身份位,
|
|
471
|
+
// 两条腿都读 cast —— 这正是「按 d.ts 抄,不按印象猜」那条纪律要防的形)。
|
|
472
|
+
const rawSourceTaskId = ev.sourceTaskId;
|
|
473
|
+
const parent = typeof parentToolCallId === 'string' && parentToolCallId.length > 0 ? parentToolCallId : undefined;
|
|
474
|
+
const sourceTaskId = typeof rawSourceTaskId === 'string' && rawSourceTaskId.length > 0 ? rawSourceTaskId : undefined;
|
|
475
|
+
// 行键:`sourceTaskId` 优先,缺席回落父调用 id(同一只子代的每一轮至少归得到同一行)。
|
|
476
|
+
// ⚠️ 回落**有损**:同父调用多子任务会并成一行 —— 那时终帧那张表的 `partial` 判别位会因
|
|
477
|
+
// 行数对不上 `nested.tasks` 而立起来(诚实缺席优先于假装分得开)。
|
|
478
|
+
const taskId = sourceTaskId ?? parent;
|
|
479
|
+
// 🔴 0.67.1 / B-090:入表条件从「行键 **且** 父调用 id 都读得出」放宽到「**行键**读得出」。
|
|
480
|
+
// 修前那个 `&& parent !== undefined` 把「只带 `sourceTaskId`」的子代整条挡在表外 ——
|
|
481
|
+
// 而 `parent` 在这里的唯一用处是**铸 chrome 增量臂的车道证明**,不是行的身份。
|
|
482
|
+
// 行键读不出(两键都缺 / 都是空串)仍然整条不入表:编一个 `"unknown"` 行会把几只子代
|
|
483
|
+
// 的账混成一只(C3 那一格守的就是这条)。
|
|
484
|
+
if (taskId !== undefined) {
|
|
485
|
+
// 🔴 发臂条件与主臂 0.65.1 / B-088 **逐字同族**:core 真会发**裸**
|
|
486
|
+
// `{type:'turn_end', usageMissing:true}`(无 usage、无 stopReason),旧条件「有 usage 才发」
|
|
487
|
+
// 会让**最诚实的那一帧**整条静默 —— 那是本仓已定谳的病形,子代这条腿不许再犯一次。
|
|
488
|
+
// 三者任一在场即发;三者皆缺席仍不发。
|
|
489
|
+
// 🔴 **`parent` 缺席时本臂不发,而行照进表**(0.67.1 定谳,理由如实写在这里):
|
|
490
|
+
// chrome 信封的车道证明 `LaneProof` 的子流臂是 `{lane:'subagent', parentToolCallId: string}`
|
|
491
|
+
// (`seam.ts`),座位门 `seatContract.checkLaneProof` 对它是**硬要求**(缺伴随位的信封
|
|
492
|
+
// 当场被吃掉,且 `run-client-core-pure-test.mjs` FIX7 ④ 有专门的投毒格守着)。这一形上
|
|
493
|
+
// **没有**父调用 id ⇒ 唯一能把臂发出去的办法是拿 `sourceTaskId` 去填那一格,而那是在
|
|
494
|
+
// 一个「父调用 id」的位上放一个**任务 id** —— 正是 C4/C5 两格守的身份位互串,也是
|
|
495
|
+
// 车道纪律(REF-CC-064)存在的理由。放宽 `LaneProof` 则是**三端 BREAKING**(宿主今天
|
|
496
|
+
// 按 `parentToolCallId: string` 读),不是一个 patch 能做的事。
|
|
497
|
+
// ⇒ 增量腿在这一形上静默,**收口快照(终帧 `_sema_nested_usage_by_task`)照带这一行**
|
|
498
|
+
// —— 账不丢,少的只是这一形的实时增量;两者本来就是「同一份账的两个时刻」。
|
|
499
|
+
if (parent !== undefined && (usage !== undefined || usageMissing || stopWord !== undefined) && ctx.emitChrome) {
|
|
500
|
+
emitChromeFireAndForget(ctx, {
|
|
501
|
+
kind: 'subagent_turn_usage',
|
|
502
|
+
laneProof: { lane: 'subagent', parentToolCallId: parent },
|
|
503
|
+
taskId,
|
|
504
|
+
...(sourceTaskId !== undefined ? { sourceTaskId } : {}),
|
|
505
|
+
parentToolCallId: parent,
|
|
506
|
+
...(usage !== undefined ? { usage } : {}),
|
|
507
|
+
...(ev.usage !== undefined ? { engineUsage: ev.usage } : {}),
|
|
508
|
+
...(usageMissing ? { usageMissing: true } : {}),
|
|
509
|
+
...(stopWord !== undefined ? { stopReason: stopWord } : {}),
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
const row = nestedUsageByTask.get(taskId) ?? { turns: 0, inputTokens: 0, outputTokens: 0 };
|
|
513
|
+
// 🔴 0.67.1(异源复审 [medium] 实抓):记下**这一行的键是回落来的**(真身份缺席)。
|
|
514
|
+
// B-090 放宽入表条件之后行键可以有两种出身,而混合出身时「拆一行 + 并一行」的计数
|
|
515
|
+
// 误差方向相反、可以恰好抵消 ⇒ 终帧那张表的 `partial` 判据要看得见出身
|
|
516
|
+
// (理由与反例逐字在 `MutableSubagentUsageRow.keyFromParentFallback` 的头注里)。
|
|
517
|
+
// 出身按行均匀:`taskId = sourceTaskId ?? parent` ⇒ 真身份在场的轮永远不会落到
|
|
518
|
+
// 回落键那一行上,所以这一位只会被同一种出身的轮反复写成同一个值。
|
|
519
|
+
if (sourceTaskId === undefined)
|
|
520
|
+
row.keyFromParentFallback = true;
|
|
521
|
+
row.turns += 1;
|
|
522
|
+
row.inputTokens += usage?.inputTokens ?? 0;
|
|
523
|
+
row.outputTokens += usage?.outputTokens ?? 0;
|
|
524
|
+
// 🔴 `cacheReadTokens` 读的是**引擎原形** `ev.usage`,不是 CC 镜像:镜像的
|
|
525
|
+
// `cacheReadInputTokens` 是**必填** number,缺席在那儿已经被折成 0
|
|
526
|
+
// (`toCcModelUsage` 的 `finiteOrZero`)⇒ 从镜像读就再也分不出「没报」与「零命中」。
|
|
527
|
+
// ⇒ 一轮都没报过 ⇒ 键**不铸**;报过之后再加 0 的那些轮是真的零命中。
|
|
528
|
+
const cacheRead = ev.usage?.cacheReadTokens;
|
|
529
|
+
if (typeof cacheRead === 'number' && Number.isFinite(cacheRead)) {
|
|
530
|
+
row.cacheReadTokens = (row.cacheReadTokens ?? 0) + cacheRead;
|
|
531
|
+
}
|
|
532
|
+
if (usageMissing)
|
|
533
|
+
row.usageMissing = true;
|
|
534
|
+
nestedUsageByTask.set(taskId, row);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
422
537
|
const outputTokens = ev.usage?.outputTokens;
|
|
423
538
|
// 🔴 异源对抗复审 [medium]③:发臂条件从「有 outputTokens」放宽到「**有话可说**」——
|
|
424
539
|
// core 会发 `{type:'turn_end', usageMissing:true, stopReason:'error'}` 这种合法帧,而
|
|
@@ -578,16 +693,26 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
578
693
|
}
|
|
579
694
|
// D-3 / B-068 · L-198(0.66.0):**终局对账那一拍**。与终帧超集键
|
|
580
695
|
// (`_sema_cost_breakdown` / `_sema_nested_usage`)同一个读器 `readRunCostFacts` ⇒ 两面同源。
|
|
696
|
+
// 🔴 0.67.1 / B-091:「同源」此前只到**读器**这一层,取并那一层(下界位的两个来源)还留在
|
|
697
|
+
// 终帧那一面上 ⇒ 两面在「流内观测到缺口、终局 stats 缄默」这一形上各说各的。现在取并
|
|
698
|
+
// 在读器里面,两面读的是**同一次计算**(第二参就是那一半来源)。
|
|
581
699
|
// 🔴 没有账就不说话:`stats` 读不出(409 拒绝信封 / park 体 / `failed` 事件帧)⇒ 不发。
|
|
582
700
|
// 🔴 子流断闸(§E2)一字不动:子代花费只经本臂的 `nestedMicroUsd`(终局 stats.nested)到账。
|
|
583
701
|
// fail-soft 同 plan_review_park:sink 抛错不影响终帧照常投影。
|
|
584
702
|
if (ev.type === 'done' && ctx.emitChrome) {
|
|
585
703
|
const doneStats = ev.result?.stats;
|
|
586
|
-
|
|
704
|
+
// 🔴 0.67.1 / B-091:**第二参必须传** —— 下界位的取并(`stats.usageMissing` ∪ 流内观测
|
|
705
|
+
// `ctx.usageMissingObserved`)已经下沉进读器;不传就是把「臂绕开取并」那条不对称原样
|
|
706
|
+
// 种回去(终帧铸了 `_sema_usage_lower_bound`、同一拍的臂上却没有 `usageLowerBound`)。
|
|
707
|
+
const costFacts = readRunCostFacts(doneStats, ctx);
|
|
587
708
|
if (costFacts !== undefined) {
|
|
588
709
|
emitChromeFireAndForget(ctx, { kind: 'run_cost_reconciled', laneProof: MAIN, ...costFacts.reconcile });
|
|
589
710
|
}
|
|
590
711
|
}
|
|
712
|
+
// L-228:见本表声明处的头注 —— 挂表与铸终帧在**同一个同步步**里,复用 ctx 的并发流不会串账。
|
|
713
|
+
// (`usageMissingObserved` 是 per-ctx 的**单调布尔**,没有「顶掉别人」这一形:复用 ctx 的两条流
|
|
714
|
+
// 里只要有一条观测到缺口,两条的数字就都该按下界读 —— 取并是安全的那一侧。)
|
|
715
|
+
ctx.nestedUsageByTask = nestedUsageByTask;
|
|
591
716
|
yield terminalToSdkResult(ev, ctx);
|
|
592
717
|
return;
|
|
593
718
|
}
|
package/dist/adapter/types.d.ts
CHANGED
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
import type { AgentEvent } from '@sema-agent/sdk';
|
|
37
37
|
import type { ModelUsage, SDKMessage } from '@sema-agent/agent-types';
|
|
38
38
|
import type { ChromeEvent } from '../seam.js';
|
|
39
|
+
import type { MutableSubagentUsageRow } from './downstream/terminalToSdkResult.js';
|
|
39
40
|
export type { ModelUsage, SDKMessage };
|
|
40
41
|
export type StampedAgentEvent = AgentEvent & {
|
|
41
42
|
id?: string;
|
|
@@ -100,6 +101,32 @@ export interface EmitContext {
|
|
|
100
101
|
* · sink 抛错**绝不影响流**,并且痕迹落回 console —— 让位的前提是它真接住了。
|
|
101
102
|
*/
|
|
102
103
|
onDroppedFrame?(info: DroppedFrameInfo): void;
|
|
104
|
+
/**
|
|
105
|
+
* L-228(0.67.0)—— **这条流上看见的 per-subagent turn 用量分表**,键 = 子任务 id
|
|
106
|
+
* (wire 缺席时回落 `parentToolCallId`)。终帧的两个超集键
|
|
107
|
+
* `_sema_nested_usage_by_task` / `_sema_nested_usage_by_task_partial` 由它铸出
|
|
108
|
+
* (唯一 mint 点 = `terminalToSdkResult.ts` 的 `nestedUsageByTaskParts`)。
|
|
109
|
+
*
|
|
110
|
+
* 🔴 **由 `runStream` 在流内写,宿主不要自己填** —— 与同接口的 `startedAtMs` 同一类
|
|
111
|
+
* (「请求是宿主构造的,这一格是驱动自己攒的」)。宿主塞一份进来 = 把一份**不是这条流看见的**
|
|
112
|
+
* 账当成这条流的,而下游那个 `partial` 判别位恰恰是靠「这条流看见了多少」才成立的。
|
|
113
|
+
* 🔴 **缺席 / 空表 ⇒ 终帧两个键都不铸**:空表会被读成「一个子代都没委派」,而真相可能是
|
|
114
|
+
* 「委派了但这条流没看见任何一轮」。
|
|
115
|
+
*/
|
|
116
|
+
nestedUsageByTask?: ReadonlyMap<string, MutableSubagentUsageRow>;
|
|
117
|
+
/**
|
|
118
|
+
* 0.67.0 —— **这条流上观测到过「某一轮没有 usage」**(core 的 `turn_end.usageMissing`)。
|
|
119
|
+
* 终帧的 `_sema_usage_lower_bound` 与 chrome 对账臂的 `usageLowerBound` 与 `stats.usageMissing`
|
|
120
|
+
* **取并**读它。
|
|
121
|
+
*
|
|
122
|
+
* 🔴 **为什么非有它不可**:`stats.usageMissing` 只在带得出 `TaskResult` 的终帧上有,而
|
|
123
|
+
* `failed` 事件帧 / 409 拒绝信封 / park 体**根本没有 stats** ⇒ 只读 stats 的话,一条**已经
|
|
124
|
+
* 观测到缺口**的 run 会在终帧上落成「判别位缺席」,而按新合同那读作「每一轮都报了 usage」——
|
|
125
|
+
* 偏偏那种终帧的 `usage` 是 `flattenUsage(undefined)` 的**全零**:「不知道」被渲成了精确零。
|
|
126
|
+
* 🔴 **由 `runStream` 在流内写,宿主不要自己填**(同 `nestedUsageByTask` / `startedAtMs`)。
|
|
127
|
+
* 🔴 **只置 `true`,从不置回 false**:一轮不知道,整条流的数字就是下界,后面的轮补不回来。
|
|
128
|
+
*/
|
|
129
|
+
usageMissingObserved?: true;
|
|
103
130
|
}
|
|
104
131
|
/** Stamp `uuid` + `session_id` onto a freshly-built arm body. */
|
|
105
132
|
export declare function stamp<T extends {
|
|
@@ -1,93 +1,118 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* src/autoModeUnavailable.ts —
|
|
3
|
-
* (0.63.0
|
|
2
|
+
* src/autoModeUnavailable.ts — 「这一次 deny 是**分类器那一轮没能裁决**」的事实读器 + 唯一措辞铸点
|
|
3
|
+
* (0.63.0 件⑧ 起;**0.67.0 随 core 7.14.0 整只换面,见下方「卡面退役 / 拒绝面继任」段**)。
|
|
4
4
|
*
|
|
5
5
|
* -- 它答的是哪一问 ---------------------------------------------------------------------------
|
|
6
|
-
* auto
|
|
7
|
-
* `
|
|
8
|
-
*
|
|
6
|
+
* auto 模式下,门会就一只调用去咨询分类器。分类器结束一轮有**三种**收场,而只有两种带一个**结构位**:
|
|
7
|
+
* · `unavailable` —— 分类器**跑不了**(模型那条腿抛了/被拒/跑过上限):一条**说得出口**的 fail-closed
|
|
8
|
+
* 拒绝,过一会儿重试就行;
|
|
9
|
+
* · `parse_error` —— 分类器**跑了并且答了**,只是答在裁决契约之外:按 BLOCK 处置,并像一次裁决一样
|
|
10
|
+
* 被拒绝上限计数;
|
|
11
|
+
* · **缺席**(在 `deniedBy: "classifier"` 旁)—— 分类器**自己的裁决**(一次 `<block>yes`)。
|
|
12
|
+
* 🔴 **它是显示元数据,不是裁决位**:引擎在门上已经判完了,没有任何东西读它来决定放行与否,
|
|
9
13
|
* 所以消费端只拿它渲一句话,**绝不**据它翻转任何裁决。
|
|
10
|
-
* 🔴 **缺席 ≠「分类器跑成了」**:绝大多数
|
|
11
|
-
*
|
|
14
|
+
* 🔴 **缺席 ≠「分类器跑成了」**:绝大多数 deny 根本没咨询过分类器(部署没武装 auto、或这次走的是
|
|
15
|
+
* 别的层)。缺席只意味着「这条门记录上没有这条事实」。
|
|
12
16
|
*
|
|
13
|
-
* -- 🔴
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
17
|
+
* -- 🔴 卡面退役 / 拒绝面继任(0.67.0;core 7.14.0 `docs/wire-consumption/7.14.0.md` 的
|
|
18
|
+
* **Retired keys (BREAKING)** 表逐字)----------------------------------------------------
|
|
19
|
+
* core 7.14.0 把 `classifierUnavailable` 这一格**从六个载体上整族删掉**(`PermissionDeniedPayload` /
|
|
20
|
+
* `PermissionResult` 的 deny 与 ask 两臂 / `AskRequest` / `AskCarry`·`AskRequestCarry` /
|
|
21
|
+
* `PendingAction.tool_approval`·`CheckpointSummary`),其中 **ask 那几处自 #661 起引擎就没写过**
|
|
22
|
+
* (退役表逐字 "never engine-written since #661" / "dead since #661")。继任者是**一个**结构位:
|
|
23
|
+
* `GateDisposition.denied.cause ∈ {"unavailable","parse_error"}`(`dist/core/gate-outcome.d.ts:168`
|
|
24
|
+
* `CLASSIFIER_DENY_CAUSES`),挂在 `tool_end.gate` / `PermissionDeniedPayload.gate` / 耐久行的
|
|
25
|
+
* resolved outcome 上,且只在 `deniedBy ∈ {classifier, policy}` 旁(`DENIED_BY_MAY_CARRY_CAUSE`)。
|
|
26
|
+
* ⇒ 本模块把 `ClassifierUnavailableView` / `classifierUnavailableOf` / `classifierUnavailableDetail`
|
|
27
|
+
* **干净切**(clean-cut,不留别名、不留兼容读),换成 {@link classifierDenyCauseOf} 与
|
|
28
|
+
* {@link classifierDenyCauseDetail}。留一个读不到的读器只会让三端继续为一件上游已经不发的事实写
|
|
29
|
+
* 渲染分支,而那正是「校旧物」的门自己要拦的形(登记在 `run-retired-vocabulary-census-test.mjs`)。
|
|
30
|
+
* 🔴 **旧耐久行的处置 = 忽略**:一条 pre-7.14 的 park 行上仍带着 `classifierUnavailable` 时,本包
|
|
31
|
+
* **不读、不渲、不崩** —— 投影按新形办(退役表逐字「a stored key on an old row is ignored by the
|
|
32
|
+
* projection」)。
|
|
24
33
|
*
|
|
25
|
-
* -- 🔴
|
|
26
|
-
* · {@link
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* `
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* 右操作数随熔断表一起没了;排除的**理由**从来就不在熔断轴上,现按理由的真出处直写)。
|
|
34
|
+
* -- 🔴 两条轴不是一张表(退役后仍然成立,只是坐标换了)--------------------------------------
|
|
35
|
+
* · {@link CLASSIFIER_DENY_CAUSES}(`unavailable` / `parse_error`)—— 「这一轮分类**是怎么收场的**」,
|
|
36
|
+
* `GateDisposition.cause` 的值域(词表属主 = core)。**闭集**:core 的 `screenGateOutcome` 把
|
|
37
|
+
* 出集的 cause 列为记录缺陷(不变量 I5),而有缺陷的门记录 server 整条不上帧。
|
|
38
|
+
* · {@link AUTO_MODE_UNAVAILABLE_CAUSES}(`error` / `timeout`)—— 一个 `unavailable` 背后的
|
|
39
|
+
* **为什么**。core 退役表逐字:「The WHY behind an `unavailable` (error vs timeout) is **not a
|
|
40
|
+
* branching fact**: the deny sentence spells it and the `auto_mode.classified` trace frame carries
|
|
41
|
+
* it」。⚠️ **本包今天不投影那条 trace 帧**(`src/` 全树 `auto_mode` 零命中),所以这张表在本包
|
|
42
|
+
* 手上**没有到达任何一条投影臂的路**:它留在公面上是作为三端共用的**词表抄件**(core 7.16.0 仍
|
|
43
|
+
* 导出它,故不构成退役),消费端**不得**期待它出现在某个 deny cause 旁边 —— 那一格的值域是上一条。
|
|
36
44
|
*
|
|
37
45
|
* -- 为什么是镜像而不是 import ---------------------------------------------------------------
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* 而 core **不是本包消费者的依赖**(既非 peer 也非 runtime dep),本包的 `.d.ts` 一旦引用它,装了
|
|
41
|
-
* 本包却没装 core 的下游会当场编译不过。⇒ 与 `engineNoticeCodes.ts` / `toolResult.ts` /
|
|
46
|
+
* `@sema-agent/core` **不是本包消费者的依赖**(既非 peer 也非 runtime dep),本包的 `.d.ts` 一旦引用
|
|
47
|
+
* 它,装了本包却没装 core 的下游会当场编译不过。⇒ 与 `engineNoticeCodes.ts` / `toolResult.ts` /
|
|
42
48
|
* `retryStatus.ts` 同一条处置:**按真字节镜像,把代价交给门** ——
|
|
43
49
|
* `run-auto-mode-unavailable-test.mjs` 对**实装 devDep core** 的产物逐词双向对账,core 一动这里就先红。
|
|
44
|
-
* 🔴 **候上游导出即换**:sdk
|
|
50
|
+
* 🔴 **候上游导出即换**:sdk 哪天镜像了 `CLASSIFIER_DENY_CAUSES` 与 `GateDisposition.cause`,
|
|
51
|
+
* 本模块的表应当整只退役改成从 sdk 取。
|
|
45
52
|
*/
|
|
46
53
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
|
|
54
|
+
* 一轮分类**怎么收场的** —— `GateDisposition.denied.cause` 的值域
|
|
55
|
+
* (core `CLASSIFIER_DENY_CAUSES`,`gate-outcome.d.ts:168` 逐词逐序镜像,7.14.0 起两词)。
|
|
56
|
+
* · `unavailable` —— 分类器跑不了(模型那条腿抛了/被拒/跑过上限);一次说得出口的 fail-closed 拒绝,
|
|
57
|
+
* 这次调用**可以过一会儿再试**;
|
|
58
|
+
* · `parse_error` —— 分类器跑了并且答了,只是答在裁决契约之外;**为安全起见拦下**,并像一次裁决一样
|
|
59
|
+
* 被拒绝上限计数 —— 与上一条的下一步**不同**,所以两个词、两句话。
|
|
60
|
+
*
|
|
52
61
|
* 🔴 形制:`Object.freeze` 的数组,**不是**只在类型面只读的 `readonly T[]` —— 后者一行 `.splice()`
|
|
53
62
|
* 就能改,而公面消费者拿到的正是这个实例(本仓已定谳的病形,同 `RESUME_RETRY_LATER_CODES`)。
|
|
54
63
|
*/
|
|
55
|
-
export declare const
|
|
56
|
-
/**
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
64
|
+
export declare const CLASSIFIER_DENY_CAUSES: readonly string[];
|
|
65
|
+
/**
|
|
66
|
+
* 这个词是不是 {@link CLASSIFIER_DENY_CAUSES} 的成员(core `isClassifierDenyCause` 的镜像)。
|
|
67
|
+
*
|
|
68
|
+
* 🔴 **这一格与 `origin` / `ruleOffersAbsence` 的开集纪律刻意不同**,理由在**上游的形**上:
|
|
69
|
+
* `cause` 出闭集时 core 的 `screenGateOutcome` 判**记录缺陷**(不变量 I5),而有缺陷的门记录
|
|
70
|
+
* server **整条不上帧** ⇒ 一个表外的 cause **结构上到不了消费端**;它到达的形式是 `gate` 整键缺席。
|
|
71
|
+
* 所以在这一格上按闭集判不会「把一个合法的新词吞成缺席」——真读到表外词只说明那条记录本不该长
|
|
72
|
+
* 这样,而把它渲成一句成因就是替引擎编事实。(`deniedBy` 那张表在 `gateVocabulary.ts` 上同一条。)
|
|
73
|
+
*/
|
|
74
|
+
export declare function isClassifierDenyCause(v: unknown): boolean;
|
|
61
75
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
76
|
+
* 一条**门记录**(`tool_end.gate` / `PermissionDeniedPayload.gate` / 耐久行的 resolved outcome,
|
|
77
|
+
* 或本包 `gateOutcomeOf` 投出的 {@link import('./gateOutcome.js').GateOutcomeView})→
|
|
78
|
+
* 「这次 deny 是分类器那一轮**没能裁决**」的成因词;不是这一形 ⇒ `undefined`,绝不抛出。
|
|
64
79
|
*
|
|
65
|
-
* 🔴
|
|
66
|
-
* `
|
|
67
|
-
*
|
|
68
|
-
* 🔴
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* ⚠️ 0.65.0 起 `breaker_open` 也走这一条(core 7.12.0 已删该词)—— **旧引擎的合法帧照旧过境**,
|
|
77
|
-
* 只是本端不再为它自铸一句解释。
|
|
78
|
-
* · **{@link NEVER_STAMPED_CAUSES} 里的词仍判缺席** —— 今天只有 `parse_error`。这不是「表外词一律拒」,
|
|
79
|
-
* 而是一条**有出处的排除**:core 的判据轴逐字说它「跑了并且答了」,读它就是替引擎编一件
|
|
80
|
-
* 它明说没发生的事。0.63.0 起的黑盒判据 G-19 钉的正是这一条,行为**一字未变**。
|
|
81
|
-
* 🔴 **只交 `cause` 一格**:顺手把整只 ask 的别的键带出来会长成第二份 ask 读面。
|
|
82
|
-
* 🔴 **端不许在自己那一侧再补一张闭集表**:未知词的正解是渲兜底句(措辞铸点已经有),不是不渲。
|
|
80
|
+
* 🔴 **一只读器吃三处**:wire 的原始 `gate` 对象、包投出的视图、以及**带 `gate` 的整只帧**
|
|
81
|
+
* —— 三者键路同形(`disposition.kind` / `disposition.cause`),各写一份就是三份台账各漂各的,
|
|
82
|
+
* 本包一贯要根治的形。
|
|
83
|
+
* 🔴 **只在 `kind === "denied"` 上答**:`allowed` 臂上不存在这一位;一条读不出处置词的记录是坏记录,
|
|
84
|
+
* 同样答缺席。
|
|
85
|
+
* 🔴 **闭集判据**(理由见 {@link isClassifierDenyCause}):表外词 ⇒ 缺席。
|
|
86
|
+
* 🔴 **不校 `deniedBy`**:core 的 `DENIED_BY_MAY_CARRY_CAUSE` 说的是 cause 只能站在 `classifier` /
|
|
87
|
+
* `policy`(继承链上祖先的冻结分类器)旁边,那是**引擎的段内不变量**,server 已按它铸;包再校一遍
|
|
88
|
+
* 只会在上游合法演进的当天把一个真读数判没(与 `gateOutcome.ts` 顶注的 I1–I4 同一条处置)。
|
|
89
|
+
* 🔴 **缺席不是断言**:缺席同时覆盖「分类器自己裁决 block 了」「这次不是分类器轮」「本部署没接分类器」
|
|
90
|
+
* 三形,端**禁**读成「分类器好着呢」。
|
|
83
91
|
*/
|
|
84
|
-
export declare function
|
|
92
|
+
export declare function classifierDenyCauseOf(gate: unknown): string | undefined;
|
|
93
|
+
/**
|
|
94
|
+
* 一轮分类**为什么**跑不了(core `AUTO_MODE_UNAVAILABLE_CAUSES`;逐词逐序镜像,core 7.12.0 起两词)。
|
|
95
|
+
* · `error` —— 模型那条腿抛了/被拒(分类时的路由失败也读在这里:派生路由的前置在任何 decide
|
|
96
|
+
* 之前就回落了,所以没有单独的词);
|
|
97
|
+
* · `timeout` —— 往返上限到了。
|
|
98
|
+
*
|
|
99
|
+
* ⚠️ **本表在本包手上没有到达任何投影臂的路**(0.67.0 如实登记,见模块顶注第三段):它是一个
|
|
100
|
+
* `auto_mode.classified` **trace 帧**上的量,而本包不投影 trace 帧。留在公面上的唯一身份是
|
|
101
|
+
* **三端共用的词表抄件**(core 7.16.0 仍导出它 ⇒ 不构成退役);消费端**不得**期待它出现在
|
|
102
|
+
* {@link classifierDenyCauseOf} 的产物旁边。
|
|
103
|
+
*
|
|
104
|
+
* 🔴 形制:`Object.freeze` 的数组,理由同 {@link CLASSIFIER_DENY_CAUSES}。
|
|
105
|
+
*/
|
|
106
|
+
export declare const AUTO_MODE_UNAVAILABLE_CAUSES: readonly string[];
|
|
85
107
|
/**
|
|
86
|
-
*
|
|
108
|
+
* 一个 deny 成因词 → 一句人话。**唯一措辞铸点**(三端共用;端零自拼)。
|
|
87
109
|
*
|
|
88
110
|
* 🔴 **按自有属性查表**(与本包其余措辞铸点同一条纪律):`Object.freeze` 不移除原型,裸下标会让
|
|
89
111
|
* 一个来自 wire 的 `constructor` / `toString` 命中 `Object.prototype` 上的**函数**并被当成一句话。
|
|
90
|
-
* 🔴 表外词 / 坏值 ⇒
|
|
91
|
-
*
|
|
112
|
+
* 🔴 表外词 / 坏值 ⇒ 一句**兜底**:仍然告诉用户「这次拒是分类器那条腿引出来的」,但**不冒充**
|
|
113
|
+
* 两句里的任何一句;原样带上那个词供运维追问上游。
|
|
114
|
+
* ⚠️ 与 {@link classifierDenyCauseOf} 的闭集判据**不矛盾**:那一层答「这条事实在不在」(闭集,
|
|
115
|
+
* 出集 = 坏记录),本层答「拿到一个词怎么渲」——**渲判据面**的消费端(以及一条从旧持久态恢复
|
|
116
|
+
* 回来的视图)可能手里就是一个表外词,它必须渲出一句诚实的话,而不是整屏崩或冒充一句已知的。
|
|
92
117
|
*/
|
|
93
|
-
export declare function
|
|
118
|
+
export declare function classifierDenyCauseDetail(cause: unknown): string;
|