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