@perstack/react 0.0.53 → 0.0.54
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/src/index.d.ts +72 -80
- package/dist/src/index.js +647 -645
- package/dist/src/index.js.map +1 -1
- package/package.json +4 -4
package/dist/src/index.js
CHANGED
|
@@ -1,677 +1,679 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { BASE_SKILL_PREFIX, createBaseToolActivity, createGeneralToolActivity } from
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { BASE_SKILL_PREFIX, createBaseToolActivity, createGeneralToolActivity } from "@perstack/core";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
//#region src/utils/stream.ts
|
|
5
|
+
const isAbortError = (err) => err instanceof DOMException && err.name === "AbortError";
|
|
6
|
+
/**
|
|
7
|
+
* Consumes an SSE stream with abort-safe iteration and error normalization.
|
|
8
|
+
*
|
|
9
|
+
* Handles the connection lifecycle shared by all streaming hooks:
|
|
10
|
+
* 1. Calls `connect` to obtain an async iterable
|
|
11
|
+
* 2. Iterates events, stopping on signal abort
|
|
12
|
+
* 3. Filters out AbortError (normal cleanup), surfaces all others
|
|
13
|
+
*/
|
|
8
14
|
async function consumeStream(connect, jobId, signal, onEvent) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
const events = await connect(jobId, signal);
|
|
16
|
+
for await (const event of events) {
|
|
17
|
+
if (signal.aborted) break;
|
|
18
|
+
onEvent(event);
|
|
19
|
+
}
|
|
14
20
|
}
|
|
15
|
-
|
|
21
|
+
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/utils/event-to-activity.ts
|
|
24
|
+
const TOOL_RESULT_EVENT_TYPES = new Set(["resolveToolResults", "attemptCompletion"]);
|
|
25
|
+
/**
|
|
26
|
+
* Converts a tool call and result to an Activity.
|
|
27
|
+
* Delegates to core's createBaseToolActivity/createGeneralToolActivity to avoid duplication.
|
|
28
|
+
*/
|
|
16
29
|
function toolToActivity(toolCall, toolResult, reasoning, meta) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
};
|
|
30
|
+
const { skillName, toolName } = toolCall;
|
|
31
|
+
return {
|
|
32
|
+
...skillName.startsWith(BASE_SKILL_PREFIX) ? createBaseToolActivity(toolName, toolCall, toolResult, reasoning) : createGeneralToolActivity(skillName, toolName, toolCall, toolResult, reasoning),
|
|
33
|
+
id: meta.id,
|
|
34
|
+
expertKey: meta.expertKey,
|
|
35
|
+
runId: meta.runId,
|
|
36
|
+
previousActivityId: meta.previousActivityId,
|
|
37
|
+
delegatedBy: meta.delegatedBy
|
|
38
|
+
};
|
|
27
39
|
}
|
|
28
40
|
function createInitialActivityProcessState() {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
41
|
+
return {
|
|
42
|
+
tools: /* @__PURE__ */ new Map(),
|
|
43
|
+
runStates: /* @__PURE__ */ new Map()
|
|
44
|
+
};
|
|
33
45
|
}
|
|
34
46
|
function getOrCreateRunState(state, runId, expertKey) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
let runState = state.runStates.get(runId);
|
|
48
|
+
if (!runState) {
|
|
49
|
+
runState = {
|
|
50
|
+
expertKey,
|
|
51
|
+
queryLogged: false,
|
|
52
|
+
completionLogged: false,
|
|
53
|
+
isComplete: false,
|
|
54
|
+
pendingDelegateCount: 0
|
|
55
|
+
};
|
|
56
|
+
state.runStates.set(runId, runState);
|
|
57
|
+
}
|
|
58
|
+
return runState;
|
|
47
59
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
60
|
+
const isRunEvent = (event) => "type" in event && "expertKey" in event;
|
|
61
|
+
const isCompleteStreamingReasoningEvent = (event) => "type" in event && event.type === "completeStreamingReasoning";
|
|
62
|
+
const isToolCallsEvent = (event) => event.type === "callTools" && "toolCalls" in event;
|
|
63
|
+
const isStopRunByDelegateEvent = (event) => event.type === "stopRunByDelegate" && "checkpoint" in event;
|
|
64
|
+
const isStopRunByInteractiveToolEvent = (event) => event.type === "stopRunByInteractiveTool" && "checkpoint" in event;
|
|
65
|
+
const isToolResultsEvent = (event) => event.type === "resolveToolResults" && "toolResults" in event;
|
|
66
|
+
const isToolResultEvent = (event) => TOOL_RESULT_EVENT_TYPES.has(event.type) && "toolResult" in event;
|
|
67
|
+
/**
|
|
68
|
+
* Wraps multiple activities in a ParallelActivitiesGroup with shared reasoning.
|
|
69
|
+
* If only one activity, returns it directly.
|
|
70
|
+
*/
|
|
55
71
|
function wrapInGroupIfParallel(activities, reasoning, meta) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
activities: activitiesWithoutReasoning
|
|
70
|
-
};
|
|
71
|
-
return [group];
|
|
72
|
+
if (activities.length <= 1) return activities;
|
|
73
|
+
const activitiesWithoutReasoning = activities.map((a) => {
|
|
74
|
+
const { reasoning: _, ...rest } = a;
|
|
75
|
+
return rest;
|
|
76
|
+
});
|
|
77
|
+
return [{
|
|
78
|
+
type: "parallelGroup",
|
|
79
|
+
id: `parallel-${activities[0].id}`,
|
|
80
|
+
expertKey: meta.expertKey,
|
|
81
|
+
runId: meta.runId,
|
|
82
|
+
reasoning,
|
|
83
|
+
activities: activitiesWithoutReasoning
|
|
84
|
+
}];
|
|
72
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Processes a RunEvent and produces Activity or ParallelActivitiesGroup items.
|
|
88
|
+
* Only processes RunEvent (state machine transitions), not RuntimeEvent.
|
|
89
|
+
*
|
|
90
|
+
* @param state - Mutable processing state
|
|
91
|
+
* @param event - The event to process
|
|
92
|
+
* @param addActivity - Callback to add a new Activity or ParallelActivitiesGroup
|
|
93
|
+
*/
|
|
73
94
|
function processRunEventToActivity(state, event, addActivity) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
for (const item of wrapped) {
|
|
304
|
-
addActivity(item);
|
|
305
|
-
}
|
|
306
|
-
runState.completedReasoning = void 0;
|
|
307
|
-
}
|
|
308
|
-
} else if (isToolResultEvent(event)) {
|
|
309
|
-
const { toolResult } = event;
|
|
310
|
-
const tool = state.tools.get(toolResult.id);
|
|
311
|
-
if (tool && !tool.logged) {
|
|
312
|
-
const activityId = `action-${tool.id}`;
|
|
313
|
-
const activity = toolToActivity(tool.toolCall, toolResult, runState.completedReasoning, {
|
|
314
|
-
id: activityId,
|
|
315
|
-
expertKey: event.expertKey,
|
|
316
|
-
runId: event.runId,
|
|
317
|
-
previousActivityId: runState.lastActivityId,
|
|
318
|
-
delegatedBy: runState.delegatedBy
|
|
319
|
-
});
|
|
320
|
-
addActivity(activity);
|
|
321
|
-
runState.lastActivityId = activityId;
|
|
322
|
-
tool.logged = true;
|
|
323
|
-
runState.completedReasoning = void 0;
|
|
324
|
-
}
|
|
325
|
-
}
|
|
95
|
+
if (isCompleteStreamingReasoningEvent(event)) {
|
|
96
|
+
const { runId, text, expertKey } = event;
|
|
97
|
+
const runState = state.runStates.get(runId);
|
|
98
|
+
if (runState) runState.completedReasoning = text;
|
|
99
|
+
else state.runStates.set(runId, {
|
|
100
|
+
expertKey,
|
|
101
|
+
queryLogged: false,
|
|
102
|
+
completionLogged: false,
|
|
103
|
+
isComplete: false,
|
|
104
|
+
pendingDelegateCount: 0,
|
|
105
|
+
completedReasoning: text
|
|
106
|
+
});
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (!isRunEvent(event)) return;
|
|
110
|
+
const runState = getOrCreateRunState(state, event.runId, event.expertKey);
|
|
111
|
+
if (event.type === "startRun") {
|
|
112
|
+
const startRunEvent = event;
|
|
113
|
+
const queryText = startRunEvent.inputMessages.find((m) => m.type === "userMessage")?.contents?.find((c) => c.type === "textPart")?.text;
|
|
114
|
+
if (!runState.delegatedBy) {
|
|
115
|
+
const delegatedByInfo = startRunEvent.initialCheckpoint?.delegatedBy;
|
|
116
|
+
if (delegatedByInfo) runState.delegatedBy = {
|
|
117
|
+
expertKey: delegatedByInfo.expert.key,
|
|
118
|
+
runId: delegatedByInfo.runId
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const isDelegationReturn = startRunEvent.initialCheckpoint?.status === "stoppedByDelegate" || startRunEvent.initialCheckpoint?.status === "stoppedByInteractiveTool";
|
|
122
|
+
if (queryText && !runState.queryLogged && !isDelegationReturn) {
|
|
123
|
+
const activityId = `query-${event.runId}`;
|
|
124
|
+
addActivity({
|
|
125
|
+
type: "query",
|
|
126
|
+
id: activityId,
|
|
127
|
+
expertKey: event.expertKey,
|
|
128
|
+
runId: event.runId,
|
|
129
|
+
previousActivityId: runState.lastActivityId,
|
|
130
|
+
delegatedBy: runState.delegatedBy,
|
|
131
|
+
text: queryText
|
|
132
|
+
});
|
|
133
|
+
runState.lastActivityId = activityId;
|
|
134
|
+
runState.queryLogged = true;
|
|
135
|
+
}
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (event.type === "resumeFromStop") {
|
|
139
|
+
if (!runState.delegatedBy) {
|
|
140
|
+
const delegatedByInfo = event.checkpoint?.delegatedBy;
|
|
141
|
+
if (delegatedByInfo) runState.delegatedBy = {
|
|
142
|
+
expertKey: delegatedByInfo.expert.key,
|
|
143
|
+
runId: delegatedByInfo.runId
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (event.type === "retry") {
|
|
149
|
+
const retryEvent = event;
|
|
150
|
+
const activityId = `retry-${event.id}`;
|
|
151
|
+
addActivity({
|
|
152
|
+
type: "retry",
|
|
153
|
+
id: activityId,
|
|
154
|
+
expertKey: event.expertKey,
|
|
155
|
+
runId: event.runId,
|
|
156
|
+
previousActivityId: runState.lastActivityId,
|
|
157
|
+
delegatedBy: runState.delegatedBy,
|
|
158
|
+
reasoning: runState.completedReasoning,
|
|
159
|
+
error: retryEvent.reason,
|
|
160
|
+
message: ""
|
|
161
|
+
});
|
|
162
|
+
runState.lastActivityId = activityId;
|
|
163
|
+
runState.completedReasoning = void 0;
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (event.type === "completeRun") {
|
|
167
|
+
if (!runState.completionLogged) {
|
|
168
|
+
const text = event.text ?? "";
|
|
169
|
+
const activityId = `completion-${event.runId}`;
|
|
170
|
+
addActivity({
|
|
171
|
+
type: "complete",
|
|
172
|
+
id: activityId,
|
|
173
|
+
expertKey: event.expertKey,
|
|
174
|
+
runId: event.runId,
|
|
175
|
+
previousActivityId: runState.lastActivityId,
|
|
176
|
+
delegatedBy: runState.delegatedBy,
|
|
177
|
+
reasoning: runState.completedReasoning,
|
|
178
|
+
text
|
|
179
|
+
});
|
|
180
|
+
runState.lastActivityId = activityId;
|
|
181
|
+
runState.completionLogged = true;
|
|
182
|
+
runState.isComplete = true;
|
|
183
|
+
runState.completedReasoning = void 0;
|
|
184
|
+
}
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if (event.type === "stopRunByError") {
|
|
188
|
+
const errorEvent = event;
|
|
189
|
+
const activityId = `error-${event.id}`;
|
|
190
|
+
addActivity({
|
|
191
|
+
type: "error",
|
|
192
|
+
id: activityId,
|
|
193
|
+
expertKey: event.expertKey,
|
|
194
|
+
runId: event.runId,
|
|
195
|
+
previousActivityId: runState.lastActivityId,
|
|
196
|
+
delegatedBy: runState.delegatedBy,
|
|
197
|
+
errorName: errorEvent.error.name,
|
|
198
|
+
error: errorEvent.error.message,
|
|
199
|
+
isRetryable: errorEvent.error.isRetryable
|
|
200
|
+
});
|
|
201
|
+
runState.lastActivityId = activityId;
|
|
202
|
+
runState.isComplete = true;
|
|
203
|
+
runState.completedReasoning = void 0;
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
if (isToolCallsEvent(event)) {
|
|
207
|
+
for (const toolCall of event.toolCalls) if (!state.tools.has(toolCall.id)) state.tools.set(toolCall.id, {
|
|
208
|
+
id: toolCall.id,
|
|
209
|
+
toolCall,
|
|
210
|
+
logged: false
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
if (isStopRunByDelegateEvent(event)) {
|
|
214
|
+
const reasoning = runState.completedReasoning;
|
|
215
|
+
const delegations = event.checkpoint.delegateTo ?? [];
|
|
216
|
+
runState.pendingDelegateCount += delegations.length;
|
|
217
|
+
const delegateActivities = [];
|
|
218
|
+
for (const delegation of delegations) {
|
|
219
|
+
const existingTool = state.tools.get(delegation.toolCallId);
|
|
220
|
+
if (!existingTool || !existingTool.logged) {
|
|
221
|
+
if (existingTool) existingTool.logged = true;
|
|
222
|
+
const activityId = `delegate-${delegation.toolCallId}`;
|
|
223
|
+
delegateActivities.push({
|
|
224
|
+
type: "delegate",
|
|
225
|
+
id: activityId,
|
|
226
|
+
expertKey: event.expertKey,
|
|
227
|
+
runId: event.runId,
|
|
228
|
+
previousActivityId: runState.lastActivityId,
|
|
229
|
+
delegatedBy: runState.delegatedBy,
|
|
230
|
+
delegateExpertKey: delegation.expert.key,
|
|
231
|
+
query: delegation.query,
|
|
232
|
+
reasoning
|
|
233
|
+
});
|
|
234
|
+
runState.lastActivityId = activityId;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
const wrapped = wrapInGroupIfParallel(delegateActivities, reasoning, {
|
|
238
|
+
expertKey: event.expertKey,
|
|
239
|
+
runId: event.runId,
|
|
240
|
+
delegatedBy: runState.delegatedBy
|
|
241
|
+
});
|
|
242
|
+
for (const item of wrapped) addActivity(item);
|
|
243
|
+
runState.completedReasoning = void 0;
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
if (isStopRunByInteractiveToolEvent(event)) {
|
|
247
|
+
const reasoning = runState.completedReasoning;
|
|
248
|
+
const pendingToolCalls = event.checkpoint.pendingToolCalls ?? [];
|
|
249
|
+
const interactiveActivities = [];
|
|
250
|
+
for (const toolCall of pendingToolCalls) {
|
|
251
|
+
const existingTool = state.tools.get(toolCall.id);
|
|
252
|
+
if (!existingTool || !existingTool.logged) {
|
|
253
|
+
if (existingTool) existingTool.logged = true;
|
|
254
|
+
const activityId = `interactive-${toolCall.id}`;
|
|
255
|
+
interactiveActivities.push({
|
|
256
|
+
type: "interactiveTool",
|
|
257
|
+
id: activityId,
|
|
258
|
+
expertKey: event.expertKey,
|
|
259
|
+
runId: event.runId,
|
|
260
|
+
previousActivityId: runState.lastActivityId,
|
|
261
|
+
delegatedBy: runState.delegatedBy,
|
|
262
|
+
skillName: toolCall.skillName,
|
|
263
|
+
toolName: toolCall.toolName,
|
|
264
|
+
args: toolCall.args,
|
|
265
|
+
reasoning
|
|
266
|
+
});
|
|
267
|
+
runState.lastActivityId = activityId;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
const wrapped = wrapInGroupIfParallel(interactiveActivities, reasoning, {
|
|
271
|
+
expertKey: event.expertKey,
|
|
272
|
+
runId: event.runId,
|
|
273
|
+
delegatedBy: runState.delegatedBy
|
|
274
|
+
});
|
|
275
|
+
for (const item of wrapped) addActivity(item);
|
|
276
|
+
runState.completedReasoning = void 0;
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
if (isToolResultsEvent(event)) {
|
|
280
|
+
const reasoning = runState.completedReasoning;
|
|
281
|
+
const toolActivities = [];
|
|
282
|
+
for (const toolResult of event.toolResults) {
|
|
283
|
+
const tool = state.tools.get(toolResult.id);
|
|
284
|
+
if (tool && !tool.logged) {
|
|
285
|
+
const activityId = `action-${tool.id}`;
|
|
286
|
+
const activity = toolToActivity(tool.toolCall, toolResult, reasoning, {
|
|
287
|
+
id: activityId,
|
|
288
|
+
expertKey: event.expertKey,
|
|
289
|
+
runId: event.runId,
|
|
290
|
+
previousActivityId: runState.lastActivityId,
|
|
291
|
+
delegatedBy: runState.delegatedBy
|
|
292
|
+
});
|
|
293
|
+
toolActivities.push(activity);
|
|
294
|
+
runState.lastActivityId = activityId;
|
|
295
|
+
tool.logged = true;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
if (toolActivities.length > 0) {
|
|
299
|
+
const wrapped = wrapInGroupIfParallel(toolActivities, reasoning, {
|
|
300
|
+
expertKey: event.expertKey,
|
|
301
|
+
runId: event.runId,
|
|
302
|
+
delegatedBy: runState.delegatedBy
|
|
303
|
+
});
|
|
304
|
+
for (const item of wrapped) addActivity(item);
|
|
305
|
+
runState.completedReasoning = void 0;
|
|
306
|
+
}
|
|
307
|
+
} else if (isToolResultEvent(event)) {
|
|
308
|
+
const { toolResult } = event;
|
|
309
|
+
const tool = state.tools.get(toolResult.id);
|
|
310
|
+
if (tool && !tool.logged) {
|
|
311
|
+
const activityId = `action-${tool.id}`;
|
|
312
|
+
addActivity(toolToActivity(tool.toolCall, toolResult, runState.completedReasoning, {
|
|
313
|
+
id: activityId,
|
|
314
|
+
expertKey: event.expertKey,
|
|
315
|
+
runId: event.runId,
|
|
316
|
+
previousActivityId: runState.lastActivityId,
|
|
317
|
+
delegatedBy: runState.delegatedBy
|
|
318
|
+
}));
|
|
319
|
+
runState.lastActivityId = activityId;
|
|
320
|
+
tool.logged = true;
|
|
321
|
+
runState.completedReasoning = void 0;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
326
324
|
}
|
|
327
325
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
326
|
+
//#endregion
|
|
327
|
+
//#region src/hooks/use-run.ts
|
|
328
|
+
const STREAMING_EVENT_TYPES = new Set([
|
|
329
|
+
"startStreamingReasoning",
|
|
330
|
+
"streamReasoning",
|
|
331
|
+
"completeStreamingReasoning",
|
|
332
|
+
"startStreamingRunResult",
|
|
333
|
+
"streamRunResult",
|
|
334
|
+
"completeStreamingRunResult"
|
|
336
335
|
]);
|
|
337
|
-
|
|
336
|
+
const isStreamingEvent = (event) => "type" in event && "expertKey" in event && STREAMING_EVENT_TYPES.has(event.type);
|
|
338
337
|
function processStreamingEvent(event, prevState) {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
isRunResultActive: false
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
},
|
|
436
|
-
handled: true
|
|
437
|
-
};
|
|
438
|
-
}
|
|
439
|
-
default:
|
|
440
|
-
return { newState: prevState, handled: false };
|
|
441
|
-
}
|
|
338
|
+
const { runId, expertKey } = event;
|
|
339
|
+
switch (event.type) {
|
|
340
|
+
case "startStreamingReasoning": return {
|
|
341
|
+
newState: {
|
|
342
|
+
...prevState,
|
|
343
|
+
runs: {
|
|
344
|
+
...prevState.runs,
|
|
345
|
+
[runId]: {
|
|
346
|
+
expertKey,
|
|
347
|
+
reasoning: "",
|
|
348
|
+
isReasoningActive: true
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
handled: true
|
|
353
|
+
};
|
|
354
|
+
case "streamReasoning": return {
|
|
355
|
+
newState: {
|
|
356
|
+
...prevState,
|
|
357
|
+
runs: {
|
|
358
|
+
...prevState.runs,
|
|
359
|
+
[runId]: {
|
|
360
|
+
...prevState.runs[runId],
|
|
361
|
+
expertKey,
|
|
362
|
+
reasoning: (prevState.runs[runId]?.reasoning ?? "") + event.delta
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
handled: true
|
|
367
|
+
};
|
|
368
|
+
case "completeStreamingReasoning": return {
|
|
369
|
+
newState: {
|
|
370
|
+
...prevState,
|
|
371
|
+
runs: {
|
|
372
|
+
...prevState.runs,
|
|
373
|
+
[runId]: {
|
|
374
|
+
...prevState.runs[runId],
|
|
375
|
+
expertKey,
|
|
376
|
+
isReasoningActive: false
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
handled: false
|
|
381
|
+
};
|
|
382
|
+
case "startStreamingRunResult": return {
|
|
383
|
+
newState: {
|
|
384
|
+
...prevState,
|
|
385
|
+
runs: {
|
|
386
|
+
...prevState.runs,
|
|
387
|
+
[runId]: {
|
|
388
|
+
expertKey,
|
|
389
|
+
reasoning: void 0,
|
|
390
|
+
isReasoningActive: false,
|
|
391
|
+
runResult: "",
|
|
392
|
+
isRunResultActive: true
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
},
|
|
396
|
+
handled: true
|
|
397
|
+
};
|
|
398
|
+
case "streamRunResult": return {
|
|
399
|
+
newState: {
|
|
400
|
+
...prevState,
|
|
401
|
+
runs: {
|
|
402
|
+
...prevState.runs,
|
|
403
|
+
[runId]: {
|
|
404
|
+
...prevState.runs[runId],
|
|
405
|
+
expertKey,
|
|
406
|
+
runResult: (prevState.runs[runId]?.runResult ?? "") + event.delta
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
},
|
|
410
|
+
handled: true
|
|
411
|
+
};
|
|
412
|
+
case "completeStreamingRunResult": return {
|
|
413
|
+
newState: {
|
|
414
|
+
...prevState,
|
|
415
|
+
runs: {
|
|
416
|
+
...prevState.runs,
|
|
417
|
+
[runId]: {
|
|
418
|
+
...prevState.runs[runId],
|
|
419
|
+
expertKey,
|
|
420
|
+
isRunResultActive: false
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
},
|
|
424
|
+
handled: true
|
|
425
|
+
};
|
|
426
|
+
default: return {
|
|
427
|
+
newState: prevState,
|
|
428
|
+
handled: false
|
|
429
|
+
};
|
|
430
|
+
}
|
|
442
431
|
}
|
|
432
|
+
/**
|
|
433
|
+
* Hook for managing Run state from RunEvent stream.
|
|
434
|
+
*
|
|
435
|
+
* Architecture:
|
|
436
|
+
* - ExpertStateEvent → ActivityOrGroup[] (accumulated, append-only)
|
|
437
|
+
* - StreamingEvent → StreamingState (latest only, for display)
|
|
438
|
+
*
|
|
439
|
+
* IMPORTANT: activities are append-only and never cleared.
|
|
440
|
+
* This is required for compatibility with Ink's <Static> component.
|
|
441
|
+
*/
|
|
443
442
|
function useRun() {
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
}
|
|
505
|
-
setEventCount((prev) => prev + historicalEvents.length);
|
|
506
|
-
const rootRunComplete = Array.from(stateRef.current.runStates.values()).some(
|
|
507
|
-
(rs) => rs.isComplete && !rs.delegatedBy
|
|
508
|
-
);
|
|
509
|
-
setIsComplete(rootRunComplete);
|
|
510
|
-
}, []);
|
|
511
|
-
return {
|
|
512
|
-
activities,
|
|
513
|
-
streaming,
|
|
514
|
-
isComplete,
|
|
515
|
-
eventCount,
|
|
516
|
-
addEvent,
|
|
517
|
-
appendHistoricalEvents,
|
|
518
|
-
clearStreaming
|
|
519
|
-
};
|
|
443
|
+
const [activities, setActivities] = useState([]);
|
|
444
|
+
const [streaming, setStreaming] = useState({ runs: {} });
|
|
445
|
+
const [eventCount, setEventCount] = useState(0);
|
|
446
|
+
const [isComplete, setIsComplete] = useState(false);
|
|
447
|
+
const stateRef = useRef(createInitialActivityProcessState());
|
|
448
|
+
const clearStreaming = useCallback(() => {
|
|
449
|
+
setStreaming({ runs: {} });
|
|
450
|
+
}, []);
|
|
451
|
+
const handleStreamingEvent = useCallback((event) => {
|
|
452
|
+
let handled = false;
|
|
453
|
+
setStreaming((prev) => {
|
|
454
|
+
const result = processStreamingEvent(event, prev);
|
|
455
|
+
handled = result.handled;
|
|
456
|
+
return result.newState;
|
|
457
|
+
});
|
|
458
|
+
return handled;
|
|
459
|
+
}, []);
|
|
460
|
+
const processEvent = useCallback((event) => {
|
|
461
|
+
const newActivities = [];
|
|
462
|
+
const addActivity = (activity) => newActivities.push(activity);
|
|
463
|
+
processRunEventToActivity(stateRef.current, event, addActivity);
|
|
464
|
+
if (newActivities.length > 0) setActivities((prev) => [...prev, ...newActivities]);
|
|
465
|
+
setIsComplete(Array.from(stateRef.current.runStates.values()).some((rs) => rs.isComplete && !rs.delegatedBy));
|
|
466
|
+
}, []);
|
|
467
|
+
const clearRunStreaming = useCallback((runId) => {
|
|
468
|
+
setStreaming((prev) => {
|
|
469
|
+
const { [runId]: _, ...rest } = prev.runs;
|
|
470
|
+
return { runs: rest };
|
|
471
|
+
});
|
|
472
|
+
}, []);
|
|
473
|
+
return {
|
|
474
|
+
activities,
|
|
475
|
+
streaming,
|
|
476
|
+
isComplete,
|
|
477
|
+
eventCount,
|
|
478
|
+
addEvent: useCallback((event) => {
|
|
479
|
+
if (isStreamingEvent(event)) {
|
|
480
|
+
if (handleStreamingEvent(event)) {
|
|
481
|
+
setEventCount((prev) => prev + 1);
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
if ("type" in event && "runId" in event && (event.type === "completeRun" || event.type === "stopRunByError")) clearRunStreaming(event.runId);
|
|
486
|
+
processEvent(event);
|
|
487
|
+
setEventCount((prev) => prev + 1);
|
|
488
|
+
}, [
|
|
489
|
+
handleStreamingEvent,
|
|
490
|
+
clearRunStreaming,
|
|
491
|
+
processEvent
|
|
492
|
+
]),
|
|
493
|
+
appendHistoricalEvents: useCallback((historicalEvents) => {
|
|
494
|
+
const newActivities = [];
|
|
495
|
+
const addActivity = (activity) => newActivities.push(activity);
|
|
496
|
+
for (const event of historicalEvents) processRunEventToActivity(stateRef.current, event, addActivity);
|
|
497
|
+
if (newActivities.length > 0) setActivities((prev) => [...prev, ...newActivities]);
|
|
498
|
+
setEventCount((prev) => prev + historicalEvents.length);
|
|
499
|
+
setIsComplete(Array.from(stateRef.current.runStates.values()).some((rs) => rs.isComplete && !rs.delegatedBy));
|
|
500
|
+
}, []),
|
|
501
|
+
clearStreaming
|
|
502
|
+
};
|
|
520
503
|
}
|
|
521
504
|
|
|
522
|
-
|
|
505
|
+
//#endregion
|
|
506
|
+
//#region src/hooks/use-job-stream.ts
|
|
523
507
|
function useJobStream(options) {
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
508
|
+
const { jobId, connect, enabled = true } = options;
|
|
509
|
+
const shouldConnect = Boolean(jobId && enabled);
|
|
510
|
+
const { activities, streaming, addEvent } = useRun();
|
|
511
|
+
const [isConnected, setIsConnected] = useState(false);
|
|
512
|
+
const [error, setError] = useState(null);
|
|
513
|
+
const addEventRef = useRef(addEvent);
|
|
514
|
+
addEventRef.current = addEvent;
|
|
515
|
+
const connectRef = useRef(connect);
|
|
516
|
+
connectRef.current = connect;
|
|
517
|
+
useEffect(() => {
|
|
518
|
+
if (!shouldConnect || !jobId) {
|
|
519
|
+
setIsConnected(false);
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
const controller = new AbortController();
|
|
523
|
+
async function run() {
|
|
524
|
+
setError(null);
|
|
525
|
+
setIsConnected(true);
|
|
526
|
+
try {
|
|
527
|
+
await consumeStream(connectRef.current, jobId, controller.signal, (event) => {
|
|
528
|
+
addEventRef.current(event);
|
|
529
|
+
});
|
|
530
|
+
} catch (err) {
|
|
531
|
+
if (isAbortError(err)) return;
|
|
532
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
533
|
+
} finally {
|
|
534
|
+
setIsConnected(false);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
run();
|
|
538
|
+
return () => {
|
|
539
|
+
controller.abort();
|
|
540
|
+
};
|
|
541
|
+
}, [shouldConnect, jobId]);
|
|
542
|
+
return {
|
|
543
|
+
activities,
|
|
544
|
+
streaming,
|
|
545
|
+
latestActivity: useMemo(() => activities.length > 0 ? activities[activities.length - 1] : null, [activities]),
|
|
546
|
+
isConnected,
|
|
547
|
+
error
|
|
548
|
+
};
|
|
563
549
|
}
|
|
550
|
+
|
|
551
|
+
//#endregion
|
|
552
|
+
//#region src/hooks/use-job-streams.ts
|
|
564
553
|
function useJobStreams(options) {
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
554
|
+
const { jobs, connect } = options;
|
|
555
|
+
const [states, setStates] = useState(/* @__PURE__ */ new Map());
|
|
556
|
+
const controllersRef = useRef(/* @__PURE__ */ new Map());
|
|
557
|
+
const activityStateRef = useRef(/* @__PURE__ */ new Map());
|
|
558
|
+
const connectRef = useRef(connect);
|
|
559
|
+
connectRef.current = connect;
|
|
560
|
+
const connectToJob = useCallback(async (jobId, signal) => {
|
|
561
|
+
setStates((prev) => {
|
|
562
|
+
const next = new Map(prev);
|
|
563
|
+
next.set(jobId, {
|
|
564
|
+
latestActivity: null,
|
|
565
|
+
isConnected: true
|
|
566
|
+
});
|
|
567
|
+
return next;
|
|
568
|
+
});
|
|
569
|
+
let state = activityStateRef.current.get(jobId);
|
|
570
|
+
if (!state) {
|
|
571
|
+
state = createInitialActivityProcessState();
|
|
572
|
+
activityStateRef.current.set(jobId, state);
|
|
573
|
+
}
|
|
574
|
+
try {
|
|
575
|
+
await consumeStream(connectRef.current, jobId, signal, (event) => {
|
|
576
|
+
let latestActivity = null;
|
|
577
|
+
processRunEventToActivity(state, event, (activity) => {
|
|
578
|
+
latestActivity = activity;
|
|
579
|
+
});
|
|
580
|
+
if (latestActivity) setStates((prev) => {
|
|
581
|
+
const next = new Map(prev);
|
|
582
|
+
next.set(jobId, {
|
|
583
|
+
latestActivity,
|
|
584
|
+
isConnected: true
|
|
585
|
+
});
|
|
586
|
+
return next;
|
|
587
|
+
});
|
|
588
|
+
});
|
|
589
|
+
} catch (err) {
|
|
590
|
+
if (isAbortError(err)) return;
|
|
591
|
+
console.error(`Stream connection failed for job ${jobId}:`, err);
|
|
592
|
+
} finally {
|
|
593
|
+
setStates((prev) => {
|
|
594
|
+
const current = prev.get(jobId);
|
|
595
|
+
if (!current) return prev;
|
|
596
|
+
const next = new Map(prev);
|
|
597
|
+
next.set(jobId, {
|
|
598
|
+
...current,
|
|
599
|
+
isConnected: false
|
|
600
|
+
});
|
|
601
|
+
return next;
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
}, []);
|
|
605
|
+
useEffect(() => {
|
|
606
|
+
const enabledIds = new Set(jobs.filter((j) => j.enabled).map((j) => j.id));
|
|
607
|
+
for (const [jobId, controller] of controllersRef.current) if (!enabledIds.has(jobId)) {
|
|
608
|
+
controller.abort();
|
|
609
|
+
controllersRef.current.delete(jobId);
|
|
610
|
+
activityStateRef.current.delete(jobId);
|
|
611
|
+
setStates((prev) => {
|
|
612
|
+
const next = new Map(prev);
|
|
613
|
+
next.delete(jobId);
|
|
614
|
+
return next;
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
for (const jobId of enabledIds) if (!controllersRef.current.has(jobId)) {
|
|
618
|
+
const controller = new AbortController();
|
|
619
|
+
controllersRef.current.set(jobId, controller);
|
|
620
|
+
connectToJob(jobId, controller.signal);
|
|
621
|
+
}
|
|
622
|
+
}, [jobs, connectToJob]);
|
|
623
|
+
useEffect(() => {
|
|
624
|
+
return () => {
|
|
625
|
+
for (const controller of controllersRef.current.values()) controller.abort();
|
|
626
|
+
controllersRef.current.clear();
|
|
627
|
+
activityStateRef.current.clear();
|
|
628
|
+
};
|
|
629
|
+
}, []);
|
|
630
|
+
return states;
|
|
641
631
|
}
|
|
642
632
|
|
|
643
|
-
|
|
633
|
+
//#endregion
|
|
634
|
+
//#region src/utils/group-by-run.ts
|
|
635
|
+
/**
|
|
636
|
+
* Extract base properties from ActivityOrGroup for grouping.
|
|
637
|
+
*/
|
|
644
638
|
function getActivityProps(activityOrGroup) {
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
639
|
+
if (activityOrGroup.type === "parallelGroup") {
|
|
640
|
+
const firstActivity = activityOrGroup.activities[0];
|
|
641
|
+
return {
|
|
642
|
+
runId: activityOrGroup.runId,
|
|
643
|
+
expertKey: activityOrGroup.expertKey,
|
|
644
|
+
delegatedBy: firstActivity?.delegatedBy
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
return activityOrGroup;
|
|
654
648
|
}
|
|
649
|
+
/**
|
|
650
|
+
* Groups activities by their runId while preserving order.
|
|
651
|
+
*
|
|
652
|
+
* This function groups activities so that each run can be displayed in its own
|
|
653
|
+
* visual section (e.g., separate <Static> components in Ink).
|
|
654
|
+
*
|
|
655
|
+
* @param activities - Array of activities or groups to group
|
|
656
|
+
* @returns Array of RunGroup objects, ordered by first appearance
|
|
657
|
+
*/
|
|
655
658
|
function groupActivitiesByRun(activities) {
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
return order.map((runId) => groupMap.get(runId));
|
|
659
|
+
const groupMap = /* @__PURE__ */ new Map();
|
|
660
|
+
const order = [];
|
|
661
|
+
for (const activityOrGroup of activities) {
|
|
662
|
+
const { runId, expertKey, delegatedBy } = getActivityProps(activityOrGroup);
|
|
663
|
+
if (!groupMap.has(runId)) {
|
|
664
|
+
groupMap.set(runId, {
|
|
665
|
+
runId,
|
|
666
|
+
expertKey,
|
|
667
|
+
activities: [],
|
|
668
|
+
delegatedBy
|
|
669
|
+
});
|
|
670
|
+
order.push(runId);
|
|
671
|
+
}
|
|
672
|
+
groupMap.get(runId).activities.push(activityOrGroup);
|
|
673
|
+
}
|
|
674
|
+
return order.map((runId) => groupMap.get(runId));
|
|
673
675
|
}
|
|
674
676
|
|
|
677
|
+
//#endregion
|
|
675
678
|
export { createInitialActivityProcessState, groupActivitiesByRun, processRunEventToActivity, toolToActivity, useJobStream, useJobStreams, useRun };
|
|
676
|
-
//# sourceMappingURL=index.js.map
|
|
677
679
|
//# sourceMappingURL=index.js.map
|