langchain_agentx_stream_ui 0.2.6 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +200 -57
- package/dist/index.js +560 -165
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -263,6 +263,17 @@ var BOUNDED_DEFAULTS = {
|
|
|
263
263
|
reconnectTimeoutMs: 6e4,
|
|
264
264
|
reconnectDelayMs: 1e3
|
|
265
265
|
};
|
|
266
|
+
var legacyAutoReconnectWarned = false;
|
|
267
|
+
function warnLegacyAutoReconnectOnce() {
|
|
268
|
+
if (legacyAutoReconnectWarned) return;
|
|
269
|
+
if (typeof process !== "undefined" && process.env.NODE_ENV === "production") {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
legacyAutoReconnectWarned = true;
|
|
273
|
+
console.warn(
|
|
274
|
+
"[langchain_agentx_stream_ui] autoReconnect is deprecated; use profile or policy.reconnect instead."
|
|
275
|
+
);
|
|
276
|
+
}
|
|
266
277
|
var AGENT_TERMINAL = {
|
|
267
278
|
primary: ["finish"],
|
|
268
279
|
allowFinishFallback: false
|
|
@@ -353,6 +364,9 @@ function resolveSseTransportPolicy(input) {
|
|
|
353
364
|
reconnectDelayMs,
|
|
354
365
|
expectedApplicationKind
|
|
355
366
|
} = input;
|
|
367
|
+
if (profile === void 0 && policyOverride === void 0 && autoReconnect !== void 0) {
|
|
368
|
+
warnLegacyAutoReconnectOnce();
|
|
369
|
+
}
|
|
356
370
|
let base;
|
|
357
371
|
if (profile) {
|
|
358
372
|
base = { ...PROFILE_PRESETS[profile] };
|
|
@@ -850,11 +864,67 @@ function AgentSession({
|
|
|
850
864
|
}
|
|
851
865
|
|
|
852
866
|
// src/view/workflow/WorkflowSession.tsx
|
|
853
|
-
import { useEffect as
|
|
867
|
+
import { useEffect as useEffect10, useMemo as useMemo9, useRef as useRef4 } from "react";
|
|
854
868
|
|
|
855
869
|
// src/core/context/WorkflowSessionContext.tsx
|
|
856
870
|
import { createContext, useContext } from "react";
|
|
857
871
|
import { useStore } from "zustand";
|
|
872
|
+
|
|
873
|
+
// src/core/workflow/workflowAuthoritySelectors.ts
|
|
874
|
+
var AUTHORITY_TERMINAL = /* @__PURE__ */ new Set([
|
|
875
|
+
"done",
|
|
876
|
+
"error",
|
|
877
|
+
"canceled",
|
|
878
|
+
"partial_success"
|
|
879
|
+
]);
|
|
880
|
+
function isAuthorityTerminalStatus(status) {
|
|
881
|
+
return status != null && AUTHORITY_TERMINAL.has(status);
|
|
882
|
+
}
|
|
883
|
+
function isAuthorityTerminal(state) {
|
|
884
|
+
return isAuthorityTerminalStatus(state.authority?.status);
|
|
885
|
+
}
|
|
886
|
+
function shouldShowGlobalSpinner(state) {
|
|
887
|
+
const auth = state.authority?.status;
|
|
888
|
+
if (auth === "canceled") return false;
|
|
889
|
+
if (isAuthorityTerminalStatus(auth)) return false;
|
|
890
|
+
if (state.display.awaitingAuthority) return false;
|
|
891
|
+
if (auth == null || auth === "pending" || auth === "running") {
|
|
892
|
+
return true;
|
|
893
|
+
}
|
|
894
|
+
const proj = state.display.projectionStatus;
|
|
895
|
+
return proj === "running" || proj === "connecting";
|
|
896
|
+
}
|
|
897
|
+
function resolveWorkflowBanner(state) {
|
|
898
|
+
if (state.display.authoritySubscribeFailed) {
|
|
899
|
+
return { kind: "sync_failed", message: "\u72B6\u6001\u540C\u6B65\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5" };
|
|
900
|
+
}
|
|
901
|
+
if (state.display.authoritySyncFailed) {
|
|
902
|
+
return { kind: "sync_failed", message: "\u540C\u6B65\u72B6\u6001\u5931\u8D25\uFF0C\u8BF7\u5237\u65B0" };
|
|
903
|
+
}
|
|
904
|
+
if (state.display.awaitingAuthority) {
|
|
905
|
+
return { kind: "awaiting_authority", message: "\u6536\u5C3E\u4E2D\u2026" };
|
|
906
|
+
}
|
|
907
|
+
const auth = state.authority?.status;
|
|
908
|
+
if (auth === "partial_success") {
|
|
909
|
+
const forced = state.display.terminalKind === "forced_closeout";
|
|
910
|
+
return {
|
|
911
|
+
kind: "partial_success",
|
|
912
|
+
message: forced ? "\u90E8\u5206\u5B8C\u6210\uFF08\u90E8\u5206\u5BB9\u5668\u672A\u6B63\u5E38\u7ED3\u675F\uFF09" : "\u90E8\u5206\u5B8C\u6210\uFF08\u6240\u6709\u5BB9\u5668\u6B63\u5E38\u7ED3\u675F\uFF09"
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
if (state.display.terminalKind === "forced_closeout" && auth === "done") {
|
|
916
|
+
return { kind: "forced_closeout", message: "Workflow \u5DF2\u7ED3\u675F\uFF08\u90E8\u5206\u5BB9\u5668\u672A\u6B63\u5E38\u5173\u95ED\uFF09" };
|
|
917
|
+
}
|
|
918
|
+
return { kind: "none", message: "" };
|
|
919
|
+
}
|
|
920
|
+
function getDisplayProjectionStatus(state) {
|
|
921
|
+
return state.display.projectionStatus;
|
|
922
|
+
}
|
|
923
|
+
function applyAuthoritySnapshotToState(state, snapshot) {
|
|
924
|
+
return { ...state, authority: snapshot };
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
// src/core/context/WorkflowSessionContext.tsx
|
|
858
928
|
var WorkflowSessionStoreContext = createContext(null);
|
|
859
929
|
function useWorkflowSessionStoreApi() {
|
|
860
930
|
const store = useContext(WorkflowSessionStoreContext);
|
|
@@ -865,7 +935,7 @@ function useWorkflowSessionStoreApi() {
|
|
|
865
935
|
}
|
|
866
936
|
function useWorkflowSessionStatus() {
|
|
867
937
|
const store = useWorkflowSessionStoreApi();
|
|
868
|
-
return useStore(store, (s) => s.state
|
|
938
|
+
return useStore(store, (s) => getDisplayProjectionStatus(s.state));
|
|
869
939
|
}
|
|
870
940
|
|
|
871
941
|
// src/core/context/WorkflowLoopReplayContext.tsx
|
|
@@ -930,6 +1000,17 @@ function containerTreeToSnapshot(tree) {
|
|
|
930
1000
|
}
|
|
931
1001
|
|
|
932
1002
|
// src/types/workflowSession.ts
|
|
1003
|
+
function createEmptyWorkflowDisplayProjection() {
|
|
1004
|
+
return {
|
|
1005
|
+
streamEnded: false,
|
|
1006
|
+
projectionStatus: "connecting",
|
|
1007
|
+
terminalKind: null,
|
|
1008
|
+
openChildIdentities: [],
|
|
1009
|
+
awaitingAuthority: false,
|
|
1010
|
+
authoritySyncFailed: false,
|
|
1011
|
+
authoritySubscribeFailed: false
|
|
1012
|
+
};
|
|
1013
|
+
}
|
|
933
1014
|
function createEmptyWorkflowProgress() {
|
|
934
1015
|
return {
|
|
935
1016
|
workflowId: null,
|
|
@@ -941,9 +1022,8 @@ function createEmptyWorkflowProgress() {
|
|
|
941
1022
|
}
|
|
942
1023
|
function createEmptyWorkflowSessionState() {
|
|
943
1024
|
return {
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
openChildIdentities: [],
|
|
1025
|
+
authority: null,
|
|
1026
|
+
display: createEmptyWorkflowDisplayProjection(),
|
|
947
1027
|
meta: { workflowRunId: null, startedAt: null, lastEventId: null },
|
|
948
1028
|
containerTree: createEmptyWorkflowContainerTreeState(),
|
|
949
1029
|
workflowProgress: createEmptyWorkflowProgress(),
|
|
@@ -2136,7 +2216,10 @@ function readOpenChildIdentities(data) {
|
|
|
2136
2216
|
if (!Array.isArray(raw)) return [];
|
|
2137
2217
|
return raw.filter((item) => typeof item === "string");
|
|
2138
2218
|
}
|
|
2139
|
-
function
|
|
2219
|
+
function projectionStatusFromTerminalKind(kind, isWorkflowFailedEvent) {
|
|
2220
|
+
return sessionStatusFromTerminalKind(kind, isWorkflowFailedEvent);
|
|
2221
|
+
}
|
|
2222
|
+
function isDisplayProjectionTerminal(status) {
|
|
2140
2223
|
return status === "done" || status === "done_with_warning" || status === "error";
|
|
2141
2224
|
}
|
|
2142
2225
|
|
|
@@ -2509,18 +2592,16 @@ function applyStructureEvent(state, event) {
|
|
|
2509
2592
|
containerTree,
|
|
2510
2593
|
state.workflowProgress
|
|
2511
2594
|
);
|
|
2512
|
-
let
|
|
2513
|
-
if (
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
let terminalKind = state.terminalKind;
|
|
2517
|
-
let openChildIdentities = state.openChildIdentities;
|
|
2595
|
+
let projectionStatus = nextState.display.projectionStatus;
|
|
2596
|
+
if (projectionStatus === "connecting") projectionStatus = "running";
|
|
2597
|
+
let terminalKind = state.display.terminalKind;
|
|
2598
|
+
let openChildIdentities = state.display.openChildIdentities;
|
|
2518
2599
|
if (projectedEvent.event_type === "workflow-end" || projectedEvent.event_type === "workflow-failed") {
|
|
2519
2600
|
const resolved = resolveWorkflowTerminalKind(
|
|
2520
2601
|
structureData,
|
|
2521
2602
|
projectedEvent.event_type
|
|
2522
2603
|
);
|
|
2523
|
-
|
|
2604
|
+
projectionStatus = projectionStatusFromTerminalKind(
|
|
2524
2605
|
resolved,
|
|
2525
2606
|
projectedEvent.event_type === "workflow-failed"
|
|
2526
2607
|
);
|
|
@@ -2529,9 +2610,12 @@ function applyStructureEvent(state, event) {
|
|
|
2529
2610
|
}
|
|
2530
2611
|
const base = {
|
|
2531
2612
|
...nextState,
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2613
|
+
display: {
|
|
2614
|
+
...nextState.display,
|
|
2615
|
+
projectionStatus,
|
|
2616
|
+
terminalKind,
|
|
2617
|
+
openChildIdentities
|
|
2618
|
+
},
|
|
2535
2619
|
containerTree,
|
|
2536
2620
|
workflowProgress
|
|
2537
2621
|
};
|
|
@@ -2599,11 +2683,9 @@ function reduceWorkflowSession(state, event, eventIndex, options) {
|
|
|
2599
2683
|
next.containerTree,
|
|
2600
2684
|
next.workflowProgress
|
|
2601
2685
|
);
|
|
2602
|
-
let
|
|
2603
|
-
if (
|
|
2604
|
-
else if (
|
|
2605
|
-
else if (reducedLoop.status === "error") status = "error";
|
|
2606
|
-
else if (status === "connecting") status = "running";
|
|
2686
|
+
let projectionStatus = next.display.projectionStatus;
|
|
2687
|
+
if (reducedLoop.status === "error") projectionStatus = "error";
|
|
2688
|
+
else if (projectionStatus === "connecting") projectionStatus = "running";
|
|
2607
2689
|
return {
|
|
2608
2690
|
...next,
|
|
2609
2691
|
workflowProgress,
|
|
@@ -2612,61 +2694,38 @@ function reduceWorkflowSession(state, event, eventIndex, options) {
|
|
|
2612
2694
|
[loopSessionId]: reducedLoop
|
|
2613
2695
|
},
|
|
2614
2696
|
activeLoopSessionId: loopSessionId,
|
|
2615
|
-
|
|
2697
|
+
display: {
|
|
2698
|
+
...next.display,
|
|
2699
|
+
projectionStatus
|
|
2700
|
+
}
|
|
2616
2701
|
};
|
|
2617
2702
|
}
|
|
2618
|
-
function
|
|
2619
|
-
if (
|
|
2620
|
-
return {
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
return { ...state, status: "done", activeLoopSessionId: null };
|
|
2626
|
-
}
|
|
2627
|
-
const root = tree.containersById[rootId];
|
|
2628
|
-
if (!root) {
|
|
2629
|
-
return { ...state, status: "done", activeLoopSessionId: null };
|
|
2703
|
+
function finalizeWorkflowDisplayOnStreamEnded(state) {
|
|
2704
|
+
if (isDisplayProjectionTerminal(state.display.projectionStatus)) {
|
|
2705
|
+
return {
|
|
2706
|
+
...state,
|
|
2707
|
+
display: { ...state.display, streamEnded: true },
|
|
2708
|
+
activeLoopSessionId: null
|
|
2709
|
+
};
|
|
2630
2710
|
}
|
|
2631
|
-
const
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
const settledStageCount = topLevelStages.filter(
|
|
2639
|
-
(s) => s.status === "completed" || s.status === "failed" || s.status === "skipped"
|
|
2640
|
-
).length;
|
|
2641
|
-
const progressCurrent = Math.max(
|
|
2642
|
-
root.display?.progress_current ?? 0,
|
|
2643
|
-
settledStageCount
|
|
2644
|
-
);
|
|
2645
|
-
const rootParallelItems = Object.values(tree.containersById).filter(
|
|
2646
|
-
(node) => node.scope === "item" && node.workflow_path === root.workflow_path && node.workflow_depth === root.workflow_depth
|
|
2647
|
-
);
|
|
2648
|
-
const rootParallelSettled = topLevelStages.length === 0 && rootParallelItems.length > 0 && rootParallelItems.every(
|
|
2649
|
-
(item) => item.status === "completed" || item.status === "failed" || item.status === "skipped"
|
|
2650
|
-
) && !Object.values(tree.containersById).some(
|
|
2651
|
-
(node) => node.container_id !== rootId && node.workflow_path === root.workflow_path && node.workflow_depth === root.workflow_depth && (node.status === "running" || node.status === "retrying" || node.status === "pending")
|
|
2652
|
-
);
|
|
2653
|
-
const workflowVisuallyComplete = root.status === "completed" || root.status === "failed" || progressTotal > 0 && progressCurrent >= progressTotal || rootParallelSettled;
|
|
2654
|
-
if (!workflowVisuallyComplete) {
|
|
2655
|
-
return state;
|
|
2711
|
+
const withStreamEnded = {
|
|
2712
|
+
...state,
|
|
2713
|
+
display: { ...state.display, streamEnded: true },
|
|
2714
|
+
activeLoopSessionId: null
|
|
2715
|
+
};
|
|
2716
|
+
if (isAuthorityTerminalStatus(state.authority?.status)) {
|
|
2717
|
+
return forceSettleRunningContainersForDisplay(withStreamEnded);
|
|
2656
2718
|
}
|
|
2719
|
+
return withStreamEnded;
|
|
2720
|
+
}
|
|
2721
|
+
function forceSettleRunningContainersForDisplay(state) {
|
|
2722
|
+
const tree = state.containerTree;
|
|
2657
2723
|
let containersById = { ...tree.containersById };
|
|
2658
2724
|
for (const [id, node] of Object.entries(containersById)) {
|
|
2659
2725
|
if (node.status === "running" || node.status === "pending") {
|
|
2660
2726
|
containersById[id] = { ...node, status: "completed", is_open: false };
|
|
2661
2727
|
}
|
|
2662
2728
|
}
|
|
2663
|
-
const settledRoot = containersById[rootId] ?? root;
|
|
2664
|
-
if (settledRoot.status !== "completed" && settledRoot.status !== "failed") {
|
|
2665
|
-
containersById = {
|
|
2666
|
-
...containersById,
|
|
2667
|
-
[rootId]: { ...settledRoot, status: "completed", is_open: false }
|
|
2668
|
-
};
|
|
2669
|
-
}
|
|
2670
2729
|
const loopTreesBySessionId = { ...state.loopTreesBySessionId };
|
|
2671
2730
|
for (const [loopSessionId, loopTree] of Object.entries(loopTreesBySessionId)) {
|
|
2672
2731
|
if (loopTree.status === "running" || loopTree.status === "connecting") {
|
|
@@ -2680,15 +2739,9 @@ function finalizeWorkflowSessionOnStreamComplete(state) {
|
|
|
2680
2739
|
(id) => containersById[id]?.is_open
|
|
2681
2740
|
)
|
|
2682
2741
|
};
|
|
2683
|
-
const workflowProgress = syncWorkflowProgressFromContainerTree(
|
|
2684
|
-
containerTree,
|
|
2685
|
-
state.workflowProgress
|
|
2686
|
-
);
|
|
2687
2742
|
return {
|
|
2688
2743
|
...state,
|
|
2689
|
-
status: workflowProgress.status === "error" ? "error" : "done",
|
|
2690
2744
|
containerTree,
|
|
2691
|
-
workflowProgress,
|
|
2692
2745
|
loopTreesBySessionId,
|
|
2693
2746
|
activeLoopSessionId: null
|
|
2694
2747
|
};
|
|
@@ -2750,7 +2803,10 @@ function finalizeWorkflowSessionOnStreamError(state, message, eventIndex, stack)
|
|
|
2750
2803
|
}
|
|
2751
2804
|
return {
|
|
2752
2805
|
...state,
|
|
2753
|
-
|
|
2806
|
+
display: {
|
|
2807
|
+
...state.display,
|
|
2808
|
+
projectionStatus: "error"
|
|
2809
|
+
},
|
|
2754
2810
|
containerTree: {
|
|
2755
2811
|
...state.containerTree,
|
|
2756
2812
|
containersById,
|
|
@@ -2795,7 +2851,10 @@ function createWorkflowSessionStore(initialState = createEmptyWorkflowSessionSta
|
|
|
2795
2851
|
state: {
|
|
2796
2852
|
...evicted,
|
|
2797
2853
|
meta: nextMeta,
|
|
2798
|
-
|
|
2854
|
+
display: {
|
|
2855
|
+
...evicted.display,
|
|
2856
|
+
projectionStatus: evicted.display.projectionStatus === "connecting" ? "running" : evicted.display.projectionStatus
|
|
2857
|
+
}
|
|
2799
2858
|
},
|
|
2800
2859
|
eventCount: eventCount + 1
|
|
2801
2860
|
});
|
|
@@ -2825,13 +2884,59 @@ function createWorkflowSessionStore(initialState = createEmptyWorkflowSessionSta
|
|
|
2825
2884
|
)
|
|
2826
2885
|
});
|
|
2827
2886
|
},
|
|
2828
|
-
|
|
2887
|
+
markStreamEnded() {
|
|
2829
2888
|
set((current) => ({
|
|
2830
2889
|
state: finalizeState(
|
|
2831
|
-
|
|
2890
|
+
finalizeWorkflowDisplayOnStreamEnded(current.state)
|
|
2832
2891
|
)
|
|
2833
2892
|
}));
|
|
2834
2893
|
},
|
|
2894
|
+
applyAuthoritySnapshot(snapshot) {
|
|
2895
|
+
set((current) => ({
|
|
2896
|
+
state: {
|
|
2897
|
+
...current.state,
|
|
2898
|
+
authority: snapshot,
|
|
2899
|
+
display: {
|
|
2900
|
+
...current.state.display,
|
|
2901
|
+
awaitingAuthority: false,
|
|
2902
|
+
authoritySyncFailed: false
|
|
2903
|
+
}
|
|
2904
|
+
}
|
|
2905
|
+
}));
|
|
2906
|
+
},
|
|
2907
|
+
setAwaitingAuthority(value) {
|
|
2908
|
+
set((current) => ({
|
|
2909
|
+
state: {
|
|
2910
|
+
...current.state,
|
|
2911
|
+
display: {
|
|
2912
|
+
...current.state.display,
|
|
2913
|
+
awaitingAuthority: value
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
}));
|
|
2917
|
+
},
|
|
2918
|
+
setAuthoritySyncFailed(value) {
|
|
2919
|
+
set((current) => ({
|
|
2920
|
+
state: {
|
|
2921
|
+
...current.state,
|
|
2922
|
+
display: {
|
|
2923
|
+
...current.state.display,
|
|
2924
|
+
authoritySyncFailed: value
|
|
2925
|
+
}
|
|
2926
|
+
}
|
|
2927
|
+
}));
|
|
2928
|
+
},
|
|
2929
|
+
setAuthoritySubscribeFailed(value) {
|
|
2930
|
+
set((current) => ({
|
|
2931
|
+
state: {
|
|
2932
|
+
...current.state,
|
|
2933
|
+
display: {
|
|
2934
|
+
...current.state.display,
|
|
2935
|
+
authoritySubscribeFailed: value
|
|
2936
|
+
}
|
|
2937
|
+
}
|
|
2938
|
+
}));
|
|
2939
|
+
},
|
|
2835
2940
|
/**
|
|
2836
2941
|
* P2:将 completed loop 加入 LRU 热缓存并淘汰溢出。
|
|
2837
2942
|
* running 保护由 collectProtectedLoopSessionIds 按状态自动覆盖,不再依赖永久 pin。
|
|
@@ -2938,11 +3043,93 @@ function createWorkflowSessionStore(initialState = createEmptyWorkflowSessionSta
|
|
|
2938
3043
|
}));
|
|
2939
3044
|
}
|
|
2940
3045
|
|
|
3046
|
+
// src/core/workflow/useWorkflowAuthorityBinding.ts
|
|
3047
|
+
import { useEffect as useEffect3, useRef as useRef3 } from "react";
|
|
3048
|
+
|
|
3049
|
+
// src/core/workflow/normalizeAuthoritySnapshot.ts
|
|
3050
|
+
var KNOWN = /* @__PURE__ */ new Set([
|
|
3051
|
+
"pending",
|
|
3052
|
+
"running",
|
|
3053
|
+
"done",
|
|
3054
|
+
"error",
|
|
3055
|
+
"canceled",
|
|
3056
|
+
"partial_success"
|
|
3057
|
+
]);
|
|
3058
|
+
function normalizeAuthoritySnapshot(snapshot) {
|
|
3059
|
+
if (KNOWN.has(snapshot.status)) {
|
|
3060
|
+
return snapshot;
|
|
3061
|
+
}
|
|
3062
|
+
if (import.meta.env?.DEV) {
|
|
3063
|
+
console.warn(
|
|
3064
|
+
`[langchain_agentx_stream_ui] unknown authority.status "${String(snapshot.status)}"; defaulting to running`
|
|
3065
|
+
);
|
|
3066
|
+
}
|
|
3067
|
+
return { ...snapshot, status: "running" };
|
|
3068
|
+
}
|
|
3069
|
+
|
|
3070
|
+
// src/core/workflow/useWorkflowAuthorityBinding.ts
|
|
3071
|
+
function useWorkflowAuthorityBinding(store, authoritySource, callbacks) {
|
|
3072
|
+
const onAuthorityTerminalRef = useRef3(callbacks.onAuthorityTerminal);
|
|
3073
|
+
onAuthorityTerminalRef.current = callbacks.onAuthorityTerminal;
|
|
3074
|
+
const terminalFiredRef = useRef3(false);
|
|
3075
|
+
useEffect3(() => {
|
|
3076
|
+
terminalFiredRef.current = false;
|
|
3077
|
+
const apply = (raw) => {
|
|
3078
|
+
const snapshot = normalizeAuthoritySnapshot(raw);
|
|
3079
|
+
store.getState().applyAuthoritySnapshot(snapshot);
|
|
3080
|
+
if (!terminalFiredRef.current && isAuthorityTerminalStatus(snapshot.status)) {
|
|
3081
|
+
terminalFiredRef.current = true;
|
|
3082
|
+
onAuthorityTerminalRef.current?.(snapshot);
|
|
3083
|
+
}
|
|
3084
|
+
};
|
|
3085
|
+
const initial = authoritySource.getSnapshot();
|
|
3086
|
+
if (initial) {
|
|
3087
|
+
apply(initial);
|
|
3088
|
+
}
|
|
3089
|
+
let unsubscribe;
|
|
3090
|
+
try {
|
|
3091
|
+
unsubscribe = authoritySource.subscribe(apply);
|
|
3092
|
+
} catch (err) {
|
|
3093
|
+
if (import.meta.env?.DEV) {
|
|
3094
|
+
console.error("[langchain_agentx_stream_ui] authoritySource.subscribe failed:", err);
|
|
3095
|
+
}
|
|
3096
|
+
store.getState().setAuthoritySubscribeFailed(true);
|
|
3097
|
+
}
|
|
3098
|
+
return () => {
|
|
3099
|
+
unsubscribe?.();
|
|
3100
|
+
};
|
|
3101
|
+
}, [store, authoritySource]);
|
|
3102
|
+
}
|
|
3103
|
+
|
|
3104
|
+
// src/core/workflow/useWorkflowAwaitingAuthorityTimers.ts
|
|
3105
|
+
import { useEffect as useEffect4 } from "react";
|
|
3106
|
+
var AWAITING_MS = 3e4;
|
|
3107
|
+
var SYNC_FAILED_MS = 12e4;
|
|
3108
|
+
function useWorkflowAwaitingAuthorityTimers(store, streamEnded, authorityStatus) {
|
|
3109
|
+
useEffect4(() => {
|
|
3110
|
+
if (!streamEnded || authorityStatus !== "running") {
|
|
3111
|
+
store.getState().setAwaitingAuthority(false);
|
|
3112
|
+
store.getState().setAuthoritySyncFailed(false);
|
|
3113
|
+
return;
|
|
3114
|
+
}
|
|
3115
|
+
const tAwait = window.setTimeout(() => {
|
|
3116
|
+
store.getState().setAwaitingAuthority(true);
|
|
3117
|
+
}, AWAITING_MS);
|
|
3118
|
+
const tFail = window.setTimeout(() => {
|
|
3119
|
+
store.getState().setAuthoritySyncFailed(true);
|
|
3120
|
+
}, SYNC_FAILED_MS);
|
|
3121
|
+
return () => {
|
|
3122
|
+
window.clearTimeout(tAwait);
|
|
3123
|
+
window.clearTimeout(tFail);
|
|
3124
|
+
};
|
|
3125
|
+
}, [store, streamEnded, authorityStatus]);
|
|
3126
|
+
}
|
|
3127
|
+
|
|
2941
3128
|
// src/view/workflow/WorkflowChrome.tsx
|
|
2942
3129
|
import { useMemo as useMemo7, useState as useState6 } from "react";
|
|
2943
3130
|
|
|
2944
3131
|
// src/view/workflow/WorkflowAggregateContainer.tsx
|
|
2945
|
-
import { useCallback, useEffect as
|
|
3132
|
+
import { useCallback, useEffect as useEffect5, useMemo as useMemo2, useState, memo } from "react";
|
|
2946
3133
|
|
|
2947
3134
|
// src/view/workflow/WorkflowContainerLine.tsx
|
|
2948
3135
|
import { Fragment, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
@@ -3352,7 +3539,7 @@ function WorkflowAggregateContainerInner({
|
|
|
3352
3539
|
const uiStatus = mapAggregateUiStatus(node);
|
|
3353
3540
|
const statusLabel = buildAggregateStatusLabel(node, siblingItems);
|
|
3354
3541
|
const [expanded, setExpanded] = useState(isRunning);
|
|
3355
|
-
|
|
3542
|
+
useEffect5(() => {
|
|
3356
3543
|
if (isRunning) setExpanded(true);
|
|
3357
3544
|
if (isDone) setExpanded(false);
|
|
3358
3545
|
}, [isRunning, isDone]);
|
|
@@ -3471,7 +3658,7 @@ var WorkflowAggregateContainer = memo(
|
|
|
3471
3658
|
);
|
|
3472
3659
|
|
|
3473
3660
|
// src/view/workflow/WorkflowParallelGroup.tsx
|
|
3474
|
-
import { useCallback as useCallback3, useEffect as
|
|
3661
|
+
import { useCallback as useCallback3, useEffect as useEffect7, useMemo as useMemo4, useState as useState3, memo as memo3 } from "react";
|
|
3475
3662
|
|
|
3476
3663
|
// src/core/workflow/workflowParallelGroupMetrics.ts
|
|
3477
3664
|
var ACTIVE_ITEM_STATUSES = /* @__PURE__ */ new Set([
|
|
@@ -3565,7 +3752,7 @@ function formatParallelGroupSummary(metrics) {
|
|
|
3565
3752
|
}
|
|
3566
3753
|
|
|
3567
3754
|
// src/view/workflow/WorkflowStageContainer.tsx
|
|
3568
|
-
import { useCallback as useCallback2, useEffect as
|
|
3755
|
+
import { useCallback as useCallback2, useEffect as useEffect6, useMemo as useMemo3, useState as useState2, memo as memo2 } from "react";
|
|
3569
3756
|
import { useStore as useStore2 } from "zustand";
|
|
3570
3757
|
|
|
3571
3758
|
// src/core/workflow/loopTreeUtils.ts
|
|
@@ -3652,7 +3839,7 @@ function WorkflowStageContainerInner({
|
|
|
3652
3839
|
const doneSummary = doneSummaryFromTree ?? (isDone ? cachedSummary : null);
|
|
3653
3840
|
const shouldAutoExpandRunning = effectivelyRunning && !isSkipped;
|
|
3654
3841
|
const [expanded, setExpanded] = useState2(shouldAutoExpandRunning);
|
|
3655
|
-
|
|
3842
|
+
useEffect6(() => {
|
|
3656
3843
|
if (shouldAutoExpandRunning) {
|
|
3657
3844
|
setExpanded(true);
|
|
3658
3845
|
return;
|
|
@@ -3677,7 +3864,7 @@ function WorkflowStageContainerInner({
|
|
|
3677
3864
|
return next;
|
|
3678
3865
|
});
|
|
3679
3866
|
}, [isDone, node.loopSessionId, loopTree, requestLoopHydration]);
|
|
3680
|
-
|
|
3867
|
+
useEffect6(() => {
|
|
3681
3868
|
const onKeyDown = (event) => {
|
|
3682
3869
|
if (!(event.ctrlKey || event.metaKey) || event.key.toLowerCase() !== "o") return;
|
|
3683
3870
|
setExpanded(true);
|
|
@@ -3830,7 +4017,7 @@ function WorkflowParallelGroupInner({
|
|
|
3830
4017
|
);
|
|
3831
4018
|
const allSettled = metrics.total > 0 && metrics.running === 0 && metrics.completed + metrics.failed >= metrics.total;
|
|
3832
4019
|
const [expanded, setExpanded] = useState3(hasActiveWork);
|
|
3833
|
-
|
|
4020
|
+
useEffect7(() => {
|
|
3834
4021
|
if (hasActiveWork) {
|
|
3835
4022
|
setExpanded(true);
|
|
3836
4023
|
return;
|
|
@@ -3842,7 +4029,7 @@ function WorkflowParallelGroupInner({
|
|
|
3842
4029
|
const toggleExpanded = useCallback3(() => {
|
|
3843
4030
|
setExpanded((prev) => !prev);
|
|
3844
4031
|
}, []);
|
|
3845
|
-
|
|
4032
|
+
useEffect7(() => {
|
|
3846
4033
|
const onKeyDown = (event) => {
|
|
3847
4034
|
if (!(event.ctrlKey || event.metaKey) || event.key.toLowerCase() !== "o") return;
|
|
3848
4035
|
setExpanded(true);
|
|
@@ -3942,7 +4129,7 @@ function WorkflowRootContainer({
|
|
|
3942
4129
|
}
|
|
3943
4130
|
|
|
3944
4131
|
// src/view/workflow/WorkflowRouteContainer.tsx
|
|
3945
|
-
import { useCallback as useCallback4, useEffect as
|
|
4132
|
+
import { useCallback as useCallback4, useEffect as useEffect8, useMemo as useMemo5, useState as useState4 } from "react";
|
|
3946
4133
|
import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
3947
4134
|
function WorkflowRouteContainer({
|
|
3948
4135
|
node,
|
|
@@ -3967,7 +4154,7 @@ function WorkflowRouteContainer({
|
|
|
3967
4154
|
const expandHint = doneSummary ? formatTeaserExpandHint(doneSummary.extraLines, verbose) : null;
|
|
3968
4155
|
const doneLabel = doneSummary?.label ?? "done";
|
|
3969
4156
|
const [expanded, setExpanded] = useState4(isRunning);
|
|
3970
|
-
|
|
4157
|
+
useEffect8(() => {
|
|
3971
4158
|
if (isSkipped) return;
|
|
3972
4159
|
if (isRunning) setExpanded(true);
|
|
3973
4160
|
if (isDone) setExpanded(false);
|
|
@@ -4043,7 +4230,7 @@ function WorkflowRouteContainer({
|
|
|
4043
4230
|
}
|
|
4044
4231
|
|
|
4045
4232
|
// src/view/workflow/WorkflowSubworkflowContainer.tsx
|
|
4046
|
-
import { useCallback as useCallback5, useEffect as
|
|
4233
|
+
import { useCallback as useCallback5, useEffect as useEffect9, useMemo as useMemo6, useState as useState5 } from "react";
|
|
4047
4234
|
import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
4048
4235
|
function buildDoneSummary(node) {
|
|
4049
4236
|
const display = node.display;
|
|
@@ -4074,7 +4261,7 @@ function WorkflowSubworkflowContainer({
|
|
|
4074
4261
|
return node.content_blocks[node.content_blocks.length - 1]?.preview ?? "";
|
|
4075
4262
|
}, [node.content_blocks]);
|
|
4076
4263
|
const [expanded, setExpanded] = useState5(effectivelyRunning);
|
|
4077
|
-
|
|
4264
|
+
useEffect9(() => {
|
|
4078
4265
|
if (effectivelyRunning) setExpanded(true);
|
|
4079
4266
|
else if (effectivelyDone) setExpanded(false);
|
|
4080
4267
|
}, [effectivelyRunning, effectivelyDone]);
|
|
@@ -4426,9 +4613,94 @@ function WorkflowChrome({
|
|
|
4426
4613
|
)) });
|
|
4427
4614
|
}
|
|
4428
4615
|
|
|
4616
|
+
// src/view/workflow/WorkflowGlobalSpinner.tsx
|
|
4617
|
+
import { useStore as useStore3 } from "zustand";
|
|
4618
|
+
import { jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
4619
|
+
function WorkflowGlobalSpinner() {
|
|
4620
|
+
const store = useWorkflowSessionStoreApi();
|
|
4621
|
+
const show = useStore3(store, (s) => shouldShowGlobalSpinner(s.state));
|
|
4622
|
+
if (!show) return null;
|
|
4623
|
+
return /* @__PURE__ */ jsx16(
|
|
4624
|
+
"div",
|
|
4625
|
+
{
|
|
4626
|
+
className: "lax-workflow-global-spinner lax-spinner-container",
|
|
4627
|
+
"data-testid": "lax-workflow-global-spinner",
|
|
4628
|
+
role: "status",
|
|
4629
|
+
children: /* @__PURE__ */ jsxs11("span", { className: "lax-spinner lax-workflow-global-spinner__inner", children: [
|
|
4630
|
+
/* @__PURE__ */ jsx16("span", { className: "lax-spinner-frame", children: "\u280B" }),
|
|
4631
|
+
/* @__PURE__ */ jsx16("span", { className: "lax-spinner-verb lax-spinner-verb--shimmer", children: "Workflow" })
|
|
4632
|
+
] })
|
|
4633
|
+
}
|
|
4634
|
+
);
|
|
4635
|
+
}
|
|
4636
|
+
|
|
4637
|
+
// src/view/workflow/WorkflowSessionBanners.tsx
|
|
4638
|
+
import { Fragment as Fragment3, jsx as jsx17, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
4639
|
+
function WorkflowSessionBanners({ state }) {
|
|
4640
|
+
const banner = resolveWorkflowBanner(state);
|
|
4641
|
+
const { display } = state;
|
|
4642
|
+
if (banner.kind === "none" && display.projectionStatus !== "error" && display.projectionStatus !== "done_with_warning" && state.internalErrors.length === 0) {
|
|
4643
|
+
return null;
|
|
4644
|
+
}
|
|
4645
|
+
return /* @__PURE__ */ jsxs12(Fragment3, { children: [
|
|
4646
|
+
banner.kind === "partial_success" ? /* @__PURE__ */ jsx17(
|
|
4647
|
+
"div",
|
|
4648
|
+
{
|
|
4649
|
+
className: "lax-workflow-banner lax-workflow-banner--partial-success",
|
|
4650
|
+
"data-testid": "lax-workflow-partial-success-banner",
|
|
4651
|
+
role: "status",
|
|
4652
|
+
children: banner.message
|
|
4653
|
+
}
|
|
4654
|
+
) : null,
|
|
4655
|
+
banner.kind === "forced_closeout" || display.projectionStatus === "done_with_warning" ? /* @__PURE__ */ jsxs12(
|
|
4656
|
+
"div",
|
|
4657
|
+
{
|
|
4658
|
+
className: "lax-workflow-closeout-warning",
|
|
4659
|
+
"data-testid": "lax-workflow-closeout-warning",
|
|
4660
|
+
role: "status",
|
|
4661
|
+
children: [
|
|
4662
|
+
/* @__PURE__ */ jsx17("strong", { children: "Workflow forced closeout" }),
|
|
4663
|
+
/* @__PURE__ */ jsxs12("span", { children: [
|
|
4664
|
+
" ",
|
|
4665
|
+
"\u2014 stream ended with unresolved child scopes; not a healthy completion."
|
|
4666
|
+
] }),
|
|
4667
|
+
display.openChildIdentities.length > 0 ? /* @__PURE__ */ jsx17("ul", { className: "lax-workflow-closeout-warning__scopes", children: display.openChildIdentities.map((identity) => /* @__PURE__ */ jsx17("li", { children: identity }, identity)) }) : null
|
|
4668
|
+
]
|
|
4669
|
+
}
|
|
4670
|
+
) : null,
|
|
4671
|
+
banner.kind === "awaiting_authority" ? /* @__PURE__ */ jsx17(
|
|
4672
|
+
"div",
|
|
4673
|
+
{
|
|
4674
|
+
className: "lax-workflow-banner lax-workflow-banner--awaiting",
|
|
4675
|
+
"data-testid": "lax-workflow-awaiting-authority-banner",
|
|
4676
|
+
role: "status",
|
|
4677
|
+
children: banner.message
|
|
4678
|
+
}
|
|
4679
|
+
) : null,
|
|
4680
|
+
banner.kind === "sync_failed" ? /* @__PURE__ */ jsx17(
|
|
4681
|
+
"div",
|
|
4682
|
+
{
|
|
4683
|
+
className: "lax-workflow-banner lax-workflow-banner--sync-failed",
|
|
4684
|
+
"data-testid": "lax-workflow-authority-sync-failed-banner",
|
|
4685
|
+
role: "alert",
|
|
4686
|
+
children: banner.message
|
|
4687
|
+
}
|
|
4688
|
+
) : null,
|
|
4689
|
+
display.projectionStatus === "error" || state.internalErrors.length > 0 ? /* @__PURE__ */ jsx17(
|
|
4690
|
+
"div",
|
|
4691
|
+
{
|
|
4692
|
+
className: "lax-workflow-error",
|
|
4693
|
+
"data-testid": "lax-workflow-error",
|
|
4694
|
+
role: "alert",
|
|
4695
|
+
children: state.internalErrors[state.internalErrors.length - 1]?.message ?? "Workflow stream error"
|
|
4696
|
+
}
|
|
4697
|
+
) : null
|
|
4698
|
+
] });
|
|
4699
|
+
}
|
|
4700
|
+
|
|
4429
4701
|
// src/view/workflow/WorkflowTaskListFooter.tsx
|
|
4430
4702
|
import { useMemo as useMemo8, useState as useState7 } from "react";
|
|
4431
|
-
import { useStore as
|
|
4703
|
+
import { useStore as useStore4 } from "zustand";
|
|
4432
4704
|
|
|
4433
4705
|
// src/view/workflow/workflowTaskListMerge.ts
|
|
4434
4706
|
var STATUS_ORDER = {
|
|
@@ -4469,7 +4741,7 @@ function deriveWorkflowTaskFooterState(state) {
|
|
|
4469
4741
|
tasks: mergeWorkflowLoopTasks(state.loopTreesBySessionId),
|
|
4470
4742
|
taskPhase: deriveWorkflowTaskPhase(trees),
|
|
4471
4743
|
hasEverShownTaskList: trees.some((tree) => tree.hasEverShownTaskList),
|
|
4472
|
-
sessionStatus: state
|
|
4744
|
+
sessionStatus: getDisplayProjectionStatus(state)
|
|
4473
4745
|
};
|
|
4474
4746
|
}
|
|
4475
4747
|
function shouldShowWorkflowTaskListFooter(state) {
|
|
@@ -4499,16 +4771,16 @@ function loopSessionDisplayLabel(loopSessionId) {
|
|
|
4499
4771
|
}
|
|
4500
4772
|
|
|
4501
4773
|
// src/view/workflow/WorkflowTaskListFooter.tsx
|
|
4502
|
-
import { jsx as
|
|
4774
|
+
import { jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
4503
4775
|
function MessageResponse({ children }) {
|
|
4504
|
-
return /* @__PURE__ */
|
|
4505
|
-
/* @__PURE__ */
|
|
4506
|
-
/* @__PURE__ */
|
|
4776
|
+
return /* @__PURE__ */ jsxs13("div", { className: "lax-message-response", children: [
|
|
4777
|
+
/* @__PURE__ */ jsx18("span", { className: "lax-message-response__marker", children: "\u23BF " }),
|
|
4778
|
+
/* @__PURE__ */ jsx18("span", { className: "lax-message-response__content", children })
|
|
4507
4779
|
] });
|
|
4508
4780
|
}
|
|
4509
4781
|
function WorkflowTaskListFooter() {
|
|
4510
4782
|
const store = useWorkflowSessionStoreApi();
|
|
4511
|
-
const state =
|
|
4783
|
+
const state = useStore4(store, (s) => s.state);
|
|
4512
4784
|
const [expanded, setExpanded] = useState7(false);
|
|
4513
4785
|
const footerState = useMemo8(() => deriveWorkflowTaskFooterState(state), [state]);
|
|
4514
4786
|
const visible = shouldShowWorkflowTaskListFooter(state);
|
|
@@ -4517,8 +4789,8 @@ function WorkflowTaskListFooter() {
|
|
|
4517
4789
|
}
|
|
4518
4790
|
const label = formatWorkflowTaskFooterLabel(state);
|
|
4519
4791
|
const groups = groupWorkflowTasksByLoop(footerState.tasks);
|
|
4520
|
-
return /* @__PURE__ */
|
|
4521
|
-
/* @__PURE__ */
|
|
4792
|
+
return /* @__PURE__ */ jsxs13("div", { className: "lax-workflow-task-list-footer", "data-testid": "lax-workflow-task-list-footer", children: [
|
|
4793
|
+
/* @__PURE__ */ jsxs13(
|
|
4522
4794
|
"button",
|
|
4523
4795
|
{
|
|
4524
4796
|
type: "button",
|
|
@@ -4528,18 +4800,18 @@ function WorkflowTaskListFooter() {
|
|
|
4528
4800
|
onClick: () => setExpanded((v) => !v),
|
|
4529
4801
|
children: [
|
|
4530
4802
|
label,
|
|
4531
|
-
/* @__PURE__ */
|
|
4803
|
+
/* @__PURE__ */ jsx18("span", { className: "lax-task-list-footer__hint", children: expanded ? " \xB7 \u2191 to hide" : " \xB7 \u2193 to view" })
|
|
4532
4804
|
]
|
|
4533
4805
|
}
|
|
4534
4806
|
),
|
|
4535
|
-
expanded ? /* @__PURE__ */
|
|
4807
|
+
expanded ? /* @__PURE__ */ jsx18(MessageResponse, { children: /* @__PURE__ */ jsx18("div", { className: "lax-workflow-task-list-groups", children: groups.map((group) => /* @__PURE__ */ jsxs13(
|
|
4536
4808
|
"div",
|
|
4537
4809
|
{
|
|
4538
4810
|
className: "lax-workflow-task-list-group",
|
|
4539
4811
|
"data-testid": `lax-workflow-task-group-${group.loopSessionId}`,
|
|
4540
4812
|
children: [
|
|
4541
|
-
/* @__PURE__ */
|
|
4542
|
-
/* @__PURE__ */
|
|
4813
|
+
/* @__PURE__ */ jsx18("div", { className: "lax-workflow-task-list-group-title", children: loopSessionDisplayLabel(group.loopSessionId) }),
|
|
4814
|
+
/* @__PURE__ */ jsx18(TaskList, { tasks: group.tasks })
|
|
4543
4815
|
]
|
|
4544
4816
|
},
|
|
4545
4817
|
group.loopSessionId
|
|
@@ -4548,10 +4820,11 @@ function WorkflowTaskListFooter() {
|
|
|
4548
4820
|
}
|
|
4549
4821
|
|
|
4550
4822
|
// src/view/workflow/WorkflowSession.tsx
|
|
4551
|
-
import { useStore as
|
|
4552
|
-
import { jsx as
|
|
4823
|
+
import { useStore as useStore5 } from "zustand";
|
|
4824
|
+
import { jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
4553
4825
|
function WorkflowSession({
|
|
4554
4826
|
source,
|
|
4827
|
+
authoritySource,
|
|
4555
4828
|
initialEvents,
|
|
4556
4829
|
registry,
|
|
4557
4830
|
tierOverrides,
|
|
@@ -4571,10 +4844,11 @@ function WorkflowSession({
|
|
|
4571
4844
|
groupParallelTools = false,
|
|
4572
4845
|
workflowScale,
|
|
4573
4846
|
onRequestLoopReplay,
|
|
4574
|
-
|
|
4847
|
+
onAuthorityTerminal,
|
|
4848
|
+
onDisplayStreamEnded,
|
|
4575
4849
|
children
|
|
4576
4850
|
}) {
|
|
4577
|
-
const storeRef =
|
|
4851
|
+
const storeRef = useRef4(null);
|
|
4578
4852
|
if (storeRef.current === null) {
|
|
4579
4853
|
const initialState = initialEvents && initialEvents.length > 0 ? reduceWorkflowEvents(initialEvents, { tierOverrides }) : void 0;
|
|
4580
4854
|
storeRef.current = createWorkflowSessionStore(initialState, {
|
|
@@ -4584,6 +4858,11 @@ function WorkflowSession({
|
|
|
4584
4858
|
maxHydratedCompletedLoops: workflowScale?.maxHydratedCompletedLoops ?? DEFAULT_WORKFLOW_SCALE_OPTIONS.maxHydratedCompletedLoops
|
|
4585
4859
|
});
|
|
4586
4860
|
}
|
|
4861
|
+
const store = storeRef.current;
|
|
4862
|
+
useWorkflowAuthorityBinding(store, authoritySource, { onAuthorityTerminal });
|
|
4863
|
+
const streamEnded = useStore5(store, (s) => s.state.display.streamEnded);
|
|
4864
|
+
const authorityStatus = useStore5(store, (s) => s.state.authority?.status);
|
|
4865
|
+
useWorkflowAwaitingAuthorityTimers(store, streamEnded, authorityStatus);
|
|
4587
4866
|
const scaleOptions = useMemo9(
|
|
4588
4867
|
() => ({ ...DEFAULT_WORKFLOW_SCALE_OPTIONS, ...workflowScale }),
|
|
4589
4868
|
[workflowScale]
|
|
@@ -4621,38 +4900,20 @@ function WorkflowSession({
|
|
|
4621
4900
|
workspaceRoot
|
|
4622
4901
|
]
|
|
4623
4902
|
);
|
|
4624
|
-
const
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
);
|
|
4628
|
-
const
|
|
4629
|
-
storeRef.current,
|
|
4630
|
-
(s) => s.state.loopTreesBySessionId
|
|
4631
|
-
);
|
|
4632
|
-
const openChildIdentities = useStore4(
|
|
4633
|
-
storeRef.current,
|
|
4634
|
-
(s) => s.state.openChildIdentities
|
|
4635
|
-
);
|
|
4636
|
-
const sessionStatus = useStore4(
|
|
4637
|
-
storeRef.current,
|
|
4638
|
-
(s) => s.state.status
|
|
4639
|
-
);
|
|
4640
|
-
const internalErrors = useStore4(
|
|
4641
|
-
storeRef.current,
|
|
4642
|
-
(s) => s.state.internalErrors
|
|
4643
|
-
);
|
|
4644
|
-
const onErrorRef = useRef3(onError);
|
|
4645
|
-
const onStreamCompleteRef = useRef3(onStreamComplete);
|
|
4903
|
+
const sessionState = useStore5(store, (s) => s.state);
|
|
4904
|
+
const containerTree = sessionState.containerTree;
|
|
4905
|
+
const loopTreesBySessionId = sessionState.loopTreesBySessionId;
|
|
4906
|
+
const onErrorRef = useRef4(onError);
|
|
4907
|
+
const onDisplayStreamEndedRef = useRef4(onDisplayStreamEnded);
|
|
4646
4908
|
onErrorRef.current = onError;
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
const store = storeRef.current;
|
|
4909
|
+
onDisplayStreamEndedRef.current = onDisplayStreamEnded;
|
|
4910
|
+
useEffect10(() => {
|
|
4650
4911
|
const controller = new AbortController();
|
|
4651
4912
|
void source.start((event, ctx) => {
|
|
4652
4913
|
store.getState().applyEvent(event, ctx);
|
|
4653
4914
|
}, controller.signal).then(() => {
|
|
4654
|
-
store.getState().
|
|
4655
|
-
|
|
4915
|
+
store.getState().markStreamEnded();
|
|
4916
|
+
onDisplayStreamEndedRef.current?.();
|
|
4656
4917
|
}).catch((err) => {
|
|
4657
4918
|
if (err instanceof SseTransportTerminalError) {
|
|
4658
4919
|
store.getState().markAsError(err);
|
|
@@ -4664,39 +4925,16 @@ function WorkflowSession({
|
|
|
4664
4925
|
return () => {
|
|
4665
4926
|
controller.abort();
|
|
4666
4927
|
};
|
|
4667
|
-
}, [source]);
|
|
4668
|
-
return /* @__PURE__ */
|
|
4928
|
+
}, [source, store]);
|
|
4929
|
+
return /* @__PURE__ */ jsx19(WorkflowSessionStoreContext.Provider, { value: store, children: /* @__PURE__ */ jsx19(WorkflowScaleContext.Provider, { value: scaleOptions, children: /* @__PURE__ */ jsx19(WorkflowLoopReplayProvider, { onRequestLoopReplay, children: /* @__PURE__ */ jsx19(InteractionBusContext.Provider, { value: busRef, children: /* @__PURE__ */ jsx19(NodeRegistryContext.Provider, { value: registryRef, children: /* @__PURE__ */ jsx19(MarkdownRendererContext.Provider, { value: markdownRenderer, children: /* @__PURE__ */ jsx19(ToolDisplayOptionsContext.Provider, { value: toolDisplayRef, children: /* @__PURE__ */ jsx19(SessionViewOptionsContext.Provider, { value: sessionViewRef, children: /* @__PURE__ */ jsxs14(
|
|
4669
4930
|
"div",
|
|
4670
4931
|
{
|
|
4671
4932
|
className: "lax-agent-session lax-workflow-session",
|
|
4672
4933
|
"data-testid": "lax-workflow-session",
|
|
4673
4934
|
children: [
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
className: "lax-workflow-closeout-warning",
|
|
4678
|
-
"data-testid": "lax-workflow-closeout-warning",
|
|
4679
|
-
role: "status",
|
|
4680
|
-
children: [
|
|
4681
|
-
/* @__PURE__ */ jsx17("strong", { children: "Workflow forced closeout" }),
|
|
4682
|
-
/* @__PURE__ */ jsxs12("span", { children: [
|
|
4683
|
-
" ",
|
|
4684
|
-
"\u2014 stream ended with unresolved child scopes; not a healthy completion."
|
|
4685
|
-
] }),
|
|
4686
|
-
openChildIdentities.length > 0 ? /* @__PURE__ */ jsx17("ul", { className: "lax-workflow-closeout-warning__scopes", children: openChildIdentities.map((identity) => /* @__PURE__ */ jsx17("li", { children: identity }, identity)) }) : null
|
|
4687
|
-
]
|
|
4688
|
-
}
|
|
4689
|
-
) : null,
|
|
4690
|
-
sessionStatus === "error" || internalErrors.length > 0 ? /* @__PURE__ */ jsx17(
|
|
4691
|
-
"div",
|
|
4692
|
-
{
|
|
4693
|
-
className: "lax-workflow-error",
|
|
4694
|
-
"data-testid": "lax-workflow-error",
|
|
4695
|
-
role: "alert",
|
|
4696
|
-
children: internalErrors[internalErrors.length - 1]?.message ?? "Workflow stream error"
|
|
4697
|
-
}
|
|
4698
|
-
) : null,
|
|
4699
|
-
/* @__PURE__ */ jsx17(
|
|
4935
|
+
/* @__PURE__ */ jsx19(WorkflowSessionBanners, { state: sessionState }),
|
|
4936
|
+
/* @__PURE__ */ jsx19(WorkflowGlobalSpinner, {}),
|
|
4937
|
+
/* @__PURE__ */ jsx19(
|
|
4700
4938
|
WorkflowChrome,
|
|
4701
4939
|
{
|
|
4702
4940
|
containerTree,
|
|
@@ -4706,13 +4944,158 @@ function WorkflowSession({
|
|
|
4706
4944
|
groupParallelTools
|
|
4707
4945
|
}
|
|
4708
4946
|
),
|
|
4709
|
-
/* @__PURE__ */
|
|
4947
|
+
/* @__PURE__ */ jsx19(WorkflowTaskListFooter, {}),
|
|
4710
4948
|
children
|
|
4711
4949
|
]
|
|
4712
4950
|
}
|
|
4713
4951
|
) }) }) }) }) }) }) }) });
|
|
4714
4952
|
}
|
|
4715
4953
|
|
|
4954
|
+
// src/core/workflow/createMockAuthoritySource.ts
|
|
4955
|
+
function defaultDoneSnapshot() {
|
|
4956
|
+
return {
|
|
4957
|
+
taskId: "mock-task",
|
|
4958
|
+
status: "done",
|
|
4959
|
+
updatedAt: Date.now()
|
|
4960
|
+
};
|
|
4961
|
+
}
|
|
4962
|
+
function defaultRunningSnapshot() {
|
|
4963
|
+
return {
|
|
4964
|
+
taskId: "mock-task",
|
|
4965
|
+
status: "running",
|
|
4966
|
+
updatedAt: Date.now()
|
|
4967
|
+
};
|
|
4968
|
+
}
|
|
4969
|
+
function createMockAuthoritySource(snapshotOrOptions) {
|
|
4970
|
+
let snapshot = null;
|
|
4971
|
+
let pendingUpdates = [];
|
|
4972
|
+
if (snapshotOrOptions == null) {
|
|
4973
|
+
snapshot = defaultRunningSnapshot();
|
|
4974
|
+
} else if (typeof snapshotOrOptions === "object" && ("pendingUpdates" in snapshotOrOptions || "snapshot" in snapshotOrOptions) && !("status" in snapshotOrOptions)) {
|
|
4975
|
+
const opts = snapshotOrOptions;
|
|
4976
|
+
snapshot = opts.snapshot ?? defaultDoneSnapshot();
|
|
4977
|
+
pendingUpdates = opts.pendingUpdates ?? [];
|
|
4978
|
+
} else if (snapshotOrOptions === null) {
|
|
4979
|
+
snapshot = null;
|
|
4980
|
+
} else {
|
|
4981
|
+
snapshot = snapshotOrOptions;
|
|
4982
|
+
}
|
|
4983
|
+
return {
|
|
4984
|
+
getSnapshot: () => snapshot,
|
|
4985
|
+
subscribe(cb) {
|
|
4986
|
+
if (snapshot) {
|
|
4987
|
+
cb(snapshot);
|
|
4988
|
+
}
|
|
4989
|
+
for (const update of pendingUpdates) {
|
|
4990
|
+
snapshot = update;
|
|
4991
|
+
cb(update);
|
|
4992
|
+
}
|
|
4993
|
+
return () => {
|
|
4994
|
+
};
|
|
4995
|
+
}
|
|
4996
|
+
};
|
|
4997
|
+
}
|
|
4998
|
+
function createDoneAuthoritySource(taskId = "mock-task") {
|
|
4999
|
+
return createMockAuthoritySource({
|
|
5000
|
+
taskId,
|
|
5001
|
+
status: "done",
|
|
5002
|
+
updatedAt: Date.now()
|
|
5003
|
+
});
|
|
5004
|
+
}
|
|
5005
|
+
function createRunningAuthoritySource(taskId = "mock-task") {
|
|
5006
|
+
return createMockAuthoritySource({
|
|
5007
|
+
taskId,
|
|
5008
|
+
status: "running",
|
|
5009
|
+
updatedAt: Date.now()
|
|
5010
|
+
});
|
|
5011
|
+
}
|
|
5012
|
+
function createControllableAuthoritySource(initial) {
|
|
5013
|
+
let snapshot = initial;
|
|
5014
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
5015
|
+
return {
|
|
5016
|
+
getSnapshot: () => snapshot,
|
|
5017
|
+
subscribe(cb) {
|
|
5018
|
+
listeners.add(cb);
|
|
5019
|
+
cb(snapshot);
|
|
5020
|
+
return () => {
|
|
5021
|
+
listeners.delete(cb);
|
|
5022
|
+
};
|
|
5023
|
+
},
|
|
5024
|
+
push(next) {
|
|
5025
|
+
snapshot = next;
|
|
5026
|
+
for (const listener of listeners) {
|
|
5027
|
+
listener(next);
|
|
5028
|
+
}
|
|
5029
|
+
}
|
|
5030
|
+
};
|
|
5031
|
+
}
|
|
5032
|
+
|
|
5033
|
+
// src/core/workflow/createPollingAuthoritySource.ts
|
|
5034
|
+
var DEFAULT_INTERVAL_MS = 2e3;
|
|
5035
|
+
function createPollingAuthoritySource(taskId, fetchFn, options) {
|
|
5036
|
+
let cached = options?.initialSnapshot ?? null;
|
|
5037
|
+
let intervalId;
|
|
5038
|
+
let pollInFlight = false;
|
|
5039
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
5040
|
+
const notify = (snap) => {
|
|
5041
|
+
cached = snap;
|
|
5042
|
+
for (const listener of listeners) {
|
|
5043
|
+
listener(snap);
|
|
5044
|
+
}
|
|
5045
|
+
};
|
|
5046
|
+
const stopPoll = () => {
|
|
5047
|
+
if (intervalId != null) {
|
|
5048
|
+
clearInterval(intervalId);
|
|
5049
|
+
intervalId = void 0;
|
|
5050
|
+
}
|
|
5051
|
+
};
|
|
5052
|
+
const pollOnce = async () => {
|
|
5053
|
+
if (pollInFlight || options?.signal?.aborted) return;
|
|
5054
|
+
pollInFlight = true;
|
|
5055
|
+
try {
|
|
5056
|
+
const snap = await fetchFn(taskId);
|
|
5057
|
+
const prev = cached;
|
|
5058
|
+
if (prev == null || prev.status !== snap.status || prev.updatedAt !== snap.updatedAt) {
|
|
5059
|
+
notify(snap);
|
|
5060
|
+
}
|
|
5061
|
+
if (isAuthorityTerminalStatus(snap.status)) {
|
|
5062
|
+
stopPoll();
|
|
5063
|
+
}
|
|
5064
|
+
} finally {
|
|
5065
|
+
pollInFlight = false;
|
|
5066
|
+
}
|
|
5067
|
+
};
|
|
5068
|
+
const startPoll = () => {
|
|
5069
|
+
if (intervalId != null || options?.signal?.aborted) return;
|
|
5070
|
+
if (cached != null && isAuthorityTerminalStatus(cached.status)) {
|
|
5071
|
+
return;
|
|
5072
|
+
}
|
|
5073
|
+
void pollOnce();
|
|
5074
|
+
intervalId = setInterval(() => {
|
|
5075
|
+
void pollOnce();
|
|
5076
|
+
}, options?.intervalMs ?? DEFAULT_INTERVAL_MS);
|
|
5077
|
+
};
|
|
5078
|
+
options?.signal?.addEventListener("abort", stopPoll, { once: true });
|
|
5079
|
+
return {
|
|
5080
|
+
getSnapshot: () => cached,
|
|
5081
|
+
subscribe(cb) {
|
|
5082
|
+
listeners.add(cb);
|
|
5083
|
+
if (cached != null) {
|
|
5084
|
+
cb(cached);
|
|
5085
|
+
}
|
|
5086
|
+
if (cached == null || !isAuthorityTerminalStatus(cached.status)) {
|
|
5087
|
+
startPoll();
|
|
5088
|
+
}
|
|
5089
|
+
return () => {
|
|
5090
|
+
listeners.delete(cb);
|
|
5091
|
+
if (listeners.size === 0) {
|
|
5092
|
+
stopPoll();
|
|
5093
|
+
}
|
|
5094
|
+
};
|
|
5095
|
+
}
|
|
5096
|
+
};
|
|
5097
|
+
}
|
|
5098
|
+
|
|
4716
5099
|
// src/view/tools/groupParallelTools.ts
|
|
4717
5100
|
function buildTimelineEntries(rootIds, byId, options = {}) {
|
|
4718
5101
|
const filtered = options.hideStandalonePermissions ? rootIds.filter((id) => byId[id]?.kind !== "permission") : [...rootIds];
|
|
@@ -4750,18 +5133,18 @@ function buildTimelineEntries(rootIds, byId, options = {}) {
|
|
|
4750
5133
|
}
|
|
4751
5134
|
|
|
4752
5135
|
// src/view/nodes/SubAgentNode.tsx
|
|
4753
|
-
import { jsx as
|
|
5136
|
+
import { jsx as jsx20, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
4754
5137
|
function SubAgentBlock({ nodeId }) {
|
|
4755
5138
|
const node = useNodeTyped(nodeId, "subagent");
|
|
4756
5139
|
const childIds = useChildren(nodeId);
|
|
4757
5140
|
const registry = useNodeRegistry();
|
|
4758
5141
|
if (!node) return null;
|
|
4759
|
-
return /* @__PURE__ */
|
|
4760
|
-
/* @__PURE__ */
|
|
5142
|
+
return /* @__PURE__ */ jsxs15("div", { className: "lax-subagent-block", "data-status": node.status, children: [
|
|
5143
|
+
/* @__PURE__ */ jsxs15("div", { className: "lax-subagent-block__header", children: [
|
|
4761
5144
|
"SubAgent: ",
|
|
4762
5145
|
node.subagentId
|
|
4763
5146
|
] }),
|
|
4764
|
-
/* @__PURE__ */
|
|
5147
|
+
/* @__PURE__ */ jsx20("div", { className: "lax-subagent-block__children", children: childIds.map((childId) => /* @__PURE__ */ jsx20(TimelineItem, { nodeId: childId, registry }, childId)) })
|
|
4765
5148
|
] });
|
|
4766
5149
|
}
|
|
4767
5150
|
|
|
@@ -4880,6 +5263,7 @@ export {
|
|
|
4880
5263
|
WorkflowTaskListFooter,
|
|
4881
5264
|
Write,
|
|
4882
5265
|
WriteToolBody,
|
|
5266
|
+
applyAuthoritySnapshotToState,
|
|
4883
5267
|
buildBashBodyBlocks,
|
|
4884
5268
|
buildCanonicalFromTree,
|
|
4885
5269
|
buildProjectionContext,
|
|
@@ -4895,21 +5279,27 @@ export {
|
|
|
4895
5279
|
configurePlantumlServer,
|
|
4896
5280
|
createActiveSessionBridge,
|
|
4897
5281
|
createAgentxSseSource,
|
|
5282
|
+
createControllableAuthoritySource,
|
|
4898
5283
|
createDefaultRegistry,
|
|
4899
5284
|
createDefaultToolRegistry,
|
|
5285
|
+
createDoneAuthoritySource,
|
|
4900
5286
|
createEmptyTree,
|
|
4901
5287
|
createEmptyWorkflowSessionState,
|
|
4902
5288
|
createInteractionBus,
|
|
5289
|
+
createMockAuthoritySource,
|
|
4903
5290
|
createMockSource,
|
|
4904
5291
|
createMultiSessionStore,
|
|
5292
|
+
createPollingAuthoritySource,
|
|
4905
5293
|
createProjectionContext,
|
|
4906
5294
|
createReplaySource,
|
|
5295
|
+
createRunningAuthoritySource,
|
|
4907
5296
|
createSessionStore,
|
|
4908
5297
|
createWorkflowSessionStore,
|
|
4909
5298
|
deriveLoopSessionIdFromAgentEvent,
|
|
4910
5299
|
displayPath,
|
|
4911
5300
|
entryKey,
|
|
4912
5301
|
filterAgentLoopReplayEvents,
|
|
5302
|
+
finalizeWorkflowDisplayOnStreamEnded,
|
|
4913
5303
|
fixDiagramSvg,
|
|
4914
5304
|
formatAgentTitle,
|
|
4915
5305
|
formatDiffFromStrings,
|
|
@@ -4922,9 +5312,12 @@ export {
|
|
|
4922
5312
|
formatTimeoutFooter,
|
|
4923
5313
|
formatToolTitle,
|
|
4924
5314
|
getChildIds,
|
|
5315
|
+
getDisplayProjectionStatus,
|
|
4925
5316
|
getMainStageIds,
|
|
4926
5317
|
getNoopInteractionBus,
|
|
4927
5318
|
isAgentLoopReplayEventType,
|
|
5319
|
+
isAuthorityTerminal,
|
|
5320
|
+
isAuthorityTerminalStatus,
|
|
4928
5321
|
isGitOperationCommand,
|
|
4929
5322
|
isGroupableToolName,
|
|
4930
5323
|
isMcpToolName,
|
|
@@ -4948,8 +5341,10 @@ export {
|
|
|
4948
5341
|
resolveShellProgressFromPayload,
|
|
4949
5342
|
resolveSseTransportPolicy,
|
|
4950
5343
|
resolveToolBody,
|
|
5344
|
+
resolveWorkflowBanner,
|
|
4951
5345
|
shouldApplyGrouping,
|
|
4952
5346
|
shouldShowExploreDetail,
|
|
5347
|
+
shouldShowGlobalSpinner,
|
|
4953
5348
|
shouldShowTaskListFooter,
|
|
4954
5349
|
streamChunk,
|
|
4955
5350
|
truncateCommand,
|