@sema-agent/client-core 0.75.0 → 0.75.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 +22 -0
- package/README.md +2 -2
- package/dist/adapt/arms.js +20 -5
- package/dist/adapt/panelTasks.d.ts +6 -2
- package/dist/adapt/panelTasks.js +6 -3
- package/dist/agentSession/backgroundView.js +2 -1
- package/dist/engineAgentPanelStore.d.ts +6 -0
- package/dist/engineAgentPanelStore.js +101 -10
- package/dist/fleet/fleetLedger.js +6 -0
- package/dist/fleet/fleetProjection.js +7 -2
- package/dist/fleet/fleetRowAgentType.d.ts +9 -0
- package/dist/fleet/fleetRowAgentType.js +59 -0
- package/dist/fleetAgentPanelProjection.js +24 -3
- package/dist/panelRunningHistory.d.ts +21 -0
- package/dist/panelRunningHistory.js +25 -0
- package/dist/seam.d.ts +11 -0
- package/docs/INTEGRATION-CLIENTS.md +49 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -49,6 +49,28 @@
|
|
|
49
49
|
> 挡住 ⇒ 本批把它机械化——④a0 对 `pending` 行**要求段头已是日期形**(`(未发布)` 直接红),阶段一
|
|
50
50
|
> commit 漏转在发布前就红,不再靠人记。
|
|
51
51
|
|
|
52
|
+
## 0.75.1(2026-09-20)
|
|
53
|
+
|
|
54
|
+
> 主题:**面板反序缓行的残洞**(外部复验读数:短命子代的最终用量在一种到达序下永远交不出)+ **缓行遇同代 running 帧的三向定形** + **`spawnName` 透传与 `agentType` 改读诚实来源**(#969 提货,CC-95)。**patch**:型面 additive(tick 臂 +1 可选位),公面值导出零增;行为面三笔(见下)。接入面 §75。
|
|
55
|
+
|
|
56
|
+
### Fixed
|
|
57
|
+
|
|
58
|
+
- **反序缓行永远放不出(0.75.0 的残洞;外部复验 D1)**:`tick{UUID, cardBound}` 先上屏、终态行随后被缓住、`end{UUID}` 最后到时,漏斗「三把可能的键」**全查不到** —— 因为后两把只在行帧**真发布过**时才登记,而缓行的定义就是「还没发布」。结果:消费端只得到一条空的 ended 行,那一行唯一带最终用量的帧被吞。现在 `end` 归一加**第四条腿**:按缓行**自带的** wire 侧键(`transcriptId` 主 / 委派卡 id 退路)反查缓行,放行仍走「代际三向判 → 同键核 → 经原漏斗归一」,且**整条腿永不 `return`** —— 原 wire 键那一行自己的 `end` 不会被它吞掉。🔴 候选**多于一条**、或候选放行后会与那条 `end` 分流到两把键时,本包**一条都不放**(宁可晚交用量,也不让一只子代分成两行);那几条行的用量仍缺席,记 KNOWN-LIMITS。
|
|
59
|
+
- **缓着终态行时后到的 `running` 帧被无条件当复活(外部复验 B4)**:最终用量会被陈旧快照顶成小数字。现在三向 —— **可证同代** = 陈旧快照(整帧不落地、零事件、缓行留着、台账保持 settled)/ **可证更新** = 复活(作废缓行)/ **判不出** = 维持 0.75.0(KL-26)。可比性只看**帧自带的**两键(沿用记住的值会把「缺席」变成「相等」);`startedAt` 的形核补齐 `Number.isFinite`,与周期判据同源(族扫同批改齐 fleet 投影与后台视图两处)。
|
|
60
|
+
- **`subagent_lifecycle.agentType` 此前发的是委派卡的 `description`**(不是类型):tick 臂的 `name` 是上游由 `description` 派生的**展示名**,喂进 `agentType` 位后宿主的 SubagentStart hook 收到的是任务描述 —— 一个看起来合理的错答案。现在按**诚实来源**三档取:tick 上的结构读类型键(亲核 sdk 9.8.1:`task_progress` 臂**没有** `agentType` / `spawnAgentType`,今天恒不命中)→ fleet 行帧的 `agentType`(在被 `deriveAgentLabel` 折进 `name` **之前**留一份,新包内叶 `src/fleet/fleetRowAgentType.ts`)→ 都没有 ⇒ **键缺席**。🔴 缺席是新的可能形,端的 hook 侧要有 absent 兜底。
|
|
61
|
+
|
|
62
|
+
### Added
|
|
63
|
+
|
|
64
|
+
- **`panel_task` tick 臂 `spawnName?`**(#969 提货,additive):结构读 wire `task_progress.spawnName`,非空串才带;**缺席 = 产生者还没有这一位**(旧 core / 旧 server),不是「没有名字」,也绝不用展示名 `name` 顶替。与 `name` 是两位:一个是展示名,一个是派生这只子代时被声明的名字。
|
|
65
|
+
|
|
66
|
+
### Changed
|
|
67
|
+
|
|
68
|
+
- 0.75.0 发车文案三处更正(已发段是冻结面,一字不动,更正记在本段):① 「`.d.ts` 改五只」实测**四只**(重跑请求要由两版 dist 逐文件 diff 反推,不按族概括);② `panelRunningHistory.d.ts` 的读口改名属**包内叶面**,不出公面 —— 「型面 additive」那句只覆盖公面,叶面这一笔当时没写;③ 「三把可能的键」的真实射程 = 行**没发布过**时只有第一把有效(本版修掉后头注已改成实情,并加了第四条腿)。
|
|
69
|
+
|
|
70
|
+
### Guards
|
|
71
|
+
|
|
72
|
+
- panel-cycle 门 96 → **170 checks**(D1 两序与退路键 / 不张冠李戴 / B4 三向 / 多候选一条都不放 / 缓行真作废 / `spawnName` 与 `agentType` 三档);变异 19 枚(18 见红 + 1 可证等价);异源对抗复审三轮每轮一条 [high] 全采修(多缓行别名覆盖 / 递归派发吞掉 wire 行的 end / 候选与 end 分流到两把键)。diff 门加一只壳侧版本时差归一器(带反空转与退役条件,**刻意不进** `ADAPTER_DIVERGENCES` —— 它不是有意偏离基线)。
|
|
73
|
+
|
|
52
74
|
## 0.75.0(2026-09-20)
|
|
53
75
|
|
|
54
76
|
> 主题:**压缩的非 compacted 结局上 chrome 面**(CC-89;此前三端都看不见压缩失败)+ **peer sdk 地板 `>=9.8.1`**(CC-81)+ **短命子代的最终用量不再丢**(0.74.5 首见即终态零事件的代价面,外部复验读数)。**minor**:① 新内部臂 `compaction_outcome` 进 `INTERNAL_SDK_ARM_TYPES` —— 自建 print / stream-json 出口的端**必须**先给它表态(吞或转),否则它会顺着兜底透传成越界帧;② peer 地板抬升。型面 additive(`ChromeEvent` +1 臂 `CompactionOutcomeChromeEvent`;值导出零增)。接入面 §74。
|
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.75.
|
|
38
|
+
**Version:** 0.75.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
|
|
@@ -319,7 +319,7 @@ public-surface guard checks that last one).
|
|
|
319
319
|
| `scripts/run-panel-identity-normalization-test.mjs` | One background subagent has two ids on the wire — the fleet row id tail and the `task_progress` task id (its transcript id). Every panel event goes through one funnel that rewrites the `tick` / `end` task id onto the fleet row's id once a `fleet-row` has registered the key (`transcriptId` first, `parentToolCallId` as the fallback), carrying the original as `wireTaskId` and marking `taskIdOrigin`; an unbound tick whose row has not arrived yet waits one beat (bounded) and is released verbatim on the next tick / `end`, when the buffer is full, or after `MAX_HELD_WIRE_TICK_BEATS` other fleet-row / end / sweep events (a `sweep` itself leaves it alone: there is no row to settle yet); a normalized `end` that carries no cycle identity borrows the registering row's, so a late close of a revived task is recognized as stale; the key table is an LRU (a task that keeps ticking is never evicted by newer registrations); the residency mark migrates with the id and both keys are cleared on settle — except that a stale (previous-cycle) terminal never clears the revived row's mark — so the notification lane can clear it. Once a tick has been delivered verbatim under its UUID, that UUID is the subagent's key: later fleet rows and fleet-side ends are rewritten onto it (the tail kept in `wireTaskId`), so a consumer sees one row in every arrival order; a late tick from a previous cycle is dropped rather than folded into the revived row. |
|
|
320
320
|
| `scripts/run-compaction-outcome-projection-test.mjs` | The `compaction_outcome` frame (a compaction that did **not** end as compacted: mooted by the task ending, failed, …) projected to an internal arm and then to the additive `compaction_outcome` chrome event — `outcome` required and verbatim (open set), `trigger` / `reason` present only when the engine sent a non-empty string, malformed frames dropped, zero transcript rows, the lane told honestly (`parentToolCallId` ⇒ subagent lane; a frame attributable only by `sourceTaskId` / `bgAgentId` is not surfaced on the main lane). |
|
|
321
321
|
| `scripts/run-approval-card-retract-test.mjs` | The approval card's **decision-free retraction** and the in-stream frame leg's **outcome hand-back**: a host that must withdraw a card that no longer has a decision channel (session switch, engine switch, a tracker reporting the ask gone) answers `{ kind: 'retracted' }` and the package sends nothing on any of the three legs (in-stream frame, suspended ask, durable park), reporting `decision: 'unresolved'` with a `retracted` flag; `aborted` / `failed` / `deny` keep their meaning (a real deny is still posted), and `onToolApprovalOutcome` hands every in-stream outcome back to the host exactly once, tolerating a throwing or rejecting callback Also the single source for the host-side approval-outcome note (`approvalOutcomeNoteOf`): `settled` is whether the decision was delivered, `retracted` is an independent key present only when the card was retracted, and `detail` is the retraction / edit-refused sentence or the refusal code and message — never a fabricated sentence. |
|
|
322
|
-
| `scripts/run-panel-cycle-identity-test.mjs` | The **cycle identity** on agent-panel events and the fleet ledger's **departure read-out**: a background agent may be revived under the same id, so `fleet-row` and `end` events now carry the wire's own `cycleSeq` / `startedAt` when present (absent means the row has no notion of generations, never "generation one"), `isStaleEngineAgentPanelEnd` is the single rule for ignoring a late `end` from a previous cycle (only when both sides carry a comparable identity; absence never drops a real terminal), a changed `cycleSeq` is a new cycle for usage stickiness and buffer coalescing, the notification lane carries `cycleSeq` only when the wire really sent `seq`, and `task_remove` frames reach the host through `onTaskRemoved` with `removeReason` / `cycleSeq` verbatim, a stale previous-generation removal leaving the newer row in place |
|
|
322
|
+
| `scripts/run-panel-cycle-identity-test.mjs` | The **cycle identity** on agent-panel events and the fleet ledger's **departure read-out**: a background agent may be revived under the same id, so `fleet-row` and `end` events now carry the wire's own `cycleSeq` / `startedAt` when present (absent means the row has no notion of generations, never "generation one"), `isStaleEngineAgentPanelEnd` is the single rule for ignoring a late `end` from a previous cycle (only when both sides carry a comparable identity; absence never drops a real terminal), a changed `cycleSeq` is a new cycle for usage stickiness and buffer coalescing, the notification lane carries `cycleSeq` only when the wire really sent `seq`, and `task_remove` frames reach the host through `onTaskRemoved` with `removeReason` / `cycleSeq` verbatim, a stale previous-generation removal leaving the newer row in place. A terminal row held back because the consumer has no such row yet is also released by the keys it carries itself (its transcript id, or the delegating call id of a subagent already on screen under its wire id), since the key tables are only written once a row has actually been published — the release still goes through the one funnel, so the subagent stays one row; a running frame arriving after such a held terminal row is a stale snapshot when both sides carry a comparable generation and it matches (no event, the held row keeps its final usage), a revival when the frame is provably newer (the held row is dropped), and is treated as a revival when neither side can be compared. A subagent lifecycle event carries `agentType` only from an honest source — the fleet row's own agent type, recorded before it is folded into the row label — and omits the key when there is none, never substituting the display name |
|
|
323
323
|
| `scripts/run-workflow-size-warning-test.mjs` | The **workflow size warning** verdict shared by every host footer / panel: a three-state result (`warn` / `ok` / `unknown`) read off the optional fleet view keys, where an unknown size is never reported as a normal one (absent `totalCount` / `tokens` without positive over-cap evidence is `unknown`, naming the missing keys), positive evidence on either axis wins regardless of absent keys, the per-agent denominator uses the engine's started count only when it is not below done+failed (a smaller value is a stale reading), otherwise falls back to the done+failed lower bound only when both keys are present — and a lower-bound denominator only yields an upper bound of the projection, which can prove *within cap* (`ok`, flagged) but never *over cap* (`unknown`, with the upper bound exposed) — and the prior is used only when the engine itself reports zero started agents; cap precedence env > explicit guideline > default, prototype keys never act as a guideline, the env reader is pure and does not fall through to the second name on a bad first value; caps, guideline table, env names and the three copy variants are single-sourced |
|
|
324
324
|
| `scripts/run-approvals-stream-live-capability-test.mjs` | The engine's live-approval-push self-description (`capabilities.approvalsStreamLive`, engine ≥7.87.1), read the same four-state way as its four sibling capability readers: an absent key is reported as not reported (never folded into `false`), the value must be a strict boolean, and the one decision the feed consumer needs — whether it must keep pulling suspended asks itself — is answered by `livePendingNeedsReconcile`, which only says no when the engine explicitly says it pushes. |
|
|
325
325
|
| `scripts/run-model-facing-anchors-test.mjs` | Five places where model-facing engine text is parsed back into card slots: the task-output cursor semantics machine-readable key wins over the spool marker text (and its absence is never read as `full`), the `--- result (…) ---` line accepts a parenthesised note, the bash header accepts the engine's exit-note and image variants and carries the note into the same benign-exit slot the structured path uses, the notification seed scan treats both producers' terminal words as terminal, and the parked background-task phrase is recognised. |
|
package/dist/adapt/arms.js
CHANGED
|
@@ -11,6 +11,8 @@ import { isWorkflowAgentTaskId, recordWorkflowAgentTaskId } from '../workflow.js
|
|
|
11
11
|
// L-215+(0.65.0;core [6908]):终局状态词的单铸谓词(三处同形一次改齐)。
|
|
12
12
|
import { isTerminalStatus } from '../runTerminal.js';
|
|
13
13
|
import { resolveEnginePanelTaskId } from '../engineAgentPanelStore.js';
|
|
14
|
+
// 0.75.1 CC-95(b):子代类型的诚实来源台账(fleet 行帧折进 name 之前留的那一份;包内叶)。
|
|
15
|
+
import { fleetRowAgentType } from '../fleet/fleetRowAgentType.js';
|
|
14
16
|
import { chrome, MAIN, transcript, messageIdentityOf } from './ids.js';
|
|
15
17
|
import { CANCEL_MESSAGE, decisionOf, flattenWireOutput, REJECT_MESSAGE, sanitizeToolUseBlock, shortTaskLabel, TASK_TOOL_NAMES, WORKFLOW_TOOL_NAMES, } from './wireShapes.js';
|
|
16
18
|
// ══ M2/M1 —— assistant(tool_use 半场 + durable 文本/思考半场)══════════════════════════════════
|
|
@@ -823,6 +825,9 @@ const taskProgressArm = function* (m, { ctx, cards, panel, inst }) {
|
|
|
823
825
|
// ("Bash npm test"),service 已 redact。PROGRESS 语义(喂状态/摘要显示槽),不是诊断。
|
|
824
826
|
// 老引擎缺席 ⇒ 两个消费面维持今天的显示。
|
|
825
827
|
const currentAction = typeof m.currentAction === 'string' && m.currentAction.length > 0 ? m.currentAction : undefined;
|
|
828
|
+
// 0.75.1 CC-95(a)—— `spawnName`:sdk 9.8.1 的 `task_progress` 臂上还没有这一位(新形才铸),所以按
|
|
829
|
+
// **结构读**取(不靠型面),非空串才算数;缺席 = 旧形,不编。
|
|
830
|
+
const spawnName = m.spawnName;
|
|
826
831
|
// inline 群组行孪生:**绑定了**的 tick 同时喂转录里那条行的统计(按委派卡 id 键)。
|
|
827
832
|
if (cardId !== undefined) {
|
|
828
833
|
yield chrome({
|
|
@@ -854,15 +859,22 @@ const taskProgressArm = function* (m, { ctx, cards, panel, inst }) {
|
|
|
854
859
|
: typeof input.task === 'string'
|
|
855
860
|
? input.task
|
|
856
861
|
: undefined;
|
|
862
|
+
// #6 常驻台账的两个写口整臂让位给 M3(module store 不许两个模块各写各的)。
|
|
863
|
+
// 0.74.3 CC-70:常驻标记在**归一后的键**下(fleet 尾段 = 通知车道清标用的那把);此前 UUID 记、17 位清 ⇒ 永不清。
|
|
864
|
+
// 🔴 0.75.1 CC-95(b):归一必须在 `panel.start` **之前**算 —— 子代类型的诚实来源是 fleet 行帧的 `agentType`,
|
|
865
|
+
// 而那本账按**行 id 尾段**记,拿 wire 的 UUID 是查不到的。
|
|
866
|
+
const identity = resolveEnginePanelTaskId(taskId, explicitParent);
|
|
857
867
|
// ⟨帧序耦合 5/7⟩ 首 tick 才 fire Start,而通知臂的 if-started 门读的就是它。
|
|
858
868
|
if (!panel.isLive(taskId)) {
|
|
859
869
|
// 这条引擎子代在壳侧**可观测的起点** = 本 turn 的首 tick。
|
|
860
|
-
|
|
861
|
-
|
|
870
|
+
// 🔴 0.75.1 CC-95(b)子代**类型**的诚实来源(此前取的是 `m.name` = 上游由委派卡 description 派生的**展示名**
|
|
871
|
+
// ⇒ 宿主的子代启动 hook 收到的是任务描述,一个看起来合理的错答案):
|
|
872
|
+
// ① wire `task_progress` 上的结构读类型键 —— 亲核 sdk 9.8.1 `dist/events.d.ts` 的 `task_progress` 臂:
|
|
873
|
+
// **没有** `agentType`、也没有 `spawnAgentType`,这条 lane 今天不报类型 ⇒ **有出处才读**,所以不读;
|
|
874
|
+
// ② fleet 行帧的 `agentType`(sdk `FleetTaskRow.agentType`,§K-7 短类型 label)—— 按归一后的尾段查;
|
|
875
|
+
// ③ 都没有 ⇒ **键缺席**(宿主 hook 侧按 absent 兜底,不编 `subagent`)。
|
|
876
|
+
yield* panel.start(taskId, fleetRowAgentType(identity.taskId));
|
|
862
877
|
}
|
|
863
|
-
// #6 常驻台账的两个写口整臂让位给 M3(module store 不许两个模块各写各的)。
|
|
864
|
-
// 0.74.3 CC-70:常驻标记在**归一后的键**下(fleet 尾段 = 通知车道清标用的那把);此前 UUID 记、17 位清 ⇒ 永不清。
|
|
865
|
-
const identity = resolveEnginePanelTaskId(taskId, explicitParent);
|
|
866
878
|
panel.noteResidency(taskId, identity.taskId);
|
|
867
879
|
panel.markLive(taskId);
|
|
868
880
|
yield chrome({
|
|
@@ -872,6 +884,9 @@ const taskProgressArm = function* (m, { ctx, cards, panel, inst }) {
|
|
|
872
884
|
kind: 'tick',
|
|
873
885
|
taskId,
|
|
874
886
|
...(typeof m.name === 'string' && m.name.length > 0 ? { name: m.name } : {}),
|
|
887
|
+
// 0.75.1 CC-95(a):派生这只子代时被声明的名字(wire `task_progress.spawnName`)。结构读、非空串才带;
|
|
888
|
+
// 缺席 = 旧形的产生者 ⇒ 键不落(不编、不拿展示名 `name` 冒充 —— 两位是两位)。
|
|
889
|
+
...(typeof spawnName === 'string' && spawnName.length > 0 ? { spawnName } : {}),
|
|
875
890
|
...(description !== undefined ? { description } : {}),
|
|
876
891
|
...(prompt !== undefined ? { prompt } : {}),
|
|
877
892
|
...(currentAction !== undefined ? { currentAction } : {}),
|
|
@@ -61,8 +61,12 @@ export interface PanelTaskLedger {
|
|
|
61
61
|
/** #6 常驻台账:live-bound ⇒ 清常驻;inert/unbound ⇒ 记常驻(module store 的**唯一**写口之一)。 */
|
|
62
62
|
/** 0.74.3 CC-70:`residentKey` = 常驻标该记在哪把键下(归一后的 fleet 尾段);缺省 = taskId。绑卡判定仍按 tick 的 taskId。 */
|
|
63
63
|
noteResidency(taskId: string, residentKey?: string): void;
|
|
64
|
-
/**
|
|
65
|
-
|
|
64
|
+
/**
|
|
65
|
+
* SubagentStart(类型恒记,fire 一生一次)。
|
|
66
|
+
* 🔴 0.75.1 CC-95(b):`agentType` **可缺席** —— 调用方查不到诚实来源时就不传,本臂让键缺席,
|
|
67
|
+
* 绝不在这里编一个占位词(此前传的是 tick 的展示名,子代启动 hook 因此收到的是委派卡的 description)。
|
|
68
|
+
*/
|
|
69
|
+
start(taskId: string, agentType?: string): Generator<AdapterOutput>;
|
|
66
70
|
/** 关卡 settle(cardId)或 turn 末/abort 防御 sweep(cardId===null)。 */
|
|
67
71
|
settle(cardId: string | null, isError: boolean, report?: string): Generator<AdapterOutput>;
|
|
68
72
|
/** #6 通知-settle 边(推送半场):真终态在此落行,session 常驻行不再被 turn sweep 假结。 */
|
package/dist/adapt/panelTasks.js
CHANGED
|
@@ -108,13 +108,16 @@ export function createPanelTaskLedger(ctx, cards, inst) {
|
|
|
108
108
|
},
|
|
109
109
|
/** SubagentStart(cli fireSubagentStartHook 的臂化):类型恒记,fire 一生一次。 */
|
|
110
110
|
*start(taskId, agentType) {
|
|
111
|
-
|
|
111
|
+
// 0.75.1 CC-95(b):类型**在场才记**(缺席不写空串进表 —— stop 那一半读的是同一份记忆,写了空串
|
|
112
|
+
// 就等于把「不知道」渲成了一个词)。Stop 的类型查找恒记(fire 与否无关)。
|
|
113
|
+
if (agentType !== undefined)
|
|
114
|
+
subagentTypeById.set(taskId, agentType);
|
|
112
115
|
// 🔴 判重 + 记号都在**唯一那条 yield 之前**(矩阵 §2.1②:零残余窗,别挪)。
|
|
113
116
|
if (inst.hasFiredSubagentStart(taskId))
|
|
114
117
|
return;
|
|
115
118
|
inst.markFiredSubagentStart(taskId);
|
|
116
|
-
ctx.probe?.('hook', `start ${taskId} type=${agentType}`);
|
|
117
|
-
yield chrome({ kind: 'subagent_lifecycle', laneProof: laneOf(taskId), phase: 'start', taskId, agentType });
|
|
119
|
+
ctx.probe?.('hook', `start ${taskId} type=${agentType ?? '-'}`);
|
|
120
|
+
yield chrome({ kind: 'subagent_lifecycle', laneProof: laneOf(taskId), phase: 'start', taskId, ...(agentType !== undefined ? { agentType } : {}) });
|
|
118
121
|
},
|
|
119
122
|
/**
|
|
120
123
|
* 结掉一张关闭中的卡所绑的面板行(`cardId`),或 turn 末/abort 结掉**本 turn 活绑**的全部行
|
|
@@ -99,7 +99,8 @@ export function fleetTaskToBackgroundRow(r) {
|
|
|
99
99
|
// [C33]③.2 / server [C36]:launch 时刻 epoch ms(revive 行=本 revive 周期 wake 时刻),
|
|
100
100
|
// ADDITIVE / tolerate-absent —— 排队中的行本就没有,缺席即键缺席(0 不是「1970 年启动」)。
|
|
101
101
|
// 两腿同一个 mapper:`snapshot` 首帧行与 `task` 增量帧行(POST-MERGE FULL row)都过这里。
|
|
102
|
-
|
|
102
|
+
// 与 fleet 投影同律:只有**有限正数**算 launch 锚(非有限值渲不出时长,只会变成 `null`)。
|
|
103
|
+
const startedAtMs = typeof r.startedAt === 'number' && Number.isFinite(r.startedAt) && r.startedAt > 0 ? r.startedAt : undefined;
|
|
103
104
|
return {
|
|
104
105
|
key: { source: 'fleet', rowId: r.id },
|
|
105
106
|
status: backgroundStatusFromFleet(r.status),
|
|
@@ -37,6 +37,12 @@ export type EngineAgentPanelEvent = {
|
|
|
37
37
|
currentAction?: string;
|
|
38
38
|
toolUses: number;
|
|
39
39
|
totalTokens: number;
|
|
40
|
+
/**
|
|
41
|
+
* 0.75.1 —— 派生这只子代时**被调用方声明的名字**(wire `task_progress.spawnName`,engine 侧新位)。
|
|
42
|
+
* 🔴 与 `name` 是两位,别合流:`name` 是**展示名**(上游由委派卡的 description 派生),本位是 spawn 时的名字。
|
|
43
|
+
* 🔴 缺席 = **旧形**(产生者还没有这一位)⇒ 键不落,绝不拿展示名冒充。
|
|
44
|
+
*/
|
|
45
|
+
spawnName?: string;
|
|
40
46
|
/**
|
|
41
47
|
* 0.74.3 CC-70:tick 上带的委派卡 id(wire `task_progress.parentToolCallId`,在场才带)—— 身份归一的**退路键**
|
|
42
48
|
* (`transcriptId` 查不到时按它反查 fleet 尾段)。
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* Buffering: ticks published before the panel mounts are buffered (bounded) and replayed to the first
|
|
23
23
|
* subscriber, so early frames in a fast turn aren't lost to mount timing.
|
|
24
24
|
*/
|
|
25
|
-
import { retirePanelRowRunning, notePanelRowEnded, panelRowEndedIdentity, peekHeldNewbornTerminalRow, takeHeldNewbornTerminalRow, dropHeldNewbornTerminalRow } from './panelRunningHistory.js';
|
|
25
|
+
import { retirePanelRowRunning, notePanelRowEnded, panelRowEndedIdentity, peekHeldNewbornTerminalRow, takeHeldNewbornTerminalRow, dropHeldNewbornTerminalRow, heldNewbornTerminalRefs } from './panelRunningHistory.js';
|
|
26
26
|
const _panelToolUsesLanePins = [true, true];
|
|
27
27
|
void _panelToolUsesLanePins;
|
|
28
28
|
/**
|
|
@@ -350,21 +350,73 @@ export function __resetEngineAgentPanelIdentityForTests() {
|
|
|
350
350
|
* 0.75.0 W2:一条 end 递达之前,先把投影缓着的「首见即终态」行放出来(它带这一行唯一的一份最终用量),经漏斗原路发布(身份归一照走);
|
|
351
351
|
* 递达之后记「本进程结过这一行」—— 投影据此把之后到的终态行当成短命子代的收尾(照发用量)而不是历史行。两步都只对当代 end(陈旧判据与销 running 历史同一只)。
|
|
352
352
|
*/
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
353
|
+
/**
|
|
354
|
+
* 这一条缓行**放出去会落在哪把消费端键上**(纯读口,不动表;与 fleet-row 臂的改写次序同序:
|
|
355
|
+
* 反向所有权 → 已发布的 wire 键 → 自己的行尾段)。第四条腿据它核「行与 end 是否同键」。
|
|
356
|
+
* 🔴 保守方向:所有权在表里就按它答(`wireOwnerOf` 遇到新一代会把它删掉再落到下一档 —— 那一档答错只会让本腿
|
|
357
|
+
* **少放**一次,不会让一只子代分成两行)。
|
|
358
|
+
*/
|
|
359
|
+
function heldRowConsumerKey(tail, held) {
|
|
360
|
+
const owner = wireIdByFleetId.get(tail);
|
|
361
|
+
if (owner !== undefined)
|
|
362
|
+
return owner.wireId;
|
|
363
|
+
return publishedWireFor(held) ?? tail;
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* 这一条 end 对那一行缓着的终态行的**代际三向**(纯读口,不动表)。先 peek 再决定:先 take 会让前代
|
|
367
|
+
* end 把当代唯一的缓行取走丢掉;
|
|
368
|
+
* · `'keep'` = end 可证比缓行**旧** ⇒ 缓行留着(等当代的那条 end);
|
|
369
|
+
* · `'drop'` = end 可证比缓行**新** ⇒ 缓行作废(前代的用量不许盖当代行);
|
|
370
|
+
* · `'release'` = 同代 / 判不出 ⇒ 可以放出;
|
|
371
|
+
* · `'none'` = 这一行没有缓着的终态行。
|
|
372
|
+
*/
|
|
373
|
+
function heldNewbornTerminalVerdict(taskId, endCycle) {
|
|
356
374
|
const held = peekHeldNewbornTerminalRow(taskId);
|
|
357
375
|
if (held === undefined || held.kind !== 'fleet-row')
|
|
358
|
-
return;
|
|
376
|
+
return 'none';
|
|
359
377
|
const heldCycle = { ...(held.cycleSeq !== undefined ? { cycleSeq: held.cycleSeq } : {}), ...(held.startedAt !== undefined ? { startedAt: held.startedAt } : {}) };
|
|
360
378
|
if (isStaleEngineAgentPanelEnd(endCycle, heldCycle))
|
|
361
|
-
return;
|
|
362
|
-
if (isStaleEngineAgentPanelEnd(heldCycle, endCycle))
|
|
379
|
+
return 'keep';
|
|
380
|
+
if (isStaleEngineAgentPanelEnd(heldCycle, endCycle))
|
|
381
|
+
return 'drop';
|
|
382
|
+
return 'release';
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* 🔴 放行前的**同键核**:这一条缓行放出去,行帧与随后那条 end 会不会落在**同一把消费端键**上?
|
|
386
|
+
*
|
|
387
|
+
* 反例(实测):一只子代的委派卡被复用 —— `tick{V, 卡 P}` 已按 V 上过屏;缓着的终态行是
|
|
388
|
+
* `{尾段 X, 转录锚 U, 卡 P}`;来的是 `end{U}`。第四条腿按转录锚找到唯一候选 X,可它一放行就被
|
|
389
|
+
* 「先发布的键为准」经卡 P 改写到 **V** 上,而这条 `end{U}` 在主路上谁也不命中、原样落在 **U**:
|
|
390
|
+
* 用量记到了 V,X 的终态却发给了 U,消费端还可能凭 `end{U}` 另开一行。
|
|
391
|
+
*
|
|
392
|
+
* 所以只在两种形下放行(其余留着缓行,等真正属于它的那条 end —— 宁可晚交一份用量,不让一只子代分成两行):
|
|
393
|
+
* ① 行会被改写到**这条 end 自己那把键**上(随后主路也落在同一把键,`ownedFleetIds` 连带结账);
|
|
394
|
+
* ② 行按**自己的尾段**上屏,而这条 end 的键正是它的转录锚且那把键没被发布过 —— 放行登记了转录钥匙,
|
|
395
|
+
* 主路随即经钥匙表归一到同一条尾段。
|
|
396
|
+
*/
|
|
397
|
+
function heldRowReleasesOntoSameKey(tail, end) {
|
|
398
|
+
const held = peekHeldNewbornTerminalRow(tail);
|
|
399
|
+
if (held === undefined || held.kind !== 'fleet-row')
|
|
400
|
+
return false;
|
|
401
|
+
const rowKey = heldRowConsumerKey(tail, held);
|
|
402
|
+
if (rowKey === end.taskId)
|
|
403
|
+
return true;
|
|
404
|
+
// 末一项 `!publishedWireIds.has(...)` 是**可证冗余**的写方守卫(删掉行为不变:若这把键发布过,
|
|
405
|
+
// 上面的 `heldRowConsumerKey` 就会把 rowKey 算成它、第一档已经返回 true,走不到这里)。留着是把
|
|
406
|
+
// 「② 只成立于那把键没上过屏」这句话钉在代码里,别当成活判据去删第一档。
|
|
407
|
+
return rowKey === tail && held.transcriptId === end.taskId && !publishedWireIds.has(end.taskId);
|
|
408
|
+
}
|
|
409
|
+
function releaseHeldNewbornTerminal(taskId, endCycle) {
|
|
410
|
+
const verdict = heldNewbornTerminalVerdict(taskId, endCycle);
|
|
411
|
+
if (verdict === 'drop') {
|
|
363
412
|
dropHeldNewbornTerminalRow(taskId);
|
|
364
413
|
return;
|
|
365
414
|
}
|
|
366
|
-
|
|
367
|
-
|
|
415
|
+
if (verdict !== 'release')
|
|
416
|
+
return;
|
|
417
|
+
const held = takeHeldNewbornTerminalRow(taskId);
|
|
418
|
+
if (held !== undefined)
|
|
419
|
+
publishEngineAgentPanelEvent(held);
|
|
368
420
|
}
|
|
369
421
|
/** 结算(end 已递达):running 历史销、ended 账单调写(可证比已结周期旧的 end 不倒写)。 */
|
|
370
422
|
function settleRowHistory(taskId, cycle) {
|
|
@@ -457,7 +509,10 @@ export function publishEngineAgentPanelEvent(ev) {
|
|
|
457
509
|
}
|
|
458
510
|
case 'end': {
|
|
459
511
|
// 0.75.0 W2:缓着的「首见即终态」行**先于一切归一**放出(异源复审 R3 [high]:放出的行帧会登记钥匙 / 所有权,之后的 end 归一必须看到它,
|
|
460
|
-
// 否则同一子代长成 UUID 行 + 尾段行两行)
|
|
512
|
+
// 否则同一子代长成 UUID 行 + 尾段行两行)。
|
|
513
|
+
// 🔴 0.75.1 —— 前三把键的**真实射程**(实测反例):尾段本身 / UUID 经钥匙解析到的尾段 / 这把 UUID 名下的反向所有权尾段,
|
|
514
|
+
// 后两把**只在行帧真发布过时才登记**,而缓行的定义就是「还没发布」⇒ `tick{UUID}` 先上屏、终态行 `{尾段, transcriptId: UUID}`
|
|
515
|
+
// 被缓住时,这三把全查不到,缓行永远放不出。所以有第四条腿:按缓行**自带的** wire 侧键(transcriptId / 退路键)反查。
|
|
461
516
|
releaseHeldNewbornTerminal(ev.taskId, ev);
|
|
462
517
|
{
|
|
463
518
|
const pre = resolveEnginePanelTaskId(ev.taskId);
|
|
@@ -470,6 +525,42 @@ export function publishEngineAgentPanelEvent(ev) {
|
|
|
470
525
|
for (const [fleetId, o] of wireIdByFleetId)
|
|
471
526
|
if (o.wireId === ev.taskId && !o.retired)
|
|
472
527
|
releaseHeldNewbornTerminal(fleetId, ev);
|
|
528
|
+
// 第四条腿:缓着的行自带 `transcriptId`(主键)与委派卡 id(退路键)—— 两把与 `publishedWireFor` 的**同一条律**:
|
|
529
|
+
// 转录键命中不看退役(同一子代的迟到终态);退路键只认**未退役**的那把 wire 键(复活沿用同一张卡)。
|
|
530
|
+
// 放行仍走 `releaseHeldNewbornTerminal`(代际三向 peek + 经原漏斗归一),所以前代的缓行会被当代终态作废而不是盖上去。
|
|
531
|
+
const heldMatches = [];
|
|
532
|
+
for (const ref of heldNewbornTerminalRefs()) {
|
|
533
|
+
if (ref.taskId === ev.taskId)
|
|
534
|
+
continue; // 第一条腿已经试过这把键
|
|
535
|
+
const byTranscript = ref.transcriptId !== undefined && ref.transcriptId === ev.taskId;
|
|
536
|
+
const byParent = ref.parentToolCallId !== undefined &&
|
|
537
|
+
publishedWireByParent.get(ref.parentToolCallId) === ev.taskId &&
|
|
538
|
+
publishedWireIds.get(ev.taskId)?.retired === false;
|
|
539
|
+
if (byTranscript || byParent)
|
|
540
|
+
heldMatches.push(ref.taskId);
|
|
541
|
+
}
|
|
542
|
+
// 命中之后**先逐条按代际三向判、不动表**,再决定放谁:
|
|
543
|
+
// · 可证更旧的缓行(前代那一行)⇒ 当场作废(它的用量不许盖当代);
|
|
544
|
+
// · end 可证更旧 ⇒ 那条缓行留着,等属于它自己的那条 end;
|
|
545
|
+
// · 剩下的「可放行」候选**恰一条** ⇒ 放行 —— 它一上屏就登记了自己的钥匙 / 所有权,本条 end 随后在主路上
|
|
546
|
+
// 归一到同一把键,所以仍是「一行一条 end」。
|
|
547
|
+
// · 🔴 候选 **≥2**(两个行尾段自称同一个转录锚 / 同一张委派卡)= **身份两义 ⇒ 一条都不放**:转录钥匙表是
|
|
548
|
+
// **单值**的(后写覆盖先写),放两条就会各自上屏成两行而主路只归一到最后那一条 ⇒ 前面那些行永远收不到结算;
|
|
549
|
+
// 换成「每行各自补一条 end」则要在这里替主路决定消费端键,会吞掉原 wire 键那一行自己的终态。身份对不上就
|
|
550
|
+
// **一帧都不摘**是本包扣留表的既有律(见 `hitl/gateLedger.ts` 的「判不出 ⇒ 一条都不摘」)。代价 = 这种形下
|
|
551
|
+
// 那几条行的用量仍缺席(与本件修之前同),换来的是绝不凭空多长一行、绝不替谁编一条终态。
|
|
552
|
+
// 真字节里两个行尾段共享一个转录锚不成立(转录锚按代际唯一),所以这一支是防御面,不是常态路径。
|
|
553
|
+
// 🔴 无论放没放,**主路照走**:原 wire 键那一行(它可能早就上过屏)自己的 end 绝不能被这条腿吞掉。
|
|
554
|
+
const releasable = [];
|
|
555
|
+
for (const tail of heldMatches) {
|
|
556
|
+
const verdict = heldNewbornTerminalVerdict(tail, ev);
|
|
557
|
+
if (verdict === 'drop')
|
|
558
|
+
dropHeldNewbornTerminalRow(tail);
|
|
559
|
+
else if (verdict === 'release' && heldRowReleasesOntoSameKey(tail, ev))
|
|
560
|
+
releasable.push(tail);
|
|
561
|
+
}
|
|
562
|
+
if (releasable.length === 1)
|
|
563
|
+
releaseHeldNewbornTerminal(releasable[0], ev);
|
|
473
564
|
}
|
|
474
565
|
// 先发布的键为准(CC-83):尾段已被改写成 UUID 的子代,fleet 侧(投影 / 通知)的 end{尾段} 也改写成 UUID
|
|
475
566
|
const wireOwner = wireOwnerOf(ev.taskId, ev);
|
|
@@ -7,6 +7,7 @@ import { enqueueBgChildNotification, isOwnWorkflowRun } from '../notifications.j
|
|
|
7
7
|
import { clearBgTerminalFacts, getBgTerminalFacts, isOwnEngineRun, recordBgParentRun, recordBgTerminalFacts, recordOwnEngineRun, } from '../subagentContentStore.js';
|
|
8
8
|
import { rowIdTail } from '../workflow.js';
|
|
9
9
|
import { recordEngineTranscriptId } from '../subagent/engineDelegatedPrompt.js';
|
|
10
|
+
import { recordFleetRowAgentType } from './fleetRowAgentType.js';
|
|
10
11
|
import { DEFAULT_SESSION_KEY } from '../sessionSlot.js';
|
|
11
12
|
import { TERMINAL_FLEET_TASK_STATUSES, projectTasks, projectWorkflows, wireCycleSeq, wireParentId, } from './fleetProjection.js';
|
|
12
13
|
/** 终态行留存宽限窗(187 的 agents 页把完成会话留在列表;窗过即净,防长会话堆行)。 */
|
|
@@ -345,6 +346,11 @@ export function createFleetLedger(hooks = {}, opts = {}) {
|
|
|
345
346
|
if (typeof row.transcriptId === 'string' && row.transcriptId.length > 0) {
|
|
346
347
|
recordEngineTranscriptId(rowIdTail(row.id), row.transcriptId);
|
|
347
348
|
}
|
|
349
|
+
// 0.75.1:`agentType`(§K-7 短类型 label)在 `projectTasks` 里会被**折进** `name`
|
|
350
|
+
// (`deriveAgentLabel` = agentName ?? agentType),折完就与显示名分不开了 —— 而 `subagent_lifecycle`
|
|
351
|
+
// 的 `agentType` 需要的正是折之前那一位。所以在这里(折之前)按行 id 尾段留一份;缺席 / 坏形不入账
|
|
352
|
+
// (读口答缺席,tick 臂让键缺席,绝不拿展示名冒充)。写方只有这一处,与转录锚台账同族同作用域。
|
|
353
|
+
recordFleetRowAgentType(rowIdTail(row.id), row.agentType);
|
|
348
354
|
// 终态帧 → 留存池(remove 同毫秒尾随,probe 实证;留存让 completed band 数得到它)。
|
|
349
355
|
// REF-CC-040(fleet2-02):非终态分支必须显式 delete —— 否则 bg 复活(SendMessage 后
|
|
350
356
|
// seq+1)时,`project()` 因 `taskMap.has(id)` 恒 continue 而永不清理过期留存条目,belt
|
|
@@ -341,7 +341,8 @@ export function projectTasks(allRows, opts) {
|
|
|
341
341
|
...(transcriptId !== undefined ? { transcriptId } : {}),
|
|
342
342
|
// launch 真锚原值透传(> 0 才算有值)。ledger 拿它算 elapsedMs 是**另一件事** —— 下游要的是
|
|
343
343
|
// 时刻本身(自己的钟去减),elapsedMs 是发帧那一刻的快照,客户端存着会漂([C36] server 判据)。
|
|
344
|
-
|
|
344
|
+
// launch 锚只认**有限正数**;非有限值按缺席(它序列化成 `null` 发出去 = 一个不存在的 launch 时刻)。
|
|
345
|
+
...(typeof r.startedAt === 'number' && Number.isFinite(r.startedAt) && r.startedAt > 0 ? { startedAt: r.startedAt } : {}),
|
|
345
346
|
...(currentTool !== undefined ? { currentTool } : {}),
|
|
346
347
|
...(parentToolCallId !== undefined ? { parentToolCallId } : {}),
|
|
347
348
|
...(stoppedBy !== undefined ? { stoppedBy } : {}),
|
|
@@ -511,7 +512,11 @@ export const FLEET_TASK_ROW_KEYS_NOT_PROJECTED = {
|
|
|
511
512
|
agentType: {
|
|
512
513
|
kind: 'by-design',
|
|
513
514
|
owner: 'client-core 规范重构轮',
|
|
514
|
-
reason: '折进 `name`(deriveAgentLabel:agentName ?? agentType,都缺才退 objective 派生)
|
|
515
|
+
reason: '折进 `name`(deriveAgentLabel:agentName ?? agentType,都缺才退 objective 派生)——渲染契约只有一个身份列,与上游的身份选择器同形。' +
|
|
516
|
+
'🔴 0.75.1 CC-95(b) 补一句射程:**本视图**仍不带它(渲染面一个身份列不变),但它不再只被折掉 —— ' +
|
|
517
|
+
'`fleet/fleetLedger` 的行帧臂在折之前把它记进 `fleet/fleetRowAgentType`(包内叶),adapt 的 tick 臂据此给 ' +
|
|
518
|
+
'`subagent_lifecycle.agentType` 取诚实来源(此前取的是展示名 = 委派卡 description 派生的串)。' +
|
|
519
|
+
'所以这条是「不进本视图」,不是「零消费者」;哪天渲染面要第二个身份列,再把它投影出来并删本条。',
|
|
515
520
|
},
|
|
516
521
|
agentName: {
|
|
517
522
|
kind: 'by-design',
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 记一条 fleet 行的 agentType(幂等;行帧不带这一位 / 坏形 ⇒ 整条不动,不擦掉已知值 ——
|
|
3
|
+
* 同一行的后续帧缺席这一位是「这帧没说」,不是「类型没了」)。
|
|
4
|
+
*/
|
|
5
|
+
export declare function recordFleetRowAgentType(taskIdTail: string, agentType: string | undefined): void;
|
|
6
|
+
/** 读口:这一行的子代类型;`undefined` = **诚实不知道**(调用方让键缺席,不许回落到展示名)。 */
|
|
7
|
+
export declare function fleetRowAgentType(taskIdTail: string | undefined): string | undefined;
|
|
8
|
+
/** 测试钩:清台账(module 级单例,同进程多组断言必须能清)。 */
|
|
9
|
+
export declare function __resetFleetRowAgentTypeForTests(): void;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fleetRowAgentType — 「这一条 fleet 行的子代是什么**类型**」的台账(0.75.1 CC-95(b);包内叶,不出公面)。
|
|
3
|
+
*
|
|
4
|
+
* ── 为什么要有它 ──────────────────────────────────────────────────────────────────────────────
|
|
5
|
+
* `subagent_lifecycle` 的 `agentType`(CC `SubagentStart.agent_type` 那一位)此前取的是 tick 的**展示名**
|
|
6
|
+
* `task_progress.name` —— 那一位是上游由委派卡的 `description` 派生的**显示串**,不是类型。于是宿主的
|
|
7
|
+
* SubagentStart hook 收到的是任务描述:一个看起来合理的错答案。
|
|
8
|
+
*
|
|
9
|
+
* 诚实来源亲核(sdk 9.8.1 实装字节):
|
|
10
|
+
* · `task_progress` 臂(`dist/events.d.ts`)上**没有** `agentType`、也没有 `spawnAgentType` —— tick 这条
|
|
11
|
+
* lane 今天根本不报类型,所以本层**不读**无出处的键(读了就是替上游发明一个字段);
|
|
12
|
+
* · `FleetTaskRow.agentType`(`dist/resources/fleet.d.ts`,§K-7 的短类型 label,如 `Explore` /
|
|
13
|
+
* `general-purpose`)**有出处** —— 但 `fleet/fleetProjection.ts` 把它**折进** `FleetTaskView.name`
|
|
14
|
+
* (`deriveAgentLabel` = `agentName ?? agentType`,登记在
|
|
15
|
+
* `FLEET_TASK_ROW_KEYS_NOT_PROJECTED` 里),折完之后它与显示名再也分不开。
|
|
16
|
+
* ⇒ 所以类型这一位要在**折之前**留一份:行帧消费臂按行 id 尾段记,tick 臂按归一后的尾段读。
|
|
17
|
+
*
|
|
18
|
+
* ── 纪律 ────────────────────────────────────────────────────────────────────────────────────
|
|
19
|
+
* 🔴 **只记 wire 真给了的**:空串 / 非串一律不入账(空串既不是类型也不是「不知道」);账上没有 ⇒ 读口
|
|
20
|
+
* 答 `undefined` = **诚实缺席**,调用方让键缺席,绝不回落到展示名或编一个 `subagent`。
|
|
21
|
+
* 🔴 作用域 = 进程级(与本包转录锚台账逐字同族:写方 = 行帧消费臂,读方 = tick 臂;两份模块实例 ⇒ 读口恒空、
|
|
22
|
+
* `agentType` 恒缺席,而两边代码看着都对)。
|
|
23
|
+
* 有界 LRU(命中即续命):上限是内存约束不是语义,被挤掉只退化成「这一行的类型不知道」。
|
|
24
|
+
*/
|
|
25
|
+
const MAX_ROWS = 2048;
|
|
26
|
+
const agentTypeByTail = new Map();
|
|
27
|
+
/**
|
|
28
|
+
* 记一条 fleet 行的 agentType(幂等;行帧不带这一位 / 坏形 ⇒ 整条不动,不擦掉已知值 ——
|
|
29
|
+
* 同一行的后续帧缺席这一位是「这帧没说」,不是「类型没了」)。
|
|
30
|
+
*/
|
|
31
|
+
export function recordFleetRowAgentType(taskIdTail, agentType) {
|
|
32
|
+
if (typeof taskIdTail !== 'string' || taskIdTail.length === 0)
|
|
33
|
+
return;
|
|
34
|
+
// 形核仍在本层做(wire 值不可信,型面说是 string 不等于到货一定是 string;空串既不是类型也不是「不知道」)。
|
|
35
|
+
if (typeof agentType !== 'string' || agentType.length === 0)
|
|
36
|
+
return;
|
|
37
|
+
agentTypeByTail.delete(taskIdTail);
|
|
38
|
+
agentTypeByTail.set(taskIdTail, agentType);
|
|
39
|
+
if (agentTypeByTail.size > MAX_ROWS) {
|
|
40
|
+
const oldest = agentTypeByTail.keys().next().value;
|
|
41
|
+
if (oldest !== undefined)
|
|
42
|
+
agentTypeByTail.delete(oldest);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/** 读口:这一行的子代类型;`undefined` = **诚实不知道**(调用方让键缺席,不许回落到展示名)。 */
|
|
46
|
+
export function fleetRowAgentType(taskIdTail) {
|
|
47
|
+
if (typeof taskIdTail !== 'string' || taskIdTail.length === 0)
|
|
48
|
+
return undefined;
|
|
49
|
+
const v = agentTypeByTail.get(taskIdTail);
|
|
50
|
+
if (v === undefined)
|
|
51
|
+
return undefined;
|
|
52
|
+
agentTypeByTail.delete(taskIdTail); // LRU 续命:还在飞的子代不被一阵新登记挤掉
|
|
53
|
+
agentTypeByTail.set(taskIdTail, v);
|
|
54
|
+
return v;
|
|
55
|
+
}
|
|
56
|
+
/** 测试钩:清台账(module 级单例,同进程多组断言必须能清)。 */
|
|
57
|
+
export function __resetFleetRowAgentTypeForTests() {
|
|
58
|
+
agentTypeByTail.clear();
|
|
59
|
+
}
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
* 就地维护第二份;该函数出过等价性事故,0.1.1 修,见 `workflow.ts` 头注)。
|
|
49
49
|
*/
|
|
50
50
|
import { isStaleEngineAgentPanelEnd, publishEngineAgentPanelAbsence, publishEngineAgentPanelEvent } from './engineAgentPanelStore.js';
|
|
51
|
-
import { __resetPanelRunningHistoryForTests, notePanelRowRunning, panelRowUnknownToConsumer, panelRowEndedIdentity, holdNewbornTerminalRow, dropHeldNewbornTerminalRow } from './panelRunningHistory.js';
|
|
51
|
+
import { __resetPanelRunningHistoryForTests, notePanelRowRunning, panelRowUnknownToConsumer, panelRowEndedIdentity, holdNewbornTerminalRow, peekHeldNewbornTerminalRow, dropHeldNewbornTerminalRow } from './panelRunningHistory.js';
|
|
52
52
|
import { rowIdTail } from './workflow.js';
|
|
53
53
|
import { TERMINAL_FLEET_TASK_STATUSES } from './fleet/fleetProjection.js';
|
|
54
54
|
import { DEFAULT_SESSION_KEY } from './sessionSlot.js';
|
|
@@ -146,7 +146,11 @@ export function projectFleetAgentRowsFor(sessionKey, rows, nowMs = Date.now()) {
|
|
|
146
146
|
const toolUses = typeof row.toolUses === 'number' ? row.toolUses : undefined;
|
|
147
147
|
const transcriptId = row.transcriptId || undefined;
|
|
148
148
|
const parentToolCallId = typeof row.parentToolCallId === 'string' && row.parentToolCallId.length > 0 ? row.parentToolCallId : undefined;
|
|
149
|
-
|
|
149
|
+
// 🔴 形核与周期判据(`isStaleEngineAgentPanelEnd` / `isNewEngineAgentPanelCycle`)**逐字同源**:
|
|
150
|
+
// 有限正数才算数。少一个 `Number.isFinite` 就会让 `Infinity` 进台账 —— 那两只判据按缺席看它,
|
|
151
|
+
// 而本层按在场看它,同一帧在两处得出相反的「可比不可比」。
|
|
152
|
+
// `Infinity` 还会被序列化成 `null` 发给消费端,渲成一个不存在的 launch 时刻。
|
|
153
|
+
const startedAt = typeof row.startedAt === 'number' && Number.isFinite(row.startedAt) && row.startedAt > 0 ? row.startedAt : undefined;
|
|
150
154
|
// 0.74.0:代际号(正整数才算;坏形 / 缺席 ⇒ undefined,绝不补 1)。
|
|
151
155
|
const cycleSeq = typeof row.cycleSeq === 'number' && Number.isInteger(row.cycleSeq) && row.cycleSeq >= 1 ? row.cycleSeq : undefined;
|
|
152
156
|
const currentTool = currentToolKeyOf(row.currentTool) !== undefined ? row.currentTool : undefined;
|
|
@@ -271,6 +275,23 @@ export function projectFleetAgentRowsFor(sessionKey, rows, nowMs = Date.now()) {
|
|
|
271
275
|
});
|
|
272
276
|
continue;
|
|
273
277
|
}
|
|
278
|
+
// 🔴 0.75.1:缓着一条「首见即终态」的行时,后到的 running 帧有**三向**(0.75.0 是无条件作废 ⇒ 同代陈旧快照
|
|
279
|
+
// 把终态那条的最终用量顶掉,面板最后显示的是快照里的小数字):
|
|
280
|
+
// · **可证同代**(两侧有可比的同一种周期键且相等)= 陈旧快照 —— 与终态分支 `settledThisCycle` 同律:
|
|
281
|
+
// 整帧不落地(零事件、台账保持 settled),缓行留着等 end;
|
|
282
|
+
// · **可证更新** ⇒ 真复活:照发 running 行,缓着的前代终态行作废(下面那句 drop);
|
|
283
|
+
// · **判不出**(任一侧没有可比的周期键)⇒ 维持 0.75.0 的行为(当复活、作废缓行)—— 缺席不制造周期,
|
|
284
|
+
// 这一向是**已知并接受**的限制(接入文档的缺口行有记)。
|
|
285
|
+
// 「可证更旧」不在这三向里:它在本循环开头就被 `isStaleEngineAgentPanelEnd(rowIdentity, prevIdentity)` 整只跳过了。
|
|
286
|
+
// 可比性只由**帧自带的**两键与台账里记着的两键决定(不能用 `knownCycleSeq` / `knownStartedAt` —— 那两位会把
|
|
287
|
+
// 缺席沿用成「与台账相等」,把「判不出」误判成「可证同代」)。判据与 `isNewEngineAgentPanelCycle` 的前件同形。
|
|
288
|
+
const cycleComparable = (cycleSeq !== undefined && prev?.cycleSeq !== undefined) || (startedAt !== undefined && prev?.startedAt !== undefined);
|
|
289
|
+
// 🔴 再加一道 `prev.settled` 的窄化:缓行只由**终态**分支铸(铸完台账即 settled),所以「缓着行而台账说它在跑」
|
|
290
|
+
// 在本会话的台账里不可达;写出来是为了让这一格永远只对**结过的行**生效 —— 缓行表是进程级、seenMap 是按
|
|
291
|
+
// sessionKey 分桶的两个作用域,不写这一句的话,别的会话缓的行会让本会话一条真在跑的行被判成陈旧快照。
|
|
292
|
+
const staleSnapshotOverHeldRow = prev !== undefined && prev.settled === true && cycleComparable && !rowNewerCycle && peekHeldNewbornTerminalRow(taskId) !== undefined;
|
|
293
|
+
if (staleSnapshotOverHeldRow)
|
|
294
|
+
continue;
|
|
274
295
|
// ① 只发变化(新行 / status 翻 / token 涨 / 累计工具数涨 / 转录锚首次到)。
|
|
275
296
|
// 🔴 后两项的比较是**单向**的:只有「这一帧真给了值且与台账不同」才算变化。帧上**缺席**
|
|
276
297
|
// 不算变化(缺席=引擎这帧没说,不是「回到不知道」)——否则新旧引擎混发时台账会在
|
|
@@ -287,7 +308,7 @@ export function projectFleetAgentRowsFor(sessionKey, rows, nowMs = Date.now()) {
|
|
|
287
308
|
(cycleSeq !== undefined && prev.cycleSeq !== cycleSeq) ||
|
|
288
309
|
(currentToolKey !== undefined && prev.currentToolKey !== currentToolKey)) {
|
|
289
310
|
notePanelRowRunning(taskId); // CC-86:真发布了这一行在跑才记历史(未变的陈旧快照零事件也零记账,复审 R3)
|
|
290
|
-
dropHeldNewbornTerminalRow(taskId); // 复活为 running ⇒ 缓着的旧终态行作废(用量以新一代为准)
|
|
311
|
+
dropHeldNewbornTerminalRow(taskId); // 复活为 running ⇒ 缓着的旧终态行作废(用量以新一代为准);可证同代的陈旧快照到不了这里(三向判定,见上)
|
|
291
312
|
publishEngineAgentPanelEvent({
|
|
292
313
|
kind: 'fleet-row',
|
|
293
314
|
taskId,
|
|
@@ -19,6 +19,27 @@ export declare function holdNewbornTerminalRow(taskId: string, ev: object): void
|
|
|
19
19
|
export declare function peekHeldNewbornTerminalRow<T extends object>(taskId: string): T | undefined;
|
|
20
20
|
export declare function takeHeldNewbornTerminalRow<T extends object>(taskId: string): T | undefined;
|
|
21
21
|
export declare function dropHeldNewbornTerminalRow(taskId: string): void;
|
|
22
|
+
/** 一条缓行**自带**的 wire 侧身份键(它是一条 fleet 行帧,两把键都可缺席)。 */
|
|
23
|
+
export interface HeldNewbornTerminalRef {
|
|
24
|
+
/** 缓行在本表里的键 = fleet 行 id 尾段。 */
|
|
25
|
+
readonly taskId: string;
|
|
26
|
+
readonly transcriptId?: string;
|
|
27
|
+
readonly parentToolCallId?: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 0.75.1(实测反例):`end` 归一的三把可能的键里,**后两把只在行帧真发布过时才登记**
|
|
31
|
+
* (`transcriptId → 尾段` 钥匙表与 `尾段 → UUID` 反向所有权都由 `publishEngineAgentPanelEvent`
|
|
32
|
+
* 的 fleet-row 臂写),而缓行的定义就是「还没发布」⇒ `tick{UUID}` 先上屏、终态行 `{尾段,
|
|
33
|
+
* transcriptId: UUID}` 被缓住、`end{UUID}` 到达时三把键全查不到,缓行永远放不出、短命子代的
|
|
34
|
+
* 最终用量丢。所以漏斗还需要第四条腿:按缓行**自己带着的** wire 侧键反查。
|
|
35
|
+
*
|
|
36
|
+
* 🔴 **为什么是遍历、不是第六本索引**:面板这一族已经有五本账(running 历史 / ended 账 / 缓行 /
|
|
37
|
+
* 所有权 / 周期水位),再加一张 `transcriptId → 缓行键` 的索引就要在 hold / take / drop / 容量淘汰
|
|
38
|
+
* 四个写口上各维护一次同步 —— 那是本族出过真回归的形(一本账写了另一本没写 ⇒ 缓行放两次或永不放)。
|
|
39
|
+
* 缓行表有界 4096 且只在 **`end` 递达**时被扫(不是每拍;一条子代一生至多几次),所以遍历的成本
|
|
40
|
+
* 上限是一条 end 读 4096 个小对象,换来的是「缓行表自己就是唯一真源」。
|
|
41
|
+
*/
|
|
42
|
+
export declare function heldNewbornTerminalRefs(): HeldNewbornTerminalRef[];
|
|
22
43
|
/**
|
|
23
44
|
* 测试钩。清四样:① 「发布过 running 且还没结」的行 id 集合 ② 溢出标(溢出后读口一律答保守)③ 「递达过 end」的行 id 集合 ④ 缓着的首见终态行。
|
|
24
45
|
* 🔴 连续场景中途别调:清掉之后,「首见即终态」的判定会把此前见过 running / 结过的行当成从没见过。
|
|
@@ -84,6 +84,31 @@ export function takeHeldNewbornTerminalRow(taskId) {
|
|
|
84
84
|
export function dropHeldNewbornTerminalRow(taskId) {
|
|
85
85
|
heldNewborn.delete(taskId);
|
|
86
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* 0.75.1(实测反例):`end` 归一的三把可能的键里,**后两把只在行帧真发布过时才登记**
|
|
89
|
+
* (`transcriptId → 尾段` 钥匙表与 `尾段 → UUID` 反向所有权都由 `publishEngineAgentPanelEvent`
|
|
90
|
+
* 的 fleet-row 臂写),而缓行的定义就是「还没发布」⇒ `tick{UUID}` 先上屏、终态行 `{尾段,
|
|
91
|
+
* transcriptId: UUID}` 被缓住、`end{UUID}` 到达时三把键全查不到,缓行永远放不出、短命子代的
|
|
92
|
+
* 最终用量丢。所以漏斗还需要第四条腿:按缓行**自己带着的** wire 侧键反查。
|
|
93
|
+
*
|
|
94
|
+
* 🔴 **为什么是遍历、不是第六本索引**:面板这一族已经有五本账(running 历史 / ended 账 / 缓行 /
|
|
95
|
+
* 所有权 / 周期水位),再加一张 `transcriptId → 缓行键` 的索引就要在 hold / take / drop / 容量淘汰
|
|
96
|
+
* 四个写口上各维护一次同步 —— 那是本族出过真回归的形(一本账写了另一本没写 ⇒ 缓行放两次或永不放)。
|
|
97
|
+
* 缓行表有界 4096 且只在 **`end` 递达**时被扫(不是每拍;一条子代一生至多几次),所以遍历的成本
|
|
98
|
+
* 上限是一条 end 读 4096 个小对象,换来的是「缓行表自己就是唯一真源」。
|
|
99
|
+
*/
|
|
100
|
+
export function heldNewbornTerminalRefs() {
|
|
101
|
+
const out = [];
|
|
102
|
+
for (const [taskId, ev] of heldNewborn) {
|
|
103
|
+
const row = ev;
|
|
104
|
+
out.push({
|
|
105
|
+
taskId,
|
|
106
|
+
...(typeof row.transcriptId === 'string' && row.transcriptId.length > 0 ? { transcriptId: row.transcriptId } : {}),
|
|
107
|
+
...(typeof row.parentToolCallId === 'string' && row.parentToolCallId.length > 0 ? { parentToolCallId: row.parentToolCallId } : {}),
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
87
112
|
/**
|
|
88
113
|
* 测试钩。清四样:① 「发布过 running 且还没结」的行 id 集合 ② 溢出标(溢出后读口一律答保守)③ 「递达过 end」的行 id 集合 ④ 缓着的首见终态行。
|
|
89
114
|
* 🔴 连续场景中途别调:清掉之后,「首见即终态」的判定会把此前见过 running / 结过的行当成从没见过。
|
package/dist/seam.d.ts
CHANGED
|
@@ -250,6 +250,17 @@ export type ChromeEvent = {
|
|
|
250
250
|
laneProof: LaneProof;
|
|
251
251
|
phase: 'start' | 'stop';
|
|
252
252
|
taskId: string;
|
|
253
|
+
/**
|
|
254
|
+
* 子代的**类型**(CC `SubagentStart.agent_type` 那一位)。0.75.1 起按**诚实来源**取,三态:
|
|
255
|
+
* ① wire `task_progress` 上的结构读类型键 —— 亲核 sdk 9.8.1 `events.d.ts` 的 `task_progress` 臂:
|
|
256
|
+
* **没有** `agentType` / `spawnAgentType` 这一位(也不在 `FleetTaskRow` 之外的任何 tick 面上),
|
|
257
|
+
* 所以本档今天恒不命中;上游哪天铸了这一位,本档先于 ② 生效;
|
|
258
|
+
* ② fleet 行帧的 `agentType`(sdk `FleetTaskRow.agentType`,§K-7 的短类型 label,如 `Explore`);
|
|
259
|
+
* ③ 两者都没有 ⇒ **键缺席**。
|
|
260
|
+
* 🔴 缺席 = 不知道这只子代是什么类型,**不是**「无类型」—— 宿主的 hook 侧要有 absent 兜底(别渲空串、别编
|
|
261
|
+
* `subagent`)。0.75.1 之前本键取的是 tick 的**展示名** `name`(上游由委派卡 description 派生)= 一个
|
|
262
|
+
* 看起来合理的错答案:子代启动 hook 收到的于是是那段描述文本。
|
|
263
|
+
*/
|
|
253
264
|
agentType?: string;
|
|
254
265
|
guard?: 'if-started';
|
|
255
266
|
}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
| 项 | 值 | 真源 |
|
|
21
21
|
|---|---|---|
|
|
22
|
-
| 本包 | `@sema-agent/client-core` **0.75.
|
|
22
|
+
| 本包 | `@sema-agent/client-core` **0.75.1**(本批发布版 = **patch**:面板短命子代反序缓行放不出的残洞(外部复验 D1)+ 同代 running 帧三向定形(B4)+ #969 提货 `spawnName` 透传与 `agentType` 改读诚实来源(CC-95);§75。上一版 0.75.0 = **minor**:压缩的非 compacted 结局上 chrome 面 `compaction_outcome`(新内部臂 —— 自建 print 出口的端先表态)+ peer sdk 地板 `>=9.8.1`;型面 additive(ChromeEvent +1 臂)、公面 1065、零 wire 新键;§74。上一版 0.74.5 = **patch**:core 7.24.0 提货 `_sema_freed_tokens?` + 首见即终态的行零事件(幽灵完成行)+ `isNewEngineAgentPanelCycle` / `approvalOutcomeNoteOf` 上公面;型面 additive、公面 1065、peer 不动;§73。上一版 0.74.4 = **patch**:面板键「先发布的键为准」(tick 归不了一的三序不再两行)+ 前代 UUID 迟到 tick 丢弃 + `readRunCancelContext` 下钻收紧;型面 additive、公面 1061、peer 不动;§72。上一版 0.74.3 = **patch**:面板子代身份归一(tick / end 的 UUID 归到 fleet 尾段,宿主零改)+ run 记录 `cancelContext` 读口与成因四词 + workflow 行 `errorCode` + reopen 第二臂两件;型面纯 additive、公面 1061、peer 不动;§71。上一版 0.74.2 = **patch**:`capabilities.deviceExecutor.management` 四态读口(缺席 ≠ false;`deviceExecutor: false` = 车道缺席;管理动词可用性三态归包);公面 1052;§70。上一版 0.74.1 = **patch**:审批卡无决断收口 + 结局回交;§69。上一版 0.74.0 = **minor**:peer sdk 地板 >=9.7.1 + covers 过境 + 面板周期身份 + 离场读口;§68。上一版 0.73.5 = **patch**:workflow 规模告警判定上收进包(`evaluateWorkflowSize` 三态:未知的规模不当作规模正常;分母口径与帽优先级单源;env 读口纯函数);型面纯 additive、公面 1044、peer 不动;§67。上一版 0.73.4 = **patch**:终帧 `permission_denials` 第三键 ⇐ 同一条流 `tool_start` 入参按 `toolCallId` join(三道闸;join 不上留 sema 载体;🔴 更正 0.73.2「自动升级收口」一句)+ 审批 feed push 侧取件失败的欠账退避重试;型面纯 additive、公面 1033、peer 不动;§66。上一版 0.73.3 = **patch**:CC-61 sdk 9.7.1 提货 + `capabilities.approvalsStreamLive` 四态读口(缺席 = 判不出;`livePendingNeedsReconcile` 只在引擎明说会推时才让对账节拍降兜底)+ CC-60 五条模型面文案锚包侧半场(cursorSemantics 机读位 / result 行注记 / bash 首行两变体与良性槽 / 通知 seed 并集 / parked 臂);型面纯 additive、公面 1032、peer 不动;§65。上一版 0.73.2 = **patch**:CC-59 悬挂 ask 全卡 —— `livePending[].frame`(server ≥7.87.0)两道闸窄读进视图,在场即走流内帧腿同一条路(入参可见、改写可转发),缺席保持盲卡;CC-34 收口(引擎半场随 7.87.0 到货,判据交黑盒);型面纯 additive、公面 1026、peer 不动;§64。上一版 0.73.1 = **patch**:CC-57 `decidePlanReview` outcome 尾句按「决断有没有生效」四态选(单铸点 `planReviewOutcomeTail`;此前本地拒发仍让模型汇报已拒绝)+ CC-56 cancel 的 409 按机器码分家(`cancelConflictFromError` 三形 / 新安全码 `cancel_run_not_running`)与提交面 429 用量窗读口 + 人话;型面纯 additive、公面 1026、peer 不动;§63。上一版 0.73.0 = **minor,型面 BREAKING**:fleet 视图八个数值键改可选 —— CC-52 `FleetTaskView.elapsedMs` / `tokens` / `queuedCount` 与 `FleetWorkflowView.doneCount` / `totalCount` / `failedCount` / `tokens` / `elapsedMs` 不再 `?? 0`(有键 = 真值含真 0,缺键 = 不知道;逐键缺席语义见 §62b)+ 台账补偿链无基数不铸;公面导出名零增减 1019、零 wire 变化、peer 不动;§62。上一版 0.72.14 = **patch**:悬挂的流内 ask 拉半场 —— CC-55 `startApprovalsFeed` 快照加 `livePending` 段并进变化摘要(键缺席 = 未报,不铸空)+ 视图 / 去重账 / 与流内帧腿同链的决断口(入参不可见上卡明说、改写不转发)+ 宿主闸对账节拍 + 待决计数读口(不知道 = null);型面纯 additive、公面 1019、peer 不动;§61。上一版 0.72.13 = **patch**:server 7.86.0 提货 —— CC-46 plan_review `permissionModeAfter` 发口(三选卡双闸:宿主声明只读起步 ∧ 引擎 ≥7.86.0;400 field_conflict 两种处置)+ CC-54 `capabilities.executionLane` 四态读面(键缺席 ≠ 不是本机车道;`toolsRunHere` 判据归包)+ CC-53 detach 版本闸三态;型面 additive(另撤回 0.72.12 的主通道 `absent` 臂,改独立缺席订阅口)、公面 1011、peer 不动;§60;**预告 0.73.0 BREAKING**:fleet 视图数值键改可选。上一版 0.72.12 = **patch**:「缺席被折成正向终态」按类清剿 —— CC-50 M1 头条(fleet 行缺席改发 `absent` 不再铸 `end{isError:false}`)+ 六站一次改齐(缺席 ⇒ `unknown`)+ 结果边界 fail-closed 取舍 + `_sema_outcome` + `Outcome unknown:` 行 + CC-51 M6(default 臂不再谎报 running)+ 普查门 + DS-06;型面 additive、公面 993、peer 不动;行为面三处真变化见 CHANGELOG 取舍段;§59。上一版 0.72.11 = **patch**:core 7.23.0 提货 —— CC-49 通告码 66 → 67 `config.write_protection_unresolved`(operator;兄弟码 `target_view_absent` = 没有视图,这枚 = 有视图解析不出)+ `permission_denials` 自动升级腿通电(`gates[].toolCallId` ⇒ `tool_use_id`;CC-34 引擎半场);型面零变化、公面 989、peer 不动、devDep core ~7.23.0;§58。上一版 0.72.10 = **patch**:CC-41 leader run 终局 `needs_human` + `result.conflict` 三形读面(none 缺席不可分 / unreadable 在场读不懂 / conflict;`salvaged[]` 原样给落盘口;措辞单源不提重试);型面纯 additive、公面 989、peer 不动;§57。上一版 0.72.9 = **patch**:server 7.85.0 提货 —— CC-30 会话内 MCP re-dial 消费口(`reconnectMcpServer` 三态 / unsupported 恒五键 / 交易文案单源)+ CC-39② memory-status `autoConsolidationArmed` 三态 + CC-45 resume 补充句渲保留秒数;型面纯 additive、公面 985、peer 不动;§56。上一版 0.72.8 = **patch**:清账三票 —— `memory:"off"` 请求键(CC-39③,server 7.84.0 S-405)+ `runStream` 第二条中性臂 `Model output error:`(CC-43)+ 注入件撞活 run 自动 steer 零卡(CC-44);型面纯 additive、公面 977、peer 不动;§55。上一版 0.72.7 = **patch**:core 7.22.0 提货 —— 通告码 66 `mcp.server_redialed` + 读器 `readMcpServerRedialed` + `wiring_manifest.readDeny` 第九段 `_sema_readDeny`(缺席 = 没有内置行判这条腿);型面纯 additive、公面 971、peer 不动、devDep core ~7.22.0;§54。上一版 0.72.6 = **patch**:cli L-379 包侧半场 —— `steerFollowIntent` steer 后续听意图三态(tail / after-decision / after-wake)+ `activeRunSelfHealRow` 续听声明第五参;型面纯 additive、公面 969、peer 不动;§53。上一版 0.72.5 = **patch**:server 7.82.1 提货 —— `capabilities.webSearch.backend` 部署默认 WebSearch 后端四态读面(键缺席 = 老引擎判不了 ≠ `none`);型面纯 additive、公面 968、peer 不动;§52。上一版 0.72.4 = **patch**:core 7.21.x 三件经 server 7.82.0 提货 —— `wiring_manifest.autoConsolidation` 第八段 `_sema_autoConsolidation`(缺席 = indeterminate 不折 false);型面纯 additive、公面 962、peer 不动;§51。上一版 0.72.3 = **patch**:core #863 记忆单系统半场 ① —— `projectAgentsForWire` 投影 `agents[].memory`(user|project|local 三词同映射引擎平面 agent:<type>);型面纯 additive、公面 962、零 wire 新键、peer 不动;§50。上一版 0.72.2 = **patch**:workflow 视图两票 —— `LiveWorkflowController.resolution()` 六态(snapshot() 的 null 三义拆开)+ `noteOwnWorkflowRun` own 台账同拍登记;型面纯 additive、公面 961、零 wire 键、peer 不动;§49。上一版 0.72.1 = **patch**:server 7.80.1 提货 —— 409 `conflict.resume_context_unavailable` 结构读数(`staleAfterSec` / `runId`;sdk 9.6.0 尚未带键,经 sdk 今天恒缺席)+ `SkillSpec.baseDir`(只在本机车道带);型面纯 additive、公面 960、peer 不动;§48。上一版 0.72.0 = **minor**:型面 BREAKING —— 39 个零消费导出退出公面(CC-32 退役批,[7419] §45 预告 / cli [7422] 同意 / web-client·desktop 过节拍沉默 = 同意;名单见 §47a S-1);运行期零 BREAKING、公面 956、peer 不动、devDep core ~7.20.1;随批:已退役名账 `removed` + export-liveness 门 G 段 + 冻结账门 ⑧c;§47。上一版 0.71.4 = **patch**:core 7.20.1 提货 —— `ENGINE_NOTICE_CODES` 65 + `wiring_manifest.writeProtection` 第七段 `_sema_writeProtection`;型面纯 additive、公面 995、peer 不动、devDep core `~7.20.1`;§46。上一版 0.71.3 = **patch**:门体系清账批 —— CC-24 export-liveness 门 + 39 个导出退役预告(0.72.0 删,表态制)/ CC-26 `as unknown as` 铸链棘轮 / CC-25 冻结账门 ⑧ 段 / CC-06 B-081 包侧半场(终帧 `_sema_effective_reasoning` / `_sema_effective_memory_scopes` + resume reopen 族 + `routePairingVerdict`);型面纯 additive、公面 986 → 995、peer 不动;§45。上一版 0.71.2 = **patch**:接入方验收两票归包 —— CC-28 B-132 decide 拒体三句去内部车道词 / CC-29 B-133 committedUuids 唯一性(重复 ⇒ 形 f);零型面变化、公面 986、peer 不动;§44。上一版 0.71.1 = **patch**:接入档打回修正 —— CC-27 `ApprovalCardRequest.readRootCandidate?` 帧→卡透传位 + 新导出型 `ReadRootCandidateView`;型面纯 additive、公面 986 不变、peer 不动;§43。上一版 0.71.0 = **minor**:型面 BREAKING 六件 —— peer sdk 地板 >=9.6.0 + agent-types >=0.3.0 / `SkillsListingTruncatedFactsView` 删 `requestedBudgetBytes?`·`laneClamped?` / `WiringManifestMcpEntry` → `WiringManifestMcpEntryView` / `MAX_AGENT_SKILLS` 删 / `decide.workflow_host_not_parked` 退役;运行期零 BREAKING。内容:core 7.20.0 提货(闭集 +5 六十四码)/ 🔴 CC-16 `text_segment_end.committedUuids[]` / CC-08 全五件(tool_disclosure / tool_progress 两 chrome 臂、wiring_manifest tools·hooks·lsp 进切片、tool_roster_delta 接上、context_usage 进切片、`readRootCandidate` 镜像)/ CC-19 `mcpEngineLegPresence` 归包 / CC-20 not_parked clean-cut;公面 986;§42 / 42y 八条)|
|
|
23
23
|
| peer:wire 契约 | `@sema-agent/sdk` **>=9.8.1**(value-level,非 type-only;**0.75.0 抬版**:9.8.x 声明 `Capabilities.deviceExecutor.management?`,9.7.1 失去物料见证;**0.74.0 曾抬到 >=9.7.1**:9.7.x 追平的六处型面是本包 0.73.3 起的编译基线,且 9.6.0 失去物料见证;**0.71.0 曾抬到 >=9.6.0**:`tool_disclosure` / `tool_progress` 两帧型与 `ToolApprovalFrame.readRootCandidate` 自 9.6.0 起;0.69.0 曾抬到 >=9.4.0(`reasoning_end` / `lastLegMcp`),0.60.0 抬到 >=8.4.0;地板由 `scripts/run-sdk-floor-test.mjs` 的 `FLOOR` 与 public-surface 门 C-CC-3「不低于见证到的最低实装版」双钉)| `package.json` `peerDependencies` |
|
|
24
24
|
| peer:会话词汇表 | `@sema-agent/agent-types` **>=0.3.0**(type-only,零运行时;0.71.0 抬版:devDep 与 peer 同步,type-superset 门 ④ 撞名预警对着 0.3.0 的 .d.ts 求值) | 同上 |
|
|
25
25
|
| runtime dep | `diff` ^9.0.0(**唯一**一条;portability 门按**等值**钉死) | `package.json` `dependencies` |
|
|
@@ -9306,3 +9306,51 @@ installCoreValuePorts({ resolveAutonomousLoopPrompt, loosenReasons, combinePolic
|
|
|
9306
9306
|
3. 面板:结过的行再收到 `fleet-row` = 用量到了,更新用量、不改状态、不重开行(cli 现行即如此);其余零变化:转录面零新增行;device 读器四态读数不变。
|
|
9307
9307
|
|
|
9308
9308
|
**包侧缺口:** ① CC-74 mcpProbe 候 server S-481;② K-04 `FleetWorkflowRow.errorCode?` 候 sdk 9.9.0(窗 10-03);③ RH-2 第二步候排序;④ CC-36 候 core #874;⑤ 后台子代的压缩结局无 lane 可挂(KL-22);其余同 §73。
|
|
9309
|
+
|
|
9310
|
+
## §75 🆕 0.75.1 面板反序缓行残洞(外部复验 D1)+ 缓行遇同代 running 三向定形(B4)+ #969 提货 spawnName / agentType(CC-95);patch
|
|
9311
|
+
|
|
9312
|
+
> **patch**:型面 additive(`panel_task` tick 臂 +1 可选位 `spawnName?`),公面**值导出零增**;行为面三笔(下表 S-1 / S-2 / S-4)。票来源:外部验收方对 0.75.0 的复验读数(D1 / B4)+ cli 直讯 #969 提货两件(CC-95;core 09-20 确认消费面不变)。
|
|
9313
|
+
|
|
9314
|
+
### 75a. 处置表
|
|
9315
|
+
|
|
9316
|
+
| # | 面 | 本包做了什么 | 端要做什么 |
|
|
9317
|
+
|---|---|---|---|
|
|
9318
|
+
| S-1 🔴 | 面板:反序缓行在一种到达序下**永远**放不出(短命子代最终用量丢) | **fixed**:`end` 归一加**第四条腿** —— 按缓行**自带的** wire 侧键(`transcriptId` 主 / 委派卡 id 退路)反查;放行走「代际三向判 → 同键核 → 经原漏斗归一」,整条腿永不 `return`(原 wire 键那一行自己的 `end` 不被吞) | 零改;消费端对已 `ended` 的行收到 `fleet-row` 仍是「更新用量、不改状态、不重开行」 |
|
|
9319
|
+
| S-2 🔴 | 面板:缓着终态行时后到的 `running` 帧被**无条件**当复活 ⇒ 最终用量被陈旧快照顶掉 | **fixed**:三向 —— 可证同代 = 陈旧快照(零事件、缓行留着)/ 可证更新 = 复活(作废缓行)/ 判不出 = 维持 0.75.0(KL-26) | 零改 |
|
|
9320
|
+
| S-3 | `panel_task` tick 臂 **+`spawnName?`**(additive) | **added**:结构读 wire `task_progress.spawnName`,非空串才带 | 想渲 spawn 名的端读这一位;**缺席 = 旧形的产生者**,渲占位归端,别拿 `name` 顶 |
|
|
9321
|
+
| S-4 🔴 | `subagent_lifecycle.agentType` 改**诚实来源**(此前取 tick 展示名 `name` = 上游由委派卡 `description` 派生 ⇒ 宿主 hook 收到的是任务描述) | **fixed**:三档 —— tick 结构读类型键(sdk 9.8.1 亲核:`task_progress` 上**没有**这一位,今天恒不命中)→ fleet 行帧 `agentType`(在被折进 `name` 之前留一份)→ 都没有 ⇒ **键缺席** | 🔴 **hook 侧加 absent 兜底**:`agentType` 从此可能不在场(以前恒在,但值是错的);缺席别渲空串、别编占位类型词 |
|
|
9322
|
+
|
|
9323
|
+
### 75b. 缺席语义(逐键)
|
|
9324
|
+
|
|
9325
|
+
- tick `.spawnName` **缺席** = 产生者还没有这一位(旧 core / 旧 server),**不是**「没有名字」;在场时非空串,空串 / 非串按缺席。与 `name` 是两位(展示名 vs 声明名),两位都缺席 ⇒ 消费端只有 `taskId`。
|
|
9326
|
+
- `subagent_lifecycle.agentType` **缺席** = 不知道这只子代是什么**类型**,不是「无类型」。判缺席一律 `=== undefined`(本包对缺席是**整键省略**)。
|
|
9327
|
+
- fleet 行帧 `agentType` 空串 / 非串 ⇒ 不入账;同一行后续帧**缺席**这一位 ⇒ 账上已知值**不擦**(「这帧没说」≠「类型没了」)。
|
|
9328
|
+
- 缓行反查的两把键都要求**非空串**才算数;委派卡那把还要求那把 wire 键**未退役**(与「先发布的键为准」的退路键同律)。
|
|
9329
|
+
- 🔴 **多候选不放**:缓行反查命中多于一条可放行候选、或候选放行后会与那条 `end` 分流到两把键时,本包**一条都不放**(宁可晚交用量,不让一只子代分成两行)—— 那几条行的用量缺席,与 0.75.0 同。
|
|
9330
|
+
|
|
9331
|
+
### 75c. 黑盒判据骨架
|
|
9332
|
+
|
|
9333
|
+
- **G1(D1 主判据)**:子代在两拍之间起止、tick 先按 wire id 上过屏、行帧车道随后给终态行(带最终用量)、通知车道 `end` 最后到 ⇒ 那一行 `state:ended` 且**两位用量在场**,`end` **恰一次**,全部事件**同一把 taskId**。0.75.0 红基线:一条空的 ended 行。
|
|
9334
|
+
- **G2(退路键腿)**:同上但终态行只带与 tick 同一张委派卡的父调用 id(不带 `transcriptId`)⇒ 读数与 G1 逐位同。
|
|
9335
|
+
- **G3(不张冠李戴)**:另一只子代的 `end` 到达 ⇒ 前一只的终态行**不**被放出(面板不多一行)。
|
|
9336
|
+
- **G4(B4 三向)**:缓着终态行(42/3,`cycleSeq:1`)后来同代 `running`(5/1)⇒ 最终显示 **42/3**;换 `cycleSeq:2` ⇒ 按复活走(5/1,前代终态不回来);两侧都不带周期键 ⇒ 与 0.75.0 逐位同(KL-26)。
|
|
9337
|
+
- **G5(CC-95 a)**:引擎发带 `spawnName` 的 `task_progress` ⇒ tick 事件带同值;旧引擎 ⇒ `'spawnName' in ev === false`。
|
|
9338
|
+
- **G6(CC-95 b)**:后台子代 fleet 行帧报 `agentType:"Explore"` ⇒ hook 收到的类型是 `Explore`;行帧不报(或同步委派子代根本没有 fleet 行)⇒ hook 收到的**类型缺席**。0.75.0 红基线:两种情形都收到 `description`。
|
|
9339
|
+
|
|
9340
|
+
### 75z. 逐键处置表
|
|
9341
|
+
|
|
9342
|
+
| # | 键 / 面 | 处置 | 坐标 / 理由 |
|
|
9343
|
+
|---|---|---|---|
|
|
9344
|
+
| 1 | 缓行反查(`transcriptId` / 委派卡 id) | **fixed** | `src/panelRunningHistory.ts` 的缓行引用读口(遍历缓行表读它自带的键,**刻意不建第六本索引**)+ `src/engineAgentPanelStore.ts` `case 'end'` 反查段 |
|
|
9345
|
+
| 2 | 同代 running 帧 | **fixed** | `src/fleetAgentPanelProjection.ts` 陈旧快照判据;族扫同批改齐 `src/fleet/fleetProjection.ts` / `src/agentSession/backgroundView.ts` 的 `startedAt` 形核 |
|
|
9346
|
+
| 3 | `task_progress.spawnName` | **consumed**(结构读) | tick 投影臂 + tick 事件臂 additive 键 |
|
|
9347
|
+
| 4 | fleet 行 `agentType` | **consumed**(折进展示名之前留一份) | 新包内叶 `src/fleet/fleetRowAgentType.ts` |
|
|
9348
|
+
| 5 | tick 上的类型键 | **declined**(无出处) | sdk 9.8.1 `task_progress` 臂无此位 ⇒ 不读无出处的键;上游铸位后再提 |
|
|
9349
|
+
|
|
9350
|
+
### 75y. 🔴 三端升级必读(固定段式,[C295])
|
|
9351
|
+
|
|
9352
|
+
1. **hook 侧加 absent 兜底**:`subagent_lifecycle.agentType` 从本版起**可能缺席**(以前恒在,但值是委派卡的 `description`)。缺席时别渲空串、别编占位类型词,按各端契约省略该字段或填 `null`。**这是本版唯一需要端配合的一笔。**
|
|
9353
|
+
2. **面板零改**:两件面板修复对消费端零接口变化。可观测差别只有两个:短命子代那一行的用量**不再为空**;也不再被陈旧的 running 快照顶成小数字。
|
|
9354
|
+
3. **`spawnName` 想渲才接**(`required:false`):不接 = 与 0.75.0 逐字同;接的话按「缺席 = 旧形」渲占位,**绝不**用展示名顶替。
|
|
9355
|
+
|
|
9356
|
+
**包侧缺口:** ① 判不出代际的那一向仍丢用量(KL-26);①b 多候选 / 分流两把键时一条都不放 ⇒ 那几条行用量仍缺席;② tick lane 上**没有**类型键 —— 同步委派子代(无 fleet 行)的 `agentType` 今天恒缺席,补它要上游在 `task_progress` 铸一位(提货单待发);③ 其余同 §74(CC-74 候 server S-481 / K-04 候 sdk 9.9.0 / RH-2 第二步 / CC-36 候 core #874 @ 7.24.3 / KL-22)。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/client-core",
|
|
3
|
-
"version": "0.75.
|
|
3
|
+
"version": "0.75.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",
|