@sema-agent/client-core 0.12.2 → 0.14.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/README.md +15 -2
- package/dist/abortableSleep.d.ts +29 -0
- package/dist/abortableSleep.js +43 -0
- package/dist/adapt/arms.d.ts +67 -0
- package/dist/adapt/arms.js +623 -0
- package/dist/adapt/ids.d.ts +22 -0
- package/dist/adapt/ids.js +34 -0
- package/dist/adapt/instanceLedger.d.ts +36 -0
- package/dist/adapt/instanceLedger.js +50 -0
- package/dist/adapt/panelTasks.d.ts +59 -0
- package/dist/adapt/panelTasks.js +193 -0
- package/dist/adapt/textStream.d.ts +63 -0
- package/dist/adapt/textStream.js +141 -0
- package/dist/adapt/toolCards.d.ts +65 -0
- package/dist/adapt/toolCards.js +100 -0
- package/dist/adapt/turnFlags.d.ts +37 -0
- package/dist/adapt/turnFlags.js +55 -0
- package/dist/adapt/wireShapes.d.ts +96 -0
- package/dist/adapt/wireShapes.js +167 -0
- package/dist/adapt.d.ts +33 -60
- package/dist/adapt.js +84 -1207
- package/dist/adapter/downstream/eventToSdkMessage.d.ts +57 -13
- package/dist/adapter/downstream/eventToSdkMessage.js +174 -106
- package/dist/adapter/downstream/terminalToSdkResult.js +151 -160
- package/dist/adapter/downstream/turnUsageToModelUsage.d.ts +36 -0
- package/dist/adapter/downstream/turnUsageToModelUsage.js +34 -6
- package/dist/adapter/runStream.d.ts +36 -4
- package/dist/adapter/runStream.js +195 -13
- package/dist/adapter/types.d.ts +28 -2
- package/dist/agentSession/backgroundView.js +4 -15
- package/dist/agentsWireCaps.d.ts +10 -6
- package/dist/agentsWireCaps.js +21 -7
- package/dist/argvFlagValue.d.ts +41 -0
- package/dist/argvFlagValue.js +69 -0
- package/dist/attachmentsWireCaps.d.ts +3 -2
- package/dist/attachmentsWireCaps.js +5 -3
- package/dist/classifierVerdictWire.d.ts +8 -0
- package/dist/classifierVerdictWire.js +8 -0
- package/dist/cloudConfigWireCaps.d.ts +28 -1
- package/dist/cloudConfigWireCaps.js +51 -11
- package/dist/controlRouter.d.ts +14 -8
- package/dist/controlRouter.js +15 -21
- package/dist/detachWire.d.ts +15 -5
- package/dist/detachWire.js +17 -7
- package/dist/effortWire.d.ts +0 -21
- package/dist/effortWire.js +6 -20
- package/dist/engineInlineTaskStats.d.ts +12 -6
- package/dist/engineWireSdk.d.ts +12 -0
- package/dist/env/localeGeo.js +2 -1
- package/dist/envFlag.d.ts +39 -0
- package/dist/envFlag.js +51 -0
- package/dist/finalVerifyWire.d.ts +7 -5
- package/dist/finalVerifyWire.js +6 -5
- package/dist/fleet/fleetLedger.d.ts +32 -9
- package/dist/fleet/fleetLedger.js +119 -34
- package/dist/fleet/fleetProjection.d.ts +44 -6
- package/dist/fleet/fleetProjection.js +52 -10
- package/dist/fleetAgentPanelProjection.d.ts +18 -1
- package/dist/fleetAgentPanelProjection.js +61 -14
- package/dist/forkWireCaps.d.ts +2 -1
- package/dist/forkWireCaps.js +5 -11
- package/dist/goalStopHook.d.ts +142 -0
- package/dist/goalStopHook.js +258 -0
- package/dist/headlessPermissionModeWire.d.ts +10 -0
- package/dist/headlessPermissionModeWire.js +24 -21
- package/dist/headlessReconnectWire.d.ts +7 -1
- package/dist/headlessReconnectWire.js +20 -2
- package/dist/hitl/approvalsFeed.d.ts +22 -1
- package/dist/hitl/approvalsFeed.js +103 -9
- package/dist/hitl/askGateWire.d.ts +27 -96
- package/dist/hitl/askGateWire.js +69 -618
- package/dist/hitl/frameRouter.d.ts +86 -0
- package/dist/hitl/frameRouter.js +383 -0
- package/dist/hitl/gateLedger.d.ts +119 -0
- package/dist/hitl/gateLedger.js +113 -0
- package/dist/hitl/hitlBridge.d.ts +75 -15
- package/dist/hitl/hitlBridge.js +94 -22
- package/dist/hitl/hitlHostSurface.d.ts +49 -0
- package/dist/hitl/hitlHostSurface.js +155 -0
- package/dist/hitl/parkResolver.d.ts +74 -0
- package/dist/hitl/parkResolver.js +250 -0
- package/dist/hitl/planReviewWire.d.ts +60 -2
- package/dist/hitl/planReviewWire.js +197 -91
- package/dist/hitl/toolApprovalWire.d.ts +67 -4
- package/dist/hitl/toolApprovalWire.js +129 -31
- package/dist/hooksWireCaps.d.ts +1 -82
- package/dist/hooksWireCaps.js +44 -237
- package/dist/host.d.ts +16 -5
- package/dist/index.d.ts +2 -0
- package/dist/index.js +15 -1
- package/dist/interactiveToolsWire.d.ts +15 -4
- package/dist/interactiveToolsWire.js +24 -26
- package/dist/limitsWire.js +7 -40
- package/dist/liveInitToolFace.d.ts +51 -6
- package/dist/liveQuestionStore.d.ts +18 -6
- package/dist/liveQuestionStore.js +15 -0
- package/dist/model/providerPresets.js +11 -1
- package/dist/notifications.d.ts +62 -2
- package/dist/notifications.js +306 -49
- package/dist/printToolResultFrame.d.ts +12 -4
- package/dist/printToolResultFrame.js +11 -21
- package/dist/retainBackgroundWireCaps.d.ts +3 -2
- package/dist/retainBackgroundWireCaps.js +5 -9
- package/dist/sandboxWire.d.ts +9 -31
- package/dist/sandboxWire.js +51 -50
- package/dist/scenarioWire.d.ts +1 -1
- package/dist/scenarioWire.js +25 -36
- package/dist/seam.d.ts +23 -6
- package/dist/seam.js +40 -30
- package/dist/seatContract.d.ts +369 -83
- package/dist/seatContract.js +585 -198
- package/dist/selfOrchestrationWireCaps.d.ts +6 -5
- package/dist/selfOrchestrationWireCaps.js +8 -12
- package/dist/sessionSlot.d.ts +8 -0
- package/dist/sessionSlot.js +1 -0
- package/dist/steering.js +2 -2
- package/dist/subagent/engineTaskHandleWire.d.ts +3 -0
- package/dist/subagent/engineTaskHandleWire.js +17 -2
- package/dist/subagentContentStore.d.ts +5 -5
- package/dist/toolResult.d.ts +89 -8
- package/dist/toolResult.js +99 -30
- package/dist/typePins.d.ts +17 -0
- package/dist/typePins.js +1 -0
- package/dist/ultracodeWireCaps.js +5 -6
- package/dist/unrefTimer.d.ts +30 -0
- package/dist/unrefTimer.js +5 -0
- package/dist/workflow.d.ts +3 -2
- package/dist/workflow.js +3 -2
- package/dist/workflowClient.d.ts +7 -0
- package/dist/workflowClient.js +47 -12
- package/package.json +3 -3
|
@@ -34,10 +34,12 @@
|
|
|
34
34
|
* 关流封装),壳的 `engineBgProbe.ts:8-9` 自述「引擎 fleet 面只有 SSE,没有一次性口」已过期。
|
|
35
35
|
* 本包出 `fleetSnapshotOptions()`;`engineBgProbe` 的手抄开流即断由壳侧换 verb(见交接报告)。
|
|
36
36
|
*
|
|
37
|
-
* ── 单实例纪律
|
|
38
|
-
* `liveBgViews` 是 module 级 Set
|
|
39
|
-
* (`
|
|
40
|
-
*
|
|
37
|
+
* ── 单实例纪律(REF-CC-044,fleet2-06:per-sessionKey 化,W1/design/161 同款原语)────────────────
|
|
38
|
+
* `liveBgViews` 是 module 级 `Map<sessionKey, Set<LiveBgView>>`:写者 = 每个 `createFleetLedger()`
|
|
39
|
+
* (登记进自己的 `opts.sessionKey`,零参 = `DEFAULT_SESSION_KEY`),读者 = 引擎温切门
|
|
40
|
+
* (`readEngineActiveBgTasks[For]`)与代际让位读面(`isFleetSessionScoped[For]`)。同一 sessionKey
|
|
41
|
+
* 两份实例 = 温切门恒读空 ⇒ respawn 会静默杀掉在飞 bg 子代;per-session 化同时堵住了跨会话污染
|
|
42
|
+
* (A 会话的 `clearAllRetainedFleetRows` 不该清掉 B 会话的池,B 断线也不该把 A 标 fresh)。
|
|
41
43
|
*/
|
|
42
44
|
import type { FleetFrame, FleetTaskRow } from '@sema-agent/sdk';
|
|
43
45
|
import { type FleetTaskView, type FleetWorkflowView, type ProjectTasksOptions } from './fleetProjection.js';
|
|
@@ -63,14 +65,20 @@ export interface EngineActiveBgReading {
|
|
|
63
65
|
* 仍广播,fail-closed 是 1.245+)。
|
|
64
66
|
*/
|
|
65
67
|
export declare function isFleetSessionScoped(): boolean;
|
|
68
|
+
/** W1 keyed variant:只看**这一个会话**登记的视图(REF-CC-044)。 */
|
|
69
|
+
export declare function isFleetSessionScopedFor(sessionKey: string): boolean;
|
|
66
70
|
/** 引擎侧在飞 bg 子代读数(union 所有未 dispose 的源,按 row.id 去重)。无 live 源 ⇒ `{[], false}`。 */
|
|
67
71
|
export declare function readEngineActiveBgTasks(): EngineActiveBgReading;
|
|
72
|
+
/** W1 keyed variant(REF-CC-044):只 union **这一个会话**登记的视图,不跨会话泄漏。 */
|
|
73
|
+
export declare function readEngineActiveBgTasksFor(sessionKey: string): EngineActiveBgReading;
|
|
68
74
|
/**
|
|
69
|
-
*
|
|
70
|
-
* R7:留存池是渲染层宽限窗,快照臂刻意不清它(重连不该抽走行)
|
|
71
|
-
*
|
|
75
|
+
* 清空**这一个会话**的所有 live 源的终态留存池 —— 会话切换(/clear、REPL 内 resume、fork)信号
|
|
76
|
+
* 一到就调。R7:留存池是渲染层宽限窗,快照臂刻意不清它(重连不该抽走行);但换了会话继续渲上一
|
|
77
|
+
* 会话的终态行(名字 = objective 派生)就是跨会话残影。
|
|
72
78
|
*/
|
|
73
79
|
export declare function clearAllRetainedFleetRows(): void;
|
|
80
|
+
/** W1 keyed variant(REF-CC-044):只清**这一个会话**的池,不广播到别的会话。 */
|
|
81
|
+
export declare function clearAllRetainedFleetRowsFor(sessionKey: string): void;
|
|
74
82
|
/** 测试钩:摘掉所有登记的视图(同进程多组断言之间互不污染)。 */
|
|
75
83
|
export declare function __resetFleetLedgerRegistryForTests(): void;
|
|
76
84
|
/**
|
|
@@ -87,12 +95,25 @@ export declare function fleetSnapshotOptions(opts?: {
|
|
|
87
95
|
session?: string;
|
|
88
96
|
timeoutMs?: number;
|
|
89
97
|
};
|
|
98
|
+
/** REF-CC-047(fleet2-09):hook_notice 具名臂(B2 禁 unknown 出公开面;Extract 同款手法已在
|
|
99
|
+
* 文件内的 `BgNotificationWire` 示范过)。 */
|
|
100
|
+
export type HookNoticeFrame = Extract<FleetFrame, {
|
|
101
|
+
type: 'hook_notice';
|
|
102
|
+
}>;
|
|
90
103
|
export interface FleetLedgerHooks {
|
|
91
104
|
/**
|
|
92
105
|
* hook_notice 帧的**写 store** 半场(判定在 `classifyHookNoticeFrame`,本台账只做分发)。
|
|
93
106
|
* 返回值忽略;不给 = 该帧被静默丢弃(端没有承载面时的正确行为)。
|
|
94
107
|
*/
|
|
95
|
-
onHookNotice?(frame:
|
|
108
|
+
onHookNotice?(frame: HookNoticeFrame, sessionScoped: boolean): void;
|
|
109
|
+
}
|
|
110
|
+
/** REF-CC-044(fleet2-06)/REF-CC-039(fleet2-01):`createFleetLedger` 的可选装配位。 */
|
|
111
|
+
export interface CreateFleetLedgerOptions {
|
|
112
|
+
/** per-session 注册键(W1 同款;多会话宿主每会话一份,零参 = `DEFAULT_SESSION_KEY`)。 */
|
|
113
|
+
sessionKey?: string;
|
|
114
|
+
/** 本地接收时钟注入(测试用确定性时钟;默认 `Date.now`)。REF-CC-039:帧到达时刻的**单一铸点**,
|
|
115
|
+
* 绝不能用 wire 上的 server 时刻(`frame.ts`)顶替 —— 两个不同时钟域的算术是缺陷,不是特性。 */
|
|
116
|
+
nowFn?: () => number;
|
|
96
117
|
}
|
|
97
118
|
export interface FleetLedgerStatus {
|
|
98
119
|
connected: boolean;
|
|
@@ -102,6 +123,8 @@ export interface FleetLedgerStatus {
|
|
|
102
123
|
bgNotifyFailClosed: boolean;
|
|
103
124
|
taskCount: number;
|
|
104
125
|
workflowCount: number;
|
|
126
|
+
/** REF-CC-051(fleet2-13):畸形帧/通知早退的留痕计数(C5 静默丢数据零容忍)。 */
|
|
127
|
+
droppedMalformed: number;
|
|
105
128
|
}
|
|
106
129
|
export interface FleetLedger {
|
|
107
130
|
/** 消费一帧(consumer-side active-set update-in-place / remove)。 */
|
|
@@ -129,4 +152,4 @@ export interface FleetLedger {
|
|
|
129
152
|
* 建一个 fleet 帧台账。**不开流、不构造 client** —— 宿主的 consume 循环把解码后的帧喂进来。
|
|
130
153
|
* (这条切缝就是设计稿 §2.5 给 fleetClient 划的那一刀:帧体归库、连接归端。)
|
|
131
154
|
*/
|
|
132
|
-
export declare function createFleetLedger(hooks?: FleetLedgerHooks): FleetLedger;
|
|
155
|
+
export declare function createFleetLedger(hooks?: FleetLedgerHooks, opts?: CreateFleetLedgerOptions): FleetLedger;
|
|
@@ -7,10 +7,15 @@ import { enqueueBgChildNotification, isOwnWorkflowRun } from '../notifications.j
|
|
|
7
7
|
import { isOwnEngineRun, recordBgParentRun, recordBgTerminalFacts, recordOwnEngineRun, } from '../subagentContentStore.js';
|
|
8
8
|
import { rowIdTail } from '../workflow.js';
|
|
9
9
|
import { recordEngineTranscriptId } from '../subagent/engineDelegatedPrompt.js';
|
|
10
|
-
import {
|
|
10
|
+
import { DEFAULT_SESSION_KEY } from '../sessionSlot.js';
|
|
11
|
+
import { TERMINAL_FLEET_TASK_STATUSES, projectTasks, projectWorkflows, wireParentId, } from './fleetProjection.js';
|
|
11
12
|
/** 终态行留存宽限窗(187 的 agents 页把完成会话留在列表;窗过即净,防长会话堆行)。 */
|
|
12
13
|
export const TERMINAL_RETAIN_MS = 60_000;
|
|
13
|
-
|
|
14
|
+
/** REF-CC-044(fleet2-06):per-sessionKey 注册表(W1 同款 `Map<sessionKey, T>` 形)。 */
|
|
15
|
+
const liveBgViews = new Map();
|
|
16
|
+
function viewsFor(sessionKey) {
|
|
17
|
+
return liveBgViews.get(sessionKey) ?? new Set();
|
|
18
|
+
}
|
|
14
19
|
/**
|
|
15
20
|
* server 代际证据读面:任一 live fleet 源的 meta 报过 `sessionScoped=true`。
|
|
16
21
|
* true ⇒ 到达本连接的**行帧**已经过 server 会话过滤,端侧行级 own 判别可以让位。
|
|
@@ -18,7 +23,11 @@ const liveBgViews = new Set();
|
|
|
18
23
|
* 仍广播,fail-closed 是 1.245+)。
|
|
19
24
|
*/
|
|
20
25
|
export function isFleetSessionScoped() {
|
|
21
|
-
|
|
26
|
+
return isFleetSessionScopedFor(DEFAULT_SESSION_KEY);
|
|
27
|
+
}
|
|
28
|
+
/** W1 keyed variant:只看**这一个会话**登记的视图(REF-CC-044)。 */
|
|
29
|
+
export function isFleetSessionScopedFor(sessionKey) {
|
|
30
|
+
for (const view of viewsFor(sessionKey)) {
|
|
22
31
|
try {
|
|
23
32
|
if (view.sessionScoped() === true)
|
|
24
33
|
return true;
|
|
@@ -31,9 +40,13 @@ export function isFleetSessionScoped() {
|
|
|
31
40
|
}
|
|
32
41
|
/** 引擎侧在飞 bg 子代读数(union 所有未 dispose 的源,按 row.id 去重)。无 live 源 ⇒ `{[], false}`。 */
|
|
33
42
|
export function readEngineActiveBgTasks() {
|
|
43
|
+
return readEngineActiveBgTasksFor(DEFAULT_SESSION_KEY);
|
|
44
|
+
}
|
|
45
|
+
/** W1 keyed variant(REF-CC-044):只 union **这一个会话**登记的视图,不跨会话泄漏。 */
|
|
46
|
+
export function readEngineActiveBgTasksFor(sessionKey) {
|
|
34
47
|
const byId = new Map();
|
|
35
48
|
let fresh = false;
|
|
36
|
-
for (const view of
|
|
49
|
+
for (const view of viewsFor(sessionKey)) {
|
|
37
50
|
try {
|
|
38
51
|
const r = view.read();
|
|
39
52
|
if (r.connected)
|
|
@@ -48,12 +61,16 @@ export function readEngineActiveBgTasks() {
|
|
|
48
61
|
return { tasks: [...byId.values()], fresh };
|
|
49
62
|
}
|
|
50
63
|
/**
|
|
51
|
-
*
|
|
52
|
-
* R7:留存池是渲染层宽限窗,快照臂刻意不清它(重连不该抽走行)
|
|
53
|
-
*
|
|
64
|
+
* 清空**这一个会话**的所有 live 源的终态留存池 —— 会话切换(/clear、REPL 内 resume、fork)信号
|
|
65
|
+
* 一到就调。R7:留存池是渲染层宽限窗,快照臂刻意不清它(重连不该抽走行);但换了会话继续渲上一
|
|
66
|
+
* 会话的终态行(名字 = objective 派生)就是跨会话残影。
|
|
54
67
|
*/
|
|
55
68
|
export function clearAllRetainedFleetRows() {
|
|
56
|
-
|
|
69
|
+
clearAllRetainedFleetRowsFor(DEFAULT_SESSION_KEY);
|
|
70
|
+
}
|
|
71
|
+
/** W1 keyed variant(REF-CC-044):只清**这一个会话**的池,不广播到别的会话。 */
|
|
72
|
+
export function clearAllRetainedFleetRowsFor(sessionKey) {
|
|
73
|
+
for (const view of viewsFor(sessionKey)) {
|
|
57
74
|
try {
|
|
58
75
|
view.clearRetained();
|
|
59
76
|
}
|
|
@@ -86,13 +103,18 @@ export function fleetSnapshotOptions(opts) {
|
|
|
86
103
|
* 建一个 fleet 帧台账。**不开流、不构造 client** —— 宿主的 consume 循环把解码后的帧喂进来。
|
|
87
104
|
* (这条切缝就是设计稿 §2.5 给 fleetClient 划的那一刀:帧体归库、连接归端。)
|
|
88
105
|
*/
|
|
89
|
-
export function createFleetLedger(hooks = {}) {
|
|
106
|
+
export function createFleetLedger(hooks = {}, opts = {}) {
|
|
107
|
+
const sessionKey = opts.sessionKey ?? DEFAULT_SESSION_KEY;
|
|
108
|
+
const nowFn = opts.nowFn ?? Date.now;
|
|
90
109
|
const taskMap = new Map();
|
|
91
110
|
const wfMap = new Map();
|
|
92
|
-
/** [1481]①「elapsed 恒 0s
|
|
111
|
+
/** [1481]①「elapsed 恒 0s」补偿:记每行**本地接收**时刻(引擎只在活动边界重发行)。
|
|
112
|
+
* REF-CC-039:字段标域改名 `receivedAtMs` —— 装的是 `nowFn()`,绝不是 wire 上的 `frame.ts`。 */
|
|
93
113
|
const rowMeta = new Map();
|
|
94
|
-
/** [1481]
|
|
114
|
+
/** [1481]②「完成即消失」补偿:终态行进留存池,宽限窗内继续投影。`expireAt` 同样锚本地钟。 */
|
|
95
115
|
const retained = new Map();
|
|
116
|
+
/** REF-CC-051(fleet2-13):畸形帧/通知早退计数(与同文件的策略性丢弃 debug 行对称留痕)。 */
|
|
117
|
+
let droppedMalformed = 0;
|
|
96
118
|
let connected = false;
|
|
97
119
|
let version = null;
|
|
98
120
|
let scoped = null;
|
|
@@ -108,19 +130,35 @@ export function createFleetLedger(hooks = {}) {
|
|
|
108
130
|
connected,
|
|
109
131
|
rows: [...taskMap.values()]
|
|
110
132
|
.filter(r => !TERMINAL_FLEET_TASK_STATUSES.has(r.status ?? ''))
|
|
111
|
-
.map(r =>
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
133
|
+
.map(r => {
|
|
134
|
+
const parentId = wireParentId(r);
|
|
135
|
+
return {
|
|
136
|
+
id: r.id,
|
|
137
|
+
// REF-CC-046(fleet2-08,含 TYPESHAPE-06):与 parentId 同款条件展开 —— `name` 是三态位
|
|
138
|
+
// (server ≥3.5.1 起真的会缺席),无条件落键会让「键在不在」判三态在序列化边界翻面。
|
|
139
|
+
...(typeof r.name === 'string' && r.name.length > 0 ? { name: r.name } : {}),
|
|
140
|
+
// REF-CC-052(fleet2-14):`status` 是 SDK 必填闭集,但脏值/缺席仍可能到达 wire —— 不知道
|
|
141
|
+
// 就说不知道,`'unknown'` 是自描述哨兵,绝不编造一个具体的、看似活跃的 `'running'`。
|
|
142
|
+
status: r.status ?? 'unknown',
|
|
143
|
+
// REF-CC-049(fleet2-11):经 wireParentId 单一铸点(悬空父脏值空串按缺席处理)。eopt:
|
|
144
|
+
// 局部变量先取一次(两次裸调用之间 TS 不共享窄化,双调用会重新变宽成 string|undefined)。
|
|
145
|
+
...(parentId !== undefined ? { parentId } : {}),
|
|
146
|
+
};
|
|
147
|
+
}),
|
|
117
148
|
}),
|
|
118
149
|
sessionScoped: () => sessionScopedMeta,
|
|
119
150
|
clearRetained: () => {
|
|
120
151
|
retained.clear();
|
|
121
152
|
},
|
|
122
153
|
};
|
|
123
|
-
|
|
154
|
+
{
|
|
155
|
+
let views = liveBgViews.get(sessionKey);
|
|
156
|
+
if (!views) {
|
|
157
|
+
views = new Set();
|
|
158
|
+
liveBgViews.set(sessionKey, views);
|
|
159
|
+
}
|
|
160
|
+
views.add(bgView);
|
|
161
|
+
}
|
|
124
162
|
function applyFrame(frame) {
|
|
125
163
|
switch (frame.type) {
|
|
126
164
|
case 'meta': {
|
|
@@ -143,10 +181,12 @@ export function createFleetLedger(hooks = {}) {
|
|
|
143
181
|
taskMap.clear();
|
|
144
182
|
wfMap.clear();
|
|
145
183
|
rowMeta.clear();
|
|
146
|
-
|
|
184
|
+
// REF-CC-039(fleet2-01):单一铸点 —— 整批快照行共享**同一次**本地接收时刻,绝不用
|
|
185
|
+
// wire 上的 `frame.ts`(server 时刻,server 省略该键时兜成 0,与本地钟不同域)。
|
|
186
|
+
const receivedAtMs = nowFn();
|
|
147
187
|
for (const row of frame.tasks) {
|
|
148
188
|
taskMap.set(row.id, row);
|
|
149
|
-
rowMeta.set(row.id, {
|
|
189
|
+
rowMeta.set(row.id, { receivedAtMs });
|
|
150
190
|
}
|
|
151
191
|
for (const row of frame.workflows)
|
|
152
192
|
wfMap.set(row.id, row);
|
|
@@ -154,10 +194,15 @@ export function createFleetLedger(hooks = {}) {
|
|
|
154
194
|
}
|
|
155
195
|
case 'task': {
|
|
156
196
|
const row = frame.row;
|
|
157
|
-
if (!row?.id)
|
|
197
|
+
if (!row?.id) {
|
|
198
|
+
droppedMalformed++;
|
|
199
|
+
debug('[fleet-frame] MALFORMED task frame dropped(row 或 row.id 缺席)');
|
|
158
200
|
break;
|
|
201
|
+
}
|
|
202
|
+
// REF-CC-039(fleet2-01):同一个字段只有一个铸点 —— 本地接收时刻,不是 `frame.ts`。
|
|
203
|
+
const receivedAtMs = nowFn();
|
|
159
204
|
taskMap.set(row.id, row); // POST-MERGE FULL row → replace by id
|
|
160
|
-
rowMeta.set(row.id, {
|
|
205
|
+
rowMeta.set(row.id, { receivedAtMs });
|
|
161
206
|
debug(`[fleet-row] id=${row.id} parent=${row.parentId ?? '-'} agentType=${row.agentType ?? '-'}` +
|
|
162
207
|
` agentName=${row.agentName ?? '-'} name=${row.name ?? '-'} status=${row.status ?? '-'}` +
|
|
163
208
|
` tokens=${row.tokens ?? 0} parentToolCallId=${row.parentToolCallId ?? '-'}`);
|
|
@@ -169,9 +214,10 @@ export function createFleetLedger(hooks = {}) {
|
|
|
169
214
|
// (或无标行)→ 登映射**不登台账**(台账只收硬证据,防无标外来行污染台账后反过来给
|
|
170
215
|
// 通知臂放行);这也保住「壳重启+resume 接回会话」的终报 hydrate;
|
|
171
216
|
// · 两者都不是(老代际广播窗)→ 不登映射(隔离优先)。
|
|
172
|
-
|
|
217
|
+
const parentId = wireParentId(row);
|
|
218
|
+
if (parentId !== undefined) {
|
|
173
219
|
const childTid = rowIdTail(row.id);
|
|
174
|
-
const parentTid = rowIdTail(
|
|
220
|
+
const parentTid = rowIdTail(parentId);
|
|
175
221
|
const parentOwned = isOwnEngineRun(parentTid);
|
|
176
222
|
if (parentOwned)
|
|
177
223
|
recordOwnEngineRun(childTid);
|
|
@@ -188,8 +234,14 @@ export function createFleetLedger(hooks = {}) {
|
|
|
188
234
|
recordEngineTranscriptId(rowIdTail(row.id), row.transcriptId);
|
|
189
235
|
}
|
|
190
236
|
// 终态帧 → 留存池(remove 同毫秒尾随,probe 实证;留存让 completed band 数得到它)。
|
|
237
|
+
// REF-CC-040(fleet2-02):非终态分支必须显式 delete —— 否则 bg 复活(SendMessage 后
|
|
238
|
+
// seq+1)时,`project()` 因 `taskMap.has(id)` 恒 continue 而永不清理过期留存条目,belt
|
|
239
|
+
// 的 `!retained.has()` 前提失守,第二周期的终态被首周期的陈旧快照挡在门外。
|
|
191
240
|
if (TERMINAL_FLEET_TASK_STATUSES.has(row.status ?? '')) {
|
|
192
|
-
retained.set(row.id, { row, expireAt:
|
|
241
|
+
retained.set(row.id, { row, expireAt: receivedAtMs + TERMINAL_RETAIN_MS });
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
retained.delete(row.id);
|
|
193
245
|
}
|
|
194
246
|
break;
|
|
195
247
|
}
|
|
@@ -198,8 +250,13 @@ export function createFleetLedger(hooks = {}) {
|
|
|
198
250
|
rowMeta.delete(frame.id);
|
|
199
251
|
break;
|
|
200
252
|
case 'workflow':
|
|
201
|
-
if (frame.row?.id)
|
|
253
|
+
if (frame.row?.id) {
|
|
202
254
|
wfMap.set(frame.row.id, frame.row);
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
droppedMalformed++;
|
|
258
|
+
debug('[fleet-frame] MALFORMED workflow frame dropped(row 或 row.id 缺席)');
|
|
259
|
+
}
|
|
203
260
|
break;
|
|
204
261
|
case 'workflow_remove':
|
|
205
262
|
wfMap.delete(frame.id);
|
|
@@ -221,10 +278,16 @@ export function createFleetLedger(hooks = {}) {
|
|
|
221
278
|
break;
|
|
222
279
|
}
|
|
223
280
|
}
|
|
224
|
-
/** bg 子代终态通知帧([446] 帧契约,service 1.119):常开 lane 即时到达 → 转注入链。
|
|
281
|
+
/** bg 子代终态通知帧([446] 帧契约,service 1.119):常开 lane 即时到达 → 转注入链。
|
|
282
|
+
* REF-CC-048(fleet2-10):参数类型此前写成 `FleetFrame extends never ? never : BgNotificationWire`
|
|
283
|
+
* —— 条件类型只对**裸类型参数**分发,`FleetFrame` 是具体 union,整个表达式恒等于
|
|
284
|
+
* `BgNotificationWire`,伪装成了一个从不成立的守卫。直写目标类型,不留误导性的仪式感。 */
|
|
225
285
|
function applyBgNotification(n) {
|
|
226
|
-
if (typeof n?.taskId !== 'string' || typeof n?.status !== 'string')
|
|
286
|
+
if (typeof n?.taskId !== 'string' || typeof n?.status !== 'string') {
|
|
287
|
+
droppedMalformed++;
|
|
288
|
+
debug('[fleet-frame] MALFORMED bg_notification dropped(taskId 或 status 缺席/非串)');
|
|
227
289
|
return;
|
|
290
|
+
}
|
|
228
291
|
// 🔴 own/foreign 判别(双开泄漏案帧级防线;[1498]④ 换锚重写):payload `sessionId` 是**子代
|
|
229
292
|
// 自身** session uuid,宿主会话在 ownerSessionId 上且出 wire 前被剥 —— 所以判别锚
|
|
230
293
|
// `parentTaskId`(spawn 该子代的 leader run id)对 own-run 台账:
|
|
@@ -282,11 +345,24 @@ export function createFleetLedger(hooks = {}) {
|
|
|
282
345
|
: {}),
|
|
283
346
|
});
|
|
284
347
|
// belt:终态**行帧**丢失(kill 竞态等)时,已知行也随通知转终态进留存池。
|
|
348
|
+
// REF-CC-040(fleet2-02):无条件覆盖(不再靠 `!retained.has()` 判「池里没有它」)—— 「谁写得
|
|
349
|
+
// 对」不该依赖清理时序;'task' 非终态分支现在会显式 delete,belt 这里覆盖同样安全。
|
|
350
|
+
// REF-CC-039:`expireAt` 同一铸点(`nowFn()`),不用 `Date.now()` 另开一个时钟域。
|
|
351
|
+
//
|
|
352
|
+
// 🔴 取舍写死(P3 wave2 回炉,复审 advisory①:finding proposal 两种形都允许,本处取的是
|
|
353
|
+
// 「无条件覆盖」而不是「按 seq 择新」,两个方向的后果各钉了一条常驻断言):
|
|
354
|
+
// · **通知晚于终态行帧** ⇒ 覆盖。通知是 settle 权威(下方 `recordBgTerminalFacts` 就是
|
|
355
|
+
// 无条件按 `n.status` 记的账),留存池若不跟着覆盖,面板行渲 `completed` 而详情页写
|
|
356
|
+
// `killed` = 同一帧两写分叉。钉:pure 门「留存池行的 status 与终态事实台账不分叉」。
|
|
357
|
+
// · **非终态行帧晚于 belt** ⇒ 抹掉 belt 结果(`case 'task'` 的 `retained.delete`)。这正是
|
|
358
|
+
// 复活语义:行又活了,首周期的终态快照本就不该继续投影。钉:pure 门「复活(非终态)
|
|
359
|
+
// 一到就清留存条目 ⇒ 首周期陈旧终态行不得复现为幽灵」。
|
|
360
|
+
// 要改成 seq 择新形,先把这两条断言的期望值一起改 —— 它们是这个取舍的可执行形。
|
|
285
361
|
const known = taskMap.get(n.taskId);
|
|
286
|
-
if (known
|
|
362
|
+
if (known) {
|
|
287
363
|
retained.set(n.taskId, {
|
|
288
364
|
row: { ...known, status: n.status },
|
|
289
|
-
expireAt:
|
|
365
|
+
expireAt: nowFn() + TERMINAL_RETAIN_MS,
|
|
290
366
|
});
|
|
291
367
|
}
|
|
292
368
|
}
|
|
@@ -313,7 +389,7 @@ export function createFleetLedger(hooks = {}) {
|
|
|
313
389
|
const meta = rowMeta.get(r.id);
|
|
314
390
|
if (!meta)
|
|
315
391
|
return r;
|
|
316
|
-
const idle = Math.max(0, nowMs - meta.
|
|
392
|
+
const idle = Math.max(0, nowMs - meta.receivedAtMs);
|
|
317
393
|
return idle > 0 ? { ...r, elapsedMs: (r.elapsedMs ?? 0) + idle } : r;
|
|
318
394
|
});
|
|
319
395
|
// ② 终态留存池:live 集没有的行在宽限窗内继续投影;被查看中的行不过期。
|
|
@@ -344,15 +420,18 @@ export function createFleetLedger(hooks = {}) {
|
|
|
344
420
|
return true;
|
|
345
421
|
// 祖链闭包:孙代行 parentId=直接父(a… handle)而非 spawn run([1502] wire 事实)——
|
|
346
422
|
// 沿 parentId 链上溯,任一环(或环的 parentId)∈ 本端 run 台账即 own。
|
|
423
|
+
// REF-CC-049(fleet2-11):`wireParentId` 单一铸点(空串按缺席),不再裸读 `cur.parentId`
|
|
424
|
+
// 做 `!== undefined` 判断 —— 那种写法对空串脏值判「有父」,与 case 'task' 那半支相反。
|
|
347
425
|
const seen = new Set();
|
|
348
426
|
let cur = r;
|
|
349
427
|
while (cur && !seen.has(cur.id)) {
|
|
350
428
|
seen.add(cur.id);
|
|
351
429
|
if (isOwnEngineRun(cur.id))
|
|
352
430
|
return true;
|
|
353
|
-
|
|
431
|
+
const parentId = wireParentId(cur);
|
|
432
|
+
if (parentId !== undefined && isOwnEngineRun(parentId))
|
|
354
433
|
return true;
|
|
355
|
-
cur =
|
|
434
|
+
cur = parentId !== undefined ? allRowsById.get(parentId) : undefined;
|
|
356
435
|
}
|
|
357
436
|
return false;
|
|
358
437
|
};
|
|
@@ -383,10 +462,16 @@ export function createFleetLedger(hooks = {}) {
|
|
|
383
462
|
bgNotifyFailClosed: bgNotifyFailClosedMeta,
|
|
384
463
|
taskCount: taskMap.size,
|
|
385
464
|
workflowCount: wfMap.size,
|
|
465
|
+
droppedMalformed,
|
|
386
466
|
}),
|
|
387
467
|
dispose() {
|
|
388
468
|
disposed = true;
|
|
389
|
-
liveBgViews.
|
|
469
|
+
const views = liveBgViews.get(sessionKey);
|
|
470
|
+
if (views) {
|
|
471
|
+
views.delete(bgView);
|
|
472
|
+
if (views.size === 0)
|
|
473
|
+
liveBgViews.delete(sessionKey);
|
|
474
|
+
}
|
|
390
475
|
},
|
|
391
476
|
__rowsForTest: () => [...taskMap.values()],
|
|
392
477
|
};
|
|
@@ -50,9 +50,13 @@ export interface FleetTaskView {
|
|
|
50
50
|
tokens: number;
|
|
51
51
|
tokenDir: 'down' | 'up';
|
|
52
52
|
queuedCount: number;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
/** REF-CC-050(fleet2-12):投影器无条件赋值(`?? false`),门 `ALWAYS_EMITTED_TASK_VIEW_KEYS`
|
|
54
|
+
* 已把它钉成「恒发」——类型必填而不是留一条测不到的缺席分支。 */
|
|
55
|
+
awaitingPlanApproval: boolean;
|
|
56
|
+
/** REF-CC-050:同上,`childCount.get(r.id) ?? 0` 恒赋值。 */
|
|
57
|
+
descendantCount: number;
|
|
58
|
+
/** REF-CC-050:同上,`viewingTaskId !== undefined && …` 恒赋值(布尔运算永不产 undefined)。 */
|
|
59
|
+
viewed: boolean;
|
|
56
60
|
/** 🔴 三态:有键 ⇒ 真值(含真 0);缺键 ⇒ 「不知道」。绝不 `?? 0`。 */
|
|
57
61
|
toolUses?: number;
|
|
58
62
|
transcriptId?: string;
|
|
@@ -133,8 +137,15 @@ export declare const TERMINAL_FLEET_TASK_STATUSES: ReadonlySet<string>;
|
|
|
133
137
|
* design/153 §7.1),server 只是尚未投影 ⇒ 这不是「猜测层」,是**预挂**。
|
|
134
138
|
* 删的收益 = 一行;删的代价 = server 补上透传那天静默说谎。
|
|
135
139
|
* ⇒ 保留 + 把「候 server 预挂」的注释换成上面这条可复核的直证。定谳权交回 PM。
|
|
140
|
+
*
|
|
141
|
+
* 🔴 REF-CC-043(fleet2-05,P2 裁 GO,宪法三问已过):`default` 分支此前渲 `'running'` ——
|
|
142
|
+
* 未知未来状态被谎报成「活跃」,后果方向是**伤害型**(用户会以为一个已经僵住/退场的子代还在跑)。
|
|
143
|
+
* 改渲 `'idle'`(闭集里本就存在的「非活跃」词,187 用它表示「非活跃、非终态、非阻塞」)—— 门
|
|
144
|
+
* `ALWAYS_EMITTED_TASK_VIEW_KEYS` 不受影响,只是同一个已知渲染词的语义更诚实。
|
|
145
|
+
* `s` 的参数类型同批收窄:`| undefined` 半支从未被真实调用点使用过(唯一生产调用点
|
|
146
|
+
* `projectTasks` 传的是 SDK 必填闭集 `FleetTaskRow['status']`),继续留着是死分支。
|
|
136
147
|
*/
|
|
137
|
-
export declare function coerceTaskStatus(s: string
|
|
148
|
+
export declare function coerceTaskStatus(s: string): FleetTaskStatus;
|
|
138
149
|
/** 中性 workflow run 状态(running|completed|failed)→ 渲染词汇。 */
|
|
139
150
|
export declare function coerceWorkflowStatus(s: string | undefined): FleetTaskStatus;
|
|
140
151
|
/**
|
|
@@ -183,6 +194,13 @@ export declare function wireCurrentTool(r: Pick<FleetTaskRow, 'currentTool'>): {
|
|
|
183
194
|
export declare function wireStartedCount(r: Pick<FleetWorkflowRow, 'startedCount'>): number | undefined;
|
|
184
195
|
/** `parentToolCallId`(P1-2)—— 归属直读键。空串按缺席处理(引擎不该发,发了也别当真)。 */
|
|
185
196
|
export declare function wireParentToolCallId(r: Pick<FleetTaskRow, 'parentToolCallId'>): string | undefined;
|
|
197
|
+
/**
|
|
198
|
+
* `parentId` —— 悬空父的存在性判定唯一铸点(REF-CC-049,fleet2-11)。B10 禁真值判定当存在性判定:
|
|
199
|
+
* 空串是脏值形(SDK 只承诺「悬空不发」,那是 server 当下行为不是类型保证),按缺席处理。
|
|
200
|
+
* `fleetLedger.ts` 与本文件的 `projectTasks` 都调它 —— 一个谓词一个铸点,防止 `!== undefined`
|
|
201
|
+
* 与真值判定两种写法在同一字段上判读相反(悬空父脏值 `""` 时曾经真的翻过面)。
|
|
202
|
+
*/
|
|
203
|
+
export declare function wireParentId(r: Pick<FleetTaskRow, 'parentId'>): string | undefined;
|
|
186
204
|
/** 终态四键之 `usage`(数值 verbatim,只挑数字位;全脏 ⇒ 整键缺席,不造空对象)。 */
|
|
187
205
|
export declare function wireRowUsage(r: Pick<FleetTaskRow, 'usage'>): FleetRowUsage | undefined;
|
|
188
206
|
/** 终态四键之 `editedFiles`(bg 子代专属;脏项逐条丢,不整键丢)。 */
|
|
@@ -220,6 +238,26 @@ export declare const FLEET_WORKFLOW_VIEW_KEYS: readonly string[];
|
|
|
220
238
|
export declare const FLEET_TASK_ROW_WIRE_KEYS: readonly string[];
|
|
221
239
|
/** SDK `FleetWorkflowRow` 的入口键名清单(同上)。 */
|
|
222
240
|
export declare const FLEET_WORKFLOW_ROW_WIRE_KEYS: readonly string[];
|
|
241
|
+
/**
|
|
242
|
+
* 不投影登记表的一条条目(REF-CC-053,fleet2-15,P2 裁 GO)。
|
|
243
|
+
*
|
|
244
|
+
* 🔴 表原先只要求「写够 20 字理由」,而一条自认的**已知缺口**(`currentAction`,补它=真行为改动)
|
|
245
|
+
* 与三条**设计取舍**(折进别的键 / 归属权威在别处)共用同一条放行通道 —— 门只能证明「有人表过态」,
|
|
246
|
+
* 证明不了这态是「已裁定不做」还是「欠着没人管」。`kind` 把两者拆成两条不同的路:
|
|
247
|
+
* · `by-design`:裁定不投影,不需要到期日(它不会自己变成待办)。
|
|
248
|
+
* · `known-gap`:真缺口,`dueDate` 到期未清 ⇒ 门红(与规范「具名豁免带属主与到期日,过期即红」
|
|
249
|
+
* 同款,`run-fleet-view-keys-test.mjs` 的 D 段做校验)。
|
|
250
|
+
*/
|
|
251
|
+
export interface NotProjectedEntry {
|
|
252
|
+
/** 为什么不投影(≥20 字,原「理由」字段原样保留)。 */
|
|
253
|
+
reason: string;
|
|
254
|
+
/** `by-design` = 裁定的架构取舍;`known-gap` = 自认的缺口,不是取舍。 */
|
|
255
|
+
kind: 'by-design' | 'known-gap';
|
|
256
|
+
/** 谁负责这条判定 / 谁负责把 known-gap 补上。 */
|
|
257
|
+
owner: string;
|
|
258
|
+
/** 仅 `known-gap` 必填:ISO 日期,过期未清 = 门红(禁无期限豁免)。 */
|
|
259
|
+
dueDate?: string;
|
|
260
|
+
}
|
|
223
261
|
/**
|
|
224
262
|
* **不投影登记表**:wire 上有、`FleetTaskView` 上**故意没有**的键,每条必须写清理由。
|
|
225
263
|
*
|
|
@@ -228,7 +266,7 @@ export declare const FLEET_WORKFLOW_ROW_WIRE_KEYS: readonly string[];
|
|
|
228
266
|
* 每个 wire 键只有两条出路:**要么投影出去,要么在这里写下不投的理由**;新 wire 键在覆盖账那条
|
|
229
267
|
* 腿上当场红,不给「悄悄没人管」留位置。
|
|
230
268
|
*/
|
|
231
|
-
export declare const FLEET_TASK_ROW_KEYS_NOT_PROJECTED: Readonly<Record<string,
|
|
269
|
+
export declare const FLEET_TASK_ROW_KEYS_NOT_PROJECTED: Readonly<Record<string, NotProjectedEntry>>;
|
|
232
270
|
/** workflow 行:wire 十键与视图十键一一同名同义 ⇒ 本表**空**(空不是「忘了填」,是覆盖账那条腿
|
|
233
271
|
* 每跑一次都在证明它该空:少投一个键就红)。 */
|
|
234
|
-
export declare const FLEET_WORKFLOW_ROW_KEYS_NOT_PROJECTED: Readonly<Record<string,
|
|
272
|
+
export declare const FLEET_WORKFLOW_ROW_KEYS_NOT_PROJECTED: Readonly<Record<string, NotProjectedEntry>>;
|
|
@@ -24,6 +24,13 @@ export const TERMINAL_FLEET_TASK_STATUSES = new Set([
|
|
|
24
24
|
* design/153 §7.1),server 只是尚未投影 ⇒ 这不是「猜测层」,是**预挂**。
|
|
25
25
|
* 删的收益 = 一行;删的代价 = server 补上透传那天静默说谎。
|
|
26
26
|
* ⇒ 保留 + 把「候 server 预挂」的注释换成上面这条可复核的直证。定谳权交回 PM。
|
|
27
|
+
*
|
|
28
|
+
* 🔴 REF-CC-043(fleet2-05,P2 裁 GO,宪法三问已过):`default` 分支此前渲 `'running'` ——
|
|
29
|
+
* 未知未来状态被谎报成「活跃」,后果方向是**伤害型**(用户会以为一个已经僵住/退场的子代还在跑)。
|
|
30
|
+
* 改渲 `'idle'`(闭集里本就存在的「非活跃」词,187 用它表示「非活跃、非终态、非阻塞」)—— 门
|
|
31
|
+
* `ALWAYS_EMITTED_TASK_VIEW_KEYS` 不受影响,只是同一个已知渲染词的语义更诚实。
|
|
32
|
+
* `s` 的参数类型同批收窄:`| undefined` 半支从未被真实调用点使用过(唯一生产调用点
|
|
33
|
+
* `projectTasks` 传的是 SDK 必填闭集 `FleetTaskRow['status']`),继续留着是死分支。
|
|
27
34
|
*/
|
|
28
35
|
export function coerceTaskStatus(s) {
|
|
29
36
|
switch (s) {
|
|
@@ -40,7 +47,8 @@ export function coerceTaskStatus(s) {
|
|
|
40
47
|
case 'parked':
|
|
41
48
|
return 'awaiting approval';
|
|
42
49
|
default:
|
|
43
|
-
|
|
50
|
+
// 未知未来状态 → 渲成非活跃中性词(开集回落;绝不谎报活跃,REF-CC-043)。
|
|
51
|
+
return 'idle';
|
|
44
52
|
}
|
|
45
53
|
}
|
|
46
54
|
/** 中性 workflow run 状态(running|completed|failed)→ 渲染词汇。 */
|
|
@@ -145,6 +153,16 @@ export function wireParentToolCallId(r) {
|
|
|
145
153
|
const v = r.parentToolCallId;
|
|
146
154
|
return typeof v === 'string' && v.length > 0 ? v : undefined;
|
|
147
155
|
}
|
|
156
|
+
/**
|
|
157
|
+
* `parentId` —— 悬空父的存在性判定唯一铸点(REF-CC-049,fleet2-11)。B10 禁真值判定当存在性判定:
|
|
158
|
+
* 空串是脏值形(SDK 只承诺「悬空不发」,那是 server 当下行为不是类型保证),按缺席处理。
|
|
159
|
+
* `fleetLedger.ts` 与本文件的 `projectTasks` 都调它 —— 一个谓词一个铸点,防止 `!== undefined`
|
|
160
|
+
* 与真值判定两种写法在同一字段上判读相反(悬空父脏值 `""` 时曾经真的翻过面)。
|
|
161
|
+
*/
|
|
162
|
+
export function wireParentId(r) {
|
|
163
|
+
const v = r.parentId;
|
|
164
|
+
return typeof v === 'string' && v.length > 0 ? v : undefined;
|
|
165
|
+
}
|
|
148
166
|
/** 终态四键之 `usage`(数值 verbatim,只挑数字位;全脏 ⇒ 整键缺席,不造空对象)。 */
|
|
149
167
|
export function wireRowUsage(r) {
|
|
150
168
|
const u = r.usage;
|
|
@@ -211,6 +229,7 @@ export function projectTasks(allRows, opts) {
|
|
|
211
229
|
const toolUses = wireToolUses(r);
|
|
212
230
|
const transcriptId = wireTranscriptId(r);
|
|
213
231
|
const currentTool = wireCurrentTool(r);
|
|
232
|
+
const parentId = wireParentId(r);
|
|
214
233
|
const parentToolCallId = wireParentToolCallId(r);
|
|
215
234
|
const stoppedBy = wireStoppedBy(r);
|
|
216
235
|
const usage = wireRowUsage(r);
|
|
@@ -222,7 +241,7 @@ export function projectTasks(allRows, opts) {
|
|
|
222
241
|
// 子行帧无 description → 从 name 复原任务描述(CC 锚:行正文恒显 description,不留空列)。
|
|
223
242
|
description: projectDescription(r.description) || taskDescFromName(r.name, r.agentType, r.agentName),
|
|
224
243
|
depth: depthOf(r),
|
|
225
|
-
...(
|
|
244
|
+
...(parentId !== undefined ? { parentId } : {}),
|
|
226
245
|
status: coerceTaskStatus(r.status),
|
|
227
246
|
elapsedMs: r.elapsedMs ?? 0,
|
|
228
247
|
tokens: r.tokens ?? 0,
|
|
@@ -368,16 +387,39 @@ export const FLEET_WORKFLOW_ROW_WIRE_KEYS = FLEET_WORKFLOW_ROW_WIRE_KEY_TUPLE;
|
|
|
368
387
|
* 腿上当场红,不给「悄悄没人管」留位置。
|
|
369
388
|
*/
|
|
370
389
|
export const FLEET_TASK_ROW_KEYS_NOT_PROJECTED = {
|
|
371
|
-
sourceLane:
|
|
372
|
-
'
|
|
373
|
-
'
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
390
|
+
sourceLane: {
|
|
391
|
+
kind: 'by-design',
|
|
392
|
+
owner: 'client-core 规范重构轮',
|
|
393
|
+
reason: 'SDK 3.0.0 新键([2070]① 让位半场:前台 delegation 复合 id 行带 "run-leg" 标)。两仓现零消费面 ——' +
|
|
394
|
+
'#95 纪律:没人读的键不盲投。已知潜在消费方=壳 wf-panel 的双生行过滤(现走 taskId 台账补偿),' +
|
|
395
|
+
'若迁到本键判别则届时投影并删本条(那是行为改动,单独批)。',
|
|
396
|
+
},
|
|
397
|
+
agentType: {
|
|
398
|
+
kind: 'by-design',
|
|
399
|
+
owner: 'client-core 规范重构轮',
|
|
400
|
+
reason: '折进 `name`(deriveAgentLabel:agentName ?? agentType,都缺才退 objective 派生)——渲染契约只有一个身份列,187 的 tjl 同形。',
|
|
401
|
+
},
|
|
402
|
+
agentName: {
|
|
403
|
+
kind: 'by-design',
|
|
404
|
+
owner: 'client-core 规范重构轮',
|
|
405
|
+
reason: '同 agentType:两位在本层合成一个身份 label,分开带出去等于把 187 的 tjl 规则推给每个消费端各写一遍。',
|
|
406
|
+
},
|
|
407
|
+
workflowRunId: {
|
|
408
|
+
kind: 'by-design',
|
|
409
|
+
owner: 'client-core 规范重构轮',
|
|
410
|
+
reason: 'workflow 归属**不走行字段**:权威判定在 adapt 的 task_progress 臂(recordWorkflowAgentTaskId,判据④),端按 taskId 尾段查台账过滤双生行。行上再带一份 = 第二真源。',
|
|
411
|
+
},
|
|
412
|
+
currentAction: {
|
|
413
|
+
kind: 'known-gap',
|
|
414
|
+
owner: 'client-core 规范重构轮',
|
|
415
|
+
dueDate: '2027-02-01',
|
|
416
|
+
reason: '结构化同源体 `currentTool` 已投影(SDK 注:currentAction 是它的**文本回落**)。🔴 诚实记账:旧端(server <1.288)只发 currentAction ⇒ 那一档上本视图今天确实拿不到活动文本,这是**已知缺口**不是设计取舍;补它 = 加 `currentAction?: string` 进本视图 + 端侧回落渲染。',
|
|
417
|
+
},
|
|
378
418
|
};
|
|
379
419
|
/** workflow 行:wire 十键与视图十键一一同名同义 ⇒ 本表**空**(空不是「忘了填」,是覆盖账那条腿
|
|
380
420
|
* 每跑一次都在证明它该空:少投一个键就红)。 */
|
|
381
421
|
export const FLEET_WORKFLOW_ROW_KEYS_NOT_PROJECTED = {};
|
|
382
|
-
|
|
422
|
+
// `Covers`(REF-CC-145/dup-12,P3 wave1 卡C-argv)改吃共用叶 ../typePins.js——四份逐字重复钉之一
|
|
423
|
+
// (`Exclude<A,B> extends never` = 「A 的每个成员都在 B 里」;不成立时类型是 `never`,赋 `true` 即编译红)。
|
|
424
|
+
const _fleetViewKeyPins = [true, true, true, true, true, true, true, true, true, true];
|
|
383
425
|
void _fleetViewKeyPins;
|
|
@@ -39,12 +39,29 @@ export interface FleetAgentRowLike {
|
|
|
39
39
|
}
|
|
40
40
|
/** 行从投影集消失多久之后按 completed 兜底 settle(重连空窗 / snapshot 清表远小于此)。 */
|
|
41
41
|
export declare const ABSENT_SETTLE_MS = 30000;
|
|
42
|
-
/**
|
|
42
|
+
/**
|
|
43
|
+
* 测试钩:清投影台账 —— **全部 sessionKey**,不是只清 default 桶。
|
|
44
|
+
*
|
|
45
|
+
* 🔴 回炉定谳(P3 wave2 复审 blocker④):per-session 化那一批一度把它收窄成
|
|
46
|
+
* `resetFleetAgentPanelProjectionFor(DEFAULT_SESSION_KEY)`。它的契约是「清投影台账」,不是一个
|
|
47
|
+
* 会话语义位 —— 收窄等于给多会话宿主的用例之间留一条跨用例串扰通道(用例 A 先
|
|
48
|
+
* `projectFleetAgentRowsFor('sess-b', …)`,用例间调零参 reset,`sess-b` 桶存活 ⇒ 下个用例首次
|
|
49
|
+
* 投影同 taskId 时 `prev` 非空且 `settled:true`,`fleet-row`/`end` 被去重门吞掉)。
|
|
50
|
+
* 对位实现同形:`fleet/fleetLedger.__resetFleetLedgerRegistryForTests()` = `liveBgViews.clear()`;
|
|
51
|
+
* W1 段五个槽位的现成规约也逐个钉的是「reset 清全部键」。
|
|
52
|
+
* 要只清一个会话请用 `resetFleetAgentPanelProjectionFor(sessionKey)`。
|
|
53
|
+
*/
|
|
43
54
|
export declare function resetFleetAgentPanelProjection(): void;
|
|
55
|
+
/** W1 keyed variant(REF-CC-045):只清**这一个会话**的桶。 */
|
|
56
|
+
export declare function resetFleetAgentPanelProjectionFor(sessionKey: string): void;
|
|
44
57
|
/** 台账读面(测试/诊断):当前记着的行数。 */
|
|
45
58
|
export declare function fleetAgentProjectionSize(): number;
|
|
59
|
+
/** W1 keyed variant。 */
|
|
60
|
+
export declare function fleetAgentProjectionSizeFor(sessionKey: string): number;
|
|
46
61
|
/**
|
|
47
62
|
* 把一次 fleet 投影(已经过 own 判别 + workflow belt 的 FleetTask[])喂给面板事件流。
|
|
48
63
|
* 幂等、无副作用地重复调用安全 —— 每秒被 footer 调用两次就是常态。
|
|
49
64
|
*/
|
|
50
65
|
export declare function projectFleetAgentRows(rows: readonly FleetAgentRowLike[], nowMs?: number): void;
|
|
66
|
+
/** W1 keyed variant(REF-CC-045):多会话宿主每会话一份台账,互不顶盖。 */
|
|
67
|
+
export declare function projectFleetAgentRowsFor(sessionKey: string, rows: readonly FleetAgentRowLike[], nowMs?: number): void;
|