langchain_agentx_stream_ui 0.3.7 → 0.4.2
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/dist/{AskUserQuestionToolBody-CUXqUkar.d.ts → AskUserQuestionToolBody-J8MqSUEs.d.ts} +2 -2
- package/dist/{MarkdownBlock-CcXZ5nTm.d.ts → MarkdownBlock-DeICJUtu.d.ts} +1 -1
- package/dist/{chunk-7QYE5PN5.js → chunk-7X7IF2A4.js} +2 -2
- package/dist/{chunk-24FLS4TF.js → chunk-AY7QLXFB.js} +165 -10
- package/dist/chunk-AY7QLXFB.js.map +1 -0
- package/dist/index.d.ts +98 -239
- package/dist/index.js +1852 -1705
- package/dist/index.js.map +1 -1
- package/dist/{multi-session-CsPvSE9d.d.ts → multi-session--0z_-ut1.d.ts} +267 -4
- package/dist/multi-session.d.ts +4 -4
- package/dist/multi-session.js +2 -2
- package/dist/{nodes-BcyPw6Fr.d.ts → nodes-7_0rCAc4.d.ts} +2 -0
- package/dist/{sessionViewOptions-DQRzPuN_.d.ts → sessionViewOptions-EIBugOkf.d.ts} +1 -1
- package/dist/tools-presentation.d.ts +3 -3
- package/dist/tools.d.ts +5 -5
- package/dist/{types-BEXSHQG7.d.ts → types-DAkw2VOv.d.ts} +1 -1
- package/dist/virtual.d.ts +1 -1
- package/dist/virtual.js +1 -1
- package/package.json +4 -2
- package/dist/chunk-24FLS4TF.js.map +0 -1
- /package/dist/{chunk-7QYE5PN5.js.map → chunk-7X7IF2A4.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
fixDiagramSvg,
|
|
24
24
|
markEventIdSeen,
|
|
25
25
|
shouldSkipDuplicateEvent
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-7X7IF2A4.js";
|
|
27
27
|
import {
|
|
28
28
|
DefaultToolBody,
|
|
29
29
|
InteractionBusContext,
|
|
@@ -62,6 +62,7 @@ import {
|
|
|
62
62
|
ToolDisplayOptionsContext,
|
|
63
63
|
ToolProjectionClassifier,
|
|
64
64
|
VirtualTimeline,
|
|
65
|
+
applyAuthoritySnapshotToState,
|
|
65
66
|
buildCanonicalFromTree,
|
|
66
67
|
buildProjectionContext,
|
|
67
68
|
buildProjectionViewContext,
|
|
@@ -76,7 +77,10 @@ import {
|
|
|
76
77
|
formatShellProgressSuffix,
|
|
77
78
|
formatTaskFooterLabel,
|
|
78
79
|
getChildIds,
|
|
80
|
+
getDisplayProjectionStatus,
|
|
79
81
|
getMainStageIds,
|
|
82
|
+
isAuthorityTerminal,
|
|
83
|
+
isAuthorityTerminalStatus,
|
|
80
84
|
isGitOperationCommand,
|
|
81
85
|
isGroupableToolName,
|
|
82
86
|
isMcpToolName,
|
|
@@ -92,11 +96,16 @@ import {
|
|
|
92
96
|
resolveEventTier,
|
|
93
97
|
resolveResponseId,
|
|
94
98
|
resolveShellProgressFromPayload,
|
|
99
|
+
resolveWorkflowBanner,
|
|
95
100
|
shouldApplyGrouping,
|
|
101
|
+
shouldShowAgentSpinner,
|
|
96
102
|
shouldShowExploreDetail,
|
|
103
|
+
shouldShowGlobalSpinner,
|
|
97
104
|
shouldShowTaskListFooter,
|
|
98
105
|
streamChunk,
|
|
99
106
|
useActiveSession,
|
|
107
|
+
useAgentAuthority,
|
|
108
|
+
useAgentAuthoritySubscribeFailed,
|
|
100
109
|
useChildren,
|
|
101
110
|
useInternalErrors,
|
|
102
111
|
useMinDisplayTime,
|
|
@@ -110,7 +119,7 @@ import {
|
|
|
110
119
|
useSessionStatus,
|
|
111
120
|
useSessionViewOptions,
|
|
112
121
|
useTimeline
|
|
113
|
-
} from "./chunk-
|
|
122
|
+
} from "./chunk-AY7QLXFB.js";
|
|
114
123
|
import {
|
|
115
124
|
Agent,
|
|
116
125
|
AgentToolBody,
|
|
@@ -157,1629 +166,1806 @@ import {
|
|
|
157
166
|
} from "./chunk-4RIOBLGB.js";
|
|
158
167
|
|
|
159
168
|
// src/view/AgentSession.tsx
|
|
160
|
-
import { useEffect as
|
|
169
|
+
import { useEffect as useEffect3, useMemo, useRef as useRef3 } from "react";
|
|
161
170
|
|
|
162
171
|
// src/core/store.ts
|
|
163
172
|
import { createStore } from "zustand/vanilla";
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
console.error("[langchain_agentx_stream_ui] reducer error:", err);
|
|
172
|
-
}
|
|
173
|
-
return {
|
|
174
|
-
tree: {
|
|
175
|
-
...tree,
|
|
176
|
-
internalErrors: [
|
|
177
|
-
...tree.internalErrors,
|
|
178
|
-
{ eventIndex, eventType: event.event_type, message, stack }
|
|
179
|
-
]
|
|
180
|
-
}
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
function createSessionStore(initialTree = createEmptyTree(), storeOptions) {
|
|
185
|
-
const reduceOpts = {
|
|
186
|
-
tierOverrides: storeOptions?.tierOverrides,
|
|
187
|
-
collectDebug: storeOptions?.debug === true,
|
|
188
|
-
subagentLeakGate: storeOptions?.subagentLeakGate === true
|
|
173
|
+
|
|
174
|
+
// src/types/workflowContainer.ts
|
|
175
|
+
function createEmptyWorkflowContainerTreeState() {
|
|
176
|
+
return {
|
|
177
|
+
rootContainerIds: [],
|
|
178
|
+
activeContainerIds: [],
|
|
179
|
+
containersById: {}
|
|
189
180
|
};
|
|
190
|
-
const initialEventCount = storeOptions?.initialEventCount ?? 0;
|
|
191
|
-
const seenEventIds = new Set(storeOptions?.initialSeenEventIds);
|
|
192
|
-
return createStore((set, get) => ({
|
|
193
|
-
tree: initialTree,
|
|
194
|
-
eventCount: initialEventCount,
|
|
195
|
-
applyEvent(event, ctx) {
|
|
196
|
-
const sseEventId = ctx?.sseEventId;
|
|
197
|
-
if (shouldSkipDuplicateEvent(seenEventIds, sseEventId)) return;
|
|
198
|
-
const { tree, eventCount } = get();
|
|
199
|
-
const { tree: reduced } = safeReduce(tree, event, eventCount, reduceOpts);
|
|
200
|
-
const nextTree = sseEventId ? {
|
|
201
|
-
...reduced,
|
|
202
|
-
meta: { ...reduced.meta, lastEventId: sseEventId }
|
|
203
|
-
} : reduced;
|
|
204
|
-
markEventIdSeen(seenEventIds, sseEventId);
|
|
205
|
-
set({ tree: nextTree, eventCount: eventCount + 1 });
|
|
206
|
-
},
|
|
207
|
-
applyEvents(events) {
|
|
208
|
-
let { tree, eventCount } = get();
|
|
209
|
-
for (const event of events) {
|
|
210
|
-
const result = safeReduce(tree, event, eventCount, reduceOpts);
|
|
211
|
-
tree = result.tree;
|
|
212
|
-
eventCount += 1;
|
|
213
|
-
}
|
|
214
|
-
set({ tree, eventCount });
|
|
215
|
-
},
|
|
216
|
-
reset() {
|
|
217
|
-
seenEventIds.clear();
|
|
218
|
-
set({ tree: createEmptyTree(), eventCount: 0 });
|
|
219
|
-
},
|
|
220
|
-
markAsError(error) {
|
|
221
|
-
const { tree } = get();
|
|
222
|
-
set({
|
|
223
|
-
tree: {
|
|
224
|
-
...tree,
|
|
225
|
-
status: "error",
|
|
226
|
-
internalErrors: error ? [
|
|
227
|
-
...tree.internalErrors,
|
|
228
|
-
{
|
|
229
|
-
eventIndex: -1,
|
|
230
|
-
eventType: "sse_connection_failed",
|
|
231
|
-
message: error.message,
|
|
232
|
-
stack: error.stack
|
|
233
|
-
}
|
|
234
|
-
] : tree.internalErrors
|
|
235
|
-
}
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
}));
|
|
239
181
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
actual;
|
|
246
|
-
constructor(expected, actual) {
|
|
247
|
-
super(
|
|
248
|
-
`application_kind mismatch: expected ${expected}, got ${actual ?? "(missing)"}`
|
|
249
|
-
);
|
|
250
|
-
this.expected = expected;
|
|
251
|
-
this.actual = actual;
|
|
252
|
-
}
|
|
253
|
-
};
|
|
254
|
-
var MissingApplicationKindError = class extends Error {
|
|
255
|
-
name = "MissingApplicationKindError";
|
|
256
|
-
constructor() {
|
|
257
|
-
super("application_kind missing in SSE meta frame");
|
|
258
|
-
}
|
|
259
|
-
};
|
|
260
|
-
|
|
261
|
-
// src/transport/sseTransportPolicy.ts
|
|
262
|
-
var BOUNDED_DEFAULTS = {
|
|
263
|
-
maxReconnects: 5,
|
|
264
|
-
reconnectTimeoutMs: 6e4,
|
|
265
|
-
reconnectDelayMs: 1e3
|
|
266
|
-
};
|
|
267
|
-
var legacyAutoReconnectWarned = false;
|
|
268
|
-
function warnLegacyAutoReconnectOnce() {
|
|
269
|
-
if (legacyAutoReconnectWarned) return;
|
|
270
|
-
if (typeof process !== "undefined" && process.env.NODE_ENV === "production") {
|
|
271
|
-
return;
|
|
182
|
+
function containerTreeToSnapshot(tree) {
|
|
183
|
+
const containers = {};
|
|
184
|
+
for (const [id, node] of Object.entries(tree.containersById)) {
|
|
185
|
+
const { loopSessionId: _loop, ...projection } = node;
|
|
186
|
+
containers[id] = projection;
|
|
272
187
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
188
|
+
return {
|
|
189
|
+
root_container_ids: [...tree.rootContainerIds],
|
|
190
|
+
active_container_ids: [...tree.activeContainerIds],
|
|
191
|
+
containers
|
|
192
|
+
};
|
|
277
193
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
};
|
|
290
|
-
var PROFILE_PRESETS = {
|
|
291
|
-
oneshot: {
|
|
292
|
-
profile: "oneshot",
|
|
293
|
-
replay: "none",
|
|
294
|
-
reconnect: {
|
|
295
|
-
mode: "off",
|
|
296
|
-
maxReconnects: 0,
|
|
297
|
-
reconnectTimeoutMs: 0,
|
|
298
|
-
reconnectDelayMs: BOUNDED_DEFAULTS.reconnectDelayMs
|
|
299
|
-
},
|
|
300
|
-
terminal: AGENT_TERMINAL
|
|
301
|
-
},
|
|
302
|
-
"oneshot-workflow": {
|
|
303
|
-
profile: "oneshot-workflow",
|
|
304
|
-
replay: "none",
|
|
305
|
-
reconnect: {
|
|
306
|
-
mode: "off",
|
|
307
|
-
maxReconnects: 0,
|
|
308
|
-
reconnectTimeoutMs: 0,
|
|
309
|
-
reconnectDelayMs: BOUNDED_DEFAULTS.reconnectDelayMs
|
|
310
|
-
},
|
|
311
|
-
terminal: WORKFLOW_TERMINAL_WITH_FALLBACK
|
|
312
|
-
},
|
|
313
|
-
"replayable-workflow": {
|
|
314
|
-
profile: "replayable-workflow",
|
|
315
|
-
replay: "history",
|
|
316
|
-
reconnect: {
|
|
317
|
-
mode: "bounded",
|
|
318
|
-
...BOUNDED_DEFAULTS
|
|
319
|
-
},
|
|
320
|
-
terminal: WORKFLOW_TERMINAL_WITH_FALLBACK
|
|
321
|
-
},
|
|
322
|
-
"workflow-tail": {
|
|
323
|
-
profile: "workflow-tail",
|
|
324
|
-
replay: "none",
|
|
325
|
-
reconnect: {
|
|
326
|
-
mode: "bounded",
|
|
327
|
-
...BOUNDED_DEFAULTS
|
|
328
|
-
},
|
|
329
|
-
terminal: WORKFLOW_TERMINAL_WITH_FALLBACK
|
|
330
|
-
}
|
|
331
|
-
};
|
|
332
|
-
function legacyTerminal(expectedApplicationKind) {
|
|
333
|
-
if (expectedApplicationKind === "workflow") {
|
|
334
|
-
return WORKFLOW_TERMINAL_STRICT;
|
|
335
|
-
}
|
|
336
|
-
return AGENT_TERMINAL;
|
|
194
|
+
|
|
195
|
+
// src/types/workflowSession.ts
|
|
196
|
+
function createEmptyWorkflowDisplayProjection() {
|
|
197
|
+
return {
|
|
198
|
+
streamEnded: false,
|
|
199
|
+
projectionStatus: "connecting",
|
|
200
|
+
terminalKind: null,
|
|
201
|
+
openChildIdentities: [],
|
|
202
|
+
awaitingAuthority: false,
|
|
203
|
+
authoritySyncFailed: false,
|
|
204
|
+
authoritySubscribeFailed: false
|
|
205
|
+
};
|
|
337
206
|
}
|
|
338
|
-
function
|
|
339
|
-
if (autoReconnect === false) {
|
|
340
|
-
return {
|
|
341
|
-
mode: "off",
|
|
342
|
-
maxReconnects: 0,
|
|
343
|
-
reconnectTimeoutMs: 0,
|
|
344
|
-
reconnectDelayMs: reconnectDelayMs ?? BOUNDED_DEFAULTS.reconnectDelayMs
|
|
345
|
-
};
|
|
346
|
-
}
|
|
207
|
+
function createEmptyWorkflowProgress() {
|
|
347
208
|
return {
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
209
|
+
workflowId: null,
|
|
210
|
+
workflowPath: null,
|
|
211
|
+
status: "running",
|
|
212
|
+
stages: [],
|
|
213
|
+
parallelItems: {}
|
|
352
214
|
};
|
|
353
215
|
}
|
|
354
|
-
function
|
|
355
|
-
if (!override) return base;
|
|
216
|
+
function createEmptyWorkflowSessionState() {
|
|
356
217
|
return {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
},
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
218
|
+
authority: null,
|
|
219
|
+
display: createEmptyWorkflowDisplayProjection(),
|
|
220
|
+
meta: { workflowRunId: null, startedAt: null, lastEventId: null },
|
|
221
|
+
containerTree: createEmptyWorkflowContainerTreeState(),
|
|
222
|
+
workflowProgress: createEmptyWorkflowProgress(),
|
|
223
|
+
loopTreesBySessionId: {},
|
|
224
|
+
stageDoneSummariesByContainerId: {},
|
|
225
|
+
hotCompletedLoopSessionIds: [],
|
|
226
|
+
activeLoopSessionId: null,
|
|
227
|
+
projectionCursor: null,
|
|
228
|
+
snapshotVersion: null,
|
|
229
|
+
reconnectSuppressedReason: null,
|
|
230
|
+
snapshotHydrateFailed: false,
|
|
231
|
+
openScopeStack: [],
|
|
232
|
+
internalErrors: []
|
|
367
233
|
};
|
|
368
234
|
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
expectedApplicationKind
|
|
376
|
-
} = input;
|
|
377
|
-
if (profile === void 0 && policyOverride === void 0 && autoReconnect !== void 0) {
|
|
378
|
-
warnLegacyAutoReconnectOnce();
|
|
379
|
-
}
|
|
380
|
-
let base;
|
|
381
|
-
if (profile) {
|
|
382
|
-
base = { ...PROFILE_PRESETS[profile] };
|
|
383
|
-
} else {
|
|
384
|
-
base = {
|
|
385
|
-
replay: "history",
|
|
386
|
-
reconnect: legacyReconnect(autoReconnect, reconnectDelayMs),
|
|
387
|
-
terminal: legacyTerminal(expectedApplicationKind)
|
|
388
|
-
};
|
|
235
|
+
|
|
236
|
+
// src/types/workflowDisplay.ts
|
|
237
|
+
function readDisplayPayload(data) {
|
|
238
|
+
const display = data.display;
|
|
239
|
+
if (display != null && typeof display === "object" && !Array.isArray(display)) {
|
|
240
|
+
return display;
|
|
389
241
|
}
|
|
390
|
-
return
|
|
242
|
+
return null;
|
|
391
243
|
}
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
244
|
+
|
|
245
|
+
// src/core/workflow/workflowContainerProjector.ts
|
|
246
|
+
var OPEN_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
247
|
+
"workflow-start",
|
|
248
|
+
"subworkflow-start",
|
|
249
|
+
"stage-start",
|
|
250
|
+
"parallel-item-start",
|
|
251
|
+
"parallel-aggregate-start",
|
|
252
|
+
"route-branch-start"
|
|
253
|
+
]);
|
|
254
|
+
var CLOSE_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
255
|
+
"workflow-end",
|
|
256
|
+
"workflow-failed",
|
|
257
|
+
"subworkflow-end",
|
|
258
|
+
"stage-done",
|
|
259
|
+
"stage-failed",
|
|
260
|
+
"parallel-item-done",
|
|
261
|
+
"parallel-item-failed",
|
|
262
|
+
"parallel-aggregate-done",
|
|
263
|
+
"parallel-aggregate-failed",
|
|
264
|
+
"route-branch-done",
|
|
265
|
+
"route-branch-failed"
|
|
266
|
+
]);
|
|
267
|
+
var EVENT_TYPE_TO_SCOPE = {
|
|
268
|
+
"workflow-start": "workflow",
|
|
269
|
+
"workflow-end": "workflow",
|
|
270
|
+
"workflow-failed": "workflow",
|
|
271
|
+
"subworkflow-start": "subworkflow",
|
|
272
|
+
"subworkflow-end": "subworkflow",
|
|
273
|
+
"stage-start": "stage",
|
|
274
|
+
"stage-done": "stage",
|
|
275
|
+
"stage-failed": "stage",
|
|
276
|
+
"parallel-item-start": "item",
|
|
277
|
+
"parallel-item-done": "item",
|
|
278
|
+
"parallel-item-failed": "item",
|
|
279
|
+
"parallel-aggregate-start": "aggregate",
|
|
280
|
+
"parallel-aggregate-done": "aggregate",
|
|
281
|
+
"parallel-aggregate-failed": "aggregate",
|
|
282
|
+
"route-branch-start": "branch",
|
|
283
|
+
"route-branch-done": "branch",
|
|
284
|
+
"route-branch-failed": "branch"
|
|
285
|
+
};
|
|
286
|
+
var CONTENT_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
287
|
+
"reasoning-start",
|
|
288
|
+
"reasoning-delta",
|
|
289
|
+
"reasoning-end",
|
|
290
|
+
"text-start",
|
|
291
|
+
"text-delta",
|
|
292
|
+
"text-end",
|
|
293
|
+
"tool-input",
|
|
294
|
+
"tool-input-start",
|
|
295
|
+
"tool-input-delta",
|
|
296
|
+
"tool-call",
|
|
297
|
+
"tool-result",
|
|
298
|
+
"tool-error",
|
|
299
|
+
"tool-progress"
|
|
300
|
+
]);
|
|
301
|
+
var SCOPE_PRIORITY = {
|
|
302
|
+
workflow: 0,
|
|
303
|
+
subworkflow: 1,
|
|
304
|
+
aggregate: 2,
|
|
305
|
+
stage: 3,
|
|
306
|
+
item: 3,
|
|
307
|
+
branch: 3
|
|
308
|
+
};
|
|
309
|
+
var FAILED_CLOSE_TYPES = /* @__PURE__ */ new Set([
|
|
310
|
+
"workflow-failed",
|
|
311
|
+
"stage-failed",
|
|
312
|
+
"parallel-item-failed",
|
|
313
|
+
"parallel-aggregate-failed",
|
|
314
|
+
"route-branch-failed"
|
|
315
|
+
]);
|
|
316
|
+
function buildContainerId(workflowPath, scope, scopeKey) {
|
|
317
|
+
return `${workflowPath}|${scope}:${scopeKey}`;
|
|
318
|
+
}
|
|
319
|
+
function readString(data, key) {
|
|
320
|
+
const value = data[key];
|
|
321
|
+
return typeof value === "string" ? value : void 0;
|
|
322
|
+
}
|
|
323
|
+
function resolveScopeKey(eventType, data) {
|
|
324
|
+
if (eventType === "workflow-start" || eventType === "workflow-end" || eventType === "workflow-failed") {
|
|
325
|
+
const workflowId = readString(data, "workflow_id");
|
|
326
|
+
if (workflowId) return workflowId;
|
|
327
|
+
return readString(data, "workflow_path") ?? "workflow";
|
|
398
328
|
}
|
|
399
|
-
if (
|
|
400
|
-
return
|
|
329
|
+
if (eventType === "subworkflow-start" || eventType === "subworkflow-end") {
|
|
330
|
+
return readString(data, "child_workflow_id") ?? "subworkflow";
|
|
401
331
|
}
|
|
402
|
-
|
|
332
|
+
if (eventType === "stage-start" || eventType === "stage-done" || eventType === "stage-failed") {
|
|
333
|
+
const stageKey = readString(data, "stage_key");
|
|
334
|
+
if (stageKey) return stageKey;
|
|
335
|
+
const stageIndex = data.stage_index;
|
|
336
|
+
return typeof stageIndex === "number" ? String(stageIndex) : "stage";
|
|
337
|
+
}
|
|
338
|
+
if (eventType === "parallel-item-start" || eventType === "parallel-item-done" || eventType === "parallel-item-failed") {
|
|
339
|
+
const itemKey = readString(data, "item_key");
|
|
340
|
+
if (itemKey) return itemKey;
|
|
341
|
+
const itemIndex = data.item_index;
|
|
342
|
+
return typeof itemIndex === "number" ? String(itemIndex) : "item";
|
|
343
|
+
}
|
|
344
|
+
if (eventType === "parallel-aggregate-start" || eventType === "parallel-aggregate-done" || eventType === "parallel-aggregate-failed") {
|
|
345
|
+
return readString(data, "aggregate_key") ?? "aggregate";
|
|
346
|
+
}
|
|
347
|
+
if (eventType === "route-branch-start" || eventType === "route-branch-done" || eventType === "route-branch-failed") {
|
|
348
|
+
return readString(data, "branch_key") ?? readString(data, "branch_node") ?? "branch";
|
|
349
|
+
}
|
|
350
|
+
const display = readDisplayPayload(data);
|
|
351
|
+
if (display?.debug_key) return display.debug_key;
|
|
352
|
+
return "unknown";
|
|
403
353
|
}
|
|
404
|
-
function
|
|
405
|
-
|
|
354
|
+
function displayStatus(data, fallback) {
|
|
355
|
+
const display = readDisplayPayload(data);
|
|
356
|
+
if (display?.status) return display.status;
|
|
357
|
+
return fallback;
|
|
406
358
|
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
359
|
+
function closeStatus(eventType) {
|
|
360
|
+
return FAILED_CLOSE_TYPES.has(eventType) ? "failed" : "completed";
|
|
361
|
+
}
|
|
362
|
+
function readPreview(data, ...keys) {
|
|
363
|
+
for (const key of keys) {
|
|
364
|
+
const value = data[key];
|
|
365
|
+
if (typeof value === "string") return value;
|
|
412
366
|
}
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
367
|
+
return "";
|
|
368
|
+
}
|
|
369
|
+
function contentBlockFromEvent(event) {
|
|
370
|
+
const { event_type: eventType } = event;
|
|
371
|
+
const payload = event.data ?? {};
|
|
372
|
+
if (eventType === "reasoning-start" || eventType === "reasoning-delta" || eventType === "reasoning-end") {
|
|
373
|
+
let preview = readPreview(payload, "delta", "text", "reasoning");
|
|
374
|
+
if (!preview && eventType === "reasoning-start") preview = "Thinking\u2026";
|
|
375
|
+
return { kind: "reasoning", preview, tool_name: null };
|
|
419
376
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
return true;
|
|
424
|
-
}
|
|
425
|
-
if (Number.isFinite(this.maxReconnects) && this.attemptCount >= this.maxReconnects) {
|
|
426
|
-
return false;
|
|
427
|
-
}
|
|
428
|
-
if (Number.isFinite(this.timeoutMs) && nowMs - this.startedAtMs > this.timeoutMs) {
|
|
429
|
-
return false;
|
|
430
|
-
}
|
|
431
|
-
return true;
|
|
377
|
+
if (eventType === "text-start" || eventType === "text-delta" || eventType === "text-end") {
|
|
378
|
+
const preview = readPreview(payload, "delta", "text");
|
|
379
|
+
return { kind: "text", preview, tool_name: null };
|
|
432
380
|
}
|
|
433
|
-
|
|
434
|
-
|
|
381
|
+
if (CONTENT_EVENT_TYPES.has(eventType)) {
|
|
382
|
+
const toolName = event.tool_name ?? readString(payload, "tool_name");
|
|
383
|
+
const preview = readPreview(payload, "summary", "output", "input", "delta") || toolName || eventType;
|
|
384
|
+
return {
|
|
385
|
+
kind: "tool",
|
|
386
|
+
preview,
|
|
387
|
+
tool_name: toolName ?? null
|
|
388
|
+
};
|
|
435
389
|
}
|
|
436
|
-
|
|
437
|
-
function shouldAutoReconnect(reconnect) {
|
|
438
|
-
return reconnect.mode !== "off";
|
|
390
|
+
return null;
|
|
439
391
|
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
392
|
+
function wouldAssignParentCreateCycle(containersById, containerId, parentId) {
|
|
393
|
+
if (!parentId) return false;
|
|
394
|
+
if (parentId === containerId) return true;
|
|
395
|
+
const seen = /* @__PURE__ */ new Set();
|
|
396
|
+
let current = parentId;
|
|
397
|
+
while (current) {
|
|
398
|
+
if (current === containerId) return true;
|
|
399
|
+
if (seen.has(current)) return true;
|
|
400
|
+
seen.add(current);
|
|
401
|
+
current = containersById[current]?.parent_container_id ?? null;
|
|
449
402
|
}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
403
|
+
return false;
|
|
404
|
+
}
|
|
405
|
+
function sanitizeContainerTreeParentLinks(containersById) {
|
|
406
|
+
const next = { ...containersById };
|
|
407
|
+
for (const [containerId, node] of Object.entries(next)) {
|
|
408
|
+
const seen = /* @__PURE__ */ new Set([containerId]);
|
|
409
|
+
let current = node.parent_container_id;
|
|
410
|
+
while (current) {
|
|
411
|
+
if (seen.has(current)) {
|
|
412
|
+
next[containerId] = { ...node, parent_container_id: null };
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
415
|
+
seen.add(current);
|
|
416
|
+
current = next[current]?.parent_container_id ?? null;
|
|
417
|
+
}
|
|
457
418
|
}
|
|
419
|
+
return next;
|
|
458
420
|
}
|
|
459
|
-
function
|
|
460
|
-
|
|
461
|
-
const
|
|
462
|
-
|
|
421
|
+
function normalizeSnapshotContainerContentBlocks(containersById) {
|
|
422
|
+
const next = {};
|
|
423
|
+
for (const [containerId, node] of Object.entries(containersById)) {
|
|
424
|
+
const loopSessionId = node.loopSessionId ?? null;
|
|
425
|
+
next[containerId] = {
|
|
426
|
+
...node,
|
|
427
|
+
content_blocks: loopSessionId ? [] : node.content_blocks ?? []
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
return next;
|
|
463
431
|
}
|
|
464
|
-
function
|
|
465
|
-
const {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
policy: policyOverride,
|
|
469
|
-
autoReconnect,
|
|
470
|
-
reconnectDelayMs,
|
|
471
|
-
onLastEventId,
|
|
472
|
-
EventSourceImpl = EventSource,
|
|
473
|
-
expectedApplicationKind
|
|
474
|
-
} = options;
|
|
475
|
-
const transportPolicy = resolveSseTransportPolicy({
|
|
476
|
-
profile,
|
|
477
|
-
policy: policyOverride,
|
|
478
|
-
autoReconnect,
|
|
479
|
-
reconnectDelayMs,
|
|
480
|
-
expectedApplicationKind
|
|
481
|
-
});
|
|
482
|
-
const reconnectDelay = transportPolicy.reconnect.reconnectDelayMs;
|
|
483
|
-
const reconnectBudget = transportPolicy.reconnect.mode === "bounded" ? new ReconnectBudget(
|
|
484
|
-
transportPolicy.reconnect.maxReconnects,
|
|
485
|
-
transportPolicy.reconnect.reconnectTimeoutMs
|
|
486
|
-
) : null;
|
|
487
|
-
return {
|
|
488
|
-
start(handler, signal) {
|
|
489
|
-
return new Promise((resolve, reject) => {
|
|
490
|
-
let es = null;
|
|
491
|
-
let handshakeOk = false;
|
|
492
|
-
let lastEventId = null;
|
|
493
|
-
let fatal = false;
|
|
494
|
-
let streamTerminal = false;
|
|
495
|
-
let bufferedDelta = null;
|
|
496
|
-
let flushTimer = null;
|
|
497
|
-
const flushBufferedDelta = () => {
|
|
498
|
-
if (flushTimer) {
|
|
499
|
-
clearTimeout(flushTimer);
|
|
500
|
-
flushTimer = null;
|
|
501
|
-
}
|
|
502
|
-
if (!bufferedDelta) return;
|
|
503
|
-
handler(bufferedDelta.event, bufferedDelta.ctx);
|
|
504
|
-
bufferedDelta = null;
|
|
505
|
-
};
|
|
506
|
-
const isBufferedDeltaEvent = (event) => event.event_type === "text-delta" || event.event_type === "reasoning-delta";
|
|
507
|
-
const mergeDeltaEvent = (prev, next) => {
|
|
508
|
-
if (prev.event_type !== next.event_type) return null;
|
|
509
|
-
if (prev.step_index !== next.step_index) return null;
|
|
510
|
-
if (prev.tool_name !== next.tool_name) return null;
|
|
511
|
-
if (prev.session_id !== next.session_id) return null;
|
|
512
|
-
const prevData = prev.data;
|
|
513
|
-
const nextData = next.data;
|
|
514
|
-
const prevChunk = prevData.delta ?? prevData.text ?? prevData.content ?? "";
|
|
515
|
-
const nextChunk = nextData.delta ?? nextData.text ?? nextData.content ?? "";
|
|
516
|
-
return {
|
|
517
|
-
...next,
|
|
518
|
-
data: {
|
|
519
|
-
...next.data,
|
|
520
|
-
text: `${prevChunk}${nextChunk}`
|
|
521
|
-
}
|
|
522
|
-
};
|
|
523
|
-
};
|
|
524
|
-
const cleanup = () => {
|
|
525
|
-
es?.close();
|
|
526
|
-
es = null;
|
|
527
|
-
};
|
|
528
|
-
const finish = (error) => {
|
|
529
|
-
flushBufferedDelta();
|
|
530
|
-
cleanup();
|
|
531
|
-
if (error) reject(error);
|
|
532
|
-
else resolve();
|
|
533
|
-
};
|
|
534
|
-
const failHandshake = (error) => {
|
|
535
|
-
fatal = true;
|
|
536
|
-
transportLog(error.message, "error");
|
|
537
|
-
cleanup();
|
|
538
|
-
finish(error);
|
|
539
|
-
};
|
|
540
|
-
const failPrematureClose = () => {
|
|
541
|
-
const error = new SseTransportTerminalError(
|
|
542
|
-
"premature_close",
|
|
543
|
-
"SSE connection lost before stream finished"
|
|
544
|
-
);
|
|
545
|
-
transportLog(error.message, "error");
|
|
546
|
-
cleanup();
|
|
547
|
-
finish(error);
|
|
548
|
-
};
|
|
549
|
-
const connect = (resumeFromId) => {
|
|
550
|
-
if (signal.aborted || fatal) {
|
|
551
|
-
finish();
|
|
552
|
-
return;
|
|
553
|
-
}
|
|
554
|
-
const connectUrl = resumeFromId != null && resumeFromId !== "" ? buildResumeStreamUrl(url, resumeFromId) : url;
|
|
555
|
-
es = new EventSourceImpl(connectUrl);
|
|
556
|
-
handshakeOk = false;
|
|
557
|
-
es.addEventListener("meta", (event) => {
|
|
558
|
-
try {
|
|
559
|
-
const meta = JSON.parse(event.data);
|
|
560
|
-
if (meta.protocol_version !== EXPECTED_PROTOCOL_VERSION) {
|
|
561
|
-
failHandshake(
|
|
562
|
-
new Error(`Unsupported protocol_version: ${meta.protocol_version}`)
|
|
563
|
-
);
|
|
564
|
-
return;
|
|
565
|
-
}
|
|
566
|
-
if (expectedApplicationKind) {
|
|
567
|
-
const actual = meta.application_kind;
|
|
568
|
-
if (actual === void 0 || actual === null) {
|
|
569
|
-
failHandshake(new MissingApplicationKindError());
|
|
570
|
-
return;
|
|
571
|
-
}
|
|
572
|
-
if (actual !== expectedApplicationKind) {
|
|
573
|
-
failHandshake(
|
|
574
|
-
new ApplicationKindMismatchError(expectedApplicationKind, actual)
|
|
575
|
-
);
|
|
576
|
-
return;
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
handshakeOk = true;
|
|
580
|
-
} catch (err) {
|
|
581
|
-
const message = err instanceof Error ? err.message : "Failed to decode meta frame";
|
|
582
|
-
failHandshake(new Error(message));
|
|
583
|
-
}
|
|
584
|
-
});
|
|
585
|
-
es.addEventListener("agentx", (event) => {
|
|
586
|
-
if (!handshakeOk) {
|
|
587
|
-
failHandshake(new Error("Received agentx frame before meta handshake"));
|
|
588
|
-
return;
|
|
589
|
-
}
|
|
590
|
-
try {
|
|
591
|
-
const agentEvent = JSON.parse(event.data);
|
|
592
|
-
const sseEventId = event.lastEventId || void 0;
|
|
593
|
-
if (sseEventId) {
|
|
594
|
-
lastEventId = sseEventId;
|
|
595
|
-
onLastEventId?.(sseEventId);
|
|
596
|
-
}
|
|
597
|
-
const ctx = sseEventId ? { sseEventId } : void 0;
|
|
598
|
-
if (isBufferedDeltaEvent(agentEvent)) {
|
|
599
|
-
if (bufferedDelta) {
|
|
600
|
-
const merged = mergeDeltaEvent(bufferedDelta.event, agentEvent);
|
|
601
|
-
if (merged) {
|
|
602
|
-
bufferedDelta = { event: merged, ctx };
|
|
603
|
-
} else {
|
|
604
|
-
flushBufferedDelta();
|
|
605
|
-
bufferedDelta = { event: agentEvent, ctx };
|
|
606
|
-
}
|
|
607
|
-
} else {
|
|
608
|
-
bufferedDelta = { event: agentEvent, ctx };
|
|
609
|
-
}
|
|
610
|
-
if (!flushTimer) {
|
|
611
|
-
flushTimer = setTimeout(() => {
|
|
612
|
-
flushBufferedDelta();
|
|
613
|
-
}, 16);
|
|
614
|
-
}
|
|
615
|
-
} else {
|
|
616
|
-
flushBufferedDelta();
|
|
617
|
-
handler(agentEvent, ctx);
|
|
618
|
-
if (isSseStreamTerminalEvent(
|
|
619
|
-
agentEvent,
|
|
620
|
-
transportPolicy.terminal,
|
|
621
|
-
expectedApplicationKind
|
|
622
|
-
)) {
|
|
623
|
-
streamTerminal = true;
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
} catch (err) {
|
|
627
|
-
const message = err instanceof Error ? err.message : "Failed to decode agentx frame";
|
|
628
|
-
transportLog(`Dropped agentx frame: ${message}`, "warn");
|
|
629
|
-
}
|
|
630
|
-
});
|
|
631
|
-
es.onerror = () => {
|
|
632
|
-
flushBufferedDelta();
|
|
633
|
-
if (signal.aborted || fatal || streamTerminal) {
|
|
634
|
-
cleanup();
|
|
635
|
-
finish();
|
|
636
|
-
return;
|
|
637
|
-
}
|
|
638
|
-
if (!handshakeOk) {
|
|
639
|
-
const error = new SseTransportTerminalError(
|
|
640
|
-
"handshake_failed",
|
|
641
|
-
"SSE connection failed before meta handshake"
|
|
642
|
-
);
|
|
643
|
-
transportLog(error.message, "error");
|
|
644
|
-
cleanup();
|
|
645
|
-
finish(error);
|
|
646
|
-
return;
|
|
647
|
-
}
|
|
648
|
-
if (shouldAutoReconnect(transportPolicy.reconnect)) {
|
|
649
|
-
if (transportPolicy.reconnect.mode === "bounded" && reconnectBudget && !reconnectBudget.canRetry()) {
|
|
650
|
-
const error = new SseTransportTerminalError(
|
|
651
|
-
"reconnect_budget_exhausted",
|
|
652
|
-
"SSE reconnect budget exhausted"
|
|
653
|
-
);
|
|
654
|
-
transportLog(error.message, "error");
|
|
655
|
-
cleanup();
|
|
656
|
-
finish(error);
|
|
657
|
-
return;
|
|
658
|
-
}
|
|
659
|
-
if (reconnectBudget) {
|
|
660
|
-
reconnectBudget.recordAttempt();
|
|
661
|
-
}
|
|
662
|
-
cleanup();
|
|
663
|
-
setTimeout(() => connect(lastEventId), reconnectDelay);
|
|
664
|
-
return;
|
|
665
|
-
}
|
|
666
|
-
failPrematureClose();
|
|
667
|
-
};
|
|
668
|
-
};
|
|
669
|
-
signal.addEventListener("abort", () => {
|
|
670
|
-
flushBufferedDelta();
|
|
671
|
-
fatal = true;
|
|
672
|
-
finish();
|
|
673
|
-
}, { once: true });
|
|
674
|
-
connect();
|
|
675
|
-
});
|
|
676
|
-
}
|
|
677
|
-
};
|
|
678
|
-
}
|
|
679
|
-
function createMockSource(events) {
|
|
680
|
-
return {
|
|
681
|
-
async start(handler, signal) {
|
|
682
|
-
for (const event of events) {
|
|
683
|
-
if (signal.aborted) break;
|
|
684
|
-
handler(event);
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
};
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
// src/view/agentLoop/ScopedSessionStore.tsx
|
|
691
|
-
import { useEffect, useRef } from "react";
|
|
692
|
-
import { jsx } from "react/jsx-runtime";
|
|
693
|
-
function ScopedSessionStoreProvider({
|
|
694
|
-
tree,
|
|
695
|
-
children
|
|
696
|
-
}) {
|
|
697
|
-
const storeRef = useRef(createSessionStore(tree, { initialEventCount: 1 }));
|
|
698
|
-
useEffect(() => {
|
|
699
|
-
storeRef.current.setState({ tree });
|
|
700
|
-
}, [tree]);
|
|
701
|
-
return /* @__PURE__ */ jsx(SessionStoreContext.Provider, { value: storeRef.current, children });
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
// src/view/agentLoop/AgentLoopView.tsx
|
|
705
|
-
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
706
|
-
function AgentLoopViewBody({
|
|
707
|
-
showTaskListFooter = true,
|
|
708
|
-
virtualized,
|
|
709
|
-
virtualizeThreshold,
|
|
710
|
-
groupParallelTools
|
|
711
|
-
}) {
|
|
712
|
-
return /* @__PURE__ */ jsx2("div", { className: "lax-agent-loop-view", "data-testid": "lax-agent-loop-view", children: /* @__PURE__ */ jsx2(
|
|
713
|
-
SessionTimeline,
|
|
714
|
-
{
|
|
715
|
-
virtualized,
|
|
716
|
-
virtualizeThreshold,
|
|
717
|
-
groupParallelTools,
|
|
718
|
-
showTaskListFooter
|
|
719
|
-
}
|
|
720
|
-
) });
|
|
721
|
-
}
|
|
722
|
-
function AgentLoopView({
|
|
723
|
-
tree,
|
|
724
|
-
showTaskListFooter = true,
|
|
725
|
-
virtualized,
|
|
726
|
-
virtualizeThreshold,
|
|
727
|
-
groupParallelTools
|
|
728
|
-
}) {
|
|
729
|
-
if (tree) {
|
|
730
|
-
return /* @__PURE__ */ jsx2(ScopedSessionStoreProvider, { tree, children: /* @__PURE__ */ jsx2(
|
|
731
|
-
AgentLoopViewBody,
|
|
732
|
-
{
|
|
733
|
-
showTaskListFooter,
|
|
734
|
-
virtualized,
|
|
735
|
-
virtualizeThreshold,
|
|
736
|
-
groupParallelTools
|
|
737
|
-
}
|
|
738
|
-
) });
|
|
432
|
+
function resolveContentScopeKey(data) {
|
|
433
|
+
for (const keyName of ["stage_key", "item_key", "branch_key", "aggregate_key", "debug_key"]) {
|
|
434
|
+
const value = readString(data, keyName);
|
|
435
|
+
if (value) return value;
|
|
739
436
|
}
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
{
|
|
743
|
-
showTaskListFooter,
|
|
744
|
-
virtualized,
|
|
745
|
-
virtualizeThreshold,
|
|
746
|
-
groupParallelTools
|
|
747
|
-
}
|
|
748
|
-
);
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
// src/view/AgentSession.tsx
|
|
752
|
-
import { jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
753
|
-
function DebugPanel() {
|
|
754
|
-
const status = useSessionStatus();
|
|
755
|
-
const errors = useInternalErrors();
|
|
756
|
-
if (errors.length === 0) return null;
|
|
757
|
-
return /* @__PURE__ */ jsxs("div", { className: "lax-debug-panel", "data-testid": "lax-debug-panel", children: [
|
|
758
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
759
|
-
"status: ",
|
|
760
|
-
status
|
|
761
|
-
] }),
|
|
762
|
-
/* @__PURE__ */ jsx3("ul", { children: errors.map((err, i) => /* @__PURE__ */ jsxs("li", { children: [
|
|
763
|
-
"[",
|
|
764
|
-
err.eventType,
|
|
765
|
-
"] ",
|
|
766
|
-
err.message
|
|
767
|
-
] }, `${err.eventIndex}-${i}`)) })
|
|
768
|
-
] });
|
|
437
|
+
const display = readDisplayPayload(data);
|
|
438
|
+
return display?.debug_key ?? null;
|
|
769
439
|
}
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
virtualized = false,
|
|
779
|
-
virtualizeThreshold,
|
|
780
|
-
interactionBus,
|
|
781
|
-
toolDisplayRegistry,
|
|
782
|
-
defaultBodyMode = "preview",
|
|
783
|
-
groupParallelTools = false,
|
|
784
|
-
permissionUiMode = "standalone",
|
|
785
|
-
markdownRenderer,
|
|
786
|
-
displayMode = "normal",
|
|
787
|
-
verbose = false,
|
|
788
|
-
exploreFullscreenBash = true,
|
|
789
|
-
memoryDir = null,
|
|
790
|
-
workspaceRoot = null,
|
|
791
|
-
subagentLeakGate = false,
|
|
792
|
-
children
|
|
793
|
-
}) {
|
|
794
|
-
const storeRef = useRef2(null);
|
|
795
|
-
if (storeRef.current === null) {
|
|
796
|
-
const replayOpts = {
|
|
797
|
-
tierOverrides: _tierOverrides,
|
|
798
|
-
collectDebug: debug,
|
|
799
|
-
subagentLeakGate
|
|
800
|
-
};
|
|
801
|
-
const initialTree = initialEvents && initialEvents.length > 0 ? replayEvents(initialEvents, replayOpts) : void 0;
|
|
802
|
-
storeRef.current = createSessionStore(initialTree, {
|
|
803
|
-
tierOverrides: _tierOverrides,
|
|
804
|
-
debug,
|
|
805
|
-
initialEventCount: initialEvents?.length ?? 0,
|
|
806
|
-
subagentLeakGate
|
|
807
|
-
});
|
|
440
|
+
var WorkflowContainerActiveSet = class {
|
|
441
|
+
openOrder = [];
|
|
442
|
+
nodes = {};
|
|
443
|
+
restoreNode(node) {
|
|
444
|
+
this.nodes[node.container_id] = { ...node };
|
|
445
|
+
if (node.is_open && !this.openOrder.includes(node.container_id)) {
|
|
446
|
+
this.openOrder.push(node.container_id);
|
|
447
|
+
}
|
|
808
448
|
}
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
()
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
[
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
)
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
if (err instanceof SseTransportTerminalError) {
|
|
855
|
-
const { tree } = store.getState();
|
|
856
|
-
if (tree.status === "running" || tree.status === "connecting") {
|
|
857
|
-
store.setState({ tree: { ...tree, status: "error" } });
|
|
449
|
+
open(node) {
|
|
450
|
+
this.nodes[node.container_id] = node;
|
|
451
|
+
if (!this.openOrder.includes(node.container_id)) {
|
|
452
|
+
this.openOrder.push(node.container_id);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
close(containerId, status) {
|
|
456
|
+
const node = this.nodes[containerId];
|
|
457
|
+
if (!node) return;
|
|
458
|
+
node.status = status;
|
|
459
|
+
node.is_open = false;
|
|
460
|
+
const idx = this.openOrder.indexOf(containerId);
|
|
461
|
+
if (idx !== -1) this.openOrder.splice(idx, 1);
|
|
462
|
+
}
|
|
463
|
+
get activeIds() {
|
|
464
|
+
return [...this.openOrder];
|
|
465
|
+
}
|
|
466
|
+
getNode(containerId) {
|
|
467
|
+
return this.nodes[containerId];
|
|
468
|
+
}
|
|
469
|
+
get nodesById() {
|
|
470
|
+
return this.nodes;
|
|
471
|
+
}
|
|
472
|
+
rankContainer(containerId) {
|
|
473
|
+
const node = this.nodes[containerId];
|
|
474
|
+
if (!node) return [-1, -1, -1, -1];
|
|
475
|
+
const openIndex = this.openOrder.indexOf(containerId);
|
|
476
|
+
const scopePriority = SCOPE_PRIORITY[node.scope] ?? 0;
|
|
477
|
+
return [node.workflow_depth, scopePriority, node.workflow_path.length, openIndex];
|
|
478
|
+
}
|
|
479
|
+
deepestActive() {
|
|
480
|
+
if (this.openOrder.length === 0) return null;
|
|
481
|
+
return this.openOrder.reduce(
|
|
482
|
+
(best, cid) => this.compareRank(cid, best) > 0 ? cid : best
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
resolveContentTarget(workflowPath, scopeKey) {
|
|
486
|
+
if (scopeKey) {
|
|
487
|
+
for (let i = this.openOrder.length - 1; i >= 0; i -= 1) {
|
|
488
|
+
const containerId = this.openOrder[i];
|
|
489
|
+
const node = this.nodes[containerId];
|
|
490
|
+
if (node.scope_key === scopeKey && node.is_open) {
|
|
491
|
+
if (workflowPath === null || node.workflow_path === workflowPath) {
|
|
492
|
+
return containerId;
|
|
493
|
+
}
|
|
858
494
|
}
|
|
859
|
-
onErrorRef.current?.(err);
|
|
860
|
-
} else if (err instanceof Error) {
|
|
861
|
-
onErrorRef.current?.(err);
|
|
862
495
|
}
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
groupParallelTools
|
|
496
|
+
}
|
|
497
|
+
if (workflowPath) {
|
|
498
|
+
const candidates = this.openOrder.filter((cid) => {
|
|
499
|
+
const node = this.nodes[cid];
|
|
500
|
+
if (!node.is_open) return false;
|
|
501
|
+
return node.workflow_path === workflowPath || workflowPath.startsWith(`${node.workflow_path}>`);
|
|
502
|
+
});
|
|
503
|
+
if (candidates.length > 0) {
|
|
504
|
+
return candidates.reduce(
|
|
505
|
+
(best, cid) => this.compareRank(cid, best) > 0 ? cid : best
|
|
506
|
+
);
|
|
875
507
|
}
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
import { useEffect as useEffect10, useMemo as useMemo9, useRef as useRef4 } from "react";
|
|
883
|
-
|
|
884
|
-
// src/core/context/WorkflowSessionContext.tsx
|
|
885
|
-
import { createContext, useContext } from "react";
|
|
886
|
-
import { useStore } from "zustand";
|
|
887
|
-
|
|
888
|
-
// src/core/workflow/workflowAuthoritySelectors.ts
|
|
889
|
-
var AUTHORITY_TERMINAL = /* @__PURE__ */ new Set([
|
|
890
|
-
"done",
|
|
891
|
-
"error",
|
|
892
|
-
"canceled",
|
|
893
|
-
"partial_success"
|
|
894
|
-
]);
|
|
895
|
-
function isAuthorityTerminalStatus(status) {
|
|
896
|
-
return status != null && AUTHORITY_TERMINAL.has(status);
|
|
897
|
-
}
|
|
898
|
-
function isAuthorityTerminal(state) {
|
|
899
|
-
return isAuthorityTerminalStatus(state.authority?.status);
|
|
900
|
-
}
|
|
901
|
-
function shouldShowGlobalSpinner(state) {
|
|
902
|
-
const auth = state.authority?.status;
|
|
903
|
-
if (auth === "canceled") return false;
|
|
904
|
-
if (isAuthorityTerminalStatus(auth)) return false;
|
|
905
|
-
if (state.display.awaitingAuthority) return false;
|
|
906
|
-
if (auth == null || auth === "pending" || auth === "running") {
|
|
907
|
-
return true;
|
|
508
|
+
}
|
|
509
|
+
const openNonWorkflow = this.openOrder.map((cid) => this.nodes[cid]).filter((node) => node.is_open && node.scope !== "workflow");
|
|
510
|
+
if (openNonWorkflow.length === 1) {
|
|
511
|
+
return openNonWorkflow[0].container_id;
|
|
512
|
+
}
|
|
513
|
+
return null;
|
|
908
514
|
}
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
515
|
+
closeDescendantsOfPath(workflowPath, status) {
|
|
516
|
+
const toClose = this.openOrder.filter((cid) => {
|
|
517
|
+
const node = this.nodes[cid];
|
|
518
|
+
return node.workflow_path === workflowPath || node.workflow_path.startsWith(`${workflowPath}>`);
|
|
519
|
+
});
|
|
520
|
+
for (const containerId of toClose) {
|
|
521
|
+
this.close(containerId, status);
|
|
522
|
+
}
|
|
915
523
|
}
|
|
916
|
-
|
|
917
|
-
|
|
524
|
+
compareRank(a, b) {
|
|
525
|
+
const ra = this.rankContainer(a);
|
|
526
|
+
const rb = this.rankContainer(b);
|
|
527
|
+
for (let i = 0; i < ra.length; i += 1) {
|
|
528
|
+
if (ra[i] > rb[i]) return 1;
|
|
529
|
+
if (ra[i] < rb[i]) return -1;
|
|
530
|
+
}
|
|
531
|
+
return 0;
|
|
918
532
|
}
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
return
|
|
533
|
+
/** 供 StateMachine 比较容器深度 rank */
|
|
534
|
+
compareContainerRank(a, b) {
|
|
535
|
+
return this.compareRank(a, b);
|
|
922
536
|
}
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
537
|
+
};
|
|
538
|
+
var WorkflowContainerStateMachine = class {
|
|
539
|
+
active = new WorkflowContainerActiveSet();
|
|
540
|
+
rootIds = [];
|
|
541
|
+
suppressContentContainerIds = /* @__PURE__ */ new Set();
|
|
542
|
+
get activeSet() {
|
|
543
|
+
return this.active;
|
|
544
|
+
}
|
|
545
|
+
setSuppressContentContainerIds(ids) {
|
|
546
|
+
this.suppressContentContainerIds = ids;
|
|
547
|
+
}
|
|
548
|
+
rehydrate(snapshot) {
|
|
549
|
+
this.rootIds.length = 0;
|
|
550
|
+
this.rootIds.push(...snapshot.root_container_ids);
|
|
551
|
+
for (const node of Object.values(snapshot.containers)) {
|
|
552
|
+
this.active.restoreNode(node);
|
|
553
|
+
}
|
|
928
554
|
}
|
|
929
|
-
|
|
930
|
-
|
|
555
|
+
apply(event) {
|
|
556
|
+
const eventType = event.event_type;
|
|
557
|
+
const data = event.data ?? {};
|
|
558
|
+
if (OPEN_EVENT_TYPES.has(eventType)) {
|
|
559
|
+
this.openContainer(eventType, data);
|
|
560
|
+
return "open";
|
|
561
|
+
}
|
|
562
|
+
if (CLOSE_EVENT_TYPES.has(eventType)) {
|
|
563
|
+
this.closeContainer(eventType, data);
|
|
564
|
+
return "close";
|
|
565
|
+
}
|
|
566
|
+
if (CONTENT_EVENT_TYPES.has(eventType)) {
|
|
567
|
+
this.appendContent(event);
|
|
568
|
+
return "append-content";
|
|
569
|
+
}
|
|
570
|
+
return null;
|
|
931
571
|
}
|
|
932
|
-
|
|
933
|
-
const forced = state.display.terminalKind === "forced_closeout";
|
|
572
|
+
snapshot() {
|
|
934
573
|
return {
|
|
935
|
-
|
|
936
|
-
|
|
574
|
+
root_container_ids: [...this.rootIds],
|
|
575
|
+
containers: { ...this.active.nodesById },
|
|
576
|
+
active_container_ids: this.active.activeIds
|
|
937
577
|
};
|
|
938
578
|
}
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
const
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
579
|
+
openContainer(eventType, data) {
|
|
580
|
+
const scope = EVENT_TYPE_TO_SCOPE[eventType];
|
|
581
|
+
if (!scope) return;
|
|
582
|
+
const workflowPath = readString(data, "workflow_path") ?? "";
|
|
583
|
+
const workflowDepth = typeof data.workflow_depth === "number" ? data.workflow_depth : 0;
|
|
584
|
+
const scopeKey = resolveScopeKey(eventType, data);
|
|
585
|
+
const containerId = buildContainerId(workflowPath, scope, scopeKey);
|
|
586
|
+
const display = readDisplayPayload(data);
|
|
587
|
+
const title = display?.title ?? scopeKey;
|
|
588
|
+
const subtitle = display?.description ?? "";
|
|
589
|
+
const pattern = display?.pattern ?? "";
|
|
590
|
+
const status = displayStatus(data, "running");
|
|
591
|
+
const existing = this.active.getNode(containerId);
|
|
592
|
+
const computedParentId = this.resolveParentId(workflowPath, scope);
|
|
593
|
+
let parentId = existing != null ? existing.parent_container_id : computedParentId;
|
|
594
|
+
if (wouldAssignParentCreateCycle(this.active.nodesById, containerId, parentId)) {
|
|
595
|
+
parentId = existing?.parent_container_id ?? null;
|
|
596
|
+
}
|
|
597
|
+
const node = {
|
|
598
|
+
container_id: containerId,
|
|
599
|
+
scope,
|
|
600
|
+
workflow_path: workflowPath,
|
|
601
|
+
workflow_depth: workflowDepth,
|
|
602
|
+
scope_key: scopeKey,
|
|
603
|
+
title,
|
|
604
|
+
subtitle,
|
|
605
|
+
status,
|
|
606
|
+
pattern,
|
|
607
|
+
parent_container_id: parentId,
|
|
608
|
+
display,
|
|
609
|
+
content_blocks: existing?.content_blocks ?? [],
|
|
610
|
+
is_open: true
|
|
946
611
|
};
|
|
612
|
+
this.active.open(node);
|
|
613
|
+
if (scope === "workflow" && !this.rootIds.includes(containerId)) {
|
|
614
|
+
this.rootIds.push(containerId);
|
|
615
|
+
}
|
|
947
616
|
}
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
}
|
|
959
|
-
|
|
960
|
-
// src/core/context/WorkflowSessionContext.tsx
|
|
961
|
-
var WorkflowSessionStoreContext = createContext(null);
|
|
962
|
-
function useWorkflowSessionStoreApi() {
|
|
963
|
-
const store = useContext(WorkflowSessionStoreContext);
|
|
964
|
-
if (!store) {
|
|
965
|
-
throw new Error("useWorkflowSessionStoreApi must be used within WorkflowSession");
|
|
966
|
-
}
|
|
967
|
-
return store;
|
|
968
|
-
}
|
|
969
|
-
function useWorkflowSessionStatus() {
|
|
970
|
-
const store = useWorkflowSessionStoreApi();
|
|
971
|
-
return useStore(store, (s) => getDisplayProjectionStatus(s.state));
|
|
972
|
-
}
|
|
973
|
-
|
|
974
|
-
// src/core/context/WorkflowLoopReplayContext.tsx
|
|
975
|
-
import { createContext as createContext2, useContext as useContext2 } from "react";
|
|
976
|
-
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
977
|
-
var WorkflowLoopReplayContext = createContext2(
|
|
978
|
-
void 0
|
|
979
|
-
);
|
|
980
|
-
function WorkflowLoopReplayProvider({
|
|
981
|
-
onRequestLoopReplay,
|
|
982
|
-
children
|
|
983
|
-
}) {
|
|
984
|
-
return /* @__PURE__ */ jsx4(WorkflowLoopReplayContext.Provider, { value: onRequestLoopReplay, children });
|
|
985
|
-
}
|
|
986
|
-
function useWorkflowLoopReplayHandler() {
|
|
987
|
-
return useContext2(WorkflowLoopReplayContext);
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
// src/core/context/WorkflowScaleContext.tsx
|
|
991
|
-
import { createContext as createContext3, useContext as useContext3 } from "react";
|
|
992
|
-
|
|
993
|
-
// src/view/workflow/workflowScaleOptions.ts
|
|
994
|
-
var DEFAULT_WORKFLOW_SCALE_OPTIONS = {
|
|
995
|
-
stageVirtualizeThreshold: 40,
|
|
996
|
-
completedStagePageSize: 25,
|
|
997
|
-
evictCompletedLoops: true,
|
|
998
|
-
onlyExpandActiveRunning: true,
|
|
999
|
-
parallelItemVirtualizeThreshold: 40,
|
|
1000
|
-
maxHydratedCompletedLoops: 4
|
|
1001
|
-
};
|
|
1002
|
-
|
|
1003
|
-
// src/core/context/WorkflowScaleContext.tsx
|
|
1004
|
-
var WorkflowScaleContext = createContext3(
|
|
1005
|
-
DEFAULT_WORKFLOW_SCALE_OPTIONS
|
|
1006
|
-
);
|
|
1007
|
-
function useWorkflowScaleOptions() {
|
|
1008
|
-
return useContext3(WorkflowScaleContext);
|
|
1009
|
-
}
|
|
1010
|
-
|
|
1011
|
-
// src/core/workflowSessionStore.ts
|
|
1012
|
-
import { createStore as createStore2 } from "zustand/vanilla";
|
|
1013
|
-
|
|
1014
|
-
// src/types/workflowContainer.ts
|
|
1015
|
-
function createEmptyWorkflowContainerTreeState() {
|
|
1016
|
-
return {
|
|
1017
|
-
rootContainerIds: [],
|
|
1018
|
-
activeContainerIds: [],
|
|
1019
|
-
containersById: {}
|
|
1020
|
-
};
|
|
1021
|
-
}
|
|
1022
|
-
function containerTreeToSnapshot(tree) {
|
|
1023
|
-
const containers = {};
|
|
1024
|
-
for (const [id, node] of Object.entries(tree.containersById)) {
|
|
1025
|
-
const { loopSessionId: _loop, ...projection } = node;
|
|
1026
|
-
containers[id] = projection;
|
|
617
|
+
closeContainer(eventType, data) {
|
|
618
|
+
const scope = EVENT_TYPE_TO_SCOPE[eventType];
|
|
619
|
+
if (!scope) return;
|
|
620
|
+
const workflowPath = readString(data, "workflow_path") ?? "";
|
|
621
|
+
const scopeKey = resolveScopeKey(eventType, data);
|
|
622
|
+
const containerId = buildContainerId(workflowPath, scope, scopeKey);
|
|
623
|
+
const status = displayStatus(data, closeStatus(eventType));
|
|
624
|
+
this.active.close(containerId, status);
|
|
625
|
+
if (scope === "workflow") {
|
|
626
|
+
this.active.closeDescendantsOfPath(workflowPath, status);
|
|
627
|
+
}
|
|
1027
628
|
}
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
status: "running",
|
|
1052
|
-
stages: [],
|
|
1053
|
-
parallelItems: {}
|
|
1054
|
-
};
|
|
1055
|
-
}
|
|
1056
|
-
function createEmptyWorkflowSessionState() {
|
|
1057
|
-
return {
|
|
1058
|
-
authority: null,
|
|
1059
|
-
display: createEmptyWorkflowDisplayProjection(),
|
|
1060
|
-
meta: { workflowRunId: null, startedAt: null, lastEventId: null },
|
|
1061
|
-
containerTree: createEmptyWorkflowContainerTreeState(),
|
|
1062
|
-
workflowProgress: createEmptyWorkflowProgress(),
|
|
1063
|
-
loopTreesBySessionId: {},
|
|
1064
|
-
stageDoneSummariesByContainerId: {},
|
|
1065
|
-
hotCompletedLoopSessionIds: [],
|
|
1066
|
-
activeLoopSessionId: null,
|
|
1067
|
-
projectionCursor: null,
|
|
1068
|
-
snapshotVersion: null,
|
|
1069
|
-
reconnectSuppressedReason: null,
|
|
1070
|
-
snapshotHydrateFailed: false,
|
|
1071
|
-
openScopeStack: [],
|
|
1072
|
-
internalErrors: []
|
|
1073
|
-
};
|
|
1074
|
-
}
|
|
1075
|
-
|
|
1076
|
-
// src/core/workflow/workflowLoopTreeUtils.ts
|
|
1077
|
-
function isWorkflowLoopTreeActive(tree) {
|
|
1078
|
-
return tree?.status === "running" || tree?.status === "connecting";
|
|
1079
|
-
}
|
|
1080
|
-
function createWorkflowRunningLoopTree(seedMs = Date.now()) {
|
|
1081
|
-
const tree = createEmptyTree();
|
|
1082
|
-
tree.status = "running";
|
|
1083
|
-
tree.meta.startedAt = seedMs;
|
|
1084
|
-
return tree;
|
|
1085
|
-
}
|
|
1086
|
-
function finalizeWorkflowLoopTree(tree, terminalStatus = "done") {
|
|
1087
|
-
if (tree.status === "done" || tree.status === "error") {
|
|
1088
|
-
return tree;
|
|
629
|
+
appendContent(event) {
|
|
630
|
+
const data = event.data ?? {};
|
|
631
|
+
const workflowPath = readString(data, "workflow_path") ?? null;
|
|
632
|
+
const scopeKey = resolveContentScopeKey(data);
|
|
633
|
+
const targetId = this.active.resolveContentTarget(workflowPath, scopeKey);
|
|
634
|
+
if (!targetId) return;
|
|
635
|
+
if (this.suppressContentContainerIds.has(targetId)) return;
|
|
636
|
+
const block = contentBlockFromEvent(event);
|
|
637
|
+
if (!block) return;
|
|
638
|
+
const node = this.active.getNode(targetId);
|
|
639
|
+
if (!node) return;
|
|
640
|
+
const last = node.content_blocks[node.content_blocks.length - 1];
|
|
641
|
+
if (last && last.kind === block.kind && (block.kind === "reasoning" || block.kind === "text")) {
|
|
642
|
+
if (block.preview) {
|
|
643
|
+
if (block.kind === "reasoning" && last.preview === "Thinking\u2026") {
|
|
644
|
+
last.preview = block.preview;
|
|
645
|
+
} else {
|
|
646
|
+
last.preview = `${last.preview}${block.preview}`;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
node.content_blocks.push(block);
|
|
1089
652
|
}
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
if (
|
|
1094
|
-
|
|
653
|
+
resolveParentId(workflowPath, scope) {
|
|
654
|
+
if (scope === "workflow") return null;
|
|
655
|
+
const openNodes = this.active.activeIds.map((cid) => this.active.getNode(cid)).filter((node) => node.is_open);
|
|
656
|
+
if (openNodes.length === 0) return null;
|
|
657
|
+
const prefixCandidates = openNodes.filter(
|
|
658
|
+
(node) => workflowPath === node.workflow_path || workflowPath.startsWith(`${node.workflow_path}>`)
|
|
659
|
+
);
|
|
660
|
+
if (prefixCandidates.length > 0) {
|
|
661
|
+
const parentCandidates = prefixCandidates.filter((node) => {
|
|
662
|
+
if (scope === "item") return node.scope !== "item";
|
|
663
|
+
if (scope === "aggregate") return node.scope !== "item" && node.scope !== "aggregate";
|
|
664
|
+
return node.scope !== scope;
|
|
665
|
+
});
|
|
666
|
+
if (parentCandidates.length > 0) {
|
|
667
|
+
const parent = parentCandidates.reduce(
|
|
668
|
+
(best, node) => this.active.compareContainerRank(node.container_id, best.container_id) > 0 ? node : best
|
|
669
|
+
);
|
|
670
|
+
if (parent.workflow_path === workflowPath && parent.scope === "workflow") {
|
|
671
|
+
return parent.container_id;
|
|
672
|
+
}
|
|
673
|
+
if (workflowPath.startsWith(`${parent.workflow_path}>`)) {
|
|
674
|
+
return parent.container_id;
|
|
675
|
+
}
|
|
676
|
+
if (parent.workflow_path === workflowPath && parent.scope !== scope) {
|
|
677
|
+
return parent.container_id;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
1095
680
|
}
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
681
|
+
const workflowCandidates = openNodes.filter((node) => node.scope === "workflow");
|
|
682
|
+
if (workflowCandidates.length > 0) {
|
|
683
|
+
return workflowCandidates.reduce(
|
|
684
|
+
(best, node) => this.active.compareContainerRank(node.container_id, best.container_id) > 0 ? node : best
|
|
685
|
+
).container_id;
|
|
1101
686
|
}
|
|
687
|
+
return null;
|
|
1102
688
|
}
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
}
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
let maxTs = started;
|
|
1119
|
-
for (const node of Object.values(tree.byId)) {
|
|
1120
|
-
if ("endedAt" in node && typeof node.endedAt === "number") {
|
|
1121
|
-
maxTs = Math.max(maxTs, node.endedAt);
|
|
1122
|
-
}
|
|
1123
|
-
if ("startedAt" in node && typeof node.startedAt === "number") {
|
|
1124
|
-
maxTs = Math.max(maxTs, node.startedAt);
|
|
689
|
+
};
|
|
690
|
+
var WorkflowContainerTreeProjector = class {
|
|
691
|
+
machine = new WorkflowContainerStateMachine();
|
|
692
|
+
rehydrate(snapshot) {
|
|
693
|
+
this.machine.rehydrate(snapshot);
|
|
694
|
+
}
|
|
695
|
+
setSuppressContentContainerIds(ids) {
|
|
696
|
+
this.machine.setSuppressContentContainerIds(ids);
|
|
697
|
+
}
|
|
698
|
+
consume(event) {
|
|
699
|
+
return this.machine.apply(event);
|
|
700
|
+
}
|
|
701
|
+
consumeAll(events) {
|
|
702
|
+
for (const event of events) {
|
|
703
|
+
this.consume(event);
|
|
1125
704
|
}
|
|
705
|
+
return this.snapshot();
|
|
1126
706
|
}
|
|
1127
|
-
|
|
1128
|
-
|
|
707
|
+
snapshot() {
|
|
708
|
+
return this.machine.snapshot();
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
function reduceContainerSnapshot(snapshot, event, options) {
|
|
712
|
+
const projector = new WorkflowContainerTreeProjector();
|
|
713
|
+
projector.rehydrate(snapshot);
|
|
714
|
+
if (options?.suppressContentContainerIds?.size) {
|
|
715
|
+
projector.setSuppressContentContainerIds(options.suppressContentContainerIds);
|
|
716
|
+
}
|
|
717
|
+
projector.consume(event);
|
|
718
|
+
return projector.snapshot();
|
|
1129
719
|
}
|
|
1130
|
-
function
|
|
1131
|
-
const
|
|
1132
|
-
|
|
1133
|
-
|
|
720
|
+
function resolveContentTargetFromSnapshot(snapshot, workflowPath, scopeKey) {
|
|
721
|
+
const active = new WorkflowContainerActiveSet();
|
|
722
|
+
for (const containerId of snapshot.active_container_ids) {
|
|
723
|
+
const node = snapshot.containers[containerId];
|
|
724
|
+
if (node?.is_open) active.restoreNode(node);
|
|
725
|
+
}
|
|
726
|
+
return active.resolveContentTarget(workflowPath, scopeKey);
|
|
1134
727
|
}
|
|
1135
|
-
function
|
|
1136
|
-
const
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
728
|
+
function mergeRuntimeContainerNodes(prev, snapshot) {
|
|
729
|
+
const out = {};
|
|
730
|
+
for (const [containerId, node] of Object.entries(snapshot.containers)) {
|
|
731
|
+
const loopSessionId = prev[containerId]?.loopSessionId ?? null;
|
|
732
|
+
out[containerId] = {
|
|
733
|
+
...node,
|
|
734
|
+
loopSessionId,
|
|
735
|
+
// D24:已绑定 loop 的容器不得保留 content_blocks 预览(过程层 SSOT 在 loop tree)。
|
|
736
|
+
content_blocks: loopSessionId ? [] : node.content_blocks
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
return out;
|
|
1140
740
|
}
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
const
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
741
|
+
|
|
742
|
+
// src/core/workflow/hydrateWorkflowSnapshot.ts
|
|
743
|
+
function isAtOrBeforeProjectionCursor(projectionCursor, eventId) {
|
|
744
|
+
if (!projectionCursor || !eventId) return false;
|
|
745
|
+
const parseSeq = (id) => {
|
|
746
|
+
const prefixed = /^evt_(\d+)$/.exec(id);
|
|
747
|
+
if (prefixed) return Number.parseInt(prefixed[1], 10);
|
|
748
|
+
const numeric = Number.parseInt(id, 10);
|
|
749
|
+
return Number.isNaN(numeric) ? null : numeric;
|
|
750
|
+
};
|
|
751
|
+
const cursorSeq = parseSeq(projectionCursor);
|
|
752
|
+
const eventSeq = parseSeq(eventId);
|
|
753
|
+
if (cursorSeq != null && eventSeq != null) {
|
|
754
|
+
return eventSeq <= cursorSeq;
|
|
755
|
+
}
|
|
756
|
+
return eventId <= projectionCursor;
|
|
1152
757
|
}
|
|
1153
|
-
function
|
|
1154
|
-
|
|
1155
|
-
const
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
758
|
+
function hydrateWorkflowFromSnapshot(state, snapshot, options = {}) {
|
|
759
|
+
const authority = options.preserveAuthority ? state.authority : null;
|
|
760
|
+
const containerTree = structuredClone(snapshot.containerTree);
|
|
761
|
+
containerTree.containersById = normalizeSnapshotContainerContentBlocks(
|
|
762
|
+
sanitizeContainerTreeParentLinks(containerTree.containersById)
|
|
763
|
+
);
|
|
764
|
+
return {
|
|
765
|
+
...createEmptyWorkflowSessionState(),
|
|
766
|
+
authority,
|
|
767
|
+
display: {
|
|
768
|
+
...snapshot.display,
|
|
769
|
+
streamEnded: false
|
|
770
|
+
},
|
|
771
|
+
containerTree,
|
|
772
|
+
projectionCursor: snapshot.cursor,
|
|
773
|
+
snapshotVersion: snapshot.snapshotVersion,
|
|
774
|
+
meta: {
|
|
775
|
+
...state.meta,
|
|
776
|
+
workflowRunId: snapshot.taskId,
|
|
777
|
+
lastEventId: snapshot.cursor,
|
|
778
|
+
startedAt: state.meta.startedAt ?? snapshot.capturedAt
|
|
779
|
+
},
|
|
780
|
+
loopTreesBySessionId: {},
|
|
781
|
+
internalErrors: [],
|
|
782
|
+
reconnectSuppressedReason: null,
|
|
783
|
+
snapshotHydrateFailed: false
|
|
784
|
+
};
|
|
1159
785
|
}
|
|
1160
786
|
|
|
1161
|
-
// src/core/
|
|
1162
|
-
function
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
787
|
+
// src/core/hydrateAskSnapshot.ts
|
|
788
|
+
function sessionTreeSnapshotToTree(snapshot) {
|
|
789
|
+
return {
|
|
790
|
+
...structuredClone(snapshot),
|
|
791
|
+
internalErrors: [],
|
|
792
|
+
debugEvents: []
|
|
793
|
+
};
|
|
1168
794
|
}
|
|
1169
|
-
function
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
795
|
+
function hydrateAskFromSnapshot(state, snapshot) {
|
|
796
|
+
return {
|
|
797
|
+
tree: sessionTreeSnapshotToTree(snapshot.sessionTree),
|
|
798
|
+
eventCount: snapshot.metadata.reducerEventCount,
|
|
799
|
+
projectionCursor: snapshot.cursor,
|
|
800
|
+
snapshotVersion: snapshot.snapshotVersion,
|
|
801
|
+
snapshotHydrateFailed: false,
|
|
802
|
+
reconnectSuppressedReason: null,
|
|
803
|
+
authority: state.authority,
|
|
804
|
+
authoritySubscribeFailed: state.authoritySubscribeFailed,
|
|
805
|
+
authorityBindingActive: state.authorityBindingActive
|
|
806
|
+
};
|
|
1178
807
|
}
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
const node = state.containerTree.containersById[containerId];
|
|
1190
|
-
if (!node?.loopSessionId) continue;
|
|
1191
|
-
if (node.status === "running" || node.status === "retrying") {
|
|
1192
|
-
protectedIds.add(node.loopSessionId);
|
|
1193
|
-
}
|
|
1194
|
-
}
|
|
1195
|
-
for (const node of Object.values(state.containerTree.containersById)) {
|
|
1196
|
-
if (!node.loopSessionId) continue;
|
|
1197
|
-
if (node.status === "running" || node.status === "retrying") {
|
|
1198
|
-
protectedIds.add(node.loopSessionId);
|
|
1199
|
-
}
|
|
1200
|
-
if ((node.status === "completed" || node.status === "failed") && hasRunningParallelSibling(node, state.containerTree)) {
|
|
1201
|
-
protectedIds.add(node.loopSessionId);
|
|
808
|
+
|
|
809
|
+
// src/core/store.ts
|
|
810
|
+
function safeReduce(tree, event, eventIndex, options) {
|
|
811
|
+
try {
|
|
812
|
+
return { tree: reduceTree(tree, event, eventIndex, options) };
|
|
813
|
+
} catch (err) {
|
|
814
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
815
|
+
const stack = err instanceof Error ? err.stack : void 0;
|
|
816
|
+
if (import.meta.env?.DEV) {
|
|
817
|
+
console.error("[langchain_agentx_stream_ui] reducer error:", err);
|
|
1202
818
|
}
|
|
819
|
+
return {
|
|
820
|
+
tree: {
|
|
821
|
+
...tree,
|
|
822
|
+
internalErrors: [
|
|
823
|
+
...tree.internalErrors,
|
|
824
|
+
{ eventIndex, eventType: event.event_type, message, stack }
|
|
825
|
+
]
|
|
826
|
+
}
|
|
827
|
+
};
|
|
1203
828
|
}
|
|
1204
|
-
return protectedIds;
|
|
1205
829
|
}
|
|
1206
|
-
function
|
|
1207
|
-
const
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
const byLoop = containersByLoopSessionId(state.containerTree);
|
|
1212
|
-
const nextLoops = { ...state.loopTreesBySessionId };
|
|
1213
|
-
const nextSummaries = {
|
|
1214
|
-
...state.stageDoneSummariesByContainerId
|
|
830
|
+
function createSessionStore(initialTree = createEmptyTree(), storeOptions) {
|
|
831
|
+
const reduceOpts = {
|
|
832
|
+
tierOverrides: storeOptions?.tierOverrides,
|
|
833
|
+
collectDebug: storeOptions?.debug === true,
|
|
834
|
+
subagentLeakGate: storeOptions?.subagentLeakGate === true
|
|
1215
835
|
};
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
836
|
+
const initialEventCount = storeOptions?.initialEventCount ?? 0;
|
|
837
|
+
const seenEventIds = new Set(storeOptions?.initialSeenEventIds);
|
|
838
|
+
const authorityBindingActive = storeOptions?.authorityBindingActive === true;
|
|
839
|
+
return createStore((set, get) => ({
|
|
840
|
+
tree: initialTree,
|
|
841
|
+
eventCount: initialEventCount,
|
|
842
|
+
projectionCursor: null,
|
|
843
|
+
snapshotVersion: null,
|
|
844
|
+
snapshotHydrateFailed: false,
|
|
845
|
+
reconnectSuppressedReason: null,
|
|
846
|
+
authority: null,
|
|
847
|
+
authoritySubscribeFailed: false,
|
|
848
|
+
authorityBindingActive,
|
|
849
|
+
applyEvent(event, ctx) {
|
|
850
|
+
const sseEventId = ctx?.sseEventId;
|
|
851
|
+
if (shouldSkipDuplicateEvent(seenEventIds, sseEventId)) return;
|
|
852
|
+
const { projectionCursor } = get();
|
|
853
|
+
if (isAtOrBeforeProjectionCursor(projectionCursor, sseEventId)) return;
|
|
854
|
+
const { tree, eventCount } = get();
|
|
855
|
+
const { tree: reduced } = safeReduce(tree, event, eventCount, reduceOpts);
|
|
856
|
+
const nextTree = sseEventId ? {
|
|
857
|
+
...reduced,
|
|
858
|
+
meta: { ...reduced.meta, lastEventId: sseEventId }
|
|
859
|
+
} : reduced;
|
|
860
|
+
markEventIdSeen(seenEventIds, sseEventId);
|
|
861
|
+
set({ tree: nextTree, eventCount: eventCount + 1 });
|
|
862
|
+
},
|
|
863
|
+
applyEvents(events) {
|
|
864
|
+
let { tree, eventCount } = get();
|
|
865
|
+
for (const event of events) {
|
|
866
|
+
const result = safeReduce(tree, event, eventCount, reduceOpts);
|
|
867
|
+
tree = result.tree;
|
|
868
|
+
eventCount += 1;
|
|
869
|
+
}
|
|
870
|
+
set({ tree, eventCount });
|
|
871
|
+
},
|
|
872
|
+
applyAuthoritySnapshot(snapshot) {
|
|
873
|
+
set({ authority: snapshot });
|
|
874
|
+
},
|
|
875
|
+
setAuthoritySubscribeFailed(failed) {
|
|
876
|
+
set({ authoritySubscribeFailed: failed });
|
|
877
|
+
},
|
|
878
|
+
hydrateFromSnapshot(snapshot) {
|
|
879
|
+
const patch = hydrateAskFromSnapshot(get(), snapshot);
|
|
880
|
+
seenEventIds.clear();
|
|
881
|
+
markEventIdSeen(seenEventIds, snapshot.cursor);
|
|
882
|
+
set(patch);
|
|
883
|
+
},
|
|
884
|
+
markSnapshotHydrateFailed() {
|
|
885
|
+
set({
|
|
886
|
+
snapshotHydrateFailed: true,
|
|
887
|
+
reconnectSuppressedReason: "snapshot_unavailable"
|
|
888
|
+
});
|
|
889
|
+
},
|
|
890
|
+
reset() {
|
|
891
|
+
seenEventIds.clear();
|
|
892
|
+
set({
|
|
893
|
+
tree: createEmptyTree(),
|
|
894
|
+
eventCount: 0,
|
|
895
|
+
projectionCursor: null,
|
|
896
|
+
snapshotVersion: null,
|
|
897
|
+
snapshotHydrateFailed: false,
|
|
898
|
+
reconnectSuppressedReason: null,
|
|
899
|
+
authority: null,
|
|
900
|
+
authoritySubscribeFailed: false
|
|
901
|
+
});
|
|
902
|
+
},
|
|
903
|
+
markAsError(error) {
|
|
904
|
+
const { tree } = get();
|
|
905
|
+
set({
|
|
906
|
+
tree: {
|
|
907
|
+
...tree,
|
|
908
|
+
status: "error",
|
|
909
|
+
internalErrors: error ? [
|
|
910
|
+
...tree.internalErrors,
|
|
911
|
+
{
|
|
912
|
+
eventIndex: -1,
|
|
913
|
+
eventType: "sse_connection_failed",
|
|
914
|
+
message: error.message,
|
|
915
|
+
stack: error.stack
|
|
916
|
+
}
|
|
917
|
+
] : tree.internalErrors
|
|
918
|
+
}
|
|
919
|
+
});
|
|
1224
920
|
}
|
|
1225
|
-
|
|
1226
|
-
}
|
|
1227
|
-
return {
|
|
1228
|
-
...state,
|
|
1229
|
-
loopTreesBySessionId: nextLoops,
|
|
1230
|
-
stageDoneSummariesByContainerId: nextSummaries
|
|
1231
|
-
};
|
|
1232
|
-
}
|
|
1233
|
-
function touchCompletedLoopSession(state, loopSessionId) {
|
|
1234
|
-
const ids = state.hotCompletedLoopSessionIds.filter((id) => id !== loopSessionId);
|
|
1235
|
-
ids.push(loopSessionId);
|
|
1236
|
-
return {
|
|
1237
|
-
...state,
|
|
1238
|
-
hotCompletedLoopSessionIds: ids
|
|
1239
|
-
};
|
|
921
|
+
}));
|
|
1240
922
|
}
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
923
|
+
|
|
924
|
+
// src/core/agent/useAgentAuthorityBinding.ts
|
|
925
|
+
import { useEffect, useRef } from "react";
|
|
926
|
+
|
|
927
|
+
// src/core/workflow/normalizeAuthoritySnapshot.ts
|
|
928
|
+
var KNOWN = /* @__PURE__ */ new Set([
|
|
929
|
+
"pending",
|
|
930
|
+
"running",
|
|
931
|
+
"done",
|
|
932
|
+
"error",
|
|
933
|
+
"canceled",
|
|
934
|
+
"partial_success"
|
|
935
|
+
]);
|
|
936
|
+
function normalizeAuthoritySnapshot(snapshot) {
|
|
937
|
+
if (KNOWN.has(snapshot.status)) {
|
|
938
|
+
return snapshot;
|
|
1244
939
|
}
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
940
|
+
if (import.meta.env?.DEV) {
|
|
941
|
+
console.warn(
|
|
942
|
+
`[langchain_agentx_stream_ui] unknown authority.status "${String(snapshot.status)}"; defaulting to running`
|
|
943
|
+
);
|
|
944
|
+
}
|
|
945
|
+
return { ...snapshot, status: "running" };
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
// src/core/agent/useAgentAuthorityBinding.ts
|
|
949
|
+
function useAgentAuthorityBinding(store, authoritySource, callbacks) {
|
|
950
|
+
const onAuthorityTerminalRef = useRef(callbacks.onAuthorityTerminal);
|
|
951
|
+
onAuthorityTerminalRef.current = callbacks.onAuthorityTerminal;
|
|
952
|
+
const terminalFiredRef = useRef(false);
|
|
953
|
+
useEffect(() => {
|
|
954
|
+
terminalFiredRef.current = false;
|
|
955
|
+
store.getState().setAuthoritySubscribeFailed(false);
|
|
956
|
+
const apply = (raw) => {
|
|
957
|
+
const snapshot = normalizeAuthoritySnapshot(raw);
|
|
958
|
+
store.getState().applyAuthoritySnapshot(snapshot);
|
|
959
|
+
if (!terminalFiredRef.current && isAuthorityTerminalStatus(snapshot.status)) {
|
|
960
|
+
terminalFiredRef.current = true;
|
|
961
|
+
onAuthorityTerminalRef.current?.(snapshot);
|
|
1260
962
|
}
|
|
1261
|
-
|
|
963
|
+
};
|
|
964
|
+
const initial = authoritySource.getSnapshot();
|
|
965
|
+
if (initial) {
|
|
966
|
+
apply(initial);
|
|
1262
967
|
}
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
968
|
+
let unsubscribe;
|
|
969
|
+
try {
|
|
970
|
+
unsubscribe = authoritySource.subscribe(apply);
|
|
971
|
+
} catch (err) {
|
|
972
|
+
if (import.meta.env?.DEV) {
|
|
973
|
+
console.error("[langchain_agentx_stream_ui] authoritySource.subscribe failed:", err);
|
|
1267
974
|
}
|
|
975
|
+
store.getState().setAuthoritySubscribeFailed(true);
|
|
1268
976
|
}
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
loopTreesBySessionId: nextLoops,
|
|
1274
|
-
stageDoneSummariesByContainerId: nextSummaries,
|
|
1275
|
-
hotCompletedLoopSessionIds: trimmedIds
|
|
1276
|
-
};
|
|
977
|
+
return () => {
|
|
978
|
+
unsubscribe?.();
|
|
979
|
+
};
|
|
980
|
+
}, [store, authoritySource]);
|
|
1277
981
|
}
|
|
1278
982
|
|
|
1279
|
-
// src/
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
983
|
+
// src/transport/applicationKind.ts
|
|
984
|
+
var ApplicationKindMismatchError = class extends Error {
|
|
985
|
+
name = "ApplicationKindMismatchError";
|
|
986
|
+
expected;
|
|
987
|
+
actual;
|
|
988
|
+
constructor(expected, actual) {
|
|
989
|
+
super(
|
|
990
|
+
`application_kind mismatch: expected ${expected}, got ${actual ?? "(missing)"}`
|
|
991
|
+
);
|
|
992
|
+
this.expected = expected;
|
|
993
|
+
this.actual = actual;
|
|
1284
994
|
}
|
|
1285
|
-
return null;
|
|
1286
|
-
}
|
|
1287
|
-
|
|
1288
|
-
// src/core/workflow/workflowContainerProjector.ts
|
|
1289
|
-
var OPEN_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
1290
|
-
"workflow-start",
|
|
1291
|
-
"subworkflow-start",
|
|
1292
|
-
"stage-start",
|
|
1293
|
-
"parallel-item-start",
|
|
1294
|
-
"parallel-aggregate-start",
|
|
1295
|
-
"route-branch-start"
|
|
1296
|
-
]);
|
|
1297
|
-
var CLOSE_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
1298
|
-
"workflow-end",
|
|
1299
|
-
"workflow-failed",
|
|
1300
|
-
"subworkflow-end",
|
|
1301
|
-
"stage-done",
|
|
1302
|
-
"stage-failed",
|
|
1303
|
-
"parallel-item-done",
|
|
1304
|
-
"parallel-item-failed",
|
|
1305
|
-
"parallel-aggregate-done",
|
|
1306
|
-
"parallel-aggregate-failed",
|
|
1307
|
-
"route-branch-done",
|
|
1308
|
-
"route-branch-failed"
|
|
1309
|
-
]);
|
|
1310
|
-
var EVENT_TYPE_TO_SCOPE = {
|
|
1311
|
-
"workflow-start": "workflow",
|
|
1312
|
-
"workflow-end": "workflow",
|
|
1313
|
-
"workflow-failed": "workflow",
|
|
1314
|
-
"subworkflow-start": "subworkflow",
|
|
1315
|
-
"subworkflow-end": "subworkflow",
|
|
1316
|
-
"stage-start": "stage",
|
|
1317
|
-
"stage-done": "stage",
|
|
1318
|
-
"stage-failed": "stage",
|
|
1319
|
-
"parallel-item-start": "item",
|
|
1320
|
-
"parallel-item-done": "item",
|
|
1321
|
-
"parallel-item-failed": "item",
|
|
1322
|
-
"parallel-aggregate-start": "aggregate",
|
|
1323
|
-
"parallel-aggregate-done": "aggregate",
|
|
1324
|
-
"parallel-aggregate-failed": "aggregate",
|
|
1325
|
-
"route-branch-start": "branch",
|
|
1326
|
-
"route-branch-done": "branch",
|
|
1327
|
-
"route-branch-failed": "branch"
|
|
1328
|
-
};
|
|
1329
|
-
var CONTENT_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
1330
|
-
"reasoning-start",
|
|
1331
|
-
"reasoning-delta",
|
|
1332
|
-
"reasoning-end",
|
|
1333
|
-
"text-start",
|
|
1334
|
-
"text-delta",
|
|
1335
|
-
"text-end",
|
|
1336
|
-
"tool-input",
|
|
1337
|
-
"tool-input-start",
|
|
1338
|
-
"tool-input-delta",
|
|
1339
|
-
"tool-call",
|
|
1340
|
-
"tool-result",
|
|
1341
|
-
"tool-error",
|
|
1342
|
-
"tool-progress"
|
|
1343
|
-
]);
|
|
1344
|
-
var SCOPE_PRIORITY = {
|
|
1345
|
-
workflow: 0,
|
|
1346
|
-
subworkflow: 1,
|
|
1347
|
-
aggregate: 2,
|
|
1348
|
-
stage: 3,
|
|
1349
|
-
item: 3,
|
|
1350
|
-
branch: 3
|
|
1351
995
|
};
|
|
1352
|
-
var
|
|
1353
|
-
"
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
"parallel-aggregate-failed",
|
|
1357
|
-
"route-branch-failed"
|
|
1358
|
-
]);
|
|
1359
|
-
function buildContainerId(workflowPath, scope, scopeKey) {
|
|
1360
|
-
return `${workflowPath}|${scope}:${scopeKey}`;
|
|
1361
|
-
}
|
|
1362
|
-
function readString(data, key) {
|
|
1363
|
-
const value = data[key];
|
|
1364
|
-
return typeof value === "string" ? value : void 0;
|
|
1365
|
-
}
|
|
1366
|
-
function resolveScopeKey(eventType, data) {
|
|
1367
|
-
if (eventType === "workflow-start" || eventType === "workflow-end" || eventType === "workflow-failed") {
|
|
1368
|
-
const workflowId = readString(data, "workflow_id");
|
|
1369
|
-
if (workflowId) return workflowId;
|
|
1370
|
-
return readString(data, "workflow_path") ?? "workflow";
|
|
1371
|
-
}
|
|
1372
|
-
if (eventType === "subworkflow-start" || eventType === "subworkflow-end") {
|
|
1373
|
-
return readString(data, "child_workflow_id") ?? "subworkflow";
|
|
1374
|
-
}
|
|
1375
|
-
if (eventType === "stage-start" || eventType === "stage-done" || eventType === "stage-failed") {
|
|
1376
|
-
const stageKey = readString(data, "stage_key");
|
|
1377
|
-
if (stageKey) return stageKey;
|
|
1378
|
-
const stageIndex = data.stage_index;
|
|
1379
|
-
return typeof stageIndex === "number" ? String(stageIndex) : "stage";
|
|
996
|
+
var MissingApplicationKindError = class extends Error {
|
|
997
|
+
name = "MissingApplicationKindError";
|
|
998
|
+
constructor() {
|
|
999
|
+
super("application_kind missing in SSE meta frame");
|
|
1380
1000
|
}
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1001
|
+
};
|
|
1002
|
+
|
|
1003
|
+
// src/transport/sseTransportPolicy.ts
|
|
1004
|
+
var BOUNDED_DEFAULTS = {
|
|
1005
|
+
maxReconnects: 5,
|
|
1006
|
+
reconnectTimeoutMs: 6e4,
|
|
1007
|
+
reconnectDelayMs: 1e3
|
|
1008
|
+
};
|
|
1009
|
+
var legacyAutoReconnectWarned = false;
|
|
1010
|
+
function warnLegacyAutoReconnectOnce() {
|
|
1011
|
+
if (legacyAutoReconnectWarned) return;
|
|
1012
|
+
if (typeof process !== "undefined" && process.env.NODE_ENV === "production") {
|
|
1013
|
+
return;
|
|
1386
1014
|
}
|
|
1387
|
-
|
|
1388
|
-
|
|
1015
|
+
legacyAutoReconnectWarned = true;
|
|
1016
|
+
console.warn(
|
|
1017
|
+
"[langchain_agentx_stream_ui] autoReconnect is deprecated; use profile or policy.reconnect instead."
|
|
1018
|
+
);
|
|
1019
|
+
}
|
|
1020
|
+
var AGENT_TERMINAL = {
|
|
1021
|
+
primary: ["finish"],
|
|
1022
|
+
allowFinishFallback: false
|
|
1023
|
+
};
|
|
1024
|
+
var WORKFLOW_TERMINAL_STRICT = {
|
|
1025
|
+
primary: ["workflow-end", "workflow-failed"],
|
|
1026
|
+
allowFinishFallback: false
|
|
1027
|
+
};
|
|
1028
|
+
var WORKFLOW_TERMINAL_WITH_FALLBACK = {
|
|
1029
|
+
primary: ["workflow-end", "workflow-failed"],
|
|
1030
|
+
allowFinishFallback: true
|
|
1031
|
+
};
|
|
1032
|
+
var PROFILE_PRESETS = {
|
|
1033
|
+
oneshot: {
|
|
1034
|
+
profile: "oneshot",
|
|
1035
|
+
replay: "none",
|
|
1036
|
+
reconnect: {
|
|
1037
|
+
mode: "off",
|
|
1038
|
+
maxReconnects: 0,
|
|
1039
|
+
reconnectTimeoutMs: 0,
|
|
1040
|
+
reconnectDelayMs: BOUNDED_DEFAULTS.reconnectDelayMs
|
|
1041
|
+
},
|
|
1042
|
+
terminal: AGENT_TERMINAL
|
|
1043
|
+
},
|
|
1044
|
+
"oneshot-workflow": {
|
|
1045
|
+
profile: "oneshot-workflow",
|
|
1046
|
+
replay: "none",
|
|
1047
|
+
reconnect: {
|
|
1048
|
+
mode: "off",
|
|
1049
|
+
maxReconnects: 0,
|
|
1050
|
+
reconnectTimeoutMs: 0,
|
|
1051
|
+
reconnectDelayMs: BOUNDED_DEFAULTS.reconnectDelayMs
|
|
1052
|
+
},
|
|
1053
|
+
terminal: WORKFLOW_TERMINAL_WITH_FALLBACK
|
|
1054
|
+
},
|
|
1055
|
+
"replayable-workflow": {
|
|
1056
|
+
profile: "replayable-workflow",
|
|
1057
|
+
replay: "history",
|
|
1058
|
+
reconnect: {
|
|
1059
|
+
mode: "bounded",
|
|
1060
|
+
...BOUNDED_DEFAULTS
|
|
1061
|
+
},
|
|
1062
|
+
terminal: WORKFLOW_TERMINAL_WITH_FALLBACK
|
|
1063
|
+
},
|
|
1064
|
+
"workflow-tail": {
|
|
1065
|
+
profile: "workflow-tail",
|
|
1066
|
+
replay: "none",
|
|
1067
|
+
reconnect: {
|
|
1068
|
+
mode: "bounded",
|
|
1069
|
+
...BOUNDED_DEFAULTS
|
|
1070
|
+
},
|
|
1071
|
+
terminal: WORKFLOW_TERMINAL_WITH_FALLBACK
|
|
1072
|
+
},
|
|
1073
|
+
"replayable-ask": {
|
|
1074
|
+
profile: "replayable-ask",
|
|
1075
|
+
replay: "history",
|
|
1076
|
+
reconnect: {
|
|
1077
|
+
mode: "bounded",
|
|
1078
|
+
...BOUNDED_DEFAULTS
|
|
1079
|
+
},
|
|
1080
|
+
terminal: AGENT_TERMINAL
|
|
1081
|
+
},
|
|
1082
|
+
"ask-tail": {
|
|
1083
|
+
profile: "ask-tail",
|
|
1084
|
+
replay: "none",
|
|
1085
|
+
reconnect: {
|
|
1086
|
+
mode: "bounded",
|
|
1087
|
+
...BOUNDED_DEFAULTS
|
|
1088
|
+
},
|
|
1089
|
+
terminal: AGENT_TERMINAL
|
|
1389
1090
|
}
|
|
1390
|
-
|
|
1391
|
-
|
|
1091
|
+
};
|
|
1092
|
+
function legacyTerminal(expectedApplicationKind) {
|
|
1093
|
+
if (expectedApplicationKind === "workflow") {
|
|
1094
|
+
return WORKFLOW_TERMINAL_STRICT;
|
|
1392
1095
|
}
|
|
1393
|
-
|
|
1394
|
-
if (display?.debug_key) return display.debug_key;
|
|
1395
|
-
return "unknown";
|
|
1396
|
-
}
|
|
1397
|
-
function displayStatus(data, fallback) {
|
|
1398
|
-
const display = readDisplayPayload(data);
|
|
1399
|
-
if (display?.status) return display.status;
|
|
1400
|
-
return fallback;
|
|
1401
|
-
}
|
|
1402
|
-
function closeStatus(eventType) {
|
|
1403
|
-
return FAILED_CLOSE_TYPES.has(eventType) ? "failed" : "completed";
|
|
1096
|
+
return AGENT_TERMINAL;
|
|
1404
1097
|
}
|
|
1405
|
-
function
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1098
|
+
function legacyReconnect(autoReconnect, reconnectDelayMs) {
|
|
1099
|
+
if (autoReconnect === false) {
|
|
1100
|
+
return {
|
|
1101
|
+
mode: "off",
|
|
1102
|
+
maxReconnects: 0,
|
|
1103
|
+
reconnectTimeoutMs: 0,
|
|
1104
|
+
reconnectDelayMs: reconnectDelayMs ?? BOUNDED_DEFAULTS.reconnectDelayMs
|
|
1105
|
+
};
|
|
1409
1106
|
}
|
|
1410
|
-
return
|
|
1107
|
+
return {
|
|
1108
|
+
mode: "unbounded",
|
|
1109
|
+
maxReconnects: Number.POSITIVE_INFINITY,
|
|
1110
|
+
reconnectTimeoutMs: Number.POSITIVE_INFINITY,
|
|
1111
|
+
reconnectDelayMs: reconnectDelayMs ?? BOUNDED_DEFAULTS.reconnectDelayMs
|
|
1112
|
+
};
|
|
1411
1113
|
}
|
|
1412
|
-
function
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1114
|
+
function mergePolicy(base, override) {
|
|
1115
|
+
if (!override) return base;
|
|
1116
|
+
return {
|
|
1117
|
+
profile: base.profile,
|
|
1118
|
+
replay: override.replay ?? base.replay,
|
|
1119
|
+
reconnect: {
|
|
1120
|
+
...base.reconnect,
|
|
1121
|
+
...override.reconnect
|
|
1122
|
+
},
|
|
1123
|
+
terminal: {
|
|
1124
|
+
primary: override.terminal?.primary ?? base.terminal.primary,
|
|
1125
|
+
allowFinishFallback: override.terminal?.allowFinishFallback ?? base.terminal.allowFinishFallback
|
|
1126
|
+
}
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
function resolveSseTransportPolicy(input) {
|
|
1130
|
+
const {
|
|
1131
|
+
profile,
|
|
1132
|
+
policy: policyOverride,
|
|
1133
|
+
autoReconnect,
|
|
1134
|
+
reconnectDelayMs,
|
|
1135
|
+
expectedApplicationKind
|
|
1136
|
+
} = input;
|
|
1137
|
+
if (profile === void 0 && policyOverride === void 0 && autoReconnect !== void 0) {
|
|
1138
|
+
warnLegacyAutoReconnectOnce();
|
|
1423
1139
|
}
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1140
|
+
let base;
|
|
1141
|
+
if (profile) {
|
|
1142
|
+
base = { ...PROFILE_PRESETS[profile] };
|
|
1143
|
+
} else {
|
|
1144
|
+
base = {
|
|
1145
|
+
replay: "history",
|
|
1146
|
+
reconnect: legacyReconnect(autoReconnect, reconnectDelayMs),
|
|
1147
|
+
terminal: legacyTerminal(expectedApplicationKind)
|
|
1431
1148
|
};
|
|
1432
1149
|
}
|
|
1433
|
-
return
|
|
1150
|
+
return mergePolicy(base, policyOverride);
|
|
1434
1151
|
}
|
|
1435
|
-
function
|
|
1436
|
-
|
|
1437
|
-
if (
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
current = containersById[current]?.parent_container_id ?? null;
|
|
1152
|
+
function isSseStreamTerminalEvent(agentEvent, terminal, expectedApplicationKind) {
|
|
1153
|
+
const eventType = agentEvent.event_type;
|
|
1154
|
+
if (eventType === "error") return true;
|
|
1155
|
+
if (terminal.primary.includes(eventType)) return true;
|
|
1156
|
+
if (terminal.allowFinishFallback && eventType === "finish" && expectedApplicationKind === "workflow") {
|
|
1157
|
+
return true;
|
|
1158
|
+
}
|
|
1159
|
+
if (!profileUsesWorkflowTerminal(terminal) && expectedApplicationKind !== "workflow" && eventType === "finish") {
|
|
1160
|
+
return true;
|
|
1445
1161
|
}
|
|
1446
1162
|
return false;
|
|
1447
1163
|
}
|
|
1448
|
-
function
|
|
1449
|
-
|
|
1450
|
-
for (const [containerId, node] of Object.entries(next)) {
|
|
1451
|
-
const seen = /* @__PURE__ */ new Set([containerId]);
|
|
1452
|
-
let current = node.parent_container_id;
|
|
1453
|
-
while (current) {
|
|
1454
|
-
if (seen.has(current)) {
|
|
1455
|
-
next[containerId] = { ...node, parent_container_id: null };
|
|
1456
|
-
break;
|
|
1457
|
-
}
|
|
1458
|
-
seen.add(current);
|
|
1459
|
-
current = next[current]?.parent_container_id ?? null;
|
|
1460
|
-
}
|
|
1461
|
-
}
|
|
1462
|
-
return next;
|
|
1164
|
+
function profileUsesWorkflowTerminal(terminal) {
|
|
1165
|
+
return terminal.primary.includes("workflow-end") || terminal.primary.includes("workflow-failed");
|
|
1463
1166
|
}
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
...node,
|
|
1470
|
-
content_blocks: loopSessionId ? [] : node.content_blocks ?? []
|
|
1471
|
-
};
|
|
1167
|
+
var ReconnectBudget = class {
|
|
1168
|
+
constructor(maxReconnects, timeoutMs, startedAtMs = Date.now()) {
|
|
1169
|
+
this.maxReconnects = maxReconnects;
|
|
1170
|
+
this.timeoutMs = timeoutMs;
|
|
1171
|
+
this.startedAtMs = startedAtMs;
|
|
1472
1172
|
}
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1173
|
+
maxReconnects;
|
|
1174
|
+
timeoutMs;
|
|
1175
|
+
startedAtMs;
|
|
1176
|
+
attemptCount = 0;
|
|
1177
|
+
get attempts() {
|
|
1178
|
+
return this.attemptCount;
|
|
1479
1179
|
}
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
openOrder = [];
|
|
1485
|
-
nodes = {};
|
|
1486
|
-
restoreNode(node) {
|
|
1487
|
-
this.nodes[node.container_id] = { ...node };
|
|
1488
|
-
if (node.is_open && !this.openOrder.includes(node.container_id)) {
|
|
1489
|
-
this.openOrder.push(node.container_id);
|
|
1180
|
+
canRetry(nowMs = Date.now()) {
|
|
1181
|
+
if (this.maxReconnects <= 0) return false;
|
|
1182
|
+
if (!Number.isFinite(this.maxReconnects) && !Number.isFinite(this.timeoutMs)) {
|
|
1183
|
+
return true;
|
|
1490
1184
|
}
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
this.nodes[node.container_id] = node;
|
|
1494
|
-
if (!this.openOrder.includes(node.container_id)) {
|
|
1495
|
-
this.openOrder.push(node.container_id);
|
|
1185
|
+
if (Number.isFinite(this.maxReconnects) && this.attemptCount >= this.maxReconnects) {
|
|
1186
|
+
return false;
|
|
1496
1187
|
}
|
|
1188
|
+
if (Number.isFinite(this.timeoutMs) && nowMs - this.startedAtMs > this.timeoutMs) {
|
|
1189
|
+
return false;
|
|
1190
|
+
}
|
|
1191
|
+
return true;
|
|
1497
1192
|
}
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
if (!node) return;
|
|
1501
|
-
node.status = status;
|
|
1502
|
-
node.is_open = false;
|
|
1503
|
-
const idx = this.openOrder.indexOf(containerId);
|
|
1504
|
-
if (idx !== -1) this.openOrder.splice(idx, 1);
|
|
1505
|
-
}
|
|
1506
|
-
get activeIds() {
|
|
1507
|
-
return [...this.openOrder];
|
|
1508
|
-
}
|
|
1509
|
-
getNode(containerId) {
|
|
1510
|
-
return this.nodes[containerId];
|
|
1511
|
-
}
|
|
1512
|
-
get nodesById() {
|
|
1513
|
-
return this.nodes;
|
|
1193
|
+
recordAttempt() {
|
|
1194
|
+
this.attemptCount += 1;
|
|
1514
1195
|
}
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1196
|
+
};
|
|
1197
|
+
function shouldAutoReconnect(reconnect) {
|
|
1198
|
+
return reconnect.mode !== "off";
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
// src/transport/createAgentxSseSource.ts
|
|
1202
|
+
var EXPECTED_PROTOCOL_VERSION = "1";
|
|
1203
|
+
var SseTransportTerminalError = class extends Error {
|
|
1204
|
+
kind;
|
|
1205
|
+
constructor(kind, message) {
|
|
1206
|
+
super(message);
|
|
1207
|
+
this.name = "SseTransportTerminalError";
|
|
1208
|
+
this.kind = kind;
|
|
1521
1209
|
}
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
);
|
|
1210
|
+
};
|
|
1211
|
+
function transportLog(message, level = "warn") {
|
|
1212
|
+
const line = `[langchain_agentx_stream_ui] SSE transport: ${message}`;
|
|
1213
|
+
if (level === "error") {
|
|
1214
|
+
console.error(line);
|
|
1215
|
+
} else {
|
|
1216
|
+
console.warn(line);
|
|
1527
1217
|
}
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1218
|
+
}
|
|
1219
|
+
function buildResumeStreamUrl(baseUrl, lastEventId, queryParam = "last_event_id") {
|
|
1220
|
+
if (!lastEventId) return baseUrl;
|
|
1221
|
+
const sep = baseUrl.includes("?") ? "&" : "?";
|
|
1222
|
+
return `${baseUrl}${sep}${encodeURIComponent(queryParam)}=${encodeURIComponent(lastEventId)}`;
|
|
1223
|
+
}
|
|
1224
|
+
function createAgentxSseSource(options) {
|
|
1225
|
+
const {
|
|
1226
|
+
url,
|
|
1227
|
+
profile,
|
|
1228
|
+
policy: policyOverride,
|
|
1229
|
+
autoReconnect,
|
|
1230
|
+
reconnectDelayMs,
|
|
1231
|
+
onLastEventId,
|
|
1232
|
+
EventSourceImpl = EventSource,
|
|
1233
|
+
expectedApplicationKind
|
|
1234
|
+
} = options;
|
|
1235
|
+
const transportPolicy = resolveSseTransportPolicy({
|
|
1236
|
+
profile,
|
|
1237
|
+
policy: policyOverride,
|
|
1238
|
+
autoReconnect,
|
|
1239
|
+
reconnectDelayMs,
|
|
1240
|
+
expectedApplicationKind
|
|
1241
|
+
});
|
|
1242
|
+
const reconnectDelay = transportPolicy.reconnect.reconnectDelayMs;
|
|
1243
|
+
const reconnectBudget = transportPolicy.reconnect.mode === "bounded" ? new ReconnectBudget(
|
|
1244
|
+
transportPolicy.reconnect.maxReconnects,
|
|
1245
|
+
transportPolicy.reconnect.reconnectTimeoutMs
|
|
1246
|
+
) : null;
|
|
1247
|
+
return {
|
|
1248
|
+
start(handler, signal) {
|
|
1249
|
+
return new Promise((resolve, reject) => {
|
|
1250
|
+
let es = null;
|
|
1251
|
+
let handshakeOk = false;
|
|
1252
|
+
let lastEventId = null;
|
|
1253
|
+
let fatal = false;
|
|
1254
|
+
let streamTerminal = false;
|
|
1255
|
+
let bufferedDelta = null;
|
|
1256
|
+
let flushTimer = null;
|
|
1257
|
+
const flushBufferedDelta = () => {
|
|
1258
|
+
if (flushTimer) {
|
|
1259
|
+
clearTimeout(flushTimer);
|
|
1260
|
+
flushTimer = null;
|
|
1536
1261
|
}
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
const
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1262
|
+
if (!bufferedDelta) return;
|
|
1263
|
+
handler(bufferedDelta.event, bufferedDelta.ctx);
|
|
1264
|
+
bufferedDelta = null;
|
|
1265
|
+
};
|
|
1266
|
+
const isBufferedDeltaEvent = (event) => event.event_type === "text-delta" || event.event_type === "reasoning-delta";
|
|
1267
|
+
const mergeDeltaEvent = (prev, next) => {
|
|
1268
|
+
if (prev.event_type !== next.event_type) return null;
|
|
1269
|
+
if (prev.step_index !== next.step_index) return null;
|
|
1270
|
+
if (prev.tool_name !== next.tool_name) return null;
|
|
1271
|
+
if (prev.session_id !== next.session_id) return null;
|
|
1272
|
+
const prevData = prev.data;
|
|
1273
|
+
const nextData = next.data;
|
|
1274
|
+
const prevChunk = prevData.delta ?? prevData.text ?? prevData.content ?? "";
|
|
1275
|
+
const nextChunk = nextData.delta ?? nextData.text ?? nextData.content ?? "";
|
|
1276
|
+
return {
|
|
1277
|
+
...next,
|
|
1278
|
+
data: {
|
|
1279
|
+
...next.data,
|
|
1280
|
+
text: `${prevChunk}${nextChunk}`
|
|
1281
|
+
}
|
|
1282
|
+
};
|
|
1283
|
+
};
|
|
1284
|
+
const cleanup = () => {
|
|
1285
|
+
es?.close();
|
|
1286
|
+
es = null;
|
|
1287
|
+
};
|
|
1288
|
+
const finish = (error) => {
|
|
1289
|
+
flushBufferedDelta();
|
|
1290
|
+
cleanup();
|
|
1291
|
+
if (error) reject(error);
|
|
1292
|
+
else resolve();
|
|
1293
|
+
};
|
|
1294
|
+
const failHandshake = (error) => {
|
|
1295
|
+
fatal = true;
|
|
1296
|
+
transportLog(error.message, "error");
|
|
1297
|
+
cleanup();
|
|
1298
|
+
finish(error);
|
|
1299
|
+
};
|
|
1300
|
+
const failPrematureClose = () => {
|
|
1301
|
+
const error = new SseTransportTerminalError(
|
|
1302
|
+
"premature_close",
|
|
1303
|
+
"SSE connection lost before stream finished"
|
|
1304
|
+
);
|
|
1305
|
+
transportLog(error.message, "error");
|
|
1306
|
+
cleanup();
|
|
1307
|
+
finish(error);
|
|
1308
|
+
};
|
|
1309
|
+
const connect = (resumeFromId) => {
|
|
1310
|
+
if (signal.aborted || fatal) {
|
|
1311
|
+
finish();
|
|
1312
|
+
return;
|
|
1313
|
+
}
|
|
1314
|
+
const connectUrl = resumeFromId != null && resumeFromId !== "" ? buildResumeStreamUrl(url, resumeFromId) : url;
|
|
1315
|
+
es = new EventSourceImpl(connectUrl);
|
|
1316
|
+
handshakeOk = false;
|
|
1317
|
+
es.addEventListener("meta", (event) => {
|
|
1318
|
+
try {
|
|
1319
|
+
const meta = JSON.parse(event.data);
|
|
1320
|
+
if (meta.protocol_version !== EXPECTED_PROTOCOL_VERSION) {
|
|
1321
|
+
failHandshake(
|
|
1322
|
+
new Error(`Unsupported protocol_version: ${meta.protocol_version}`)
|
|
1323
|
+
);
|
|
1324
|
+
return;
|
|
1325
|
+
}
|
|
1326
|
+
if (expectedApplicationKind) {
|
|
1327
|
+
const actual = meta.application_kind;
|
|
1328
|
+
if (actual === void 0 || actual === null) {
|
|
1329
|
+
failHandshake(new MissingApplicationKindError());
|
|
1330
|
+
return;
|
|
1331
|
+
}
|
|
1332
|
+
if (actual !== expectedApplicationKind) {
|
|
1333
|
+
failHandshake(
|
|
1334
|
+
new ApplicationKindMismatchError(expectedApplicationKind, actual)
|
|
1335
|
+
);
|
|
1336
|
+
return;
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
handshakeOk = true;
|
|
1340
|
+
} catch (err) {
|
|
1341
|
+
const message = err instanceof Error ? err.message : "Failed to decode meta frame";
|
|
1342
|
+
failHandshake(new Error(message));
|
|
1343
|
+
}
|
|
1344
|
+
});
|
|
1345
|
+
es.addEventListener("agentx", (event) => {
|
|
1346
|
+
if (!handshakeOk) {
|
|
1347
|
+
failHandshake(new Error("Received agentx frame before meta handshake"));
|
|
1348
|
+
return;
|
|
1349
|
+
}
|
|
1350
|
+
try {
|
|
1351
|
+
const agentEvent = JSON.parse(event.data);
|
|
1352
|
+
const sseEventId = event.lastEventId || void 0;
|
|
1353
|
+
if (sseEventId) {
|
|
1354
|
+
lastEventId = sseEventId;
|
|
1355
|
+
onLastEventId?.(sseEventId);
|
|
1356
|
+
}
|
|
1357
|
+
const ctx = sseEventId ? { sseEventId } : void 0;
|
|
1358
|
+
if (isBufferedDeltaEvent(agentEvent)) {
|
|
1359
|
+
if (bufferedDelta) {
|
|
1360
|
+
const merged = mergeDeltaEvent(bufferedDelta.event, agentEvent);
|
|
1361
|
+
if (merged) {
|
|
1362
|
+
bufferedDelta = { event: merged, ctx };
|
|
1363
|
+
} else {
|
|
1364
|
+
flushBufferedDelta();
|
|
1365
|
+
bufferedDelta = { event: agentEvent, ctx };
|
|
1366
|
+
}
|
|
1367
|
+
} else {
|
|
1368
|
+
bufferedDelta = { event: agentEvent, ctx };
|
|
1369
|
+
}
|
|
1370
|
+
if (!flushTimer) {
|
|
1371
|
+
flushTimer = setTimeout(() => {
|
|
1372
|
+
flushBufferedDelta();
|
|
1373
|
+
}, 16);
|
|
1374
|
+
}
|
|
1375
|
+
} else {
|
|
1376
|
+
flushBufferedDelta();
|
|
1377
|
+
handler(agentEvent, ctx);
|
|
1378
|
+
if (isSseStreamTerminalEvent(
|
|
1379
|
+
agentEvent,
|
|
1380
|
+
transportPolicy.terminal,
|
|
1381
|
+
expectedApplicationKind
|
|
1382
|
+
)) {
|
|
1383
|
+
streamTerminal = true;
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
} catch (err) {
|
|
1387
|
+
const message = err instanceof Error ? err.message : "Failed to decode agentx frame";
|
|
1388
|
+
transportLog(`Dropped agentx frame: ${message}`, "warn");
|
|
1389
|
+
}
|
|
1390
|
+
});
|
|
1391
|
+
es.onerror = () => {
|
|
1392
|
+
flushBufferedDelta();
|
|
1393
|
+
if (signal.aborted || fatal || streamTerminal) {
|
|
1394
|
+
cleanup();
|
|
1395
|
+
finish();
|
|
1396
|
+
return;
|
|
1397
|
+
}
|
|
1398
|
+
if (!handshakeOk) {
|
|
1399
|
+
const error = new SseTransportTerminalError(
|
|
1400
|
+
"handshake_failed",
|
|
1401
|
+
"SSE connection failed before meta handshake"
|
|
1402
|
+
);
|
|
1403
|
+
transportLog(error.message, "error");
|
|
1404
|
+
cleanup();
|
|
1405
|
+
finish(error);
|
|
1406
|
+
return;
|
|
1407
|
+
}
|
|
1408
|
+
if (shouldAutoReconnect(transportPolicy.reconnect)) {
|
|
1409
|
+
if (transportPolicy.reconnect.mode === "bounded" && reconnectBudget && !reconnectBudget.canRetry()) {
|
|
1410
|
+
const error = new SseTransportTerminalError(
|
|
1411
|
+
"reconnect_budget_exhausted",
|
|
1412
|
+
"SSE reconnect budget exhausted"
|
|
1413
|
+
);
|
|
1414
|
+
transportLog(error.message, "error");
|
|
1415
|
+
cleanup();
|
|
1416
|
+
finish(error);
|
|
1417
|
+
return;
|
|
1418
|
+
}
|
|
1419
|
+
if (reconnectBudget) {
|
|
1420
|
+
reconnectBudget.recordAttempt();
|
|
1421
|
+
}
|
|
1422
|
+
cleanup();
|
|
1423
|
+
setTimeout(() => connect(lastEventId), reconnectDelay);
|
|
1424
|
+
return;
|
|
1425
|
+
}
|
|
1426
|
+
failPrematureClose();
|
|
1427
|
+
};
|
|
1428
|
+
};
|
|
1429
|
+
signal.addEventListener("abort", () => {
|
|
1430
|
+
flushBufferedDelta();
|
|
1431
|
+
fatal = true;
|
|
1432
|
+
finish();
|
|
1433
|
+
}, { once: true });
|
|
1434
|
+
connect();
|
|
1435
|
+
});
|
|
1555
1436
|
}
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1437
|
+
};
|
|
1438
|
+
}
|
|
1439
|
+
function createMockSource(events) {
|
|
1440
|
+
return {
|
|
1441
|
+
async start(handler, signal) {
|
|
1442
|
+
for (const event of events) {
|
|
1443
|
+
if (signal.aborted) break;
|
|
1444
|
+
handler(event);
|
|
1445
|
+
}
|
|
1565
1446
|
}
|
|
1566
|
-
}
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
// src/view/agentLoop/ScopedSessionStore.tsx
|
|
1451
|
+
import { useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
1452
|
+
import { jsx } from "react/jsx-runtime";
|
|
1453
|
+
function ScopedSessionStoreProvider({
|
|
1454
|
+
tree,
|
|
1455
|
+
children
|
|
1456
|
+
}) {
|
|
1457
|
+
const storeRef = useRef2(createSessionStore(tree, { initialEventCount: 1 }));
|
|
1458
|
+
useEffect2(() => {
|
|
1459
|
+
storeRef.current.setState({ tree });
|
|
1460
|
+
}, [tree]);
|
|
1461
|
+
return /* @__PURE__ */ jsx(SessionStoreContext.Provider, { value: storeRef.current, children });
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
// src/view/agentLoop/AgentLoopView.tsx
|
|
1465
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
1466
|
+
function AgentLoopViewBody({
|
|
1467
|
+
showTaskListFooter = true,
|
|
1468
|
+
virtualized,
|
|
1469
|
+
virtualizeThreshold,
|
|
1470
|
+
groupParallelTools
|
|
1471
|
+
}) {
|
|
1472
|
+
return /* @__PURE__ */ jsx2("div", { className: "lax-agent-loop-view", "data-testid": "lax-agent-loop-view", children: /* @__PURE__ */ jsx2(
|
|
1473
|
+
SessionTimeline,
|
|
1474
|
+
{
|
|
1475
|
+
virtualized,
|
|
1476
|
+
virtualizeThreshold,
|
|
1477
|
+
groupParallelTools,
|
|
1478
|
+
showTaskListFooter
|
|
1573
1479
|
}
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1480
|
+
) });
|
|
1481
|
+
}
|
|
1482
|
+
function AgentLoopView({
|
|
1483
|
+
tree,
|
|
1484
|
+
showTaskListFooter = true,
|
|
1485
|
+
virtualized,
|
|
1486
|
+
virtualizeThreshold,
|
|
1487
|
+
groupParallelTools
|
|
1488
|
+
}) {
|
|
1489
|
+
if (tree) {
|
|
1490
|
+
return /* @__PURE__ */ jsx2(ScopedSessionStoreProvider, { tree, children: /* @__PURE__ */ jsx2(
|
|
1491
|
+
AgentLoopViewBody,
|
|
1492
|
+
{
|
|
1493
|
+
showTaskListFooter,
|
|
1494
|
+
virtualized,
|
|
1495
|
+
virtualizeThreshold,
|
|
1496
|
+
groupParallelTools
|
|
1497
|
+
}
|
|
1498
|
+
) });
|
|
1590
1499
|
}
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1500
|
+
return /* @__PURE__ */ jsx2(
|
|
1501
|
+
AgentLoopViewBody,
|
|
1502
|
+
{
|
|
1503
|
+
showTaskListFooter,
|
|
1504
|
+
virtualized,
|
|
1505
|
+
virtualizeThreshold,
|
|
1506
|
+
groupParallelTools
|
|
1596
1507
|
}
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1508
|
+
);
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
// src/view/AgentSessionBanners.tsx
|
|
1512
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
1513
|
+
function AgentSessionBanners({ snapshotHydrateFailed }) {
|
|
1514
|
+
if (!snapshotHydrateFailed) return null;
|
|
1515
|
+
return /* @__PURE__ */ jsx3(
|
|
1516
|
+
"div",
|
|
1517
|
+
{
|
|
1518
|
+
className: "lax-agent-banner lax-agent-banner--snapshot-unavailable",
|
|
1519
|
+
"data-testid": "lax-agent-snapshot-unavailable-banner",
|
|
1520
|
+
role: "status",
|
|
1521
|
+
children: "\u5C55\u793A\u6001\u4E0D\u53EF\u7528 / \u4F1A\u8BDD\u5DF2\u8FC7\u671F"
|
|
1604
1522
|
}
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1523
|
+
);
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
// src/view/AgentSession.tsx
|
|
1527
|
+
import { useStore } from "zustand";
|
|
1528
|
+
import { jsx as jsx4, jsxs } from "react/jsx-runtime";
|
|
1529
|
+
function DebugPanel() {
|
|
1530
|
+
const status = useSessionStatus();
|
|
1531
|
+
const errors = useInternalErrors();
|
|
1532
|
+
if (errors.length === 0) return null;
|
|
1533
|
+
return /* @__PURE__ */ jsxs("div", { className: "lax-debug-panel", "data-testid": "lax-debug-panel", children: [
|
|
1534
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1535
|
+
"status: ",
|
|
1536
|
+
status
|
|
1537
|
+
] }),
|
|
1538
|
+
/* @__PURE__ */ jsx4("ul", { children: errors.map((err, i) => /* @__PURE__ */ jsxs("li", { children: [
|
|
1539
|
+
"[",
|
|
1540
|
+
err.eventType,
|
|
1541
|
+
"] ",
|
|
1542
|
+
err.message
|
|
1543
|
+
] }, `${err.eventIndex}-${i}`)) })
|
|
1544
|
+
] });
|
|
1545
|
+
}
|
|
1546
|
+
function AgentAuthorityBinder({
|
|
1547
|
+
store,
|
|
1548
|
+
authoritySource,
|
|
1549
|
+
onAuthorityTerminal
|
|
1550
|
+
}) {
|
|
1551
|
+
useAgentAuthorityBinding(store, authoritySource, { onAuthorityTerminal });
|
|
1552
|
+
return null;
|
|
1553
|
+
}
|
|
1554
|
+
function AgentSnapshotBootstrap({
|
|
1555
|
+
store,
|
|
1556
|
+
snapshotBootstrap,
|
|
1557
|
+
snapshotHydrateFailed = false
|
|
1558
|
+
}) {
|
|
1559
|
+
useEffect3(() => {
|
|
1560
|
+
if (snapshotBootstrap) {
|
|
1561
|
+
store.getState().hydrateFromSnapshot(snapshotBootstrap);
|
|
1608
1562
|
}
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1563
|
+
}, [snapshotBootstrap, store]);
|
|
1564
|
+
useEffect3(() => {
|
|
1565
|
+
if (snapshotHydrateFailed) {
|
|
1566
|
+
store.getState().markSnapshotHydrateFailed();
|
|
1612
1567
|
}
|
|
1613
|
-
|
|
1568
|
+
}, [snapshotHydrateFailed, store]);
|
|
1569
|
+
const showBanner = useStore(store, (s) => s.snapshotHydrateFailed);
|
|
1570
|
+
return /* @__PURE__ */ jsx4(AgentSessionBanners, { snapshotHydrateFailed: showBanner });
|
|
1571
|
+
}
|
|
1572
|
+
function AgentSession({
|
|
1573
|
+
source,
|
|
1574
|
+
authoritySource,
|
|
1575
|
+
onAuthorityTerminal,
|
|
1576
|
+
initialEvents,
|
|
1577
|
+
registry,
|
|
1578
|
+
tierOverrides: _tierOverrides,
|
|
1579
|
+
onError,
|
|
1580
|
+
autoReconnect: _autoReconnect,
|
|
1581
|
+
debug = false,
|
|
1582
|
+
virtualized = false,
|
|
1583
|
+
virtualizeThreshold,
|
|
1584
|
+
interactionBus,
|
|
1585
|
+
toolDisplayRegistry,
|
|
1586
|
+
defaultBodyMode = "preview",
|
|
1587
|
+
groupParallelTools = false,
|
|
1588
|
+
permissionUiMode = "standalone",
|
|
1589
|
+
markdownRenderer,
|
|
1590
|
+
displayMode = "normal",
|
|
1591
|
+
verbose = false,
|
|
1592
|
+
exploreFullscreenBash = true,
|
|
1593
|
+
memoryDir = null,
|
|
1594
|
+
workspaceRoot = null,
|
|
1595
|
+
subagentLeakGate = false,
|
|
1596
|
+
snapshotBootstrap,
|
|
1597
|
+
snapshotHydrateFailed = false,
|
|
1598
|
+
children
|
|
1599
|
+
}) {
|
|
1600
|
+
const storeRef = useRef3(null);
|
|
1601
|
+
if (storeRef.current === null) {
|
|
1602
|
+
const replayOpts = {
|
|
1603
|
+
tierOverrides: _tierOverrides,
|
|
1604
|
+
collectDebug: debug,
|
|
1605
|
+
subagentLeakGate
|
|
1606
|
+
};
|
|
1607
|
+
const initialTree = initialEvents && initialEvents.length > 0 ? replayEvents(initialEvents, replayOpts) : void 0;
|
|
1608
|
+
storeRef.current = createSessionStore(initialTree, {
|
|
1609
|
+
tierOverrides: _tierOverrides,
|
|
1610
|
+
debug,
|
|
1611
|
+
initialEventCount: initialEvents?.length ?? 0,
|
|
1612
|
+
subagentLeakGate,
|
|
1613
|
+
authorityBindingActive: authoritySource != null
|
|
1614
|
+
});
|
|
1614
1615
|
}
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1616
|
+
const registryRef = useMemo(() => registry ?? createDefaultRegistry(), [registry]);
|
|
1617
|
+
const busRef = useMemo(
|
|
1618
|
+
() => interactionBus ?? getNoopInteractionBus(),
|
|
1619
|
+
[interactionBus]
|
|
1620
|
+
);
|
|
1621
|
+
const toolDisplayRef = useMemo(
|
|
1622
|
+
() => ({
|
|
1623
|
+
registry: toolDisplayRegistry ?? createDefaultToolRegistry(),
|
|
1624
|
+
defaultBodyMode
|
|
1625
|
+
}),
|
|
1626
|
+
[toolDisplayRegistry, defaultBodyMode]
|
|
1627
|
+
);
|
|
1628
|
+
const sessionViewRef = useMemo(
|
|
1629
|
+
() => ({
|
|
1630
|
+
...DEFAULT_SESSION_VIEW_OPTIONS,
|
|
1631
|
+
groupParallelTools,
|
|
1632
|
+
permissionUiMode,
|
|
1633
|
+
verboseReasoning: debug,
|
|
1634
|
+
displayMode,
|
|
1635
|
+
verbose,
|
|
1636
|
+
exploreFullscreenBash,
|
|
1637
|
+
memoryDir,
|
|
1638
|
+
workspaceRoot,
|
|
1639
|
+
subagentLeakGate
|
|
1640
|
+
}),
|
|
1641
|
+
[
|
|
1642
|
+
groupParallelTools,
|
|
1643
|
+
permissionUiMode,
|
|
1644
|
+
debug,
|
|
1645
|
+
displayMode,
|
|
1646
|
+
verbose,
|
|
1647
|
+
exploreFullscreenBash,
|
|
1648
|
+
memoryDir,
|
|
1649
|
+
workspaceRoot,
|
|
1650
|
+
subagentLeakGate
|
|
1651
|
+
]
|
|
1652
|
+
);
|
|
1653
|
+
const onErrorRef = useRef3(onError);
|
|
1654
|
+
onErrorRef.current = onError;
|
|
1655
|
+
useEffect3(() => {
|
|
1656
|
+
const store = storeRef.current;
|
|
1657
|
+
const controller = new AbortController();
|
|
1658
|
+
void source.start((event, ctx) => {
|
|
1659
|
+
store.getState().applyEvent(event, ctx);
|
|
1660
|
+
}, controller.signal).catch((err) => {
|
|
1661
|
+
if (err instanceof SseTransportTerminalError) {
|
|
1662
|
+
const sessionState = store.getState();
|
|
1663
|
+
if (sessionState.authorityBindingActive && !isAuthorityTerminalStatus(sessionState.authority?.status)) {
|
|
1664
|
+
onErrorRef.current?.(err);
|
|
1665
|
+
return;
|
|
1666
|
+
}
|
|
1667
|
+
const { tree } = sessionState;
|
|
1668
|
+
if (tree.status === "running" || tree.status === "connecting") {
|
|
1669
|
+
store.setState({ tree: { ...tree, status: "error" } });
|
|
1670
|
+
}
|
|
1671
|
+
onErrorRef.current?.(err);
|
|
1672
|
+
} else if (err instanceof Error) {
|
|
1673
|
+
onErrorRef.current?.(err);
|
|
1674
|
+
}
|
|
1675
|
+
});
|
|
1676
|
+
return () => {
|
|
1677
|
+
controller.abort();
|
|
1620
1678
|
};
|
|
1679
|
+
}, [source]);
|
|
1680
|
+
return /* @__PURE__ */ jsx4(SessionStoreContext.Provider, { value: storeRef.current, children: /* @__PURE__ */ jsx4(InteractionBusContext.Provider, { value: busRef, children: /* @__PURE__ */ jsx4(NodeRegistryContext.Provider, { value: registryRef, children: /* @__PURE__ */ jsx4(MarkdownRendererContext.Provider, { value: markdownRenderer, children: /* @__PURE__ */ jsx4(ToolDisplayOptionsContext.Provider, { value: toolDisplayRef, children: /* @__PURE__ */ jsx4(SessionViewOptionsContext.Provider, { value: sessionViewRef, children: /* @__PURE__ */ jsxs("div", { className: "lax-agent-session", "data-testid": "lax-agent-session", children: [
|
|
1681
|
+
/* @__PURE__ */ jsx4(
|
|
1682
|
+
AgentSnapshotBootstrap,
|
|
1683
|
+
{
|
|
1684
|
+
store: storeRef.current,
|
|
1685
|
+
snapshotBootstrap,
|
|
1686
|
+
snapshotHydrateFailed
|
|
1687
|
+
}
|
|
1688
|
+
),
|
|
1689
|
+
authoritySource ? /* @__PURE__ */ jsx4(
|
|
1690
|
+
AgentAuthorityBinder,
|
|
1691
|
+
{
|
|
1692
|
+
store: storeRef.current,
|
|
1693
|
+
authoritySource,
|
|
1694
|
+
onAuthorityTerminal
|
|
1695
|
+
}
|
|
1696
|
+
) : null,
|
|
1697
|
+
children ?? /* @__PURE__ */ jsx4(
|
|
1698
|
+
AgentLoopView,
|
|
1699
|
+
{
|
|
1700
|
+
virtualized,
|
|
1701
|
+
virtualizeThreshold,
|
|
1702
|
+
groupParallelTools
|
|
1703
|
+
}
|
|
1704
|
+
),
|
|
1705
|
+
debug ? /* @__PURE__ */ jsx4(DebugPanel, {}) : null
|
|
1706
|
+
] }) }) }) }) }) }) });
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
// src/view/workflow/WorkflowSession.tsx
|
|
1710
|
+
import { useEffect as useEffect11, useMemo as useMemo9, useRef as useRef5 } from "react";
|
|
1711
|
+
|
|
1712
|
+
// src/core/context/WorkflowSessionContext.tsx
|
|
1713
|
+
import { createContext, useContext } from "react";
|
|
1714
|
+
import { useStore as useStore2 } from "zustand";
|
|
1715
|
+
var WorkflowSessionStoreContext = createContext(null);
|
|
1716
|
+
function useWorkflowSessionStoreApi() {
|
|
1717
|
+
const store = useContext(WorkflowSessionStoreContext);
|
|
1718
|
+
if (!store) {
|
|
1719
|
+
throw new Error("useWorkflowSessionStoreApi must be used within WorkflowSession");
|
|
1621
1720
|
}
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1721
|
+
return store;
|
|
1722
|
+
}
|
|
1723
|
+
function useWorkflowSessionStatus() {
|
|
1724
|
+
const store = useWorkflowSessionStoreApi();
|
|
1725
|
+
return useStore2(store, (s) => getDisplayProjectionStatus(s.state));
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
// src/core/context/WorkflowLoopReplayContext.tsx
|
|
1729
|
+
import { createContext as createContext2, useContext as useContext2 } from "react";
|
|
1730
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
1731
|
+
var WorkflowLoopReplayContext = createContext2(
|
|
1732
|
+
void 0
|
|
1733
|
+
);
|
|
1734
|
+
function WorkflowLoopReplayProvider({
|
|
1735
|
+
onRequestLoopReplay,
|
|
1736
|
+
children
|
|
1737
|
+
}) {
|
|
1738
|
+
return /* @__PURE__ */ jsx5(WorkflowLoopReplayContext.Provider, { value: onRequestLoopReplay, children });
|
|
1739
|
+
}
|
|
1740
|
+
function useWorkflowLoopReplayHandler() {
|
|
1741
|
+
return useContext2(WorkflowLoopReplayContext);
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
// src/core/context/WorkflowScaleContext.tsx
|
|
1745
|
+
import { createContext as createContext3, useContext as useContext3 } from "react";
|
|
1746
|
+
|
|
1747
|
+
// src/view/workflow/workflowScaleOptions.ts
|
|
1748
|
+
var DEFAULT_WORKFLOW_SCALE_OPTIONS = {
|
|
1749
|
+
stageVirtualizeThreshold: 40,
|
|
1750
|
+
completedStagePageSize: 25,
|
|
1751
|
+
evictCompletedLoops: true,
|
|
1752
|
+
onlyExpandActiveRunning: true,
|
|
1753
|
+
parallelItemVirtualizeThreshold: 40,
|
|
1754
|
+
maxHydratedCompletedLoops: 4
|
|
1755
|
+
};
|
|
1756
|
+
|
|
1757
|
+
// src/core/context/WorkflowScaleContext.tsx
|
|
1758
|
+
var WorkflowScaleContext = createContext3(
|
|
1759
|
+
DEFAULT_WORKFLOW_SCALE_OPTIONS
|
|
1760
|
+
);
|
|
1761
|
+
function useWorkflowScaleOptions() {
|
|
1762
|
+
return useContext3(WorkflowScaleContext);
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
// src/core/workflowSessionStore.ts
|
|
1766
|
+
import { createStore as createStore2 } from "zustand/vanilla";
|
|
1767
|
+
|
|
1768
|
+
// src/core/workflow/workflowLoopTreeUtils.ts
|
|
1769
|
+
function isWorkflowLoopTreeActive(tree) {
|
|
1770
|
+
return tree?.status === "running" || tree?.status === "connecting";
|
|
1771
|
+
}
|
|
1772
|
+
function createWorkflowRunningLoopTree(seedMs = Date.now()) {
|
|
1773
|
+
const tree = createEmptyTree();
|
|
1774
|
+
tree.status = "running";
|
|
1775
|
+
tree.meta.startedAt = seedMs;
|
|
1776
|
+
return tree;
|
|
1777
|
+
}
|
|
1778
|
+
function finalizeWorkflowLoopTree(tree, terminalStatus = "done") {
|
|
1779
|
+
if (tree.status === "done" || tree.status === "error") {
|
|
1780
|
+
return tree;
|
|
1659
1781
|
}
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
if (!
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
const containerId = buildContainerId(workflowPath, scope, scopeKey);
|
|
1666
|
-
const status = displayStatus(data, closeStatus(eventType));
|
|
1667
|
-
this.active.close(containerId, status);
|
|
1668
|
-
if (scope === "workflow") {
|
|
1669
|
-
this.active.closeDescendantsOfPath(workflowPath, status);
|
|
1782
|
+
const byId = { ...tree.byId };
|
|
1783
|
+
for (const [id, node] of Object.entries(byId)) {
|
|
1784
|
+
if (!node) continue;
|
|
1785
|
+
if ((node.kind === "text" || node.kind === "reasoning") && node.status === "streaming") {
|
|
1786
|
+
byId[id] = { ...node, status: "done" };
|
|
1670
1787
|
}
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
const targetId = this.active.resolveContentTarget(workflowPath, scopeKey);
|
|
1677
|
-
if (!targetId) return;
|
|
1678
|
-
if (this.suppressContentContainerIds.has(targetId)) return;
|
|
1679
|
-
const block = contentBlockFromEvent(event);
|
|
1680
|
-
if (!block) return;
|
|
1681
|
-
const node = this.active.getNode(targetId);
|
|
1682
|
-
if (!node) return;
|
|
1683
|
-
const last = node.content_blocks[node.content_blocks.length - 1];
|
|
1684
|
-
if (last && last.kind === block.kind && (block.kind === "reasoning" || block.kind === "text")) {
|
|
1685
|
-
if (block.preview) {
|
|
1686
|
-
if (block.kind === "reasoning" && last.preview === "Thinking\u2026") {
|
|
1687
|
-
last.preview = block.preview;
|
|
1688
|
-
} else {
|
|
1689
|
-
last.preview = `${last.preview}${block.preview}`;
|
|
1690
|
-
}
|
|
1691
|
-
}
|
|
1692
|
-
return;
|
|
1788
|
+
if (node.kind === "tool_call" && node.status === "running") {
|
|
1789
|
+
byId[id] = {
|
|
1790
|
+
...node,
|
|
1791
|
+
status: terminalStatus === "error" ? "failed" : "done"
|
|
1792
|
+
};
|
|
1693
1793
|
}
|
|
1694
|
-
node.content_blocks.push(block);
|
|
1695
1794
|
}
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
}
|
|
1716
|
-
if (workflowPath.startsWith(`${parent.workflow_path}>`)) {
|
|
1717
|
-
return parent.container_id;
|
|
1718
|
-
}
|
|
1719
|
-
if (parent.workflow_path === workflowPath && parent.scope !== scope) {
|
|
1720
|
-
return parent.container_id;
|
|
1721
|
-
}
|
|
1722
|
-
}
|
|
1795
|
+
return {
|
|
1796
|
+
...tree,
|
|
1797
|
+
byId,
|
|
1798
|
+
activeRoundId: null,
|
|
1799
|
+
status: terminalStatus
|
|
1800
|
+
};
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1803
|
+
// src/core/workflow/stageDoneSummary.ts
|
|
1804
|
+
function countToolUses(tree) {
|
|
1805
|
+
return Object.values(tree.byId).filter((node) => node.kind === "tool_call").length;
|
|
1806
|
+
}
|
|
1807
|
+
function computeDurationSeconds(tree) {
|
|
1808
|
+
const started = tree.meta.startedAt;
|
|
1809
|
+
if (started == null) return null;
|
|
1810
|
+
let maxTs = started;
|
|
1811
|
+
for (const node of Object.values(tree.byId)) {
|
|
1812
|
+
if ("endedAt" in node && typeof node.endedAt === "number") {
|
|
1813
|
+
maxTs = Math.max(maxTs, node.endedAt);
|
|
1723
1814
|
}
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
return workflowCandidates.reduce(
|
|
1727
|
-
(best, node) => this.active.compareContainerRank(node.container_id, best.container_id) > 0 ? node : best
|
|
1728
|
-
).container_id;
|
|
1815
|
+
if ("startedAt" in node && typeof node.startedAt === "number") {
|
|
1816
|
+
maxTs = Math.max(maxTs, node.startedAt);
|
|
1729
1817
|
}
|
|
1730
|
-
return null;
|
|
1731
1818
|
}
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1819
|
+
const seconds = Math.round((maxTs - started) / 1e3);
|
|
1820
|
+
return seconds > 0 ? seconds : null;
|
|
1821
|
+
}
|
|
1822
|
+
function lastLoopTextContent(tree) {
|
|
1823
|
+
const textNodes = Object.values(tree.byId).filter((node) => node.kind === "text");
|
|
1824
|
+
if (textNodes.length === 0) return "";
|
|
1825
|
+
return textNodes[textNodes.length - 1].accumulated.trim();
|
|
1826
|
+
}
|
|
1827
|
+
function pickTeaserLine(fullText) {
|
|
1828
|
+
const lines = fullText.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
1829
|
+
if (lines.length === 0) return "";
|
|
1830
|
+
const nonTableLine = lines.find((line) => !line.startsWith("|"));
|
|
1831
|
+
return nonTableLine ?? lines[0] ?? "";
|
|
1832
|
+
}
|
|
1833
|
+
function buildStageDoneSummary(tree) {
|
|
1834
|
+
if (tree.status !== "done" && tree.status !== "error") return null;
|
|
1835
|
+
const toolUses = countToolUses(tree);
|
|
1836
|
+
const duration = computeDurationSeconds(tree);
|
|
1837
|
+
const durationSuffix = duration != null ? ` \xB7 ${duration}s` : "";
|
|
1838
|
+
const label = toolUses > 0 ? `Done (${toolUses} tool use${toolUses === 1 ? "" : "s"}${durationSuffix})` : `Done (1 agent turn${durationSuffix})`;
|
|
1839
|
+
const fullText = lastLoopTextContent(tree);
|
|
1840
|
+
const lines = fullText.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
1841
|
+
const teaser = pickTeaserLine(fullText);
|
|
1842
|
+
const extraLines = Math.max(0, lines.length - 1);
|
|
1843
|
+
return { label, teaser, extraLines };
|
|
1844
|
+
}
|
|
1845
|
+
function formatTeaserExpandHint(extraLines, verbose) {
|
|
1846
|
+
if (extraLines <= 0) return null;
|
|
1847
|
+
const shown = verbose ? Math.min(extraLines, 2) : 0;
|
|
1848
|
+
const hidden = extraLines - shown;
|
|
1849
|
+
if (hidden <= 0) return null;
|
|
1850
|
+
return `\u2026 +${hidden} lines (ctrl+o to expand)`;
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
// src/core/workflow/workflowLoopEviction.ts
|
|
1854
|
+
function containersByLoopSessionId(tree) {
|
|
1855
|
+
const map = /* @__PURE__ */ new Map();
|
|
1856
|
+
for (const node of Object.values(tree.containersById)) {
|
|
1857
|
+
if (node.loopSessionId) map.set(node.loopSessionId, node);
|
|
1858
|
+
}
|
|
1859
|
+
return map;
|
|
1860
|
+
}
|
|
1861
|
+
function hasRunningParallelSibling(node, tree) {
|
|
1862
|
+
if (node.scope !== "item" || !node.parent_container_id) return false;
|
|
1863
|
+
for (const sibling of Object.values(tree.containersById)) {
|
|
1864
|
+
if (sibling.container_id === node.container_id) continue;
|
|
1865
|
+
if (sibling.scope !== "item") continue;
|
|
1866
|
+
if (sibling.parent_container_id !== node.parent_container_id) continue;
|
|
1867
|
+
if (sibling.status === "running" || sibling.status === "retrying") return true;
|
|
1737
1868
|
}
|
|
1738
|
-
|
|
1739
|
-
|
|
1869
|
+
return false;
|
|
1870
|
+
}
|
|
1871
|
+
function collectProtectedLoopSessionIds(state, pinnedLoopSessionIds) {
|
|
1872
|
+
const protectedIds = new Set(Object.keys(pinnedLoopSessionIds));
|
|
1873
|
+
const byLoop = containersByLoopSessionId(state.containerTree);
|
|
1874
|
+
if (state.activeLoopSessionId) {
|
|
1875
|
+
const activeContainer = byLoop.get(state.activeLoopSessionId);
|
|
1876
|
+
if (activeContainer && (activeContainer.status === "running" || activeContainer.status === "retrying")) {
|
|
1877
|
+
protectedIds.add(state.activeLoopSessionId);
|
|
1878
|
+
}
|
|
1740
1879
|
}
|
|
1741
|
-
|
|
1742
|
-
|
|
1880
|
+
for (const containerId of state.containerTree.activeContainerIds) {
|
|
1881
|
+
const node = state.containerTree.containersById[containerId];
|
|
1882
|
+
if (!node?.loopSessionId) continue;
|
|
1883
|
+
if (node.status === "running" || node.status === "retrying") {
|
|
1884
|
+
protectedIds.add(node.loopSessionId);
|
|
1885
|
+
}
|
|
1743
1886
|
}
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1887
|
+
for (const node of Object.values(state.containerTree.containersById)) {
|
|
1888
|
+
if (!node.loopSessionId) continue;
|
|
1889
|
+
if (node.status === "running" || node.status === "retrying") {
|
|
1890
|
+
protectedIds.add(node.loopSessionId);
|
|
1891
|
+
}
|
|
1892
|
+
if ((node.status === "completed" || node.status === "failed") && hasRunningParallelSibling(node, state.containerTree)) {
|
|
1893
|
+
protectedIds.add(node.loopSessionId);
|
|
1747
1894
|
}
|
|
1748
|
-
return this.snapshot();
|
|
1749
1895
|
}
|
|
1750
|
-
|
|
1751
|
-
|
|
1896
|
+
return protectedIds;
|
|
1897
|
+
}
|
|
1898
|
+
function evictInactiveLoopTrees(state, pinnedLoopSessionIds) {
|
|
1899
|
+
const protectedIds = collectProtectedLoopSessionIds(state, pinnedLoopSessionIds);
|
|
1900
|
+
for (const id of state.hotCompletedLoopSessionIds) {
|
|
1901
|
+
protectedIds.add(id);
|
|
1752
1902
|
}
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
const
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1903
|
+
const byLoop = containersByLoopSessionId(state.containerTree);
|
|
1904
|
+
const nextLoops = { ...state.loopTreesBySessionId };
|
|
1905
|
+
const nextSummaries = {
|
|
1906
|
+
...state.stageDoneSummariesByContainerId
|
|
1907
|
+
};
|
|
1908
|
+
for (const [loopSessionId, tree] of Object.entries(state.loopTreesBySessionId)) {
|
|
1909
|
+
if (protectedIds.has(loopSessionId)) continue;
|
|
1910
|
+
const container = byLoop.get(loopSessionId);
|
|
1911
|
+
if (!container) continue;
|
|
1912
|
+
if (container.status !== "completed" && container.status !== "failed") continue;
|
|
1913
|
+
const summary = buildStageDoneSummary(tree);
|
|
1914
|
+
if (summary) {
|
|
1915
|
+
nextSummaries[container.container_id] = container.scope === "aggregate" ? { ...summary, fullMarkdown: lastLoopTextContent(tree) } : summary;
|
|
1916
|
+
}
|
|
1917
|
+
delete nextLoops[loopSessionId];
|
|
1759
1918
|
}
|
|
1760
|
-
|
|
1761
|
-
|
|
1919
|
+
return {
|
|
1920
|
+
...state,
|
|
1921
|
+
loopTreesBySessionId: nextLoops,
|
|
1922
|
+
stageDoneSummariesByContainerId: nextSummaries
|
|
1923
|
+
};
|
|
1762
1924
|
}
|
|
1763
|
-
function
|
|
1764
|
-
const
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1925
|
+
function touchCompletedLoopSession(state, loopSessionId) {
|
|
1926
|
+
const ids = state.hotCompletedLoopSessionIds.filter((id) => id !== loopSessionId);
|
|
1927
|
+
ids.push(loopSessionId);
|
|
1928
|
+
return {
|
|
1929
|
+
...state,
|
|
1930
|
+
hotCompletedLoopSessionIds: ids
|
|
1931
|
+
};
|
|
1770
1932
|
}
|
|
1771
|
-
function
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
const loopSessionId = prev[containerId]?.loopSessionId ?? null;
|
|
1775
|
-
out[containerId] = {
|
|
1776
|
-
...node,
|
|
1777
|
-
loopSessionId,
|
|
1778
|
-
// D24:已绑定 loop 的容器不得保留 content_blocks 预览(过程层 SSOT 在 loop tree)。
|
|
1779
|
-
content_blocks: loopSessionId ? [] : node.content_blocks
|
|
1780
|
-
};
|
|
1933
|
+
function evictCompletedLoopOverflow(state, maxHydratedCompletedLoops) {
|
|
1934
|
+
if (state.hotCompletedLoopSessionIds.length <= maxHydratedCompletedLoops) {
|
|
1935
|
+
return state;
|
|
1781
1936
|
}
|
|
1782
|
-
|
|
1937
|
+
const overflow = state.hotCompletedLoopSessionIds.length - maxHydratedCompletedLoops;
|
|
1938
|
+
const candidateEvictIds = state.hotCompletedLoopSessionIds.slice(0, overflow);
|
|
1939
|
+
let trimmedIds = state.hotCompletedLoopSessionIds.slice(overflow);
|
|
1940
|
+
const byLoop = containersByLoopSessionId(state.containerTree);
|
|
1941
|
+
const nextLoops = { ...state.loopTreesBySessionId };
|
|
1942
|
+
const nextSummaries = {
|
|
1943
|
+
...state.stageDoneSummariesByContainerId
|
|
1944
|
+
};
|
|
1945
|
+
for (const id of candidateEvictIds) {
|
|
1946
|
+
const tree = nextLoops[id];
|
|
1947
|
+
if (!tree) continue;
|
|
1948
|
+
const container = byLoop.get(id);
|
|
1949
|
+
if (container && hasRunningParallelSibling(container, state.containerTree)) {
|
|
1950
|
+
if (!trimmedIds.includes(id)) {
|
|
1951
|
+
trimmedIds = [id, ...trimmedIds];
|
|
1952
|
+
}
|
|
1953
|
+
continue;
|
|
1954
|
+
}
|
|
1955
|
+
if (container) {
|
|
1956
|
+
const summary = buildStageDoneSummary(tree);
|
|
1957
|
+
if (summary) {
|
|
1958
|
+
nextSummaries[container.container_id] = container.scope === "aggregate" ? { ...summary, fullMarkdown: lastLoopTextContent(tree) } : summary;
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
delete nextLoops[id];
|
|
1962
|
+
}
|
|
1963
|
+
return {
|
|
1964
|
+
...state,
|
|
1965
|
+
loopTreesBySessionId: nextLoops,
|
|
1966
|
+
stageDoneSummariesByContainerId: nextSummaries,
|
|
1967
|
+
hotCompletedLoopSessionIds: trimmedIds
|
|
1968
|
+
};
|
|
1783
1969
|
}
|
|
1784
1970
|
|
|
1785
1971
|
// src/core/workflow/loopSessionUtils.ts
|
|
@@ -2846,45 +3032,6 @@ function reduceWorkflowEvents(events, options) {
|
|
|
2846
3032
|
);
|
|
2847
3033
|
}
|
|
2848
3034
|
|
|
2849
|
-
// src/core/workflow/hydrateWorkflowSnapshot.ts
|
|
2850
|
-
function isAtOrBeforeProjectionCursor(projectionCursor, eventId) {
|
|
2851
|
-
if (!projectionCursor || !eventId) return false;
|
|
2852
|
-
const cursorNum = Number.parseInt(projectionCursor, 10);
|
|
2853
|
-
const eventNum = Number.parseInt(eventId, 10);
|
|
2854
|
-
if (!Number.isNaN(cursorNum) && !Number.isNaN(eventNum)) {
|
|
2855
|
-
return eventNum <= cursorNum;
|
|
2856
|
-
}
|
|
2857
|
-
return eventId <= projectionCursor;
|
|
2858
|
-
}
|
|
2859
|
-
function hydrateWorkflowFromSnapshot(state, snapshot, options = {}) {
|
|
2860
|
-
const authority = options.preserveAuthority ? state.authority : null;
|
|
2861
|
-
const containerTree = structuredClone(snapshot.containerTree);
|
|
2862
|
-
containerTree.containersById = normalizeSnapshotContainerContentBlocks(
|
|
2863
|
-
sanitizeContainerTreeParentLinks(containerTree.containersById)
|
|
2864
|
-
);
|
|
2865
|
-
return {
|
|
2866
|
-
...createEmptyWorkflowSessionState(),
|
|
2867
|
-
authority,
|
|
2868
|
-
display: {
|
|
2869
|
-
...snapshot.display,
|
|
2870
|
-
streamEnded: false
|
|
2871
|
-
},
|
|
2872
|
-
containerTree,
|
|
2873
|
-
projectionCursor: snapshot.cursor,
|
|
2874
|
-
snapshotVersion: snapshot.snapshotVersion,
|
|
2875
|
-
meta: {
|
|
2876
|
-
...state.meta,
|
|
2877
|
-
workflowRunId: snapshot.taskId,
|
|
2878
|
-
lastEventId: snapshot.cursor,
|
|
2879
|
-
startedAt: state.meta.startedAt ?? snapshot.capturedAt
|
|
2880
|
-
},
|
|
2881
|
-
loopTreesBySessionId: {},
|
|
2882
|
-
internalErrors: [],
|
|
2883
|
-
reconnectSuppressedReason: null,
|
|
2884
|
-
snapshotHydrateFailed: false
|
|
2885
|
-
};
|
|
2886
|
-
}
|
|
2887
|
-
|
|
2888
3035
|
// src/core/workflowSessionStore.ts
|
|
2889
3036
|
var DEFAULT_HYDRATE_CHUNK_SIZE = 50;
|
|
2890
3037
|
function nextAnimationFrame() {
|
|
@@ -3197,35 +3344,12 @@ function createWorkflowSessionStore(initialState = createEmptyWorkflowSessionSta
|
|
|
3197
3344
|
}
|
|
3198
3345
|
|
|
3199
3346
|
// src/core/workflow/useWorkflowAuthorityBinding.ts
|
|
3200
|
-
import { useEffect as
|
|
3201
|
-
|
|
3202
|
-
// src/core/workflow/normalizeAuthoritySnapshot.ts
|
|
3203
|
-
var KNOWN = /* @__PURE__ */ new Set([
|
|
3204
|
-
"pending",
|
|
3205
|
-
"running",
|
|
3206
|
-
"done",
|
|
3207
|
-
"error",
|
|
3208
|
-
"canceled",
|
|
3209
|
-
"partial_success"
|
|
3210
|
-
]);
|
|
3211
|
-
function normalizeAuthoritySnapshot(snapshot) {
|
|
3212
|
-
if (KNOWN.has(snapshot.status)) {
|
|
3213
|
-
return snapshot;
|
|
3214
|
-
}
|
|
3215
|
-
if (import.meta.env?.DEV) {
|
|
3216
|
-
console.warn(
|
|
3217
|
-
`[langchain_agentx_stream_ui] unknown authority.status "${String(snapshot.status)}"; defaulting to running`
|
|
3218
|
-
);
|
|
3219
|
-
}
|
|
3220
|
-
return { ...snapshot, status: "running" };
|
|
3221
|
-
}
|
|
3222
|
-
|
|
3223
|
-
// src/core/workflow/useWorkflowAuthorityBinding.ts
|
|
3347
|
+
import { useEffect as useEffect4, useRef as useRef4 } from "react";
|
|
3224
3348
|
function useWorkflowAuthorityBinding(store, authoritySource, callbacks) {
|
|
3225
|
-
const onAuthorityTerminalRef =
|
|
3349
|
+
const onAuthorityTerminalRef = useRef4(callbacks.onAuthorityTerminal);
|
|
3226
3350
|
onAuthorityTerminalRef.current = callbacks.onAuthorityTerminal;
|
|
3227
|
-
const terminalFiredRef =
|
|
3228
|
-
|
|
3351
|
+
const terminalFiredRef = useRef4(false);
|
|
3352
|
+
useEffect4(() => {
|
|
3229
3353
|
terminalFiredRef.current = false;
|
|
3230
3354
|
const apply = (raw) => {
|
|
3231
3355
|
const snapshot = normalizeAuthoritySnapshot(raw);
|
|
@@ -3255,11 +3379,11 @@ function useWorkflowAuthorityBinding(store, authoritySource, callbacks) {
|
|
|
3255
3379
|
}
|
|
3256
3380
|
|
|
3257
3381
|
// src/core/workflow/useWorkflowAwaitingAuthorityTimers.ts
|
|
3258
|
-
import { useEffect as
|
|
3382
|
+
import { useEffect as useEffect5 } from "react";
|
|
3259
3383
|
var AWAITING_MS = 3e4;
|
|
3260
3384
|
var SYNC_FAILED_MS = 12e4;
|
|
3261
3385
|
function useWorkflowAwaitingAuthorityTimers(store, streamEnded, authorityStatus) {
|
|
3262
|
-
|
|
3386
|
+
useEffect5(() => {
|
|
3263
3387
|
if (!streamEnded || authorityStatus !== "running") {
|
|
3264
3388
|
store.getState().setAwaitingAuthority(false);
|
|
3265
3389
|
store.getState().setAuthoritySyncFailed(false);
|
|
@@ -3282,11 +3406,11 @@ function useWorkflowAwaitingAuthorityTimers(store, streamEnded, authorityStatus)
|
|
|
3282
3406
|
import { useMemo as useMemo7, useState as useState6 } from "react";
|
|
3283
3407
|
|
|
3284
3408
|
// src/view/workflow/WorkflowAggregateContainer.tsx
|
|
3285
|
-
import { useCallback, useEffect as
|
|
3286
|
-
import { useStore as
|
|
3409
|
+
import { useCallback, useEffect as useEffect6, useMemo as useMemo2, useState, memo } from "react";
|
|
3410
|
+
import { useStore as useStore3 } from "zustand";
|
|
3287
3411
|
|
|
3288
3412
|
// src/view/workflow/WorkflowContainerLine.tsx
|
|
3289
|
-
import { Fragment, jsx as
|
|
3413
|
+
import { Fragment, jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
3290
3414
|
function WorkflowContainerLine({
|
|
3291
3415
|
title,
|
|
3292
3416
|
description,
|
|
@@ -3313,7 +3437,7 @@ function WorkflowContainerLine({
|
|
|
3313
3437
|
className
|
|
3314
3438
|
].filter(Boolean).join(" ");
|
|
3315
3439
|
const content = /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
3316
|
-
showDot ? /* @__PURE__ */
|
|
3440
|
+
showDot ? /* @__PURE__ */ jsx6(
|
|
3317
3441
|
"span",
|
|
3318
3442
|
{
|
|
3319
3443
|
className: "lax-workflow-container-line__status-dot lax-workflow-stage-container__status-dot",
|
|
@@ -3321,7 +3445,7 @@ function WorkflowContainerLine({
|
|
|
3321
3445
|
"aria-hidden": true
|
|
3322
3446
|
}
|
|
3323
3447
|
) : null,
|
|
3324
|
-
/* @__PURE__ */
|
|
3448
|
+
/* @__PURE__ */ jsx6("span", { className: "lax-workflow-container-line__title", "data-testid": titleTestId, children: title }),
|
|
3325
3449
|
description ? /* @__PURE__ */ jsxs2(
|
|
3326
3450
|
"span",
|
|
3327
3451
|
{
|
|
@@ -3334,12 +3458,12 @@ function WorkflowContainerLine({
|
|
|
3334
3458
|
]
|
|
3335
3459
|
}
|
|
3336
3460
|
) : null,
|
|
3337
|
-
meta.length > 0 ? /* @__PURE__ */
|
|
3338
|
-
verbose && debugScopeKey ? /* @__PURE__ */
|
|
3339
|
-
/* @__PURE__ */
|
|
3461
|
+
meta.length > 0 ? /* @__PURE__ */ jsx6("span", { className: "lax-workflow-container-line__meta", "data-testid": titleTestId ? `${titleTestId}-meta` : void 0, children: meta.join(" \xB7 ") }) : null,
|
|
3462
|
+
verbose && debugScopeKey ? /* @__PURE__ */ jsx6("span", { className: "lax-workflow-container-line__debug", "data-testid": "lax-workflow-id", children: debugScopeKey }) : null,
|
|
3463
|
+
/* @__PURE__ */ jsx6("span", { className: "lax-workflow-container-line__status", children: statusLabel })
|
|
3340
3464
|
] });
|
|
3341
3465
|
if (onToggle && interactive !== false) {
|
|
3342
|
-
return /* @__PURE__ */
|
|
3466
|
+
return /* @__PURE__ */ jsx6(
|
|
3343
3467
|
"button",
|
|
3344
3468
|
{
|
|
3345
3469
|
type: "button",
|
|
@@ -3353,7 +3477,7 @@ function WorkflowContainerLine({
|
|
|
3353
3477
|
);
|
|
3354
3478
|
}
|
|
3355
3479
|
if (onToggle) {
|
|
3356
|
-
return /* @__PURE__ */
|
|
3480
|
+
return /* @__PURE__ */ jsx6(
|
|
3357
3481
|
"div",
|
|
3358
3482
|
{
|
|
3359
3483
|
className: `${lineClass} lax-workflow-container-line--static-toggle`,
|
|
@@ -3373,11 +3497,11 @@ function WorkflowContainerLine({
|
|
|
3373
3497
|
}
|
|
3374
3498
|
);
|
|
3375
3499
|
}
|
|
3376
|
-
return /* @__PURE__ */
|
|
3500
|
+
return /* @__PURE__ */ jsx6("div", { className: lineClass, style, children: content });
|
|
3377
3501
|
}
|
|
3378
3502
|
|
|
3379
3503
|
// src/view/workflow/WorkflowExpandTrigger.tsx
|
|
3380
|
-
import { jsx as
|
|
3504
|
+
import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
3381
3505
|
function WorkflowExpandTrigger({
|
|
3382
3506
|
summary,
|
|
3383
3507
|
summaryTestId,
|
|
@@ -3402,7 +3526,7 @@ function WorkflowExpandTrigger({
|
|
|
3402
3526
|
"\u23BF ",
|
|
3403
3527
|
summary
|
|
3404
3528
|
] }),
|
|
3405
|
-
teaser ? /* @__PURE__ */
|
|
3529
|
+
teaser ? /* @__PURE__ */ jsx7(
|
|
3406
3530
|
"span",
|
|
3407
3531
|
{
|
|
3408
3532
|
className: "lax-workflow-expand-trigger__teaser",
|
|
@@ -3410,21 +3534,21 @@ function WorkflowExpandTrigger({
|
|
|
3410
3534
|
children: teaser
|
|
3411
3535
|
}
|
|
3412
3536
|
) : null,
|
|
3413
|
-
expandHint ? /* @__PURE__ */
|
|
3537
|
+
expandHint ? /* @__PURE__ */ jsx7("span", { className: "lax-workflow-expand-trigger__hint", children: expandHint }) : null
|
|
3414
3538
|
]
|
|
3415
3539
|
}
|
|
3416
3540
|
);
|
|
3417
3541
|
}
|
|
3418
3542
|
|
|
3419
3543
|
// src/view/workflow/WorkflowMarkdownPreview.tsx
|
|
3420
|
-
import { jsx as
|
|
3544
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
3421
3545
|
function WorkflowMarkdownPreview({
|
|
3422
3546
|
content,
|
|
3423
3547
|
testId,
|
|
3424
3548
|
className = "lax-workflow-markdown-preview"
|
|
3425
3549
|
}) {
|
|
3426
3550
|
if (!content.trim()) return null;
|
|
3427
|
-
return /* @__PURE__ */
|
|
3551
|
+
return /* @__PURE__ */ jsx8("div", { className, "data-testid": testId, children: /* @__PURE__ */ jsx8(RichMarkdown, { content, deferDiagrams: true }) });
|
|
3428
3552
|
}
|
|
3429
3553
|
|
|
3430
3554
|
// src/core/workflow/loopTreeUtils.ts
|
|
@@ -3434,7 +3558,7 @@ function hasTimelineNodes(tree) {
|
|
|
3434
3558
|
}
|
|
3435
3559
|
|
|
3436
3560
|
// src/view/workflow/workflowStageDoneBody.tsx
|
|
3437
|
-
import { jsx as
|
|
3561
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
3438
3562
|
function WorkflowStageDoneBody({
|
|
3439
3563
|
loopTree,
|
|
3440
3564
|
testId,
|
|
@@ -3444,7 +3568,7 @@ function WorkflowStageDoneBody({
|
|
|
3444
3568
|
bodyClassName = "lax-workflow-stage-container__body"
|
|
3445
3569
|
}) {
|
|
3446
3570
|
if (loopTree && hasTimelineNodes(loopTree)) {
|
|
3447
|
-
return /* @__PURE__ */
|
|
3571
|
+
return /* @__PURE__ */ jsx9("div", { className: bodyClassName, "data-testid": testId, children: /* @__PURE__ */ jsx9(
|
|
3448
3572
|
AgentLoopView,
|
|
3449
3573
|
{
|
|
3450
3574
|
tree: loopTree,
|
|
@@ -3455,12 +3579,12 @@ function WorkflowStageDoneBody({
|
|
|
3455
3579
|
}
|
|
3456
3580
|
) });
|
|
3457
3581
|
}
|
|
3458
|
-
return /* @__PURE__ */
|
|
3582
|
+
return /* @__PURE__ */ jsx9(
|
|
3459
3583
|
"div",
|
|
3460
3584
|
{
|
|
3461
3585
|
className: `${bodyClassName} lax-workflow-stage-container__empty-hint`,
|
|
3462
3586
|
"data-testid": testId,
|
|
3463
|
-
children: /* @__PURE__ */
|
|
3587
|
+
children: /* @__PURE__ */ jsx9("span", { className: "lax-workflow-stage-container__empty-hint-text", children: "\uFF08\u6B64\u9636\u6BB5\u6682\u65E0 Agent \u65F6\u95F4\u7EBF\u5185\u5BB9\uFF09" })
|
|
3464
3588
|
}
|
|
3465
3589
|
);
|
|
3466
3590
|
}
|
|
@@ -3707,7 +3831,7 @@ function workflowContainerViewPropsEqual(prev, next) {
|
|
|
3707
3831
|
}
|
|
3708
3832
|
|
|
3709
3833
|
// src/view/workflow/WorkflowAggregateContainer.tsx
|
|
3710
|
-
import { jsx as
|
|
3834
|
+
import { jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
3711
3835
|
function mergeContentPreview(node) {
|
|
3712
3836
|
const blocks = node.content_blocks;
|
|
3713
3837
|
if (blocks.length === 0) return "";
|
|
@@ -3736,11 +3860,11 @@ function WorkflowAggregateContainerInner({
|
|
|
3736
3860
|
const { verbose } = useSessionViewOptions();
|
|
3737
3861
|
const store = useWorkflowSessionStoreApi();
|
|
3738
3862
|
const replayHandler = useWorkflowLoopReplayHandler();
|
|
3739
|
-
const cachedSummary =
|
|
3863
|
+
const cachedSummary = useStore3(
|
|
3740
3864
|
store,
|
|
3741
3865
|
(s) => s.state.stageDoneSummariesByContainerId[node.container_id]
|
|
3742
3866
|
);
|
|
3743
|
-
const pinLoopSession =
|
|
3867
|
+
const pinLoopSession = useStore3(store, (s) => s.pinLoopSession);
|
|
3744
3868
|
const isRunning = node.status === "running" || node.status === "retrying";
|
|
3745
3869
|
const isWaiting = node.status === "pending" || node.status === "blocked";
|
|
3746
3870
|
const isDone = node.status === "completed" || node.status === "failed";
|
|
@@ -3765,7 +3889,7 @@ function WorkflowAggregateContainerInner({
|
|
|
3765
3889
|
const uiStatus = mapAggregateUiStatus(node);
|
|
3766
3890
|
const statusLabel = buildAggregateStatusLabel(node, siblingItems);
|
|
3767
3891
|
const [expanded, setExpanded] = useState(isRunning);
|
|
3768
|
-
|
|
3892
|
+
useEffect6(() => {
|
|
3769
3893
|
if (isRunning) setExpanded(true);
|
|
3770
3894
|
if (isDone) setExpanded(false);
|
|
3771
3895
|
}, [isRunning, isDone]);
|
|
@@ -3784,7 +3908,7 @@ function WorkflowAggregateContainerInner({
|
|
|
3784
3908
|
return next;
|
|
3785
3909
|
});
|
|
3786
3910
|
}, [isDone, node.loopSessionId, loopTree, requestLoopHydration]);
|
|
3787
|
-
|
|
3911
|
+
useEffect6(() => {
|
|
3788
3912
|
const onKeyDown = (event) => {
|
|
3789
3913
|
if (!(event.ctrlKey || event.metaKey) || event.key.toLowerCase() !== "o") return;
|
|
3790
3914
|
setExpanded(true);
|
|
@@ -3794,7 +3918,7 @@ function WorkflowAggregateContainerInner({
|
|
|
3794
3918
|
return () => window.removeEventListener("keydown", onKeyDown);
|
|
3795
3919
|
}, [requestLoopHydration]);
|
|
3796
3920
|
const shellToggle = isRunning ? toggleExpanded : isDone ? toggleExpanded : void 0;
|
|
3797
|
-
const markdownPreview = bodyMarkdown ? /* @__PURE__ */
|
|
3921
|
+
const markdownPreview = bodyMarkdown ? /* @__PURE__ */ jsx10(
|
|
3798
3922
|
WorkflowMarkdownPreview,
|
|
3799
3923
|
{
|
|
3800
3924
|
content: bodyMarkdown,
|
|
@@ -3811,7 +3935,7 @@ function WorkflowAggregateContainerInner({
|
|
|
3811
3935
|
"data-container-scope": "aggregate",
|
|
3812
3936
|
style: { "--lax-workflow-depth": depth },
|
|
3813
3937
|
children: [
|
|
3814
|
-
/* @__PURE__ */
|
|
3938
|
+
/* @__PURE__ */ jsx10(
|
|
3815
3939
|
WorkflowContainerLine,
|
|
3816
3940
|
{
|
|
3817
3941
|
title: node.title,
|
|
@@ -3827,8 +3951,8 @@ function WorkflowAggregateContainerInner({
|
|
|
3827
3951
|
}
|
|
3828
3952
|
),
|
|
3829
3953
|
isWaiting && waitingTeaser ? /* @__PURE__ */ jsxs4("div", { className: "lax-workflow-aggregate__waiting-teaser", "data-testid": `${testId}-waiting`, children: [
|
|
3830
|
-
/* @__PURE__ */
|
|
3831
|
-
/* @__PURE__ */
|
|
3954
|
+
/* @__PURE__ */ jsx10("span", { className: "lax-workflow-aggregate__waiting-prefix", children: "\u23BF " }),
|
|
3955
|
+
/* @__PURE__ */ jsx10(
|
|
3832
3956
|
WorkflowMarkdownPreview,
|
|
3833
3957
|
{
|
|
3834
3958
|
content: waitingTeaser,
|
|
@@ -3836,7 +3960,7 @@ function WorkflowAggregateContainerInner({
|
|
|
3836
3960
|
}
|
|
3837
3961
|
)
|
|
3838
3962
|
] }) : null,
|
|
3839
|
-
expanded && isRunning && loopTree ? /* @__PURE__ */
|
|
3963
|
+
expanded && isRunning && loopTree ? /* @__PURE__ */ jsx10("div", { className: "lax-workflow-stage-container__body", "data-testid": `${testId}-body`, children: /* @__PURE__ */ jsx10(
|
|
3840
3964
|
AgentLoopView,
|
|
3841
3965
|
{
|
|
3842
3966
|
tree: loopTree,
|
|
@@ -3846,7 +3970,7 @@ function WorkflowAggregateContainerInner({
|
|
|
3846
3970
|
groupParallelTools
|
|
3847
3971
|
}
|
|
3848
3972
|
) }) : null,
|
|
3849
|
-
expanded && isRunning && !loopTree && bodyMarkdown ? /* @__PURE__ */
|
|
3973
|
+
expanded && isRunning && !loopTree && bodyMarkdown ? /* @__PURE__ */ jsx10(
|
|
3850
3974
|
"div",
|
|
3851
3975
|
{
|
|
3852
3976
|
className: "lax-workflow-stage-container__body lax-workflow-stage-container__content-preview",
|
|
@@ -3855,7 +3979,7 @@ function WorkflowAggregateContainerInner({
|
|
|
3855
3979
|
}
|
|
3856
3980
|
) : null,
|
|
3857
3981
|
isDone && !expanded ? /* @__PURE__ */ jsxs4("div", { className: "lax-workflow-aggregate__done", "data-testid": `${testId}-done`, children: [
|
|
3858
|
-
canExpandDone ? /* @__PURE__ */
|
|
3982
|
+
canExpandDone ? /* @__PURE__ */ jsx10(
|
|
3859
3983
|
WorkflowExpandTrigger,
|
|
3860
3984
|
{
|
|
3861
3985
|
summary: `${node.title} \xB7 done`,
|
|
@@ -3865,10 +3989,10 @@ function WorkflowAggregateContainerInner({
|
|
|
3865
3989
|
testId: `${testId}-done-trigger`
|
|
3866
3990
|
}
|
|
3867
3991
|
) : null,
|
|
3868
|
-
markdownPreview ? /* @__PURE__ */
|
|
3869
|
-
!bodyMarkdown && expandHint ? /* @__PURE__ */
|
|
3992
|
+
markdownPreview ? /* @__PURE__ */ jsx10("div", { className: "lax-workflow-aggregate__done-preview", "data-testid": `${testId}-teaser`, children: markdownPreview }) : null,
|
|
3993
|
+
!bodyMarkdown && expandHint ? /* @__PURE__ */ jsx10("span", { className: "lax-workflow-expand-trigger__hint", children: expandHint }) : null
|
|
3870
3994
|
] }) : null,
|
|
3871
|
-
isDone && expanded ? loopTree ? /* @__PURE__ */
|
|
3995
|
+
isDone && expanded ? loopTree ? /* @__PURE__ */ jsx10(
|
|
3872
3996
|
WorkflowStageDoneBody,
|
|
3873
3997
|
{
|
|
3874
3998
|
node,
|
|
@@ -3878,19 +4002,19 @@ function WorkflowAggregateContainerInner({
|
|
|
3878
4002
|
virtualizeThreshold,
|
|
3879
4003
|
groupParallelTools
|
|
3880
4004
|
}
|
|
3881
|
-
) : bodyMarkdown ? /* @__PURE__ */
|
|
4005
|
+
) : bodyMarkdown ? /* @__PURE__ */ jsx10(
|
|
3882
4006
|
"div",
|
|
3883
4007
|
{
|
|
3884
4008
|
className: "lax-workflow-stage-container__body lax-workflow-stage-container__content-preview",
|
|
3885
4009
|
"data-testid": `${testId}-body`,
|
|
3886
4010
|
children: markdownPreview
|
|
3887
4011
|
}
|
|
3888
|
-
) : usesScopedLoop ? /* @__PURE__ */
|
|
4012
|
+
) : usesScopedLoop ? /* @__PURE__ */ jsx10(
|
|
3889
4013
|
"div",
|
|
3890
4014
|
{
|
|
3891
4015
|
className: "lax-workflow-stage-container__body lax-workflow-loop-replay-pending",
|
|
3892
4016
|
"data-testid": `${testId}-body`,
|
|
3893
|
-
children: /* @__PURE__ */
|
|
4017
|
+
children: /* @__PURE__ */ jsx10("p", { className: "lax-workflow-loop-replay-pending__hint", children: replayHandler ? "Loading loop details\u2026" : "Loop details evicted from memory." })
|
|
3894
4018
|
}
|
|
3895
4019
|
) : null : null
|
|
3896
4020
|
]
|
|
@@ -3914,7 +4038,7 @@ var WorkflowAggregateContainer = memo(
|
|
|
3914
4038
|
);
|
|
3915
4039
|
|
|
3916
4040
|
// src/view/workflow/WorkflowParallelGroup.tsx
|
|
3917
|
-
import { useCallback as useCallback3, useEffect as
|
|
4041
|
+
import { useCallback as useCallback3, useEffect as useEffect8, useMemo as useMemo4, useState as useState3, memo as memo3 } from "react";
|
|
3918
4042
|
|
|
3919
4043
|
// src/core/workflow/workflowParallelGroupMetrics.ts
|
|
3920
4044
|
var ACTIVE_ITEM_STATUSES = /* @__PURE__ */ new Set([
|
|
@@ -4008,9 +4132,9 @@ function formatParallelGroupSummary(metrics) {
|
|
|
4008
4132
|
}
|
|
4009
4133
|
|
|
4010
4134
|
// src/view/workflow/WorkflowStageContainer.tsx
|
|
4011
|
-
import { useCallback as useCallback2, useEffect as
|
|
4012
|
-
import { useStore as
|
|
4013
|
-
import { jsx as
|
|
4135
|
+
import { useCallback as useCallback2, useEffect as useEffect7, useMemo as useMemo3, useState as useState2, memo as memo2 } from "react";
|
|
4136
|
+
import { useStore as useStore4 } from "zustand";
|
|
4137
|
+
import { jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
4014
4138
|
function containerTestId(node) {
|
|
4015
4139
|
if (node.scope === "item") return `lax-workflow-parallel-${node.scope_key}`;
|
|
4016
4140
|
if (node.scope === "stage") return `lax-workflow-stage-${node.scope_key}`;
|
|
@@ -4033,13 +4157,13 @@ function WorkflowStageContainerInner({
|
|
|
4033
4157
|
const { verbose } = useSessionViewOptions();
|
|
4034
4158
|
const store = useWorkflowSessionStoreApi();
|
|
4035
4159
|
const replayHandler = useWorkflowLoopReplayHandler();
|
|
4036
|
-
const activeLoopSessionId =
|
|
4037
|
-
const activeContainerIds =
|
|
4038
|
-
const cachedSummary =
|
|
4160
|
+
const activeLoopSessionId = useStore4(store, (s) => s.state.activeLoopSessionId);
|
|
4161
|
+
const activeContainerIds = useStore4(store, (s) => s.state.containerTree.activeContainerIds);
|
|
4162
|
+
const cachedSummary = useStore4(
|
|
4039
4163
|
store,
|
|
4040
4164
|
(s) => s.state.stageDoneSummariesByContainerId[node.container_id]
|
|
4041
4165
|
);
|
|
4042
|
-
const pinLoopSession =
|
|
4166
|
+
const pinLoopSession = useStore4(store, (s) => s.pinLoopSession);
|
|
4043
4167
|
const isRunning = node.status === "running" || node.status === "retrying";
|
|
4044
4168
|
const isDone = node.status === "completed" || node.status === "failed";
|
|
4045
4169
|
const isSkipped = node.status === "skipped";
|
|
@@ -4055,7 +4179,7 @@ function WorkflowStageContainerInner({
|
|
|
4055
4179
|
const doneSummary = doneSummaryFromTree ?? (isDone ? cachedSummary : null);
|
|
4056
4180
|
const shouldAutoExpandRunning = effectivelyRunning && !isSkipped;
|
|
4057
4181
|
const [expanded, setExpanded] = useState2(shouldAutoExpandRunning);
|
|
4058
|
-
|
|
4182
|
+
useEffect7(() => {
|
|
4059
4183
|
if (shouldAutoExpandRunning) {
|
|
4060
4184
|
setExpanded(true);
|
|
4061
4185
|
return;
|
|
@@ -4080,7 +4204,7 @@ function WorkflowStageContainerInner({
|
|
|
4080
4204
|
return next;
|
|
4081
4205
|
});
|
|
4082
4206
|
}, [isDone, node.loopSessionId, loopTree, requestLoopHydration]);
|
|
4083
|
-
|
|
4207
|
+
useEffect7(() => {
|
|
4084
4208
|
const onKeyDown = (event) => {
|
|
4085
4209
|
if (!(event.ctrlKey || event.metaKey) || event.key.toLowerCase() !== "o") return;
|
|
4086
4210
|
setExpanded(true);
|
|
@@ -4115,7 +4239,7 @@ function WorkflowStageContainerInner({
|
|
|
4115
4239
|
"--lax-workflow-indent-unit": workflowIndentUnit(depth)
|
|
4116
4240
|
},
|
|
4117
4241
|
children: [
|
|
4118
|
-
/* @__PURE__ */
|
|
4242
|
+
/* @__PURE__ */ jsx11(
|
|
4119
4243
|
WorkflowContainerLine,
|
|
4120
4244
|
{
|
|
4121
4245
|
title: displayTitle,
|
|
@@ -4139,8 +4263,8 @@ function WorkflowStageContainerInner({
|
|
|
4139
4263
|
className: "lax-workflow-stage-container__done-teaser-line",
|
|
4140
4264
|
"data-testid": `${testId}-teaser`,
|
|
4141
4265
|
children: [
|
|
4142
|
-
/* @__PURE__ */
|
|
4143
|
-
/* @__PURE__ */
|
|
4266
|
+
/* @__PURE__ */ jsx11("span", { className: "lax-workflow-stage-container__done-teaser-prefix", children: "\u23BF" }),
|
|
4267
|
+
/* @__PURE__ */ jsx11(
|
|
4144
4268
|
WorkflowMarkdownPreview,
|
|
4145
4269
|
{
|
|
4146
4270
|
content: teaserText,
|
|
@@ -4150,14 +4274,14 @@ function WorkflowStageContainerInner({
|
|
|
4150
4274
|
]
|
|
4151
4275
|
}
|
|
4152
4276
|
) : null,
|
|
4153
|
-
expandHint ? /* @__PURE__ */
|
|
4277
|
+
expandHint ? /* @__PURE__ */ jsx11("span", { className: "lax-workflow-expand-trigger__hint", children: expandHint }) : null
|
|
4154
4278
|
] }) : null,
|
|
4155
|
-
expanded && isRunning && !usesScopedLoop && node.content_blocks.length > 0 ? /* @__PURE__ */
|
|
4279
|
+
expanded && isRunning && !usesScopedLoop && node.content_blocks.length > 0 ? /* @__PURE__ */ jsx11(
|
|
4156
4280
|
"div",
|
|
4157
4281
|
{
|
|
4158
4282
|
className: "lax-workflow-stage-container__body lax-workflow-stage-container__content-preview",
|
|
4159
4283
|
"data-testid": bodyTestId(node),
|
|
4160
|
-
children: node.content_blocks.map((block, index) => /* @__PURE__ */
|
|
4284
|
+
children: node.content_blocks.map((block, index) => /* @__PURE__ */ jsx11(
|
|
4161
4285
|
WorkflowMarkdownPreview,
|
|
4162
4286
|
{
|
|
4163
4287
|
content: block.preview,
|
|
@@ -4167,12 +4291,12 @@ function WorkflowStageContainerInner({
|
|
|
4167
4291
|
))
|
|
4168
4292
|
}
|
|
4169
4293
|
) : null,
|
|
4170
|
-
expanded && effectivelyRunning && runningLoopTree ? /* @__PURE__ */
|
|
4294
|
+
expanded && effectivelyRunning && runningLoopTree ? /* @__PURE__ */ jsx11(
|
|
4171
4295
|
"div",
|
|
4172
4296
|
{
|
|
4173
4297
|
className: isItem ? "lax-workflow-parallel-item__body lax-workflow-stage-container__body" : "lax-workflow-stage-container__body",
|
|
4174
4298
|
"data-testid": bodyTestId(node),
|
|
4175
|
-
children: /* @__PURE__ */
|
|
4299
|
+
children: /* @__PURE__ */ jsx11(
|
|
4176
4300
|
AgentLoopView,
|
|
4177
4301
|
{
|
|
4178
4302
|
tree: runningLoopTree,
|
|
@@ -4184,7 +4308,7 @@ function WorkflowStageContainerInner({
|
|
|
4184
4308
|
)
|
|
4185
4309
|
}
|
|
4186
4310
|
) : null,
|
|
4187
|
-
effectivelyDone && expanded ? loopTree ? /* @__PURE__ */
|
|
4311
|
+
effectivelyDone && expanded ? loopTree ? /* @__PURE__ */ jsx11(
|
|
4188
4312
|
WorkflowStageDoneBody,
|
|
4189
4313
|
{
|
|
4190
4314
|
node,
|
|
@@ -4201,7 +4325,7 @@ function WorkflowStageContainerInner({
|
|
|
4201
4325
|
className: "lax-workflow-stage-container__body lax-workflow-loop-replay-pending",
|
|
4202
4326
|
"data-testid": bodyTestId(node),
|
|
4203
4327
|
children: [
|
|
4204
|
-
teaserText ? /* @__PURE__ */
|
|
4328
|
+
teaserText ? /* @__PURE__ */ jsx11(
|
|
4205
4329
|
WorkflowMarkdownPreview,
|
|
4206
4330
|
{
|
|
4207
4331
|
content: teaserText,
|
|
@@ -4209,7 +4333,7 @@ function WorkflowStageContainerInner({
|
|
|
4209
4333
|
testId: `${testId}-replay-teaser`
|
|
4210
4334
|
}
|
|
4211
4335
|
) : null,
|
|
4212
|
-
/* @__PURE__ */
|
|
4336
|
+
/* @__PURE__ */ jsx11("p", { className: "lax-workflow-loop-replay-pending__hint", children: replayHandler ? "Loading loop details\u2026" : "Loop details evicted from memory." })
|
|
4213
4337
|
]
|
|
4214
4338
|
}
|
|
4215
4339
|
) : null
|
|
@@ -4227,7 +4351,7 @@ function workflowStageContainerPropsEqual(prev, next) {
|
|
|
4227
4351
|
var WorkflowStageContainer = memo2(WorkflowStageContainerInner, workflowStageContainerPropsEqual);
|
|
4228
4352
|
|
|
4229
4353
|
// src/view/workflow/WorkflowParallelGroup.tsx
|
|
4230
|
-
import { jsx as
|
|
4354
|
+
import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
4231
4355
|
function resolveLoopTree(node, loopTreesBySessionId) {
|
|
4232
4356
|
if (!node.loopSessionId) return void 0;
|
|
4233
4357
|
return loopTreesBySessionId[node.loopSessionId];
|
|
@@ -4249,7 +4373,7 @@ function WorkflowParallelGroupInner({
|
|
|
4249
4373
|
);
|
|
4250
4374
|
const allSettled = metrics.total > 0 && metrics.running === 0 && metrics.completed + metrics.failed >= metrics.total;
|
|
4251
4375
|
const [expanded, setExpanded] = useState3(hasActiveWork);
|
|
4252
|
-
|
|
4376
|
+
useEffect8(() => {
|
|
4253
4377
|
if (hasActiveWork) {
|
|
4254
4378
|
setExpanded(true);
|
|
4255
4379
|
return;
|
|
@@ -4261,7 +4385,7 @@ function WorkflowParallelGroupInner({
|
|
|
4261
4385
|
const toggleExpanded = useCallback3(() => {
|
|
4262
4386
|
setExpanded((prev) => !prev);
|
|
4263
4387
|
}, []);
|
|
4264
|
-
|
|
4388
|
+
useEffect8(() => {
|
|
4265
4389
|
const onKeyDown = (event) => {
|
|
4266
4390
|
if (!(event.ctrlKey || event.metaKey) || event.key.toLowerCase() !== "o") return;
|
|
4267
4391
|
setExpanded(true);
|
|
@@ -4271,7 +4395,7 @@ function WorkflowParallelGroupInner({
|
|
|
4271
4395
|
}, []);
|
|
4272
4396
|
const renderItem = (index) => {
|
|
4273
4397
|
const item = items[index];
|
|
4274
|
-
return /* @__PURE__ */
|
|
4398
|
+
return /* @__PURE__ */ jsx12(
|
|
4275
4399
|
WorkflowStageContainer,
|
|
4276
4400
|
{
|
|
4277
4401
|
node: item,
|
|
@@ -4290,7 +4414,7 @@ function WorkflowParallelGroupInner({
|
|
|
4290
4414
|
className: "lax-workflow-parallel-group",
|
|
4291
4415
|
"data-testid": `lax-workflow-parallel-group-${groupKey}`,
|
|
4292
4416
|
children: [
|
|
4293
|
-
/* @__PURE__ */
|
|
4417
|
+
/* @__PURE__ */ jsx12(
|
|
4294
4418
|
WorkflowExpandTrigger,
|
|
4295
4419
|
{
|
|
4296
4420
|
summary,
|
|
@@ -4301,7 +4425,7 @@ function WorkflowParallelGroupInner({
|
|
|
4301
4425
|
className: "lax-workflow-parallel-group__summary"
|
|
4302
4426
|
}
|
|
4303
4427
|
),
|
|
4304
|
-
expanded ? /* @__PURE__ */
|
|
4428
|
+
expanded ? /* @__PURE__ */ jsx12("div", { className: "lax-workflow-parallel-list", "data-testid": "lax-workflow-parallel-list", children: items.map((_, index) => renderItem(index)) }) : null
|
|
4305
4429
|
]
|
|
4306
4430
|
}
|
|
4307
4431
|
);
|
|
@@ -4326,7 +4450,7 @@ function workflowParallelGroupPropsEqual(prev, next) {
|
|
|
4326
4450
|
var WorkflowParallelGroup = memo3(WorkflowParallelGroupInner, workflowParallelGroupPropsEqual);
|
|
4327
4451
|
|
|
4328
4452
|
// src/view/workflow/WorkflowRootContainer.tsx
|
|
4329
|
-
import { jsx as
|
|
4453
|
+
import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
4330
4454
|
function WorkflowRootContainer({
|
|
4331
4455
|
node,
|
|
4332
4456
|
children,
|
|
@@ -4341,7 +4465,7 @@ function WorkflowRootContainer({
|
|
|
4341
4465
|
const statusLabel = mapWorkflowRootStatusLabel(nodeStatus, sessionStatus);
|
|
4342
4466
|
const meta = containersById ? buildEffectiveWorkflowRootMeta(node, containersById) : buildWorkflowRootMeta(node);
|
|
4343
4467
|
return /* @__PURE__ */ jsxs7("div", { className: "lax-workflow-root", "data-testid": "lax-workflow-root", children: [
|
|
4344
|
-
/* @__PURE__ */
|
|
4468
|
+
/* @__PURE__ */ jsx13(
|
|
4345
4469
|
WorkflowContainerLine,
|
|
4346
4470
|
{
|
|
4347
4471
|
variant: "root",
|
|
@@ -4356,13 +4480,13 @@ function WorkflowRootContainer({
|
|
|
4356
4480
|
verbose
|
|
4357
4481
|
}
|
|
4358
4482
|
),
|
|
4359
|
-
children ? /* @__PURE__ */
|
|
4483
|
+
children ? /* @__PURE__ */ jsx13("div", { className: "lax-workflow-container-tree", children }) : null
|
|
4360
4484
|
] });
|
|
4361
4485
|
}
|
|
4362
4486
|
|
|
4363
4487
|
// src/view/workflow/WorkflowRouteContainer.tsx
|
|
4364
|
-
import { useCallback as useCallback4, useEffect as
|
|
4365
|
-
import { jsx as
|
|
4488
|
+
import { useCallback as useCallback4, useEffect as useEffect9, useMemo as useMemo5, useState as useState4 } from "react";
|
|
4489
|
+
import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
4366
4490
|
function WorkflowRouteContainer({
|
|
4367
4491
|
node,
|
|
4368
4492
|
loopTree,
|
|
@@ -4386,7 +4510,7 @@ function WorkflowRouteContainer({
|
|
|
4386
4510
|
const expandHint = doneSummary ? formatTeaserExpandHint(doneSummary.extraLines, verbose) : null;
|
|
4387
4511
|
const doneLabel = doneSummary?.label ?? "done";
|
|
4388
4512
|
const [expanded, setExpanded] = useState4(isRunning);
|
|
4389
|
-
|
|
4513
|
+
useEffect9(() => {
|
|
4390
4514
|
if (isSkipped) return;
|
|
4391
4515
|
if (isRunning) setExpanded(true);
|
|
4392
4516
|
if (isDone) setExpanded(false);
|
|
@@ -4408,7 +4532,7 @@ function WorkflowRouteContainer({
|
|
|
4408
4532
|
"data-container-scope": "branch",
|
|
4409
4533
|
style: depthStyle,
|
|
4410
4534
|
children: [
|
|
4411
|
-
/* @__PURE__ */
|
|
4535
|
+
/* @__PURE__ */ jsx14(
|
|
4412
4536
|
WorkflowContainerLine,
|
|
4413
4537
|
{
|
|
4414
4538
|
title: node.title,
|
|
@@ -4423,7 +4547,7 @@ function WorkflowRouteContainer({
|
|
|
4423
4547
|
descriptionTestId: `${testId}-desc`
|
|
4424
4548
|
}
|
|
4425
4549
|
),
|
|
4426
|
-
!isSkipped && expanded && isRunning && loopTree ? /* @__PURE__ */
|
|
4550
|
+
!isSkipped && expanded && isRunning && loopTree ? /* @__PURE__ */ jsx14("div", { className: "lax-workflow-stage-container__body", "data-testid": `${testId}-body`, children: /* @__PURE__ */ jsx14(
|
|
4427
4551
|
AgentLoopView,
|
|
4428
4552
|
{
|
|
4429
4553
|
tree: loopTree,
|
|
@@ -4433,7 +4557,7 @@ function WorkflowRouteContainer({
|
|
|
4433
4557
|
groupParallelTools
|
|
4434
4558
|
}
|
|
4435
4559
|
) }) : null,
|
|
4436
|
-
!isSkipped && isDone && !expanded ? /* @__PURE__ */
|
|
4560
|
+
!isSkipped && isDone && !expanded ? /* @__PURE__ */ jsx14(
|
|
4437
4561
|
WorkflowExpandTrigger,
|
|
4438
4562
|
{
|
|
4439
4563
|
summary: `${node.title} \xB7 ${doneLabel}`,
|
|
@@ -4445,7 +4569,7 @@ function WorkflowRouteContainer({
|
|
|
4445
4569
|
teaserTestId: teaserText ? `${testId}-teaser` : void 0
|
|
4446
4570
|
}
|
|
4447
4571
|
) : null,
|
|
4448
|
-
!isSkipped && isDone && expanded ? /* @__PURE__ */
|
|
4572
|
+
!isSkipped && isDone && expanded ? /* @__PURE__ */ jsx14(
|
|
4449
4573
|
WorkflowStageDoneBody,
|
|
4450
4574
|
{
|
|
4451
4575
|
node,
|
|
@@ -4462,8 +4586,8 @@ function WorkflowRouteContainer({
|
|
|
4462
4586
|
}
|
|
4463
4587
|
|
|
4464
4588
|
// src/view/workflow/WorkflowSubworkflowContainer.tsx
|
|
4465
|
-
import { useCallback as useCallback5, useEffect as
|
|
4466
|
-
import { jsx as
|
|
4589
|
+
import { useCallback as useCallback5, useEffect as useEffect10, useMemo as useMemo6, useState as useState5 } from "react";
|
|
4590
|
+
import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
4467
4591
|
function buildDoneSummary(node) {
|
|
4468
4592
|
const display = node.display;
|
|
4469
4593
|
if (display?.progress_total != null) {
|
|
@@ -4493,7 +4617,7 @@ function WorkflowSubworkflowContainer({
|
|
|
4493
4617
|
return node.content_blocks[node.content_blocks.length - 1]?.preview ?? "";
|
|
4494
4618
|
}, [node.content_blocks]);
|
|
4495
4619
|
const [expanded, setExpanded] = useState5(effectivelyRunning);
|
|
4496
|
-
|
|
4620
|
+
useEffect10(() => {
|
|
4497
4621
|
if (effectivelyRunning) setExpanded(true);
|
|
4498
4622
|
else if (effectivelyDone) setExpanded(false);
|
|
4499
4623
|
}, [effectivelyRunning, effectivelyDone]);
|
|
@@ -4514,7 +4638,7 @@ function WorkflowSubworkflowContainer({
|
|
|
4514
4638
|
"data-container-scope": "subworkflow",
|
|
4515
4639
|
style: depthStyle,
|
|
4516
4640
|
children: [
|
|
4517
|
-
showShellLine ? /* @__PURE__ */
|
|
4641
|
+
showShellLine ? /* @__PURE__ */ jsx15(
|
|
4518
4642
|
WorkflowContainerLine,
|
|
4519
4643
|
{
|
|
4520
4644
|
variant: "root",
|
|
@@ -4534,7 +4658,7 @@ function WorkflowSubworkflowContainer({
|
|
|
4534
4658
|
verbose
|
|
4535
4659
|
}
|
|
4536
4660
|
) : null,
|
|
4537
|
-
expanded && children ? /* @__PURE__ */
|
|
4661
|
+
expanded && children ? /* @__PURE__ */ jsx15(
|
|
4538
4662
|
"div",
|
|
4539
4663
|
{
|
|
4540
4664
|
className: "lax-workflow-subworkflow-boundary",
|
|
@@ -4543,7 +4667,7 @@ function WorkflowSubworkflowContainer({
|
|
|
4543
4667
|
children
|
|
4544
4668
|
}
|
|
4545
4669
|
) : null,
|
|
4546
|
-
!expanded && children ? /* @__PURE__ */
|
|
4670
|
+
!expanded && children ? /* @__PURE__ */ jsx15(
|
|
4547
4671
|
WorkflowExpandTrigger,
|
|
4548
4672
|
{
|
|
4549
4673
|
summary: `${node.title} \xB7 ${effectivelyDone ? buildDoneSummary(node) : "running"}`,
|
|
@@ -4562,7 +4686,7 @@ function WorkflowSubworkflowContainer({
|
|
|
4562
4686
|
}
|
|
4563
4687
|
|
|
4564
4688
|
// src/view/workflow/WorkflowChrome.tsx
|
|
4565
|
-
import { Fragment as Fragment2, jsx as
|
|
4689
|
+
import { Fragment as Fragment2, jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
4566
4690
|
function collectChildContainers(containersById, parentId) {
|
|
4567
4691
|
return Object.values(containersById).filter((node) => node.parent_container_id === parentId).sort((a, b) => a.scope_key.localeCompare(b.scope_key));
|
|
4568
4692
|
}
|
|
@@ -4581,7 +4705,7 @@ function renderLeafContainer(node, depth, containerTree, loopTreesBySessionId, v
|
|
|
4581
4705
|
const subtreeActive = options?.subtreeHasActiveWork ?? subtreeHasActiveWork(containerTree.containersById, node.container_id);
|
|
4582
4706
|
switch (node.scope) {
|
|
4583
4707
|
case "aggregate":
|
|
4584
|
-
return /* @__PURE__ */
|
|
4708
|
+
return /* @__PURE__ */ jsx16(
|
|
4585
4709
|
WorkflowAggregateContainer,
|
|
4586
4710
|
{
|
|
4587
4711
|
node,
|
|
@@ -4593,7 +4717,7 @@ function renderLeafContainer(node, depth, containerTree, loopTreesBySessionId, v
|
|
|
4593
4717
|
node.container_id
|
|
4594
4718
|
);
|
|
4595
4719
|
case "branch":
|
|
4596
|
-
return /* @__PURE__ */
|
|
4720
|
+
return /* @__PURE__ */ jsx16(
|
|
4597
4721
|
WorkflowRouteContainer,
|
|
4598
4722
|
{
|
|
4599
4723
|
node,
|
|
@@ -4604,7 +4728,7 @@ function renderLeafContainer(node, depth, containerTree, loopTreesBySessionId, v
|
|
|
4604
4728
|
node.container_id
|
|
4605
4729
|
);
|
|
4606
4730
|
default:
|
|
4607
|
-
return /* @__PURE__ */
|
|
4731
|
+
return /* @__PURE__ */ jsx16(
|
|
4608
4732
|
WorkflowStageContainer,
|
|
4609
4733
|
{
|
|
4610
4734
|
node,
|
|
@@ -4620,7 +4744,7 @@ function renderLeafContainer(node, depth, containerTree, loopTreesBySessionId, v
|
|
|
4620
4744
|
}
|
|
4621
4745
|
function renderItemSiblings(items, groupKey, depth, containerTree, loopTreesBySessionId, viewProps) {
|
|
4622
4746
|
if (items.length >= 2) {
|
|
4623
|
-
return /* @__PURE__ */
|
|
4747
|
+
return /* @__PURE__ */ jsx16(
|
|
4624
4748
|
WorkflowParallelGroup,
|
|
4625
4749
|
{
|
|
4626
4750
|
groupKey,
|
|
@@ -4668,7 +4792,7 @@ function renderContainerNode(node, containerTree, loopTreesBySessionId, viewProp
|
|
|
4668
4792
|
);
|
|
4669
4793
|
const hasNested = collectChildContainers(containerTree.containersById, node.container_id).length > 0;
|
|
4670
4794
|
if (node.scope === "subworkflow") {
|
|
4671
|
-
return /* @__PURE__ */
|
|
4795
|
+
return /* @__PURE__ */ jsx16(
|
|
4672
4796
|
WorkflowSubworkflowContainer,
|
|
4673
4797
|
{
|
|
4674
4798
|
node,
|
|
@@ -4698,7 +4822,7 @@ function renderContainerNode(node, containerTree, loopTreesBySessionId, viewProp
|
|
|
4698
4822
|
viewProps,
|
|
4699
4823
|
{ suppressHostScopedLoop }
|
|
4700
4824
|
),
|
|
4701
|
-
hasNested ? /* @__PURE__ */
|
|
4825
|
+
hasNested ? /* @__PURE__ */ jsx16("div", { className: "lax-workflow-container-children", children: nested }) : null
|
|
4702
4826
|
]
|
|
4703
4827
|
},
|
|
4704
4828
|
node.container_id
|
|
@@ -4724,9 +4848,9 @@ function renderWorkflowStageRow(node, containerTree, loopTreesBySessionId, viewP
|
|
|
4724
4848
|
viewProps
|
|
4725
4849
|
);
|
|
4726
4850
|
if (as === "div") {
|
|
4727
|
-
return /* @__PURE__ */
|
|
4851
|
+
return /* @__PURE__ */ jsx16("div", { className, role: "listitem", children: content }, node.container_id);
|
|
4728
4852
|
}
|
|
4729
|
-
return /* @__PURE__ */
|
|
4853
|
+
return /* @__PURE__ */ jsx16("li", { className, children: content }, node.container_id);
|
|
4730
4854
|
}
|
|
4731
4855
|
function WorkflowStageListPanel({
|
|
4732
4856
|
stages,
|
|
@@ -4747,7 +4871,7 @@ function WorkflowStageListPanel({
|
|
|
4747
4871
|
partition.totalDoneCount - partition.visibleDoneCount
|
|
4748
4872
|
);
|
|
4749
4873
|
return /* @__PURE__ */ jsxs10(Fragment2, { children: [
|
|
4750
|
-
/* @__PURE__ */
|
|
4874
|
+
/* @__PURE__ */ jsx16("ol", { className: "lax-workflow-stage-list", "data-testid": "lax-workflow-stage-list", children: partition.visible.map(
|
|
4751
4875
|
(node) => renderWorkflowStageRow(node, containerTree, loopTreesBySessionId, viewProps)
|
|
4752
4876
|
) }),
|
|
4753
4877
|
partition.hiddenCount > 0 ? /* @__PURE__ */ jsxs10(
|
|
@@ -4766,7 +4890,7 @@ function WorkflowStageListPanel({
|
|
|
4766
4890
|
]
|
|
4767
4891
|
}
|
|
4768
4892
|
) : null,
|
|
4769
|
-
extraCompletedPages > 0 && partition.hiddenCount === 0 ? /* @__PURE__ */
|
|
4893
|
+
extraCompletedPages > 0 && partition.hiddenCount === 0 ? /* @__PURE__ */ jsx16(
|
|
4770
4894
|
"button",
|
|
4771
4895
|
{
|
|
4772
4896
|
type: "button",
|
|
@@ -4802,7 +4926,7 @@ function renderWorkflowChildren(workflowNode, containerTree, loopTreesBySessionI
|
|
|
4802
4926
|
(node) => node.scope !== "stage" && node.scope !== "item"
|
|
4803
4927
|
);
|
|
4804
4928
|
return /* @__PURE__ */ jsxs10(Fragment2, { children: [
|
|
4805
|
-
stages.length > 0 ? /* @__PURE__ */
|
|
4929
|
+
stages.length > 0 ? /* @__PURE__ */ jsx16(
|
|
4806
4930
|
WorkflowStageListPanel,
|
|
4807
4931
|
{
|
|
4808
4932
|
stages,
|
|
@@ -4827,7 +4951,7 @@ function WorkflowChrome({
|
|
|
4827
4951
|
const viewProps = { virtualized, virtualizeThreshold, groupParallelTools };
|
|
4828
4952
|
const rootNodes = containerTree.rootContainerIds.map((id) => containerTree.containersById[id]).filter((node) => node != null);
|
|
4829
4953
|
if (rootNodes.length === 0) {
|
|
4830
|
-
return /* @__PURE__ */
|
|
4954
|
+
return /* @__PURE__ */ jsx16("div", { className: "lax-workflow-chrome", "data-testid": "lax-workflow-chrome", children: /* @__PURE__ */ jsx16(
|
|
4831
4955
|
WorkflowContainerLine,
|
|
4832
4956
|
{
|
|
4833
4957
|
variant: "root",
|
|
@@ -4837,7 +4961,7 @@ function WorkflowChrome({
|
|
|
4837
4961
|
}
|
|
4838
4962
|
) });
|
|
4839
4963
|
}
|
|
4840
|
-
return /* @__PURE__ */
|
|
4964
|
+
return /* @__PURE__ */ jsx16("div", { className: "lax-workflow-chrome", "data-testid": "lax-workflow-chrome", children: rootNodes.map((workflowNode) => /* @__PURE__ */ jsx16(
|
|
4841
4965
|
WorkflowRootContainer,
|
|
4842
4966
|
{
|
|
4843
4967
|
node: workflowNode,
|
|
@@ -4854,28 +4978,28 @@ function WorkflowChrome({
|
|
|
4854
4978
|
}
|
|
4855
4979
|
|
|
4856
4980
|
// src/view/workflow/WorkflowGlobalSpinner.tsx
|
|
4857
|
-
import { useStore as
|
|
4858
|
-
import { jsx as
|
|
4981
|
+
import { useStore as useStore5 } from "zustand";
|
|
4982
|
+
import { jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
4859
4983
|
function WorkflowGlobalSpinner() {
|
|
4860
4984
|
const store = useWorkflowSessionStoreApi();
|
|
4861
|
-
const show =
|
|
4985
|
+
const show = useStore5(store, (s) => shouldShowGlobalSpinner(s.state));
|
|
4862
4986
|
if (!show) return null;
|
|
4863
|
-
return /* @__PURE__ */
|
|
4987
|
+
return /* @__PURE__ */ jsx17(
|
|
4864
4988
|
"div",
|
|
4865
4989
|
{
|
|
4866
4990
|
className: "lax-workflow-global-spinner lax-spinner-container",
|
|
4867
4991
|
"data-testid": "lax-workflow-global-spinner",
|
|
4868
4992
|
role: "status",
|
|
4869
4993
|
children: /* @__PURE__ */ jsxs11("span", { className: "lax-spinner lax-workflow-global-spinner__inner", children: [
|
|
4870
|
-
/* @__PURE__ */
|
|
4871
|
-
/* @__PURE__ */
|
|
4994
|
+
/* @__PURE__ */ jsx17("span", { className: "lax-spinner-frame", children: "\u280B" }),
|
|
4995
|
+
/* @__PURE__ */ jsx17("span", { className: "lax-spinner-verb lax-spinner-verb--shimmer", children: "Workflow" })
|
|
4872
4996
|
] })
|
|
4873
4997
|
}
|
|
4874
4998
|
);
|
|
4875
4999
|
}
|
|
4876
5000
|
|
|
4877
5001
|
// src/view/workflow/WorkflowSessionBanners.tsx
|
|
4878
|
-
import { Fragment as Fragment3, jsx as
|
|
5002
|
+
import { Fragment as Fragment3, jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
4879
5003
|
function WorkflowSessionBanners({ state }) {
|
|
4880
5004
|
const banner = resolveWorkflowBanner(state);
|
|
4881
5005
|
const { display } = state;
|
|
@@ -4884,7 +5008,7 @@ function WorkflowSessionBanners({ state }) {
|
|
|
4884
5008
|
}
|
|
4885
5009
|
const showTransportError = (display.projectionStatus === "error" || state.internalErrors.length > 0) && banner.kind !== "projection_degraded" && banner.kind !== "authority_error";
|
|
4886
5010
|
return /* @__PURE__ */ jsxs12(Fragment3, { children: [
|
|
4887
|
-
banner.kind === "authority_error" ? /* @__PURE__ */
|
|
5011
|
+
banner.kind === "authority_error" ? /* @__PURE__ */ jsx18(
|
|
4888
5012
|
"div",
|
|
4889
5013
|
{
|
|
4890
5014
|
className: "lax-workflow-banner lax-workflow-banner--authority-error",
|
|
@@ -4893,7 +5017,7 @@ function WorkflowSessionBanners({ state }) {
|
|
|
4893
5017
|
children: banner.message
|
|
4894
5018
|
}
|
|
4895
5019
|
) : null,
|
|
4896
|
-
banner.kind === "projection_degraded" ? /* @__PURE__ */
|
|
5020
|
+
banner.kind === "projection_degraded" ? /* @__PURE__ */ jsx18(
|
|
4897
5021
|
"div",
|
|
4898
5022
|
{
|
|
4899
5023
|
className: "lax-workflow-banner lax-workflow-banner--projection-degraded",
|
|
@@ -4902,7 +5026,7 @@ function WorkflowSessionBanners({ state }) {
|
|
|
4902
5026
|
children: banner.message
|
|
4903
5027
|
}
|
|
4904
5028
|
) : null,
|
|
4905
|
-
banner.kind === "snapshot_unavailable" ? /* @__PURE__ */
|
|
5029
|
+
banner.kind === "snapshot_unavailable" ? /* @__PURE__ */ jsx18(
|
|
4906
5030
|
"div",
|
|
4907
5031
|
{
|
|
4908
5032
|
className: "lax-workflow-banner lax-workflow-banner--snapshot-unavailable",
|
|
@@ -4911,7 +5035,7 @@ function WorkflowSessionBanners({ state }) {
|
|
|
4911
5035
|
children: banner.message
|
|
4912
5036
|
}
|
|
4913
5037
|
) : null,
|
|
4914
|
-
banner.kind === "partial_success" ? /* @__PURE__ */
|
|
5038
|
+
banner.kind === "partial_success" ? /* @__PURE__ */ jsx18(
|
|
4915
5039
|
"div",
|
|
4916
5040
|
{
|
|
4917
5041
|
className: "lax-workflow-banner lax-workflow-banner--partial-success",
|
|
@@ -4927,16 +5051,16 @@ function WorkflowSessionBanners({ state }) {
|
|
|
4927
5051
|
"data-testid": "lax-workflow-closeout-warning",
|
|
4928
5052
|
role: "status",
|
|
4929
5053
|
children: [
|
|
4930
|
-
/* @__PURE__ */
|
|
5054
|
+
/* @__PURE__ */ jsx18("strong", { children: "Workflow forced closeout" }),
|
|
4931
5055
|
/* @__PURE__ */ jsxs12("span", { children: [
|
|
4932
5056
|
" ",
|
|
4933
5057
|
"\u2014 stream ended with unresolved child scopes; not a healthy completion."
|
|
4934
5058
|
] }),
|
|
4935
|
-
display.openChildIdentities.length > 0 ? /* @__PURE__ */
|
|
5059
|
+
display.openChildIdentities.length > 0 ? /* @__PURE__ */ jsx18("ul", { className: "lax-workflow-closeout-warning__scopes", children: display.openChildIdentities.map((identity) => /* @__PURE__ */ jsx18("li", { children: identity }, identity)) }) : null
|
|
4936
5060
|
]
|
|
4937
5061
|
}
|
|
4938
5062
|
) : null,
|
|
4939
|
-
banner.kind === "awaiting_authority" ? /* @__PURE__ */
|
|
5063
|
+
banner.kind === "awaiting_authority" ? /* @__PURE__ */ jsx18(
|
|
4940
5064
|
"div",
|
|
4941
5065
|
{
|
|
4942
5066
|
className: "lax-workflow-banner lax-workflow-banner--awaiting",
|
|
@@ -4945,7 +5069,7 @@ function WorkflowSessionBanners({ state }) {
|
|
|
4945
5069
|
children: banner.message
|
|
4946
5070
|
}
|
|
4947
5071
|
) : null,
|
|
4948
|
-
banner.kind === "sync_failed" ? /* @__PURE__ */
|
|
5072
|
+
banner.kind === "sync_failed" ? /* @__PURE__ */ jsx18(
|
|
4949
5073
|
"div",
|
|
4950
5074
|
{
|
|
4951
5075
|
className: "lax-workflow-banner lax-workflow-banner--sync-failed",
|
|
@@ -4954,7 +5078,7 @@ function WorkflowSessionBanners({ state }) {
|
|
|
4954
5078
|
children: banner.message
|
|
4955
5079
|
}
|
|
4956
5080
|
) : null,
|
|
4957
|
-
showTransportError ? /* @__PURE__ */
|
|
5081
|
+
showTransportError ? /* @__PURE__ */ jsx18(
|
|
4958
5082
|
"div",
|
|
4959
5083
|
{
|
|
4960
5084
|
className: "lax-workflow-error",
|
|
@@ -4968,7 +5092,7 @@ function WorkflowSessionBanners({ state }) {
|
|
|
4968
5092
|
|
|
4969
5093
|
// src/view/workflow/WorkflowTaskListFooter.tsx
|
|
4970
5094
|
import { useMemo as useMemo8, useState as useState7 } from "react";
|
|
4971
|
-
import { useStore as
|
|
5095
|
+
import { useStore as useStore6 } from "zustand";
|
|
4972
5096
|
|
|
4973
5097
|
// src/view/workflow/workflowTaskListMerge.ts
|
|
4974
5098
|
var STATUS_ORDER = {
|
|
@@ -5039,16 +5163,16 @@ function loopSessionDisplayLabel(loopSessionId) {
|
|
|
5039
5163
|
}
|
|
5040
5164
|
|
|
5041
5165
|
// src/view/workflow/WorkflowTaskListFooter.tsx
|
|
5042
|
-
import { jsx as
|
|
5166
|
+
import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
5043
5167
|
function MessageResponse({ children }) {
|
|
5044
5168
|
return /* @__PURE__ */ jsxs13("div", { className: "lax-message-response", children: [
|
|
5045
|
-
/* @__PURE__ */
|
|
5046
|
-
/* @__PURE__ */
|
|
5169
|
+
/* @__PURE__ */ jsx19("span", { className: "lax-message-response__marker", children: "\u23BF " }),
|
|
5170
|
+
/* @__PURE__ */ jsx19("span", { className: "lax-message-response__content", children })
|
|
5047
5171
|
] });
|
|
5048
5172
|
}
|
|
5049
5173
|
function WorkflowTaskListFooter() {
|
|
5050
5174
|
const store = useWorkflowSessionStoreApi();
|
|
5051
|
-
const state =
|
|
5175
|
+
const state = useStore6(store, (s) => s.state);
|
|
5052
5176
|
const [expanded, setExpanded] = useState7(false);
|
|
5053
5177
|
const footerState = useMemo8(() => deriveWorkflowTaskFooterState(state), [state]);
|
|
5054
5178
|
const visible = shouldShowWorkflowTaskListFooter(state);
|
|
@@ -5068,18 +5192,18 @@ function WorkflowTaskListFooter() {
|
|
|
5068
5192
|
onClick: () => setExpanded((v) => !v),
|
|
5069
5193
|
children: [
|
|
5070
5194
|
label,
|
|
5071
|
-
/* @__PURE__ */
|
|
5195
|
+
/* @__PURE__ */ jsx19("span", { className: "lax-task-list-footer__hint", children: expanded ? " \xB7 \u2191 to hide" : " \xB7 \u2193 to view" })
|
|
5072
5196
|
]
|
|
5073
5197
|
}
|
|
5074
5198
|
),
|
|
5075
|
-
expanded ? /* @__PURE__ */
|
|
5199
|
+
expanded ? /* @__PURE__ */ jsx19(MessageResponse, { children: /* @__PURE__ */ jsx19("div", { className: "lax-workflow-task-list-groups", children: groups.map((group) => /* @__PURE__ */ jsxs13(
|
|
5076
5200
|
"div",
|
|
5077
5201
|
{
|
|
5078
5202
|
className: "lax-workflow-task-list-group",
|
|
5079
5203
|
"data-testid": `lax-workflow-task-group-${group.loopSessionId}`,
|
|
5080
5204
|
children: [
|
|
5081
|
-
/* @__PURE__ */
|
|
5082
|
-
/* @__PURE__ */
|
|
5205
|
+
/* @__PURE__ */ jsx19("div", { className: "lax-workflow-task-list-group-title", children: loopSessionDisplayLabel(group.loopSessionId) }),
|
|
5206
|
+
/* @__PURE__ */ jsx19(TaskList, { tasks: group.tasks })
|
|
5083
5207
|
]
|
|
5084
5208
|
},
|
|
5085
5209
|
group.loopSessionId
|
|
@@ -5088,8 +5212,8 @@ function WorkflowTaskListFooter() {
|
|
|
5088
5212
|
}
|
|
5089
5213
|
|
|
5090
5214
|
// src/view/workflow/WorkflowSession.tsx
|
|
5091
|
-
import { useStore as
|
|
5092
|
-
import { jsx as
|
|
5215
|
+
import { useStore as useStore7 } from "zustand";
|
|
5216
|
+
import { jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
5093
5217
|
function WorkflowSession({
|
|
5094
5218
|
source,
|
|
5095
5219
|
authoritySource,
|
|
@@ -5118,7 +5242,7 @@ function WorkflowSession({
|
|
|
5118
5242
|
onDisplayStreamEnded,
|
|
5119
5243
|
children
|
|
5120
5244
|
}) {
|
|
5121
|
-
const storeRef =
|
|
5245
|
+
const storeRef = useRef5(null);
|
|
5122
5246
|
if (storeRef.current === null) {
|
|
5123
5247
|
const initialState = initialEvents && initialEvents.length > 0 ? reduceWorkflowEvents(initialEvents, { tierOverrides }) : void 0;
|
|
5124
5248
|
storeRef.current = createWorkflowSessionStore(initialState, {
|
|
@@ -5130,15 +5254,15 @@ function WorkflowSession({
|
|
|
5130
5254
|
}
|
|
5131
5255
|
const store = storeRef.current;
|
|
5132
5256
|
useWorkflowAuthorityBinding(store, authoritySource, { onAuthorityTerminal });
|
|
5133
|
-
const streamEnded =
|
|
5134
|
-
const authorityStatus =
|
|
5257
|
+
const streamEnded = useStore7(store, (s) => s.state.display.streamEnded);
|
|
5258
|
+
const authorityStatus = useStore7(store, (s) => s.state.authority?.status);
|
|
5135
5259
|
useWorkflowAwaitingAuthorityTimers(store, streamEnded, authorityStatus);
|
|
5136
|
-
|
|
5260
|
+
useEffect11(() => {
|
|
5137
5261
|
if (snapshotBootstrap) {
|
|
5138
5262
|
store.getState().hydrateFromSnapshot(snapshotBootstrap, { preserveAuthority: true });
|
|
5139
5263
|
}
|
|
5140
5264
|
}, [snapshotBootstrap, store]);
|
|
5141
|
-
|
|
5265
|
+
useEffect11(() => {
|
|
5142
5266
|
if (snapshotHydrateFailed) {
|
|
5143
5267
|
store.getState().markSnapshotHydrateFailed();
|
|
5144
5268
|
}
|
|
@@ -5180,15 +5304,15 @@ function WorkflowSession({
|
|
|
5180
5304
|
workspaceRoot
|
|
5181
5305
|
]
|
|
5182
5306
|
);
|
|
5183
|
-
const sessionState =
|
|
5307
|
+
const sessionState = useStore7(store, (s) => s.state);
|
|
5184
5308
|
const containerTree = sessionState.containerTree;
|
|
5185
5309
|
const loopTreesBySessionId = sessionState.loopTreesBySessionId;
|
|
5186
|
-
const onErrorRef =
|
|
5187
|
-
const onDisplayStreamEndedRef =
|
|
5188
|
-
const streamAbortRef =
|
|
5310
|
+
const onErrorRef = useRef5(onError);
|
|
5311
|
+
const onDisplayStreamEndedRef = useRef5(onDisplayStreamEnded);
|
|
5312
|
+
const streamAbortRef = useRef5(null);
|
|
5189
5313
|
onErrorRef.current = onError;
|
|
5190
5314
|
onDisplayStreamEndedRef.current = onDisplayStreamEnded;
|
|
5191
|
-
|
|
5315
|
+
useEffect11(() => {
|
|
5192
5316
|
if (!source) return void 0;
|
|
5193
5317
|
const controller = new AbortController();
|
|
5194
5318
|
streamAbortRef.current = controller;
|
|
@@ -5210,20 +5334,20 @@ function WorkflowSession({
|
|
|
5210
5334
|
streamAbortRef.current = null;
|
|
5211
5335
|
};
|
|
5212
5336
|
}, [source, store]);
|
|
5213
|
-
|
|
5337
|
+
useEffect11(() => {
|
|
5214
5338
|
if (isAuthorityTerminalStatus(authorityStatus)) {
|
|
5215
5339
|
streamAbortRef.current?.abort();
|
|
5216
5340
|
}
|
|
5217
5341
|
}, [authorityStatus]);
|
|
5218
|
-
return /* @__PURE__ */
|
|
5342
|
+
return /* @__PURE__ */ jsx20(WorkflowSessionStoreContext.Provider, { value: store, children: /* @__PURE__ */ jsx20(WorkflowScaleContext.Provider, { value: scaleOptions, children: /* @__PURE__ */ jsx20(WorkflowLoopReplayProvider, { onRequestLoopReplay, children: /* @__PURE__ */ jsx20(InteractionBusContext.Provider, { value: busRef, children: /* @__PURE__ */ jsx20(NodeRegistryContext.Provider, { value: registryRef, children: /* @__PURE__ */ jsx20(MarkdownRendererContext.Provider, { value: markdownRenderer, children: /* @__PURE__ */ jsx20(ToolDisplayOptionsContext.Provider, { value: toolDisplayRef, children: /* @__PURE__ */ jsx20(SessionViewOptionsContext.Provider, { value: sessionViewRef, children: /* @__PURE__ */ jsxs14(
|
|
5219
5343
|
"div",
|
|
5220
5344
|
{
|
|
5221
5345
|
className: "lax-agent-session lax-workflow-session",
|
|
5222
5346
|
"data-testid": "lax-workflow-session",
|
|
5223
5347
|
children: [
|
|
5224
|
-
/* @__PURE__ */
|
|
5225
|
-
/* @__PURE__ */
|
|
5226
|
-
/* @__PURE__ */
|
|
5348
|
+
/* @__PURE__ */ jsx20(WorkflowSessionBanners, { state: sessionState }),
|
|
5349
|
+
/* @__PURE__ */ jsx20(WorkflowGlobalSpinner, {}),
|
|
5350
|
+
/* @__PURE__ */ jsx20(
|
|
5227
5351
|
WorkflowChrome,
|
|
5228
5352
|
{
|
|
5229
5353
|
containerTree,
|
|
@@ -5233,7 +5357,7 @@ function WorkflowSession({
|
|
|
5233
5357
|
groupParallelTools
|
|
5234
5358
|
}
|
|
5235
5359
|
),
|
|
5236
|
-
/* @__PURE__ */
|
|
5360
|
+
/* @__PURE__ */ jsx20(WorkflowTaskListFooter, {}),
|
|
5237
5361
|
children
|
|
5238
5362
|
]
|
|
5239
5363
|
}
|
|
@@ -5320,6 +5444,17 @@ function createControllableAuthoritySource(initial) {
|
|
|
5320
5444
|
}
|
|
5321
5445
|
|
|
5322
5446
|
// src/core/workflow/createPollingAuthoritySource.ts
|
|
5447
|
+
function isFatalAuthorityPollStatus(status) {
|
|
5448
|
+
return status === 404 || status === 410;
|
|
5449
|
+
}
|
|
5450
|
+
var AuthorityPollFatalError = class extends Error {
|
|
5451
|
+
status;
|
|
5452
|
+
constructor(status, message) {
|
|
5453
|
+
super(message ?? `authority poll fatal: ${status}`);
|
|
5454
|
+
this.name = "AuthorityPollFatalError";
|
|
5455
|
+
this.status = status;
|
|
5456
|
+
}
|
|
5457
|
+
};
|
|
5323
5458
|
var DEFAULT_INTERVAL_MS = 2e3;
|
|
5324
5459
|
function createPollingAuthoritySource(taskId, fetchFn, options) {
|
|
5325
5460
|
let cached = options?.initialSnapshot ?? null;
|
|
@@ -5350,6 +5485,10 @@ function createPollingAuthoritySource(taskId, fetchFn, options) {
|
|
|
5350
5485
|
if (isAuthorityTerminalStatus(snap.status)) {
|
|
5351
5486
|
stopPoll();
|
|
5352
5487
|
}
|
|
5488
|
+
} catch (err) {
|
|
5489
|
+
if (err instanceof AuthorityPollFatalError) {
|
|
5490
|
+
stopPoll();
|
|
5491
|
+
}
|
|
5353
5492
|
} finally {
|
|
5354
5493
|
pollInFlight = false;
|
|
5355
5494
|
}
|
|
@@ -5422,7 +5561,7 @@ function buildTimelineEntries(rootIds, byId, options = {}) {
|
|
|
5422
5561
|
}
|
|
5423
5562
|
|
|
5424
5563
|
// src/view/nodes/SubAgentNode.tsx
|
|
5425
|
-
import { jsx as
|
|
5564
|
+
import { jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
5426
5565
|
function SubAgentBlock({ nodeId }) {
|
|
5427
5566
|
const node = useNodeTyped(nodeId, "subagent");
|
|
5428
5567
|
const childIds = useChildren(nodeId);
|
|
@@ -5433,7 +5572,7 @@ function SubAgentBlock({ nodeId }) {
|
|
|
5433
5572
|
"SubAgent: ",
|
|
5434
5573
|
node.subagentId
|
|
5435
5574
|
] }),
|
|
5436
|
-
/* @__PURE__ */
|
|
5575
|
+
/* @__PURE__ */ jsx21("div", { className: "lax-subagent-block__children", children: childIds.map((childId) => /* @__PURE__ */ jsx21(TimelineItem, { nodeId: childId, registry }, childId)) })
|
|
5437
5576
|
] });
|
|
5438
5577
|
}
|
|
5439
5578
|
|
|
@@ -5470,10 +5609,12 @@ export {
|
|
|
5470
5609
|
Agent,
|
|
5471
5610
|
AgentLoopView,
|
|
5472
5611
|
AgentSession,
|
|
5612
|
+
AgentSessionBanners,
|
|
5473
5613
|
AgentToolBody,
|
|
5474
5614
|
ApplicationKindMismatchError,
|
|
5475
5615
|
AskUserQuestion,
|
|
5476
5616
|
AskUserQuestionToolBody,
|
|
5617
|
+
AuthorityPollFatalError,
|
|
5477
5618
|
BOUNDED_DEFAULTS,
|
|
5478
5619
|
Bash,
|
|
5479
5620
|
BashToolBody,
|
|
@@ -5604,11 +5745,13 @@ export {
|
|
|
5604
5745
|
getDisplayProjectionStatus,
|
|
5605
5746
|
getMainStageIds,
|
|
5606
5747
|
getNoopInteractionBus,
|
|
5748
|
+
hydrateAskFromSnapshot,
|
|
5607
5749
|
hydrateWorkflowFromSnapshot,
|
|
5608
5750
|
isAgentLoopReplayEventType,
|
|
5609
5751
|
isAtOrBeforeProjectionCursor,
|
|
5610
5752
|
isAuthorityTerminal,
|
|
5611
5753
|
isAuthorityTerminalStatus,
|
|
5754
|
+
isFatalAuthorityPollStatus,
|
|
5612
5755
|
isGitOperationCommand,
|
|
5613
5756
|
isGroupableToolName,
|
|
5614
5757
|
isMcpToolName,
|
|
@@ -5633,13 +5776,17 @@ export {
|
|
|
5633
5776
|
resolveSseTransportPolicy,
|
|
5634
5777
|
resolveToolBody,
|
|
5635
5778
|
resolveWorkflowBanner,
|
|
5779
|
+
sessionTreeSnapshotToTree,
|
|
5636
5780
|
shouldApplyGrouping,
|
|
5781
|
+
shouldShowAgentSpinner,
|
|
5637
5782
|
shouldShowExploreDetail,
|
|
5638
5783
|
shouldShowGlobalSpinner,
|
|
5639
5784
|
shouldShowTaskListFooter,
|
|
5640
5785
|
streamChunk,
|
|
5641
5786
|
truncateCommand,
|
|
5642
5787
|
useActiveSession,
|
|
5788
|
+
useAgentAuthority,
|
|
5789
|
+
useAgentAuthoritySubscribeFailed,
|
|
5643
5790
|
useChildren,
|
|
5644
5791
|
useInteractionBus,
|
|
5645
5792
|
useInternalErrors,
|