langchain_agentx_stream_ui 0.2.7 → 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 +197 -57
- package/dist/index.js +546 -165
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -864,11 +864,67 @@ function AgentSession({
|
|
|
864
864
|
}
|
|
865
865
|
|
|
866
866
|
// src/view/workflow/WorkflowSession.tsx
|
|
867
|
-
import { useEffect as
|
|
867
|
+
import { useEffect as useEffect10, useMemo as useMemo9, useRef as useRef4 } from "react";
|
|
868
868
|
|
|
869
869
|
// src/core/context/WorkflowSessionContext.tsx
|
|
870
870
|
import { createContext, useContext } from "react";
|
|
871
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
|
|
872
928
|
var WorkflowSessionStoreContext = createContext(null);
|
|
873
929
|
function useWorkflowSessionStoreApi() {
|
|
874
930
|
const store = useContext(WorkflowSessionStoreContext);
|
|
@@ -879,7 +935,7 @@ function useWorkflowSessionStoreApi() {
|
|
|
879
935
|
}
|
|
880
936
|
function useWorkflowSessionStatus() {
|
|
881
937
|
const store = useWorkflowSessionStoreApi();
|
|
882
|
-
return useStore(store, (s) => s.state
|
|
938
|
+
return useStore(store, (s) => getDisplayProjectionStatus(s.state));
|
|
883
939
|
}
|
|
884
940
|
|
|
885
941
|
// src/core/context/WorkflowLoopReplayContext.tsx
|
|
@@ -944,6 +1000,17 @@ function containerTreeToSnapshot(tree) {
|
|
|
944
1000
|
}
|
|
945
1001
|
|
|
946
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
|
+
}
|
|
947
1014
|
function createEmptyWorkflowProgress() {
|
|
948
1015
|
return {
|
|
949
1016
|
workflowId: null,
|
|
@@ -955,9 +1022,8 @@ function createEmptyWorkflowProgress() {
|
|
|
955
1022
|
}
|
|
956
1023
|
function createEmptyWorkflowSessionState() {
|
|
957
1024
|
return {
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
openChildIdentities: [],
|
|
1025
|
+
authority: null,
|
|
1026
|
+
display: createEmptyWorkflowDisplayProjection(),
|
|
961
1027
|
meta: { workflowRunId: null, startedAt: null, lastEventId: null },
|
|
962
1028
|
containerTree: createEmptyWorkflowContainerTreeState(),
|
|
963
1029
|
workflowProgress: createEmptyWorkflowProgress(),
|
|
@@ -2150,7 +2216,10 @@ function readOpenChildIdentities(data) {
|
|
|
2150
2216
|
if (!Array.isArray(raw)) return [];
|
|
2151
2217
|
return raw.filter((item) => typeof item === "string");
|
|
2152
2218
|
}
|
|
2153
|
-
function
|
|
2219
|
+
function projectionStatusFromTerminalKind(kind, isWorkflowFailedEvent) {
|
|
2220
|
+
return sessionStatusFromTerminalKind(kind, isWorkflowFailedEvent);
|
|
2221
|
+
}
|
|
2222
|
+
function isDisplayProjectionTerminal(status) {
|
|
2154
2223
|
return status === "done" || status === "done_with_warning" || status === "error";
|
|
2155
2224
|
}
|
|
2156
2225
|
|
|
@@ -2523,18 +2592,16 @@ function applyStructureEvent(state, event) {
|
|
|
2523
2592
|
containerTree,
|
|
2524
2593
|
state.workflowProgress
|
|
2525
2594
|
);
|
|
2526
|
-
let
|
|
2527
|
-
if (
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
let terminalKind = state.terminalKind;
|
|
2531
|
-
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;
|
|
2532
2599
|
if (projectedEvent.event_type === "workflow-end" || projectedEvent.event_type === "workflow-failed") {
|
|
2533
2600
|
const resolved = resolveWorkflowTerminalKind(
|
|
2534
2601
|
structureData,
|
|
2535
2602
|
projectedEvent.event_type
|
|
2536
2603
|
);
|
|
2537
|
-
|
|
2604
|
+
projectionStatus = projectionStatusFromTerminalKind(
|
|
2538
2605
|
resolved,
|
|
2539
2606
|
projectedEvent.event_type === "workflow-failed"
|
|
2540
2607
|
);
|
|
@@ -2543,9 +2610,12 @@ function applyStructureEvent(state, event) {
|
|
|
2543
2610
|
}
|
|
2544
2611
|
const base = {
|
|
2545
2612
|
...nextState,
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2613
|
+
display: {
|
|
2614
|
+
...nextState.display,
|
|
2615
|
+
projectionStatus,
|
|
2616
|
+
terminalKind,
|
|
2617
|
+
openChildIdentities
|
|
2618
|
+
},
|
|
2549
2619
|
containerTree,
|
|
2550
2620
|
workflowProgress
|
|
2551
2621
|
};
|
|
@@ -2613,11 +2683,9 @@ function reduceWorkflowSession(state, event, eventIndex, options) {
|
|
|
2613
2683
|
next.containerTree,
|
|
2614
2684
|
next.workflowProgress
|
|
2615
2685
|
);
|
|
2616
|
-
let
|
|
2617
|
-
if (
|
|
2618
|
-
else if (
|
|
2619
|
-
else if (reducedLoop.status === "error") status = "error";
|
|
2620
|
-
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";
|
|
2621
2689
|
return {
|
|
2622
2690
|
...next,
|
|
2623
2691
|
workflowProgress,
|
|
@@ -2626,61 +2694,38 @@ function reduceWorkflowSession(state, event, eventIndex, options) {
|
|
|
2626
2694
|
[loopSessionId]: reducedLoop
|
|
2627
2695
|
},
|
|
2628
2696
|
activeLoopSessionId: loopSessionId,
|
|
2629
|
-
|
|
2697
|
+
display: {
|
|
2698
|
+
...next.display,
|
|
2699
|
+
projectionStatus
|
|
2700
|
+
}
|
|
2630
2701
|
};
|
|
2631
2702
|
}
|
|
2632
|
-
function
|
|
2633
|
-
if (
|
|
2634
|
-
return {
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
return { ...state, status: "done", activeLoopSessionId: null };
|
|
2640
|
-
}
|
|
2641
|
-
const root = tree.containersById[rootId];
|
|
2642
|
-
if (!root) {
|
|
2643
|
-
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
|
+
};
|
|
2644
2710
|
}
|
|
2645
|
-
const
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
const settledStageCount = topLevelStages.filter(
|
|
2653
|
-
(s) => s.status === "completed" || s.status === "failed" || s.status === "skipped"
|
|
2654
|
-
).length;
|
|
2655
|
-
const progressCurrent = Math.max(
|
|
2656
|
-
root.display?.progress_current ?? 0,
|
|
2657
|
-
settledStageCount
|
|
2658
|
-
);
|
|
2659
|
-
const rootParallelItems = Object.values(tree.containersById).filter(
|
|
2660
|
-
(node) => node.scope === "item" && node.workflow_path === root.workflow_path && node.workflow_depth === root.workflow_depth
|
|
2661
|
-
);
|
|
2662
|
-
const rootParallelSettled = topLevelStages.length === 0 && rootParallelItems.length > 0 && rootParallelItems.every(
|
|
2663
|
-
(item) => item.status === "completed" || item.status === "failed" || item.status === "skipped"
|
|
2664
|
-
) && !Object.values(tree.containersById).some(
|
|
2665
|
-
(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")
|
|
2666
|
-
);
|
|
2667
|
-
const workflowVisuallyComplete = root.status === "completed" || root.status === "failed" || progressTotal > 0 && progressCurrent >= progressTotal || rootParallelSettled;
|
|
2668
|
-
if (!workflowVisuallyComplete) {
|
|
2669
|
-
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);
|
|
2670
2718
|
}
|
|
2719
|
+
return withStreamEnded;
|
|
2720
|
+
}
|
|
2721
|
+
function forceSettleRunningContainersForDisplay(state) {
|
|
2722
|
+
const tree = state.containerTree;
|
|
2671
2723
|
let containersById = { ...tree.containersById };
|
|
2672
2724
|
for (const [id, node] of Object.entries(containersById)) {
|
|
2673
2725
|
if (node.status === "running" || node.status === "pending") {
|
|
2674
2726
|
containersById[id] = { ...node, status: "completed", is_open: false };
|
|
2675
2727
|
}
|
|
2676
2728
|
}
|
|
2677
|
-
const settledRoot = containersById[rootId] ?? root;
|
|
2678
|
-
if (settledRoot.status !== "completed" && settledRoot.status !== "failed") {
|
|
2679
|
-
containersById = {
|
|
2680
|
-
...containersById,
|
|
2681
|
-
[rootId]: { ...settledRoot, status: "completed", is_open: false }
|
|
2682
|
-
};
|
|
2683
|
-
}
|
|
2684
2729
|
const loopTreesBySessionId = { ...state.loopTreesBySessionId };
|
|
2685
2730
|
for (const [loopSessionId, loopTree] of Object.entries(loopTreesBySessionId)) {
|
|
2686
2731
|
if (loopTree.status === "running" || loopTree.status === "connecting") {
|
|
@@ -2694,15 +2739,9 @@ function finalizeWorkflowSessionOnStreamComplete(state) {
|
|
|
2694
2739
|
(id) => containersById[id]?.is_open
|
|
2695
2740
|
)
|
|
2696
2741
|
};
|
|
2697
|
-
const workflowProgress = syncWorkflowProgressFromContainerTree(
|
|
2698
|
-
containerTree,
|
|
2699
|
-
state.workflowProgress
|
|
2700
|
-
);
|
|
2701
2742
|
return {
|
|
2702
2743
|
...state,
|
|
2703
|
-
status: workflowProgress.status === "error" ? "error" : "done",
|
|
2704
2744
|
containerTree,
|
|
2705
|
-
workflowProgress,
|
|
2706
2745
|
loopTreesBySessionId,
|
|
2707
2746
|
activeLoopSessionId: null
|
|
2708
2747
|
};
|
|
@@ -2764,7 +2803,10 @@ function finalizeWorkflowSessionOnStreamError(state, message, eventIndex, stack)
|
|
|
2764
2803
|
}
|
|
2765
2804
|
return {
|
|
2766
2805
|
...state,
|
|
2767
|
-
|
|
2806
|
+
display: {
|
|
2807
|
+
...state.display,
|
|
2808
|
+
projectionStatus: "error"
|
|
2809
|
+
},
|
|
2768
2810
|
containerTree: {
|
|
2769
2811
|
...state.containerTree,
|
|
2770
2812
|
containersById,
|
|
@@ -2809,7 +2851,10 @@ function createWorkflowSessionStore(initialState = createEmptyWorkflowSessionSta
|
|
|
2809
2851
|
state: {
|
|
2810
2852
|
...evicted,
|
|
2811
2853
|
meta: nextMeta,
|
|
2812
|
-
|
|
2854
|
+
display: {
|
|
2855
|
+
...evicted.display,
|
|
2856
|
+
projectionStatus: evicted.display.projectionStatus === "connecting" ? "running" : evicted.display.projectionStatus
|
|
2857
|
+
}
|
|
2813
2858
|
},
|
|
2814
2859
|
eventCount: eventCount + 1
|
|
2815
2860
|
});
|
|
@@ -2839,13 +2884,59 @@ function createWorkflowSessionStore(initialState = createEmptyWorkflowSessionSta
|
|
|
2839
2884
|
)
|
|
2840
2885
|
});
|
|
2841
2886
|
},
|
|
2842
|
-
|
|
2887
|
+
markStreamEnded() {
|
|
2843
2888
|
set((current) => ({
|
|
2844
2889
|
state: finalizeState(
|
|
2845
|
-
|
|
2890
|
+
finalizeWorkflowDisplayOnStreamEnded(current.state)
|
|
2846
2891
|
)
|
|
2847
2892
|
}));
|
|
2848
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
|
+
},
|
|
2849
2940
|
/**
|
|
2850
2941
|
* P2:将 completed loop 加入 LRU 热缓存并淘汰溢出。
|
|
2851
2942
|
* running 保护由 collectProtectedLoopSessionIds 按状态自动覆盖,不再依赖永久 pin。
|
|
@@ -2952,11 +3043,93 @@ function createWorkflowSessionStore(initialState = createEmptyWorkflowSessionSta
|
|
|
2952
3043
|
}));
|
|
2953
3044
|
}
|
|
2954
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
|
+
|
|
2955
3128
|
// src/view/workflow/WorkflowChrome.tsx
|
|
2956
3129
|
import { useMemo as useMemo7, useState as useState6 } from "react";
|
|
2957
3130
|
|
|
2958
3131
|
// src/view/workflow/WorkflowAggregateContainer.tsx
|
|
2959
|
-
import { useCallback, useEffect as
|
|
3132
|
+
import { useCallback, useEffect as useEffect5, useMemo as useMemo2, useState, memo } from "react";
|
|
2960
3133
|
|
|
2961
3134
|
// src/view/workflow/WorkflowContainerLine.tsx
|
|
2962
3135
|
import { Fragment, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
@@ -3366,7 +3539,7 @@ function WorkflowAggregateContainerInner({
|
|
|
3366
3539
|
const uiStatus = mapAggregateUiStatus(node);
|
|
3367
3540
|
const statusLabel = buildAggregateStatusLabel(node, siblingItems);
|
|
3368
3541
|
const [expanded, setExpanded] = useState(isRunning);
|
|
3369
|
-
|
|
3542
|
+
useEffect5(() => {
|
|
3370
3543
|
if (isRunning) setExpanded(true);
|
|
3371
3544
|
if (isDone) setExpanded(false);
|
|
3372
3545
|
}, [isRunning, isDone]);
|
|
@@ -3485,7 +3658,7 @@ var WorkflowAggregateContainer = memo(
|
|
|
3485
3658
|
);
|
|
3486
3659
|
|
|
3487
3660
|
// src/view/workflow/WorkflowParallelGroup.tsx
|
|
3488
|
-
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";
|
|
3489
3662
|
|
|
3490
3663
|
// src/core/workflow/workflowParallelGroupMetrics.ts
|
|
3491
3664
|
var ACTIVE_ITEM_STATUSES = /* @__PURE__ */ new Set([
|
|
@@ -3579,7 +3752,7 @@ function formatParallelGroupSummary(metrics) {
|
|
|
3579
3752
|
}
|
|
3580
3753
|
|
|
3581
3754
|
// src/view/workflow/WorkflowStageContainer.tsx
|
|
3582
|
-
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";
|
|
3583
3756
|
import { useStore as useStore2 } from "zustand";
|
|
3584
3757
|
|
|
3585
3758
|
// src/core/workflow/loopTreeUtils.ts
|
|
@@ -3666,7 +3839,7 @@ function WorkflowStageContainerInner({
|
|
|
3666
3839
|
const doneSummary = doneSummaryFromTree ?? (isDone ? cachedSummary : null);
|
|
3667
3840
|
const shouldAutoExpandRunning = effectivelyRunning && !isSkipped;
|
|
3668
3841
|
const [expanded, setExpanded] = useState2(shouldAutoExpandRunning);
|
|
3669
|
-
|
|
3842
|
+
useEffect6(() => {
|
|
3670
3843
|
if (shouldAutoExpandRunning) {
|
|
3671
3844
|
setExpanded(true);
|
|
3672
3845
|
return;
|
|
@@ -3691,7 +3864,7 @@ function WorkflowStageContainerInner({
|
|
|
3691
3864
|
return next;
|
|
3692
3865
|
});
|
|
3693
3866
|
}, [isDone, node.loopSessionId, loopTree, requestLoopHydration]);
|
|
3694
|
-
|
|
3867
|
+
useEffect6(() => {
|
|
3695
3868
|
const onKeyDown = (event) => {
|
|
3696
3869
|
if (!(event.ctrlKey || event.metaKey) || event.key.toLowerCase() !== "o") return;
|
|
3697
3870
|
setExpanded(true);
|
|
@@ -3844,7 +4017,7 @@ function WorkflowParallelGroupInner({
|
|
|
3844
4017
|
);
|
|
3845
4018
|
const allSettled = metrics.total > 0 && metrics.running === 0 && metrics.completed + metrics.failed >= metrics.total;
|
|
3846
4019
|
const [expanded, setExpanded] = useState3(hasActiveWork);
|
|
3847
|
-
|
|
4020
|
+
useEffect7(() => {
|
|
3848
4021
|
if (hasActiveWork) {
|
|
3849
4022
|
setExpanded(true);
|
|
3850
4023
|
return;
|
|
@@ -3856,7 +4029,7 @@ function WorkflowParallelGroupInner({
|
|
|
3856
4029
|
const toggleExpanded = useCallback3(() => {
|
|
3857
4030
|
setExpanded((prev) => !prev);
|
|
3858
4031
|
}, []);
|
|
3859
|
-
|
|
4032
|
+
useEffect7(() => {
|
|
3860
4033
|
const onKeyDown = (event) => {
|
|
3861
4034
|
if (!(event.ctrlKey || event.metaKey) || event.key.toLowerCase() !== "o") return;
|
|
3862
4035
|
setExpanded(true);
|
|
@@ -3956,7 +4129,7 @@ function WorkflowRootContainer({
|
|
|
3956
4129
|
}
|
|
3957
4130
|
|
|
3958
4131
|
// src/view/workflow/WorkflowRouteContainer.tsx
|
|
3959
|
-
import { useCallback as useCallback4, useEffect as
|
|
4132
|
+
import { useCallback as useCallback4, useEffect as useEffect8, useMemo as useMemo5, useState as useState4 } from "react";
|
|
3960
4133
|
import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
3961
4134
|
function WorkflowRouteContainer({
|
|
3962
4135
|
node,
|
|
@@ -3981,7 +4154,7 @@ function WorkflowRouteContainer({
|
|
|
3981
4154
|
const expandHint = doneSummary ? formatTeaserExpandHint(doneSummary.extraLines, verbose) : null;
|
|
3982
4155
|
const doneLabel = doneSummary?.label ?? "done";
|
|
3983
4156
|
const [expanded, setExpanded] = useState4(isRunning);
|
|
3984
|
-
|
|
4157
|
+
useEffect8(() => {
|
|
3985
4158
|
if (isSkipped) return;
|
|
3986
4159
|
if (isRunning) setExpanded(true);
|
|
3987
4160
|
if (isDone) setExpanded(false);
|
|
@@ -4057,7 +4230,7 @@ function WorkflowRouteContainer({
|
|
|
4057
4230
|
}
|
|
4058
4231
|
|
|
4059
4232
|
// src/view/workflow/WorkflowSubworkflowContainer.tsx
|
|
4060
|
-
import { useCallback as useCallback5, useEffect as
|
|
4233
|
+
import { useCallback as useCallback5, useEffect as useEffect9, useMemo as useMemo6, useState as useState5 } from "react";
|
|
4061
4234
|
import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
4062
4235
|
function buildDoneSummary(node) {
|
|
4063
4236
|
const display = node.display;
|
|
@@ -4088,7 +4261,7 @@ function WorkflowSubworkflowContainer({
|
|
|
4088
4261
|
return node.content_blocks[node.content_blocks.length - 1]?.preview ?? "";
|
|
4089
4262
|
}, [node.content_blocks]);
|
|
4090
4263
|
const [expanded, setExpanded] = useState5(effectivelyRunning);
|
|
4091
|
-
|
|
4264
|
+
useEffect9(() => {
|
|
4092
4265
|
if (effectivelyRunning) setExpanded(true);
|
|
4093
4266
|
else if (effectivelyDone) setExpanded(false);
|
|
4094
4267
|
}, [effectivelyRunning, effectivelyDone]);
|
|
@@ -4440,9 +4613,94 @@ function WorkflowChrome({
|
|
|
4440
4613
|
)) });
|
|
4441
4614
|
}
|
|
4442
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
|
+
|
|
4443
4701
|
// src/view/workflow/WorkflowTaskListFooter.tsx
|
|
4444
4702
|
import { useMemo as useMemo8, useState as useState7 } from "react";
|
|
4445
|
-
import { useStore as
|
|
4703
|
+
import { useStore as useStore4 } from "zustand";
|
|
4446
4704
|
|
|
4447
4705
|
// src/view/workflow/workflowTaskListMerge.ts
|
|
4448
4706
|
var STATUS_ORDER = {
|
|
@@ -4483,7 +4741,7 @@ function deriveWorkflowTaskFooterState(state) {
|
|
|
4483
4741
|
tasks: mergeWorkflowLoopTasks(state.loopTreesBySessionId),
|
|
4484
4742
|
taskPhase: deriveWorkflowTaskPhase(trees),
|
|
4485
4743
|
hasEverShownTaskList: trees.some((tree) => tree.hasEverShownTaskList),
|
|
4486
|
-
sessionStatus: state
|
|
4744
|
+
sessionStatus: getDisplayProjectionStatus(state)
|
|
4487
4745
|
};
|
|
4488
4746
|
}
|
|
4489
4747
|
function shouldShowWorkflowTaskListFooter(state) {
|
|
@@ -4513,16 +4771,16 @@ function loopSessionDisplayLabel(loopSessionId) {
|
|
|
4513
4771
|
}
|
|
4514
4772
|
|
|
4515
4773
|
// src/view/workflow/WorkflowTaskListFooter.tsx
|
|
4516
|
-
import { jsx as
|
|
4774
|
+
import { jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
4517
4775
|
function MessageResponse({ children }) {
|
|
4518
|
-
return /* @__PURE__ */
|
|
4519
|
-
/* @__PURE__ */
|
|
4520
|
-
/* @__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 })
|
|
4521
4779
|
] });
|
|
4522
4780
|
}
|
|
4523
4781
|
function WorkflowTaskListFooter() {
|
|
4524
4782
|
const store = useWorkflowSessionStoreApi();
|
|
4525
|
-
const state =
|
|
4783
|
+
const state = useStore4(store, (s) => s.state);
|
|
4526
4784
|
const [expanded, setExpanded] = useState7(false);
|
|
4527
4785
|
const footerState = useMemo8(() => deriveWorkflowTaskFooterState(state), [state]);
|
|
4528
4786
|
const visible = shouldShowWorkflowTaskListFooter(state);
|
|
@@ -4531,8 +4789,8 @@ function WorkflowTaskListFooter() {
|
|
|
4531
4789
|
}
|
|
4532
4790
|
const label = formatWorkflowTaskFooterLabel(state);
|
|
4533
4791
|
const groups = groupWorkflowTasksByLoop(footerState.tasks);
|
|
4534
|
-
return /* @__PURE__ */
|
|
4535
|
-
/* @__PURE__ */
|
|
4792
|
+
return /* @__PURE__ */ jsxs13("div", { className: "lax-workflow-task-list-footer", "data-testid": "lax-workflow-task-list-footer", children: [
|
|
4793
|
+
/* @__PURE__ */ jsxs13(
|
|
4536
4794
|
"button",
|
|
4537
4795
|
{
|
|
4538
4796
|
type: "button",
|
|
@@ -4542,18 +4800,18 @@ function WorkflowTaskListFooter() {
|
|
|
4542
4800
|
onClick: () => setExpanded((v) => !v),
|
|
4543
4801
|
children: [
|
|
4544
4802
|
label,
|
|
4545
|
-
/* @__PURE__ */
|
|
4803
|
+
/* @__PURE__ */ jsx18("span", { className: "lax-task-list-footer__hint", children: expanded ? " \xB7 \u2191 to hide" : " \xB7 \u2193 to view" })
|
|
4546
4804
|
]
|
|
4547
4805
|
}
|
|
4548
4806
|
),
|
|
4549
|
-
expanded ? /* @__PURE__ */
|
|
4807
|
+
expanded ? /* @__PURE__ */ jsx18(MessageResponse, { children: /* @__PURE__ */ jsx18("div", { className: "lax-workflow-task-list-groups", children: groups.map((group) => /* @__PURE__ */ jsxs13(
|
|
4550
4808
|
"div",
|
|
4551
4809
|
{
|
|
4552
4810
|
className: "lax-workflow-task-list-group",
|
|
4553
4811
|
"data-testid": `lax-workflow-task-group-${group.loopSessionId}`,
|
|
4554
4812
|
children: [
|
|
4555
|
-
/* @__PURE__ */
|
|
4556
|
-
/* @__PURE__ */
|
|
4813
|
+
/* @__PURE__ */ jsx18("div", { className: "lax-workflow-task-list-group-title", children: loopSessionDisplayLabel(group.loopSessionId) }),
|
|
4814
|
+
/* @__PURE__ */ jsx18(TaskList, { tasks: group.tasks })
|
|
4557
4815
|
]
|
|
4558
4816
|
},
|
|
4559
4817
|
group.loopSessionId
|
|
@@ -4562,10 +4820,11 @@ function WorkflowTaskListFooter() {
|
|
|
4562
4820
|
}
|
|
4563
4821
|
|
|
4564
4822
|
// src/view/workflow/WorkflowSession.tsx
|
|
4565
|
-
import { useStore as
|
|
4566
|
-
import { jsx as
|
|
4823
|
+
import { useStore as useStore5 } from "zustand";
|
|
4824
|
+
import { jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
4567
4825
|
function WorkflowSession({
|
|
4568
4826
|
source,
|
|
4827
|
+
authoritySource,
|
|
4569
4828
|
initialEvents,
|
|
4570
4829
|
registry,
|
|
4571
4830
|
tierOverrides,
|
|
@@ -4585,10 +4844,11 @@ function WorkflowSession({
|
|
|
4585
4844
|
groupParallelTools = false,
|
|
4586
4845
|
workflowScale,
|
|
4587
4846
|
onRequestLoopReplay,
|
|
4588
|
-
|
|
4847
|
+
onAuthorityTerminal,
|
|
4848
|
+
onDisplayStreamEnded,
|
|
4589
4849
|
children
|
|
4590
4850
|
}) {
|
|
4591
|
-
const storeRef =
|
|
4851
|
+
const storeRef = useRef4(null);
|
|
4592
4852
|
if (storeRef.current === null) {
|
|
4593
4853
|
const initialState = initialEvents && initialEvents.length > 0 ? reduceWorkflowEvents(initialEvents, { tierOverrides }) : void 0;
|
|
4594
4854
|
storeRef.current = createWorkflowSessionStore(initialState, {
|
|
@@ -4598,6 +4858,11 @@ function WorkflowSession({
|
|
|
4598
4858
|
maxHydratedCompletedLoops: workflowScale?.maxHydratedCompletedLoops ?? DEFAULT_WORKFLOW_SCALE_OPTIONS.maxHydratedCompletedLoops
|
|
4599
4859
|
});
|
|
4600
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);
|
|
4601
4866
|
const scaleOptions = useMemo9(
|
|
4602
4867
|
() => ({ ...DEFAULT_WORKFLOW_SCALE_OPTIONS, ...workflowScale }),
|
|
4603
4868
|
[workflowScale]
|
|
@@ -4635,38 +4900,20 @@ function WorkflowSession({
|
|
|
4635
4900
|
workspaceRoot
|
|
4636
4901
|
]
|
|
4637
4902
|
);
|
|
4638
|
-
const
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
);
|
|
4642
|
-
const
|
|
4643
|
-
storeRef.current,
|
|
4644
|
-
(s) => s.state.loopTreesBySessionId
|
|
4645
|
-
);
|
|
4646
|
-
const openChildIdentities = useStore4(
|
|
4647
|
-
storeRef.current,
|
|
4648
|
-
(s) => s.state.openChildIdentities
|
|
4649
|
-
);
|
|
4650
|
-
const sessionStatus = useStore4(
|
|
4651
|
-
storeRef.current,
|
|
4652
|
-
(s) => s.state.status
|
|
4653
|
-
);
|
|
4654
|
-
const internalErrors = useStore4(
|
|
4655
|
-
storeRef.current,
|
|
4656
|
-
(s) => s.state.internalErrors
|
|
4657
|
-
);
|
|
4658
|
-
const onErrorRef = useRef3(onError);
|
|
4659
|
-
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);
|
|
4660
4908
|
onErrorRef.current = onError;
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
const store = storeRef.current;
|
|
4909
|
+
onDisplayStreamEndedRef.current = onDisplayStreamEnded;
|
|
4910
|
+
useEffect10(() => {
|
|
4664
4911
|
const controller = new AbortController();
|
|
4665
4912
|
void source.start((event, ctx) => {
|
|
4666
4913
|
store.getState().applyEvent(event, ctx);
|
|
4667
4914
|
}, controller.signal).then(() => {
|
|
4668
|
-
store.getState().
|
|
4669
|
-
|
|
4915
|
+
store.getState().markStreamEnded();
|
|
4916
|
+
onDisplayStreamEndedRef.current?.();
|
|
4670
4917
|
}).catch((err) => {
|
|
4671
4918
|
if (err instanceof SseTransportTerminalError) {
|
|
4672
4919
|
store.getState().markAsError(err);
|
|
@@ -4678,39 +4925,16 @@ function WorkflowSession({
|
|
|
4678
4925
|
return () => {
|
|
4679
4926
|
controller.abort();
|
|
4680
4927
|
};
|
|
4681
|
-
}, [source]);
|
|
4682
|
-
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(
|
|
4683
4930
|
"div",
|
|
4684
4931
|
{
|
|
4685
4932
|
className: "lax-agent-session lax-workflow-session",
|
|
4686
4933
|
"data-testid": "lax-workflow-session",
|
|
4687
4934
|
children: [
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
className: "lax-workflow-closeout-warning",
|
|
4692
|
-
"data-testid": "lax-workflow-closeout-warning",
|
|
4693
|
-
role: "status",
|
|
4694
|
-
children: [
|
|
4695
|
-
/* @__PURE__ */ jsx17("strong", { children: "Workflow forced closeout" }),
|
|
4696
|
-
/* @__PURE__ */ jsxs12("span", { children: [
|
|
4697
|
-
" ",
|
|
4698
|
-
"\u2014 stream ended with unresolved child scopes; not a healthy completion."
|
|
4699
|
-
] }),
|
|
4700
|
-
openChildIdentities.length > 0 ? /* @__PURE__ */ jsx17("ul", { className: "lax-workflow-closeout-warning__scopes", children: openChildIdentities.map((identity) => /* @__PURE__ */ jsx17("li", { children: identity }, identity)) }) : null
|
|
4701
|
-
]
|
|
4702
|
-
}
|
|
4703
|
-
) : null,
|
|
4704
|
-
sessionStatus === "error" || internalErrors.length > 0 ? /* @__PURE__ */ jsx17(
|
|
4705
|
-
"div",
|
|
4706
|
-
{
|
|
4707
|
-
className: "lax-workflow-error",
|
|
4708
|
-
"data-testid": "lax-workflow-error",
|
|
4709
|
-
role: "alert",
|
|
4710
|
-
children: internalErrors[internalErrors.length - 1]?.message ?? "Workflow stream error"
|
|
4711
|
-
}
|
|
4712
|
-
) : null,
|
|
4713
|
-
/* @__PURE__ */ jsx17(
|
|
4935
|
+
/* @__PURE__ */ jsx19(WorkflowSessionBanners, { state: sessionState }),
|
|
4936
|
+
/* @__PURE__ */ jsx19(WorkflowGlobalSpinner, {}),
|
|
4937
|
+
/* @__PURE__ */ jsx19(
|
|
4714
4938
|
WorkflowChrome,
|
|
4715
4939
|
{
|
|
4716
4940
|
containerTree,
|
|
@@ -4720,13 +4944,158 @@ function WorkflowSession({
|
|
|
4720
4944
|
groupParallelTools
|
|
4721
4945
|
}
|
|
4722
4946
|
),
|
|
4723
|
-
/* @__PURE__ */
|
|
4947
|
+
/* @__PURE__ */ jsx19(WorkflowTaskListFooter, {}),
|
|
4724
4948
|
children
|
|
4725
4949
|
]
|
|
4726
4950
|
}
|
|
4727
4951
|
) }) }) }) }) }) }) }) });
|
|
4728
4952
|
}
|
|
4729
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
|
+
|
|
4730
5099
|
// src/view/tools/groupParallelTools.ts
|
|
4731
5100
|
function buildTimelineEntries(rootIds, byId, options = {}) {
|
|
4732
5101
|
const filtered = options.hideStandalonePermissions ? rootIds.filter((id) => byId[id]?.kind !== "permission") : [...rootIds];
|
|
@@ -4764,18 +5133,18 @@ function buildTimelineEntries(rootIds, byId, options = {}) {
|
|
|
4764
5133
|
}
|
|
4765
5134
|
|
|
4766
5135
|
// src/view/nodes/SubAgentNode.tsx
|
|
4767
|
-
import { jsx as
|
|
5136
|
+
import { jsx as jsx20, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
4768
5137
|
function SubAgentBlock({ nodeId }) {
|
|
4769
5138
|
const node = useNodeTyped(nodeId, "subagent");
|
|
4770
5139
|
const childIds = useChildren(nodeId);
|
|
4771
5140
|
const registry = useNodeRegistry();
|
|
4772
5141
|
if (!node) return null;
|
|
4773
|
-
return /* @__PURE__ */
|
|
4774
|
-
/* @__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: [
|
|
4775
5144
|
"SubAgent: ",
|
|
4776
5145
|
node.subagentId
|
|
4777
5146
|
] }),
|
|
4778
|
-
/* @__PURE__ */
|
|
5147
|
+
/* @__PURE__ */ jsx20("div", { className: "lax-subagent-block__children", children: childIds.map((childId) => /* @__PURE__ */ jsx20(TimelineItem, { nodeId: childId, registry }, childId)) })
|
|
4779
5148
|
] });
|
|
4780
5149
|
}
|
|
4781
5150
|
|
|
@@ -4894,6 +5263,7 @@ export {
|
|
|
4894
5263
|
WorkflowTaskListFooter,
|
|
4895
5264
|
Write,
|
|
4896
5265
|
WriteToolBody,
|
|
5266
|
+
applyAuthoritySnapshotToState,
|
|
4897
5267
|
buildBashBodyBlocks,
|
|
4898
5268
|
buildCanonicalFromTree,
|
|
4899
5269
|
buildProjectionContext,
|
|
@@ -4909,21 +5279,27 @@ export {
|
|
|
4909
5279
|
configurePlantumlServer,
|
|
4910
5280
|
createActiveSessionBridge,
|
|
4911
5281
|
createAgentxSseSource,
|
|
5282
|
+
createControllableAuthoritySource,
|
|
4912
5283
|
createDefaultRegistry,
|
|
4913
5284
|
createDefaultToolRegistry,
|
|
5285
|
+
createDoneAuthoritySource,
|
|
4914
5286
|
createEmptyTree,
|
|
4915
5287
|
createEmptyWorkflowSessionState,
|
|
4916
5288
|
createInteractionBus,
|
|
5289
|
+
createMockAuthoritySource,
|
|
4917
5290
|
createMockSource,
|
|
4918
5291
|
createMultiSessionStore,
|
|
5292
|
+
createPollingAuthoritySource,
|
|
4919
5293
|
createProjectionContext,
|
|
4920
5294
|
createReplaySource,
|
|
5295
|
+
createRunningAuthoritySource,
|
|
4921
5296
|
createSessionStore,
|
|
4922
5297
|
createWorkflowSessionStore,
|
|
4923
5298
|
deriveLoopSessionIdFromAgentEvent,
|
|
4924
5299
|
displayPath,
|
|
4925
5300
|
entryKey,
|
|
4926
5301
|
filterAgentLoopReplayEvents,
|
|
5302
|
+
finalizeWorkflowDisplayOnStreamEnded,
|
|
4927
5303
|
fixDiagramSvg,
|
|
4928
5304
|
formatAgentTitle,
|
|
4929
5305
|
formatDiffFromStrings,
|
|
@@ -4936,9 +5312,12 @@ export {
|
|
|
4936
5312
|
formatTimeoutFooter,
|
|
4937
5313
|
formatToolTitle,
|
|
4938
5314
|
getChildIds,
|
|
5315
|
+
getDisplayProjectionStatus,
|
|
4939
5316
|
getMainStageIds,
|
|
4940
5317
|
getNoopInteractionBus,
|
|
4941
5318
|
isAgentLoopReplayEventType,
|
|
5319
|
+
isAuthorityTerminal,
|
|
5320
|
+
isAuthorityTerminalStatus,
|
|
4942
5321
|
isGitOperationCommand,
|
|
4943
5322
|
isGroupableToolName,
|
|
4944
5323
|
isMcpToolName,
|
|
@@ -4962,8 +5341,10 @@ export {
|
|
|
4962
5341
|
resolveShellProgressFromPayload,
|
|
4963
5342
|
resolveSseTransportPolicy,
|
|
4964
5343
|
resolveToolBody,
|
|
5344
|
+
resolveWorkflowBanner,
|
|
4965
5345
|
shouldApplyGrouping,
|
|
4966
5346
|
shouldShowExploreDetail,
|
|
5347
|
+
shouldShowGlobalSpinner,
|
|
4967
5348
|
shouldShowTaskListFooter,
|
|
4968
5349
|
streamChunk,
|
|
4969
5350
|
truncateCommand,
|