@opengeni/react 0.23.0 → 0.25.5
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/{chunk-UQS7OQDE.js → chunk-F4CFWKVI.js} +334 -64
- package/dist/chunk-F4CFWKVI.js.map +1 -0
- package/dist/chunk-GQN2QIR2.js +3767 -0
- package/dist/chunk-GQN2QIR2.js.map +1 -0
- package/dist/{chunk-I3BJZIG5.js → chunk-HIWPQYWI.js} +2 -120
- package/dist/chunk-HIWPQYWI.js.map +1 -0
- package/dist/chunk-M7X4JZOD.js +121 -0
- package/dist/chunk-M7X4JZOD.js.map +1 -0
- package/dist/{chunk-UAHPKLB6.js → chunk-MP6237JB.js} +571 -975
- package/dist/chunk-MP6237JB.js.map +1 -0
- package/dist/chunk-OZDLELJQ.js +937 -0
- package/dist/chunk-OZDLELJQ.js.map +1 -0
- package/dist/{chunk-CATATY77.js → chunk-TMH6HZWF.js} +8 -7
- package/dist/chunk-TMH6HZWF.js.map +1 -0
- package/dist/{chunk-6XUS5VFM.js → chunk-YU5PGUK7.js} +6 -4
- package/dist/{chunk-6XUS5VFM.js.map → chunk-YU5PGUK7.js.map} +1 -1
- package/dist/{composer-CVm5uoUI.d.ts → composer-DoC4veX1.d.ts} +6 -63
- package/dist/composer.d.ts +3 -2
- package/dist/composer.js +4 -3
- package/dist/fleet-decision-projection-SLNZMS42.js +241 -0
- package/dist/fleet-decision-projection-SLNZMS42.js.map +1 -0
- package/dist/fleet-decision-row-GGCAT4E4.js +351 -0
- package/dist/fleet-decision-row-GGCAT4E4.js.map +1 -0
- package/dist/index.d.ts +17 -259
- package/dist/index.js +1042 -5058
- package/dist/index.js.map +1 -1
- package/dist/machines.js +3 -2
- package/dist/session-Du_FsrZ1.d.ts +264 -0
- package/dist/session-ui-BqQDH7YV.d.ts +183 -0
- package/dist/session-ui.d.ts +7 -0
- package/dist/session-ui.js +17 -0
- package/dist/session-ui.js.map +1 -0
- package/dist/session.d.ts +4 -2
- package/dist/session.js +26 -9
- package/dist/{use-composer-By1wn3MM.d.ts → use-composer-BnEzheSk.d.ts} +8 -2
- package/dist/use-file-attachments-C0kpHrs9.d.ts +76 -0
- package/dist/use-turn-queue-3rkJwjFv.d.ts +290 -0
- package/package.json +6 -2
- package/src/components/composer.tsx +7 -6
- package/src/components/message-timeline.tsx +94 -54
- package/src/hooks/use-composer.ts +470 -73
- package/src/hooks/use-file-attachments.ts +96 -3
- package/src/hooks/use-goal.ts +23 -5
- package/src/hooks/use-session-events.ts +5 -0
- package/src/hooks/use-session-lineage.ts +1 -1
- package/src/hooks/use-session.ts +9 -6
- package/src/hooks/use-workspace-sessions.ts +30 -5
- package/src/session-ui.ts +13 -0
- package/src/session.ts +15 -0
- package/src/timeline/activity-rail.tsx +18 -2
- package/src/timeline/fleet-decision-projection.ts +366 -0
- package/src/timeline/fleet-decision-row.tsx +412 -0
- package/src/timeline/index.ts +2 -0
- package/src/timeline/projection.ts +19 -8
- package/src/timeline/tool-display-name.ts +16 -0
- package/src/timeline/tool-renderers.tsx +1 -1
- package/src/timeline/types.ts +68 -1
- package/dist/chunk-CATATY77.js.map +0 -1
- package/dist/chunk-I3BJZIG5.js.map +0 -1
- package/dist/chunk-UAHPKLB6.js.map +0 -1
- package/dist/chunk-UQS7OQDE.js.map +0 -1
- package/dist/session-Db00TRwl.d.ts +0 -446
|
@@ -1,930 +1,102 @@
|
|
|
1
1
|
import {
|
|
2
|
-
CREDIT_EXHAUSTION_MESSAGE,
|
|
3
|
-
humanizeFailureReason,
|
|
4
|
-
isCreditExhaustion,
|
|
5
|
-
tryParseJson,
|
|
6
2
|
useDebouncedCallback,
|
|
7
3
|
useEmbeddedHumanInputSession,
|
|
8
4
|
useEmbeddedSession,
|
|
9
5
|
useEmbeddedSessionMcpApprovalPolicy,
|
|
10
6
|
useMutationRunner,
|
|
7
|
+
useOpenGeni,
|
|
11
8
|
usePolledValue,
|
|
12
9
|
useSessionEventTrigger
|
|
13
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-HIWPQYWI.js";
|
|
11
|
+
import {
|
|
12
|
+
buildTimeline,
|
|
13
|
+
groupTimeline,
|
|
14
|
+
sessionStatusFromEvents
|
|
15
|
+
} from "./chunk-OZDLELJQ.js";
|
|
14
16
|
|
|
15
|
-
// src/
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
};
|
|
29
|
-
const finalizeOpen = (turnId, disposition = "complete") => {
|
|
30
|
-
for (const item of items) {
|
|
31
|
-
if (turnId !== void 0 && "turnId" in item && item.turnId && turnId && item.turnId !== turnId) {
|
|
32
|
-
continue;
|
|
33
|
-
}
|
|
34
|
-
if ((item.kind === "agent-message" || item.kind === "reasoning") && item.streaming) {
|
|
35
|
-
item.streaming = false;
|
|
36
|
-
}
|
|
37
|
-
if ((item.kind === "tool-call" || item.kind === "worker") && item.status === "running") {
|
|
38
|
-
item.status = disposition;
|
|
39
|
-
}
|
|
40
|
-
if (item.kind === "sandbox" && item.status === "running") {
|
|
41
|
-
item.status = disposition;
|
|
42
|
-
}
|
|
17
|
+
// src/hooks/use-session.ts
|
|
18
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
19
|
+
function isTitleEvent(event) {
|
|
20
|
+
return event.type === "session.title_set";
|
|
21
|
+
}
|
|
22
|
+
function useSession(sessionId, options = {}) {
|
|
23
|
+
const { client, workspaceId, workspaceControlEvent, registerSessionReconciler } = useOpenGeni(options);
|
|
24
|
+
const enabled = (options.enabled ?? true) && Boolean(sessionId);
|
|
25
|
+
const [override, setOverride] = useState(null);
|
|
26
|
+
const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
|
|
27
|
+
const load = useCallback(async () => {
|
|
28
|
+
if (!sessionId) {
|
|
29
|
+
return null;
|
|
43
30
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
id: event.id,
|
|
71
|
-
text: typeof payload.text === "string" ? payload.text : "",
|
|
72
|
-
resources: resourceRefs(payload.resources),
|
|
73
|
-
tools: toolRefs(payload.tools),
|
|
74
|
-
occurredAt: event.occurredAt
|
|
75
|
-
});
|
|
76
|
-
break;
|
|
77
|
-
}
|
|
78
|
-
case "agent.message.delta": {
|
|
79
|
-
const text = typeof payload.text === "string" ? payload.text : "";
|
|
80
|
-
if (!text) {
|
|
81
|
-
break;
|
|
82
|
-
}
|
|
83
|
-
const open = last();
|
|
84
|
-
if (open?.kind === "agent-message" && open.streaming && open.turnId === turnId) {
|
|
85
|
-
open.text += text;
|
|
86
|
-
break;
|
|
87
|
-
}
|
|
88
|
-
closeStreamingTail();
|
|
89
|
-
items.push({
|
|
90
|
-
kind: "agent-message",
|
|
91
|
-
id: event.id,
|
|
92
|
-
turnId,
|
|
93
|
-
text,
|
|
94
|
-
streaming: true,
|
|
95
|
-
occurredAt: event.occurredAt
|
|
96
|
-
});
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
99
|
-
case "agent.message.completed": {
|
|
100
|
-
const text = typeof payload.text === "string" ? payload.text : "";
|
|
101
|
-
const open = [...items].reverse().find(
|
|
102
|
-
(item) => item.kind === "agent-message" && item.turnId === turnId
|
|
103
|
-
);
|
|
104
|
-
if (open && (open.streaming || !open.text || text === open.text || text.startsWith(open.text))) {
|
|
105
|
-
if (!open.text || text && text.startsWith(open.text)) {
|
|
106
|
-
open.text = text || open.text;
|
|
107
|
-
}
|
|
108
|
-
open.streaming = false;
|
|
109
|
-
break;
|
|
110
|
-
}
|
|
111
|
-
if (text) {
|
|
112
|
-
items.push({
|
|
113
|
-
kind: "agent-message",
|
|
114
|
-
id: event.id,
|
|
115
|
-
turnId,
|
|
116
|
-
text,
|
|
117
|
-
streaming: false,
|
|
118
|
-
occurredAt: event.occurredAt
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
break;
|
|
122
|
-
}
|
|
123
|
-
case "agent.reasoning.delta": {
|
|
124
|
-
const text = reasoningText(event.payload);
|
|
125
|
-
if (!text) {
|
|
126
|
-
break;
|
|
127
|
-
}
|
|
128
|
-
const open = last();
|
|
129
|
-
if (open?.kind === "reasoning" && open.streaming && open.turnId === turnId) {
|
|
130
|
-
open.text += text;
|
|
131
|
-
break;
|
|
132
|
-
}
|
|
133
|
-
closeStreamingTail();
|
|
134
|
-
items.push({
|
|
135
|
-
kind: "reasoning",
|
|
136
|
-
id: event.id,
|
|
137
|
-
turnId,
|
|
138
|
-
text,
|
|
139
|
-
streaming: true,
|
|
140
|
-
occurredAt: event.occurredAt
|
|
141
|
-
});
|
|
142
|
-
break;
|
|
143
|
-
}
|
|
144
|
-
case "agent.toolCall.created": {
|
|
145
|
-
const name = typeof payload.name === "string" ? payload.name : "tool";
|
|
146
|
-
const callId = typeof payload.id === "string" ? payload.id : null;
|
|
147
|
-
const args = payload.arguments ?? null;
|
|
148
|
-
closeStreamingTail();
|
|
149
|
-
if (name === WORKER_SPAWN_TOOL || name === WORKER_MESSAGE_TOOL) {
|
|
150
|
-
items.push({
|
|
151
|
-
kind: "worker",
|
|
152
|
-
id: event.id,
|
|
153
|
-
turnId,
|
|
154
|
-
callId,
|
|
155
|
-
action: name === WORKER_SPAWN_TOOL ? "spawn" : "message",
|
|
156
|
-
prompt: workerPrompt(args),
|
|
157
|
-
workerSessionId: extractSessionRef(args),
|
|
158
|
-
status: "running",
|
|
159
|
-
occurredAt: event.occurredAt
|
|
160
|
-
});
|
|
161
|
-
break;
|
|
162
|
-
}
|
|
163
|
-
items.push({
|
|
164
|
-
kind: "tool-call",
|
|
165
|
-
id: event.id,
|
|
166
|
-
turnId,
|
|
167
|
-
callId,
|
|
168
|
-
name,
|
|
169
|
-
arguments: args,
|
|
170
|
-
output: void 0,
|
|
171
|
-
// The provider-native item drives the per-tool renderers (apply_patch
|
|
172
|
-
// operation, computer_call action, web_search providerData, …).
|
|
173
|
-
raw: payload.raw,
|
|
174
|
-
status: "running",
|
|
175
|
-
occurredAt: event.occurredAt
|
|
176
|
-
});
|
|
177
|
-
break;
|
|
178
|
-
}
|
|
179
|
-
case "agent.toolCall.output": {
|
|
180
|
-
const callId = typeof payload.id === "string" ? payload.id : null;
|
|
181
|
-
const target = findOpenCall(items, callId);
|
|
182
|
-
if (!target) {
|
|
183
|
-
break;
|
|
184
|
-
}
|
|
185
|
-
if (target.kind === "worker") {
|
|
186
|
-
target.status = isErrorOutput(payload) ? "failed" : "complete";
|
|
187
|
-
target.workerSessionId = target.workerSessionId ?? extractSessionRef(payload.output);
|
|
188
|
-
break;
|
|
189
|
-
}
|
|
190
|
-
target.status = isErrorOutput(payload) ? "failed" : "complete";
|
|
191
|
-
target.output = payload.output;
|
|
192
|
-
break;
|
|
193
|
-
}
|
|
194
|
-
case "sandbox.operation.started":
|
|
195
|
-
case "sandbox.operation.completed":
|
|
196
|
-
case "sandbox.operation.failed": {
|
|
197
|
-
const name = typeof payload.name === "string" ? payload.name : "sandbox";
|
|
198
|
-
const status = event.type.endsWith(".failed") ? "failed" : event.type.endsWith(".completed") ? "complete" : "running";
|
|
199
|
-
if ((name === "repository-clone" || name === "file-resource-download") && status !== "failed") {
|
|
200
|
-
break;
|
|
201
|
-
}
|
|
202
|
-
const existing = findOpenSandbox(items, name);
|
|
203
|
-
if (existing && status !== "running") {
|
|
204
|
-
existing.status = status;
|
|
205
|
-
if (payload.origin === "created" || payload.origin === "restored" || payload.origin === "resumed") {
|
|
206
|
-
existing.origin = payload.origin;
|
|
207
|
-
}
|
|
208
|
-
const message = failureMessage(payload);
|
|
209
|
-
if (message) {
|
|
210
|
-
existing.output = existing.output ? `${existing.output}
|
|
211
|
-
${message}` : message;
|
|
212
|
-
}
|
|
213
|
-
break;
|
|
214
|
-
}
|
|
215
|
-
if (!existing) {
|
|
216
|
-
closeStreamingTail();
|
|
217
|
-
items.push({
|
|
218
|
-
kind: "sandbox",
|
|
219
|
-
id: event.id,
|
|
220
|
-
turnId,
|
|
221
|
-
name,
|
|
222
|
-
command: typeof payload.command === "string" ? payload.command : null,
|
|
223
|
-
output: failureMessage(payload) ?? "",
|
|
224
|
-
origin: payload.origin === "created" || payload.origin === "restored" || payload.origin === "resumed" ? payload.origin : null,
|
|
225
|
-
status,
|
|
226
|
-
occurredAt: event.occurredAt
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
break;
|
|
230
|
-
}
|
|
231
|
-
case "sandbox.command.output.delta": {
|
|
232
|
-
const text = typeof payload.chunk === "string" ? payload.chunk : typeof payload.text === "string" ? payload.text : typeof payload.output === "string" ? payload.output : "";
|
|
233
|
-
if (!text) {
|
|
234
|
-
break;
|
|
235
|
-
}
|
|
236
|
-
const open = (typeof payload.name === "string" ? findOpenSandbox(items, payload.name) : void 0) ?? [...items].reverse().find(
|
|
237
|
-
(item) => item.kind === "sandbox" && item.status === "running"
|
|
238
|
-
);
|
|
239
|
-
if (open) {
|
|
240
|
-
open.output += text;
|
|
241
|
-
}
|
|
242
|
-
break;
|
|
243
|
-
}
|
|
244
|
-
case "session.status.changed": {
|
|
245
|
-
const status = payload.status;
|
|
246
|
-
if (!isSessionStatus(status)) {
|
|
247
|
-
break;
|
|
248
|
-
}
|
|
249
|
-
if (!ATTENTION_STATUSES.has(status)) {
|
|
250
|
-
break;
|
|
251
|
-
}
|
|
252
|
-
const previous = [...items].reverse().find((item) => item.kind === "session-status");
|
|
253
|
-
if (previous?.status === status) {
|
|
254
|
-
break;
|
|
255
|
-
}
|
|
256
|
-
items.push({ kind: "session-status", id: event.id, status, occurredAt: event.occurredAt });
|
|
257
|
-
break;
|
|
258
|
-
}
|
|
259
|
-
case "session.requiresAction": {
|
|
260
|
-
finalizeOpen(turnId);
|
|
261
|
-
items.push({
|
|
262
|
-
kind: "notice",
|
|
263
|
-
id: event.id,
|
|
264
|
-
tone: "waiting",
|
|
265
|
-
text: "Approval needed \u2014 the turn is paused until someone decides.",
|
|
266
|
-
occurredAt: event.occurredAt
|
|
267
|
-
});
|
|
268
|
-
break;
|
|
269
|
-
}
|
|
270
|
-
case "session.context.compacted": {
|
|
271
|
-
closeStreamingTail();
|
|
272
|
-
const before = typeof payload.estimatedTokensBefore === "number" ? Math.round(payload.estimatedTokensBefore).toLocaleString("en-US") : null;
|
|
273
|
-
const after = typeof payload.estimatedTokensAfter === "number" ? Math.round(payload.estimatedTokensAfter).toLocaleString("en-US") : null;
|
|
274
|
-
items.push({
|
|
275
|
-
kind: "notice",
|
|
276
|
-
id: event.id,
|
|
277
|
-
tone: "waiting",
|
|
278
|
-
text: before && after ? `Active conversation history compacted from approximately ${before} to ${after} tokens.` : "Context compacted so the turn could continue.",
|
|
279
|
-
occurredAt: event.occurredAt
|
|
280
|
-
});
|
|
281
|
-
break;
|
|
282
|
-
}
|
|
283
|
-
case "session.context.compaction.skipped": {
|
|
284
|
-
closeStreamingTail();
|
|
285
|
-
const reason = typeof payload.reason === "string" ? payload.reason : null;
|
|
286
|
-
items.push({
|
|
287
|
-
kind: "notice",
|
|
288
|
-
id: event.id,
|
|
289
|
-
tone: reason === "summarization_failed" ? "failed" : "waiting",
|
|
290
|
-
text: reason === "no_history" ? "Context compaction skipped because there is no active history to compact." : reason === "replacement_not_smaller" ? "Context compaction skipped because the generated checkpoint would not reduce the context." : reason === "replacement_unchanged" ? "Context compaction stopped because it reproduced the same checkpoint without making progress." : reason === "summarization_failed" ? "Context compaction failed without replacing the active conversation history. Request it again to retry." : "Context compaction was not needed.",
|
|
291
|
-
occurredAt: event.occurredAt
|
|
292
|
-
});
|
|
293
|
-
break;
|
|
294
|
-
}
|
|
295
|
-
case "turn.recovery.requested": {
|
|
296
|
-
break;
|
|
297
|
-
}
|
|
298
|
-
case "turn.event.rejected_late": {
|
|
299
|
-
break;
|
|
300
|
-
}
|
|
301
|
-
case "tool.auth_needed":
|
|
302
|
-
case "credential.auth_needed": {
|
|
303
|
-
closeStreamingTail();
|
|
304
|
-
items.push({
|
|
305
|
-
kind: "auth-needed",
|
|
306
|
-
id: event.id,
|
|
307
|
-
turnId,
|
|
308
|
-
providerDomain: typeof payload.providerDomain === "string" ? payload.providerDomain : "",
|
|
309
|
-
connectionId: typeof payload.connectionId === "string" ? payload.connectionId : null,
|
|
310
|
-
reason: authNeededReason(payload.reason),
|
|
311
|
-
scopes: stringList(payload.scopes),
|
|
312
|
-
resource: typeof payload.resource === "string" ? payload.resource : null,
|
|
313
|
-
toolName: typeof payload.toolName === "string" ? payload.toolName : null,
|
|
314
|
-
authorizationUrl: typeof payload.authorizationUrl === "string" ? payload.authorizationUrl : null,
|
|
315
|
-
occurredAt: event.occurredAt
|
|
316
|
-
});
|
|
317
|
-
break;
|
|
318
|
-
}
|
|
319
|
-
case "turn.completed": {
|
|
320
|
-
if (payload.maintenance === "context_compaction") {
|
|
321
|
-
finalizeOpen(turnId);
|
|
322
|
-
break;
|
|
323
|
-
}
|
|
324
|
-
if (isCreditExhaustionPayload(payload)) {
|
|
325
|
-
finalizeOpen(turnId);
|
|
326
|
-
items.push(turnEndItem(event, "failed", CREDIT_EXHAUSTION_MESSAGE));
|
|
327
|
-
items.push({
|
|
328
|
-
kind: "notice",
|
|
329
|
-
id: event.id,
|
|
330
|
-
tone: "failed",
|
|
331
|
-
text: CREDIT_EXHAUSTION_MESSAGE,
|
|
332
|
-
occurredAt: event.occurredAt
|
|
333
|
-
});
|
|
334
|
-
break;
|
|
335
|
-
}
|
|
336
|
-
finalizeOpen(turnId);
|
|
337
|
-
items.push(turnEndItem(event, "complete", null));
|
|
338
|
-
break;
|
|
339
|
-
}
|
|
340
|
-
case "turn.failed": {
|
|
341
|
-
const hadActivity = hasTurnActivity(items, turnId);
|
|
342
|
-
const failureText = isCreditExhaustionPayload(payload) ? CREDIT_EXHAUSTION_MESSAGE : failureMessage(payload);
|
|
343
|
-
finalizeOpen(turnId, "cancelled");
|
|
344
|
-
items.push(turnEndItem(event, "failed", failureText));
|
|
345
|
-
if (!hadActivity) {
|
|
346
|
-
items.push({
|
|
347
|
-
kind: "notice",
|
|
348
|
-
id: event.id,
|
|
349
|
-
tone: "failed",
|
|
350
|
-
text: failureText ?? "The turn failed.",
|
|
351
|
-
occurredAt: event.occurredAt
|
|
352
|
-
});
|
|
353
|
-
}
|
|
354
|
-
break;
|
|
355
|
-
}
|
|
356
|
-
case "turn.cancelled": {
|
|
357
|
-
if (turnId && !prescan.startedTurnIds.has(turnId)) {
|
|
358
|
-
break;
|
|
359
|
-
}
|
|
360
|
-
const hadActivity = hasTurnActivity(items, turnId);
|
|
361
|
-
finalizeOpen(turnId, "cancelled");
|
|
362
|
-
items.push(turnEndItem(event, "cancelled", null));
|
|
363
|
-
if (!hadActivity) {
|
|
364
|
-
items.push({
|
|
365
|
-
kind: "notice",
|
|
366
|
-
id: event.id,
|
|
367
|
-
tone: "cancelled",
|
|
368
|
-
text: "Interrupted.",
|
|
369
|
-
occurredAt: event.occurredAt
|
|
370
|
-
});
|
|
371
|
-
}
|
|
372
|
-
break;
|
|
31
|
+
const fetched = await client.getSession(workspaceId, sessionId);
|
|
32
|
+
setOverride(null);
|
|
33
|
+
return fetched;
|
|
34
|
+
}, [client, workspaceId, sessionId]);
|
|
35
|
+
const { data, loading, error, refresh } = usePolledValue(load, {
|
|
36
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
37
|
+
enabled
|
|
38
|
+
});
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (enabled && workspaceControlEvent) void refresh();
|
|
41
|
+
}, [enabled, refresh, workspaceControlEvent]);
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (!sessionId || !enabled) return;
|
|
44
|
+
return registerSessionReconciler(sessionId, "session", refresh);
|
|
45
|
+
}, [enabled, refresh, registerSessionReconciler, sessionId]);
|
|
46
|
+
const base = data ?? null;
|
|
47
|
+
const session = useMemo(
|
|
48
|
+
() => base && override && override.id === base.id ? { ...base, title: override.title, titleSource: override.titleSource } : base,
|
|
49
|
+
[base, override]
|
|
50
|
+
);
|
|
51
|
+
const onTitleEvent = useCallback(
|
|
52
|
+
(event) => {
|
|
53
|
+
const payload = event.payload ?? {};
|
|
54
|
+
const title = payload.title;
|
|
55
|
+
if (typeof title !== "string") {
|
|
56
|
+
return;
|
|
373
57
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
const
|
|
377
|
-
if (
|
|
378
|
-
|
|
379
|
-
items.push(memory);
|
|
58
|
+
const source = payload.source === "user" || payload.source === "agent" ? payload.source : null;
|
|
59
|
+
setOverride((current) => {
|
|
60
|
+
const next = current ?? base;
|
|
61
|
+
if (!next) {
|
|
62
|
+
return current;
|
|
380
63
|
}
|
|
381
|
-
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
case "goal.continuation": {
|
|
390
|
-
items.push({
|
|
391
|
-
kind: "goal",
|
|
392
|
-
id: event.id,
|
|
393
|
-
action: event.type.slice("goal.".length),
|
|
394
|
-
text: goalText(payload),
|
|
395
|
-
occurredAt: event.occurredAt
|
|
396
|
-
});
|
|
397
|
-
break;
|
|
398
|
-
}
|
|
399
|
-
default:
|
|
400
|
-
break;
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
return items;
|
|
404
|
-
}
|
|
405
|
-
function isCreditExhaustionPayload(payload) {
|
|
406
|
-
return isCreditExhaustion({
|
|
407
|
-
error: typeof payload.error === "string" ? payload.error : null,
|
|
408
|
-
detail: typeof payload.detail === "string" ? payload.detail : null,
|
|
409
|
-
segmentLimit: typeof payload.segmentLimit === "string" ? payload.segmentLimit : null
|
|
64
|
+
return { ...next, title, titleSource: source };
|
|
65
|
+
});
|
|
66
|
+
},
|
|
67
|
+
[base]
|
|
68
|
+
);
|
|
69
|
+
useSessionEventTrigger(client, workspaceId, sessionId, isTitleEvent, onTitleEvent, {
|
|
70
|
+
enabled,
|
|
71
|
+
...options.events !== void 0 ? { events: options.events } : {}
|
|
410
72
|
});
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
const event = ordered[index];
|
|
416
|
-
if (event?.type !== "turn.completed" && event?.type !== "turn.failed" && event?.type !== "turn.cancelled") {
|
|
417
|
-
continue;
|
|
418
|
-
}
|
|
419
|
-
return isCreditExhaustionPayload(asRecord(event.payload));
|
|
420
|
-
}
|
|
421
|
-
return false;
|
|
422
|
-
}
|
|
423
|
-
function sessionStatusFromEvents(events) {
|
|
424
|
-
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
425
|
-
const event = events[index];
|
|
426
|
-
if (event?.type !== "session.status.changed") {
|
|
427
|
-
continue;
|
|
428
|
-
}
|
|
429
|
-
const status = asRecord(event.payload).status;
|
|
430
|
-
if (isSessionStatus(status)) {
|
|
431
|
-
return status;
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
return null;
|
|
435
|
-
}
|
|
436
|
-
function isActivityItem(item) {
|
|
437
|
-
switch (item.kind) {
|
|
438
|
-
case "reasoning":
|
|
439
|
-
case "tool-call":
|
|
440
|
-
case "worker":
|
|
441
|
-
case "sandbox":
|
|
442
|
-
case "memory":
|
|
443
|
-
return true;
|
|
444
|
-
default:
|
|
445
|
-
return false;
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
function groupTimeline(items) {
|
|
449
|
-
const groups = [];
|
|
450
|
-
for (const item of items) {
|
|
451
|
-
if (isActivityItem(item)) {
|
|
452
|
-
const open = groups[groups.length - 1];
|
|
453
|
-
if (open?.kind === "activity" && open.outcome === void 0) {
|
|
454
|
-
open.items.push(item);
|
|
455
|
-
} else {
|
|
456
|
-
groups.push({ kind: "activity", id: `activity-${item.id}`, items: [item] });
|
|
457
|
-
}
|
|
458
|
-
continue;
|
|
459
|
-
}
|
|
460
|
-
if (item.kind === "turn-end") {
|
|
461
|
-
stampTurnOutcome(groups, item);
|
|
462
|
-
foldSettledTurn(groups, item);
|
|
463
|
-
continue;
|
|
464
|
-
}
|
|
465
|
-
groups.push({ kind: "item", item });
|
|
466
|
-
}
|
|
467
|
-
return groups;
|
|
468
|
-
}
|
|
469
|
-
function prescanTurnAnchors(events) {
|
|
470
|
-
const ordered = [...events].sort((a, b) => a.sequence - b.sequence);
|
|
471
|
-
const queuedTurnByTrigger = /* @__PURE__ */ new Map();
|
|
472
|
-
const startSeqByTrigger = /* @__PURE__ */ new Map();
|
|
473
|
-
const cancelledTurnIds = /* @__PURE__ */ new Set();
|
|
474
|
-
const fallbackSeqByTurn = /* @__PURE__ */ new Map();
|
|
475
|
-
const startedTurnIds = /* @__PURE__ */ new Set();
|
|
476
|
-
for (const event of ordered) {
|
|
477
|
-
const payload = asRecord(event.payload);
|
|
478
|
-
const turnId = event.turnId ?? null;
|
|
479
|
-
if (event.type === "turn.queued") {
|
|
480
|
-
const triggerEventId = typeof payload.triggerEventId === "string" ? payload.triggerEventId : null;
|
|
481
|
-
const queuedTurnId = typeof payload.turnId === "string" ? payload.turnId : turnId;
|
|
482
|
-
if (triggerEventId && queuedTurnId) {
|
|
483
|
-
queuedTurnByTrigger.set(triggerEventId, queuedTurnId);
|
|
484
|
-
}
|
|
485
|
-
continue;
|
|
486
|
-
}
|
|
487
|
-
if (event.type === "turn.started") {
|
|
488
|
-
const triggerEventId = typeof payload.triggerEventId === "string" ? payload.triggerEventId : null;
|
|
489
|
-
if (triggerEventId && !startSeqByTrigger.has(triggerEventId)) {
|
|
490
|
-
startSeqByTrigger.set(triggerEventId, event.sequence);
|
|
491
|
-
}
|
|
492
|
-
if (turnId) {
|
|
493
|
-
startedTurnIds.add(turnId);
|
|
494
|
-
}
|
|
495
|
-
} else if (event.type === "turn.cancelled" && turnId) {
|
|
496
|
-
cancelledTurnIds.add(turnId);
|
|
497
|
-
}
|
|
498
|
-
if (turnId && isTurnExecutionEvidence(event.type)) {
|
|
499
|
-
const previous = fallbackSeqByTurn.get(turnId);
|
|
500
|
-
if (previous === void 0 || event.sequence < previous) {
|
|
501
|
-
fallbackSeqByTurn.set(turnId, event.sequence);
|
|
73
|
+
const updateTitle = useCallback(
|
|
74
|
+
async (title) => {
|
|
75
|
+
if (!sessionId) {
|
|
76
|
+
return null;
|
|
502
77
|
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
for (const [triggerEventId, turnId] of queuedTurnByTrigger) {
|
|
509
|
-
if (!startSeqByTrigger.has(triggerEventId)) {
|
|
510
|
-
const fallbackSeq = fallbackSeqByTurn.get(turnId);
|
|
511
|
-
if (fallbackSeq !== void 0) {
|
|
512
|
-
startSeqByTrigger.set(triggerEventId, fallbackSeq);
|
|
78
|
+
const result = await run(() => client.updateSession(workspaceId, sessionId, { title }));
|
|
79
|
+
if (result) {
|
|
80
|
+
setOverride(result);
|
|
513
81
|
}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
for (const [triggerEventId, turnId] of queuedTurnByTrigger) {
|
|
518
|
-
if (cancelledTurnIds.has(turnId) && !startSeqByTrigger.has(triggerEventId) && !fallbackSeqByTurn.has(turnId)) {
|
|
519
|
-
cancelledBeforeStartTriggers.add(triggerEventId);
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
return { queuedTurnByTrigger, startSeqByTrigger, cancelledBeforeStartTriggers, startedTurnIds };
|
|
523
|
-
}
|
|
524
|
-
function orderTimelineEvents(events, prescan) {
|
|
525
|
-
const ordered = [...events].sort((a, b) => a.sequence - b.sequence);
|
|
526
|
-
const insertions = /* @__PURE__ */ new Map();
|
|
527
|
-
for (const event of ordered) {
|
|
528
|
-
if (event.type !== "user.message") {
|
|
529
|
-
continue;
|
|
530
|
-
}
|
|
531
|
-
const queuedTurnId = prescan.queuedTurnByTrigger.get(event.id);
|
|
532
|
-
if (!queuedTurnId) {
|
|
533
|
-
pushInsertion(insertions, event.sequence, event);
|
|
534
|
-
continue;
|
|
535
|
-
}
|
|
536
|
-
if (prescan.cancelledBeforeStartTriggers.has(event.id)) {
|
|
537
|
-
continue;
|
|
538
|
-
}
|
|
539
|
-
const startSeq = prescan.startSeqByTrigger.get(event.id);
|
|
540
|
-
if (startSeq !== void 0) {
|
|
541
|
-
pushInsertion(insertions, startSeq, event);
|
|
542
|
-
continue;
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
const projected = [];
|
|
546
|
-
for (const event of ordered) {
|
|
547
|
-
const before = insertions.get(event.sequence);
|
|
548
|
-
if (before) {
|
|
549
|
-
projected.push(...before);
|
|
550
|
-
}
|
|
551
|
-
if (event.type !== "user.message") {
|
|
552
|
-
projected.push(event);
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
return projected;
|
|
556
|
-
}
|
|
557
|
-
function pushInsertion(insertions, sequence, event) {
|
|
558
|
-
const bucket = insertions.get(sequence);
|
|
559
|
-
if (bucket) {
|
|
560
|
-
bucket.push(event);
|
|
561
|
-
} else {
|
|
562
|
-
insertions.set(sequence, [event]);
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
function isAgentActivityEvent(type) {
|
|
566
|
-
return type.startsWith("agent.") || type.startsWith("sandbox.");
|
|
567
|
-
}
|
|
568
|
-
function isTurnExecutionEvidence(type) {
|
|
569
|
-
return isAgentActivityEvent(type) || type === "turn.completed" || type === "turn.failed" || type === "turn.recovery.requested" || type === "turn.capacity_waiting" || type === "session.requiresAction" || type === "tool.auth_needed" || type === "credential.auth_needed" || type.startsWith("rig.setup.") || type === "codex.capacity.waiting" || type === "codex.capacity.resumed";
|
|
570
|
-
}
|
|
571
|
-
function turnEndItem(event, outcome, failureText) {
|
|
572
|
-
return {
|
|
573
|
-
kind: "turn-end",
|
|
574
|
-
id: `${event.id}-turn-end`,
|
|
575
|
-
turnId: event.turnId ?? null,
|
|
576
|
-
outcome,
|
|
577
|
-
failureText,
|
|
578
|
-
occurredAt: event.occurredAt
|
|
579
|
-
};
|
|
580
|
-
}
|
|
581
|
-
function hasTurnActivity(items, turnId) {
|
|
582
|
-
if (turnId) {
|
|
583
|
-
return items.some((item) => isActivityItem(item) && item.turnId === turnId);
|
|
584
|
-
}
|
|
585
|
-
for (let index = items.length - 1; index >= 0; index -= 1) {
|
|
586
|
-
const item = items[index];
|
|
587
|
-
if (!item || item.kind === "turn-end" || item.kind === "user-message") {
|
|
588
|
-
return false;
|
|
589
|
-
}
|
|
590
|
-
if (isActivityItem(item)) {
|
|
591
|
-
return true;
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
return false;
|
|
595
|
-
}
|
|
596
|
-
function stampTurnOutcome(groups, turnEnd) {
|
|
597
|
-
if (turnEnd.turnId === null) {
|
|
598
|
-
const trailing = groups[groups.length - 1];
|
|
599
|
-
if (trailing?.kind === "activity" && trailing.outcome === void 0) {
|
|
600
|
-
applyTurnOutcome(trailing, turnEnd);
|
|
601
|
-
}
|
|
602
|
-
return;
|
|
603
|
-
}
|
|
604
|
-
for (const group of groups) {
|
|
605
|
-
if (group.kind !== "activity" || group.outcome !== void 0) {
|
|
606
|
-
continue;
|
|
607
|
-
}
|
|
608
|
-
if (group.items.some((activity) => activity.turnId === turnEnd.turnId)) {
|
|
609
|
-
applyTurnOutcome(group, turnEnd);
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
function applyTurnOutcome(group, turnEnd) {
|
|
614
|
-
if (turnEnd.outcome === "complete") {
|
|
615
|
-
group.outcome = "complete";
|
|
616
|
-
return;
|
|
617
|
-
}
|
|
618
|
-
const hasFailed = group.items.some((item) => "status" in item && item.status === "failed");
|
|
619
|
-
const hasInterrupted = group.items.some(
|
|
620
|
-
(item) => "status" in item && item.status === "cancelled"
|
|
621
|
-
);
|
|
622
|
-
group.outcome = hasFailed ? "failed" : hasInterrupted ? "cancelled" : "complete";
|
|
623
|
-
if (turnEnd.failureText && hasFailed) {
|
|
624
|
-
group.failureText = turnEnd.failureText;
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
function foldSettledTurn(groups, turnEnd) {
|
|
628
|
-
let startIndex = groups.length;
|
|
629
|
-
let stoppedAtForeignTurn = false;
|
|
630
|
-
while (startIndex > 0) {
|
|
631
|
-
const previous = groups[startIndex - 1];
|
|
632
|
-
if (isTurnBoundary(previous)) {
|
|
633
|
-
break;
|
|
634
|
-
}
|
|
635
|
-
if (belongsToDifferentTurn(previous, turnEnd.turnId)) {
|
|
636
|
-
stoppedAtForeignTurn = true;
|
|
637
|
-
break;
|
|
638
|
-
}
|
|
639
|
-
startIndex -= 1;
|
|
640
|
-
}
|
|
641
|
-
if (stoppedAtForeignTurn) {
|
|
642
|
-
while (startIndex < groups.length && isBetweenTurnDivider(groups[startIndex])) {
|
|
643
|
-
startIndex += 1;
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
const collected = groups.slice(startIndex);
|
|
647
|
-
if (collected.length === 0) {
|
|
648
|
-
return;
|
|
649
|
-
}
|
|
650
|
-
const finalMessage = extractFinalAgentMessage(collected, turnEnd);
|
|
651
|
-
const body = finalMessage ? collected.slice(0, -1) : collected;
|
|
652
|
-
if (body.length === 0) {
|
|
653
|
-
return;
|
|
654
|
-
}
|
|
655
|
-
const firstOccurredAt = groupStartedAt(body[0]) ?? turnEnd.occurredAt;
|
|
656
|
-
const turnGroup = {
|
|
657
|
-
kind: "turn",
|
|
658
|
-
id: `turn-${turnEnd.turnId ?? turnEnd.id}`,
|
|
659
|
-
outcome: turnEnd.outcome,
|
|
660
|
-
startedAt: firstOccurredAt,
|
|
661
|
-
endedAt: turnEnd.occurredAt,
|
|
662
|
-
groups: body
|
|
663
|
-
};
|
|
664
|
-
if (turnEnd.failureText) {
|
|
665
|
-
turnGroup.failureText = turnEnd.failureText;
|
|
666
|
-
}
|
|
667
|
-
groups.splice(
|
|
668
|
-
startIndex,
|
|
669
|
-
collected.length,
|
|
670
|
-
...finalMessage ? [turnGroup, finalMessage] : [turnGroup]
|
|
671
|
-
);
|
|
672
|
-
}
|
|
673
|
-
function isTurnBoundary(group) {
|
|
674
|
-
return group?.kind === "turn" || group?.kind === "item" && group.item.kind === "user-message";
|
|
675
|
-
}
|
|
676
|
-
function belongsToDifferentTurn(group, turnId) {
|
|
677
|
-
if (!group || !turnId) {
|
|
678
|
-
return false;
|
|
679
|
-
}
|
|
680
|
-
if (group.kind === "activity") {
|
|
681
|
-
return group.items.length > 0 && group.items.every((item) => item.turnId !== null && item.turnId !== turnId);
|
|
682
|
-
}
|
|
683
|
-
return group.kind === "item" && group.item.kind === "agent-message" && group.item.turnId !== null && group.item.turnId !== turnId;
|
|
684
|
-
}
|
|
685
|
-
function isBetweenTurnDivider(group) {
|
|
686
|
-
return group?.kind === "item" && group.item.kind === "session-status" && group.item.status !== "running";
|
|
687
|
-
}
|
|
688
|
-
function extractFinalAgentMessage(groups, turnEnd) {
|
|
689
|
-
const tail = groups[groups.length - 1];
|
|
690
|
-
if (tail?.kind !== "item" || tail.item.kind !== "agent-message" || tail.item.streaming) {
|
|
691
|
-
return null;
|
|
692
|
-
}
|
|
693
|
-
if (tail.item.turnId && turnEnd.turnId && tail.item.turnId !== turnEnd.turnId) {
|
|
694
|
-
return null;
|
|
695
|
-
}
|
|
696
|
-
return tail;
|
|
697
|
-
}
|
|
698
|
-
function groupStartedAt(group) {
|
|
699
|
-
if (!group) {
|
|
700
|
-
return void 0;
|
|
701
|
-
}
|
|
702
|
-
switch (group.kind) {
|
|
703
|
-
case "item":
|
|
704
|
-
return group.item.occurredAt;
|
|
705
|
-
case "activity":
|
|
706
|
-
return group.items[0]?.occurredAt;
|
|
707
|
-
case "turn":
|
|
708
|
-
return group.startedAt;
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
function asRecord(value) {
|
|
712
|
-
return value !== null && typeof value === "object" ? value : {};
|
|
713
|
-
}
|
|
714
|
-
var SESSION_STATUSES = [
|
|
715
|
-
"queued",
|
|
716
|
-
"running",
|
|
717
|
-
"idle",
|
|
718
|
-
"requires_action",
|
|
719
|
-
"failed",
|
|
720
|
-
"cancelled"
|
|
721
|
-
];
|
|
722
|
-
var ATTENTION_STATUSES = /* @__PURE__ */ new Set([
|
|
723
|
-
"requires_action",
|
|
724
|
-
"failed",
|
|
725
|
-
"cancelled"
|
|
726
|
-
]);
|
|
727
|
-
function resourceRefs(value) {
|
|
728
|
-
if (!Array.isArray(value)) {
|
|
729
|
-
return [];
|
|
730
|
-
}
|
|
731
|
-
return value.filter((entry) => {
|
|
732
|
-
const record = asRecord(entry);
|
|
733
|
-
if (record.kind === "repository") {
|
|
734
|
-
return typeof record.uri === "string" && typeof record.ref === "string";
|
|
735
|
-
}
|
|
736
|
-
return record.kind === "file" && typeof record.fileId === "string";
|
|
737
|
-
});
|
|
738
|
-
}
|
|
739
|
-
function toolRefs(value) {
|
|
740
|
-
if (!Array.isArray(value)) {
|
|
741
|
-
return [];
|
|
742
|
-
}
|
|
743
|
-
return value.filter((entry) => {
|
|
744
|
-
const record = asRecord(entry);
|
|
745
|
-
return record.kind === "mcp" && typeof record.id === "string";
|
|
746
|
-
});
|
|
747
|
-
}
|
|
748
|
-
function workerCompletionPayload(value) {
|
|
749
|
-
const payload = asRecord(value);
|
|
750
|
-
const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
751
|
-
if (typeof payload.childSessionId !== "string" || !uuidPattern.test(payload.childSessionId) || typeof payload.status !== "string" || payload.status.trim() === "") {
|
|
752
|
-
return null;
|
|
753
|
-
}
|
|
754
|
-
const goal = asRecord(payload.goal);
|
|
755
|
-
return {
|
|
756
|
-
childSessionId: payload.childSessionId,
|
|
757
|
-
childStatus: payload.status,
|
|
758
|
-
goalStatus: typeof goal.status === "string" ? goal.status : null,
|
|
759
|
-
goalText: typeof goal.text === "string" ? goal.text : null,
|
|
760
|
-
evidence: typeof goal.evidence === "string" ? goal.evidence : null,
|
|
761
|
-
// Console pauses and several server paths put the human explanation on
|
|
762
|
-
// `rationale`, not `pausedReason` — same fallback the goal pill uses, so a
|
|
763
|
-
// paused worker card never shows "Worker paused" with the reason missing.
|
|
764
|
-
pausedReason: typeof goal.pausedReason === "string" ? goal.pausedReason : typeof goal.rationale === "string" ? goal.rationale : null
|
|
765
|
-
};
|
|
766
|
-
}
|
|
767
|
-
function isSessionStatus(value) {
|
|
768
|
-
return typeof value === "string" && SESSION_STATUSES.includes(value);
|
|
769
|
-
}
|
|
770
|
-
function isErrorOutput(payload) {
|
|
771
|
-
if (payload.error === true || payload.failed === true) {
|
|
772
|
-
return true;
|
|
773
|
-
}
|
|
774
|
-
const output = payload.output;
|
|
775
|
-
return !!output && typeof output === "object" && output.isError === true;
|
|
776
|
-
}
|
|
777
|
-
function findOpenCall(items, callId) {
|
|
778
|
-
const reversed = [...items].reverse();
|
|
779
|
-
const isCall = (item) => item.kind === "tool-call" || item.kind === "worker";
|
|
780
|
-
if (callId) {
|
|
781
|
-
const byId = reversed.find((item) => isCall(item) && item.callId === callId);
|
|
782
|
-
if (byId) {
|
|
783
|
-
return byId;
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
return reversed.find(
|
|
787
|
-
(item) => isCall(item) && item.status === "running"
|
|
788
|
-
);
|
|
789
|
-
}
|
|
790
|
-
function findOpenSandbox(items, name) {
|
|
791
|
-
return [...items].reverse().find(
|
|
792
|
-
(item) => item.kind === "sandbox" && item.name === name && item.status === "running"
|
|
82
|
+
return result;
|
|
83
|
+
},
|
|
84
|
+
[client, workspaceId, sessionId, run]
|
|
793
85
|
);
|
|
794
|
-
}
|
|
795
|
-
function failureMessage(payload) {
|
|
796
|
-
for (const key of ["error", "message"]) {
|
|
797
|
-
const value = payload[key];
|
|
798
|
-
if (typeof value === "string" && value.trim().length > 0) {
|
|
799
|
-
return humanizeFailureReason(value);
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
return null;
|
|
803
|
-
}
|
|
804
|
-
function goalText(payload) {
|
|
805
|
-
if (typeof payload.text === "string" && payload.text) {
|
|
806
|
-
return payload.text;
|
|
807
|
-
}
|
|
808
|
-
const goal = asRecord(payload.goal);
|
|
809
|
-
if (typeof goal.text === "string" && goal.text) {
|
|
810
|
-
return goal.text;
|
|
811
|
-
}
|
|
812
|
-
if (typeof payload.prompt === "string" && payload.prompt) {
|
|
813
|
-
return payload.prompt;
|
|
814
|
-
}
|
|
815
|
-
return null;
|
|
816
|
-
}
|
|
817
|
-
function memoryItem(id, type, turnId, payload, occurredAt) {
|
|
818
|
-
const memoryId = typeof payload.memoryId === "string" && payload.memoryId ? payload.memoryId : null;
|
|
819
|
-
if (!memoryId) {
|
|
820
|
-
return null;
|
|
821
|
-
}
|
|
822
|
-
const replacementPreview = typeof payload.replacementPreview === "string" ? payload.replacementPreview : void 0;
|
|
823
|
-
const replacementMemoryId = typeof payload.replacementMemoryId === "string" ? payload.replacementMemoryId : void 0;
|
|
824
|
-
const action = typeof payload.action === "string" ? payload.action : void 0;
|
|
825
86
|
return {
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
...action ? { action } : {},
|
|
835
|
-
memoryId,
|
|
836
|
-
...replacementMemoryId ? { replacementMemoryId } : {},
|
|
837
|
-
occurredAt
|
|
87
|
+
session,
|
|
88
|
+
loading,
|
|
89
|
+
error,
|
|
90
|
+
refresh,
|
|
91
|
+
updateTitle,
|
|
92
|
+
updating: mutating,
|
|
93
|
+
mutationError,
|
|
94
|
+
clearMutationError
|
|
838
95
|
};
|
|
839
96
|
}
|
|
840
|
-
var AUTH_NEEDED_REASONS = /* @__PURE__ */ new Set([
|
|
841
|
-
"missing_connection",
|
|
842
|
-
"expired",
|
|
843
|
-
"insufficient_scope",
|
|
844
|
-
"refresh_failed",
|
|
845
|
-
"unsupported_auth",
|
|
846
|
-
"resource_scope_unavailable"
|
|
847
|
-
]);
|
|
848
|
-
function authNeededReason(value) {
|
|
849
|
-
return typeof value === "string" && AUTH_NEEDED_REASONS.has(value) ? value : null;
|
|
850
|
-
}
|
|
851
|
-
function stringList(value) {
|
|
852
|
-
return Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && entry.trim().length > 0) : [];
|
|
853
|
-
}
|
|
854
|
-
function reasoningText(payload) {
|
|
855
|
-
const record = asRecord(payload);
|
|
856
|
-
if (typeof record.text === "string") {
|
|
857
|
-
return record.text;
|
|
858
|
-
}
|
|
859
|
-
const content = asRecord(asRecord(record.item).rawItem).content;
|
|
860
|
-
if (!Array.isArray(content)) {
|
|
861
|
-
return "";
|
|
862
|
-
}
|
|
863
|
-
return content.map((part) => {
|
|
864
|
-
const text = asRecord(part).text;
|
|
865
|
-
return typeof text === "string" ? text : "";
|
|
866
|
-
}).join("");
|
|
867
|
-
}
|
|
868
|
-
function workerPrompt(args) {
|
|
869
|
-
const record = asRecord(typeof args === "string" ? tryParseJson(args) : args);
|
|
870
|
-
for (const key of ["initialMessage", "message", "text", "prompt"]) {
|
|
871
|
-
const value = record[key];
|
|
872
|
-
if (typeof value === "string" && value.trim().length > 0) {
|
|
873
|
-
return value;
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
return null;
|
|
877
|
-
}
|
|
878
|
-
function extractSessionRef(value, depth = 0) {
|
|
879
|
-
if (depth > 6 || value === null || value === void 0) {
|
|
880
|
-
return null;
|
|
881
|
-
}
|
|
882
|
-
if (typeof value === "string") {
|
|
883
|
-
return extractSessionRef(tryParseJson(value), depth + 1);
|
|
884
|
-
}
|
|
885
|
-
if (Array.isArray(value)) {
|
|
886
|
-
for (const entry of value) {
|
|
887
|
-
const found = extractSessionRef(entry, depth + 1);
|
|
888
|
-
if (found) {
|
|
889
|
-
return found;
|
|
890
|
-
}
|
|
891
|
-
}
|
|
892
|
-
return null;
|
|
893
|
-
}
|
|
894
|
-
if (typeof value !== "object") {
|
|
895
|
-
return null;
|
|
896
|
-
}
|
|
897
|
-
const record = value;
|
|
898
|
-
if (typeof record.sessionId === "string" && looksLikeId(record.sessionId)) {
|
|
899
|
-
return record.sessionId;
|
|
900
|
-
}
|
|
901
|
-
if (typeof record.id === "string" && looksLikeId(record.id) && ("status" in record || "workspaceId" in record || "initialMessage" in record)) {
|
|
902
|
-
return record.id;
|
|
903
|
-
}
|
|
904
|
-
for (const key of ["structuredContent", "session", "result", "content"]) {
|
|
905
|
-
if (key in record) {
|
|
906
|
-
const found = extractSessionRef(record[key], depth + 1);
|
|
907
|
-
if (found) {
|
|
908
|
-
return found;
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
if (typeof record.text === "string") {
|
|
913
|
-
return extractSessionRef(tryParseJson(record.text), depth + 1);
|
|
914
|
-
}
|
|
915
|
-
return null;
|
|
916
|
-
}
|
|
917
|
-
function looksLikeId(value) {
|
|
918
|
-
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
|
|
919
|
-
}
|
|
920
|
-
function toolDisplayName(name) {
|
|
921
|
-
const boundary = name.indexOf("__");
|
|
922
|
-
const toolPart = boundary >= 0 ? name.slice(boundary + 2) : name;
|
|
923
|
-
return toolPart.replace(/[_-]+/g, " ").trim();
|
|
924
|
-
}
|
|
925
97
|
|
|
926
98
|
// src/hooks/use-session-events.ts
|
|
927
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
99
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useRef, useState as useState2 } from "react";
|
|
928
100
|
var INITIAL_TAIL_PAGE_SIZE = 1e3;
|
|
929
101
|
var OLDER_PAGE_SIZE = 5e3;
|
|
930
102
|
var INITIAL_FETCH_CAP = 1;
|
|
@@ -973,16 +145,16 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
973
145
|
const replay = options.replay ?? "windowed";
|
|
974
146
|
const fullReplay = replay === "full" || after !== 0;
|
|
975
147
|
const streamKey = `${workspaceId}\0${sessionId ?? ""}\0${after}\0${fullReplay ? "full" : "windowed"}`;
|
|
976
|
-
const [eventWindow, setEventWindow] =
|
|
977
|
-
const [connectionState, setConnectionState] =
|
|
978
|
-
const [error, setError] =
|
|
979
|
-
const [hasOlder, setHasOlder] =
|
|
980
|
-
const [sessionStatusProjection, setSessionStatusProjection] =
|
|
148
|
+
const [eventWindow, setEventWindow] = useState2(EMPTY_EVENT_WINDOW);
|
|
149
|
+
const [connectionState, setConnectionState] = useState2("idle");
|
|
150
|
+
const [error, setError] = useState2(null);
|
|
151
|
+
const [hasOlder, setHasOlder] = useState2(false);
|
|
152
|
+
const [sessionStatusProjection, setSessionStatusProjection] = useState2(
|
|
981
153
|
null
|
|
982
154
|
);
|
|
983
|
-
const [initialLoading, setInitialLoading] =
|
|
984
|
-
const [loadingOlder, setLoadingOlder] =
|
|
985
|
-
const [streamEpoch, setStreamEpoch] =
|
|
155
|
+
const [initialLoading, setInitialLoading] = useState2(true);
|
|
156
|
+
const [loadingOlder, setLoadingOlder] = useState2(false);
|
|
157
|
+
const [streamEpoch, setStreamEpoch] = useState2(0);
|
|
986
158
|
const lastSequenceRef = useRef(after);
|
|
987
159
|
const streamResumeSequenceRef = useRef(after);
|
|
988
160
|
const oldestSequenceRef = useRef(null);
|
|
@@ -997,8 +169,8 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
997
169
|
sequence: after,
|
|
998
170
|
status: null
|
|
999
171
|
});
|
|
1000
|
-
const [stateStreamKey, setStateStreamKey] =
|
|
1001
|
-
|
|
172
|
+
const [stateStreamKey, setStateStreamKey] = useState2(streamKey);
|
|
173
|
+
useEffect2(() => {
|
|
1002
174
|
if (streamKeyRef.current !== streamKey) {
|
|
1003
175
|
streamKeyRef.current = streamKey;
|
|
1004
176
|
setStateStreamKey(streamKey);
|
|
@@ -1167,7 +339,7 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
1167
339
|
streamEpoch,
|
|
1168
340
|
reconcileSession
|
|
1169
341
|
]);
|
|
1170
|
-
const loadOlder =
|
|
342
|
+
const loadOlder = useCallback2(async () => {
|
|
1171
343
|
if (!sessionId || loadingOlderRef.current || !hasOlderRef.current) {
|
|
1172
344
|
return false;
|
|
1173
345
|
}
|
|
@@ -1229,7 +401,7 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
1229
401
|
}, [client, workspaceId, sessionId]);
|
|
1230
402
|
const identityMatches = stateStreamKey === streamKey;
|
|
1231
403
|
const visibleEvents = identityMatches ? eventWindow.events : EMPTY_EVENTS;
|
|
1232
|
-
const timeline =
|
|
404
|
+
const timeline = useMemo2(() => buildTimeline(visibleEvents), [visibleEvents]);
|
|
1233
405
|
return {
|
|
1234
406
|
events: visibleEvents,
|
|
1235
407
|
timeline,
|
|
@@ -1540,7 +712,12 @@ async function loadPreviousPage(client, workspaceId, sessionId, before, options)
|
|
|
1540
712
|
const page = await client.listEvents(workspaceId, sessionId, {
|
|
1541
713
|
before,
|
|
1542
714
|
limit: requested,
|
|
1543
|
-
compact: true
|
|
715
|
+
compact: true,
|
|
716
|
+
// Monitoring summaries omit correlation fields once a bounded durable
|
|
717
|
+
// payload exceeds their preview threshold. The browser timeline needs the
|
|
718
|
+
// sanitized stored payload so persisted tool outputs still match their
|
|
719
|
+
// calls and expose truthful truncation telemetry after a reload.
|
|
720
|
+
payloadMode: "full"
|
|
1544
721
|
});
|
|
1545
722
|
if (options.signal?.aborted) {
|
|
1546
723
|
throw abortError();
|
|
@@ -1560,11 +737,11 @@ function maxResumeSequence(events) {
|
|
|
1560
737
|
return events.reduce((max, event) => Math.max(max, eventResumeSequence(event)), 0);
|
|
1561
738
|
}
|
|
1562
739
|
function eventResumeSequence(event) {
|
|
1563
|
-
const payload =
|
|
740
|
+
const payload = asRecord(event.payload);
|
|
1564
741
|
const coalescedUntil = Number(payload.coalescedUntil);
|
|
1565
742
|
return Math.max(event.sequence, Number.isFinite(coalescedUntil) ? Math.floor(coalescedUntil) : 0);
|
|
1566
743
|
}
|
|
1567
|
-
function
|
|
744
|
+
function asRecord(value) {
|
|
1568
745
|
return value && typeof value === "object" ? value : {};
|
|
1569
746
|
}
|
|
1570
747
|
function assertAscending(events) {
|
|
@@ -1592,8 +769,199 @@ function abortError() {
|
|
|
1592
769
|
return error;
|
|
1593
770
|
}
|
|
1594
771
|
|
|
772
|
+
// src/hooks/use-file-attachments.ts
|
|
773
|
+
import { useCallback as useCallback3, useRef as useRef2, useState as useState3 } from "react";
|
|
774
|
+
var isImage = (file) => file.type.startsWith("image/");
|
|
775
|
+
function useFileAttachments(options = {}) {
|
|
776
|
+
const { client, workspaceId } = useOpenGeni(options);
|
|
777
|
+
const pasteFilter = options.pasteFilter ?? isImage;
|
|
778
|
+
const [attachments, setAttachments] = useState3([]);
|
|
779
|
+
const sources = useRef2(/* @__PURE__ */ new Map());
|
|
780
|
+
const startUpload = useCallback3(
|
|
781
|
+
(id, file) => {
|
|
782
|
+
void client.uploadFile(workspaceId, {
|
|
783
|
+
filename: file.name || "file",
|
|
784
|
+
contentType: file.type || "application/octet-stream",
|
|
785
|
+
data: file
|
|
786
|
+
}).then((asset) => {
|
|
787
|
+
sources.current.delete(id);
|
|
788
|
+
setAttachments(
|
|
789
|
+
(current) => current.map(
|
|
790
|
+
(attachment) => attachment.id === id ? {
|
|
791
|
+
...attachment,
|
|
792
|
+
status: "ready",
|
|
793
|
+
file: asset,
|
|
794
|
+
name: asset.filename,
|
|
795
|
+
contentType: asset.contentType,
|
|
796
|
+
sizeBytes: asset.sizeBytes,
|
|
797
|
+
error: void 0
|
|
798
|
+
} : attachment
|
|
799
|
+
)
|
|
800
|
+
);
|
|
801
|
+
}).catch((error) => {
|
|
802
|
+
setAttachments(
|
|
803
|
+
(current) => current.map(
|
|
804
|
+
(attachment) => attachment.id === id ? {
|
|
805
|
+
...attachment,
|
|
806
|
+
status: "failed",
|
|
807
|
+
error: error instanceof Error ? error.message : String(error)
|
|
808
|
+
} : attachment
|
|
809
|
+
)
|
|
810
|
+
);
|
|
811
|
+
});
|
|
812
|
+
},
|
|
813
|
+
[client, workspaceId]
|
|
814
|
+
);
|
|
815
|
+
const addFiles = useCallback3(
|
|
816
|
+
(files) => {
|
|
817
|
+
for (const file of files) {
|
|
818
|
+
const id = crypto.randomUUID();
|
|
819
|
+
sources.current.set(id, file);
|
|
820
|
+
const previewUrl = isImage(file) ? URL.createObjectURL(file) : void 0;
|
|
821
|
+
setAttachments((current) => [
|
|
822
|
+
...current,
|
|
823
|
+
{
|
|
824
|
+
id,
|
|
825
|
+
name: file.name || "image",
|
|
826
|
+
contentType: file.type || "application/octet-stream",
|
|
827
|
+
sizeBytes: file.size,
|
|
828
|
+
status: "uploading",
|
|
829
|
+
...previewUrl ? { previewUrl } : {}
|
|
830
|
+
}
|
|
831
|
+
]);
|
|
832
|
+
startUpload(id, file);
|
|
833
|
+
}
|
|
834
|
+
},
|
|
835
|
+
[startUpload]
|
|
836
|
+
);
|
|
837
|
+
const retry = useCallback3(
|
|
838
|
+
(id) => {
|
|
839
|
+
const file = sources.current.get(id);
|
|
840
|
+
if (!file) {
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
setAttachments(
|
|
844
|
+
(current) => current.map(
|
|
845
|
+
(attachment) => attachment.id === id ? { ...attachment, status: "uploading", error: void 0 } : attachment
|
|
846
|
+
)
|
|
847
|
+
);
|
|
848
|
+
startUpload(id, file);
|
|
849
|
+
},
|
|
850
|
+
[startUpload]
|
|
851
|
+
);
|
|
852
|
+
const addFromPaste = useCallback3(
|
|
853
|
+
(event) => {
|
|
854
|
+
const clipboardFiles = event.clipboardData?.files;
|
|
855
|
+
if (!clipboardFiles) {
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
const files = [...clipboardFiles].filter(pasteFilter);
|
|
859
|
+
if (files.length > 0) {
|
|
860
|
+
addFiles(files);
|
|
861
|
+
}
|
|
862
|
+
},
|
|
863
|
+
[addFiles, pasteFilter]
|
|
864
|
+
);
|
|
865
|
+
const restoreReadyFiles = useCallback3(
|
|
866
|
+
(files) => {
|
|
867
|
+
const incoming = /* @__PURE__ */ new Map();
|
|
868
|
+
for (const file of files) {
|
|
869
|
+
if (file.status === "ready" && file.workspaceId === workspaceId) {
|
|
870
|
+
incoming.set(file.id, file);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
setAttachments((current) => {
|
|
874
|
+
const unresolved = current.filter((attachment) => attachment.status !== "ready");
|
|
875
|
+
const existingReady = new Map(
|
|
876
|
+
current.flatMap(
|
|
877
|
+
(attachment) => attachment.status === "ready" && attachment.file ? [[attachment.file.id, attachment]] : []
|
|
878
|
+
)
|
|
879
|
+
);
|
|
880
|
+
const restored = [...incoming.values()].map((file) => {
|
|
881
|
+
const existing = existingReady.get(file.id);
|
|
882
|
+
return existing ? {
|
|
883
|
+
...existing,
|
|
884
|
+
name: file.filename,
|
|
885
|
+
contentType: file.contentType,
|
|
886
|
+
sizeBytes: file.sizeBytes,
|
|
887
|
+
status: "ready",
|
|
888
|
+
file,
|
|
889
|
+
error: void 0
|
|
890
|
+
} : {
|
|
891
|
+
id: `restored:${file.id}`,
|
|
892
|
+
name: file.filename,
|
|
893
|
+
contentType: file.contentType,
|
|
894
|
+
sizeBytes: file.sizeBytes,
|
|
895
|
+
status: "ready",
|
|
896
|
+
file
|
|
897
|
+
// No source File and no object URL: server metadata is the
|
|
898
|
+
// only authority restored across page/device boundaries.
|
|
899
|
+
};
|
|
900
|
+
});
|
|
901
|
+
for (const [fileId, attachment] of existingReady) {
|
|
902
|
+
if (!incoming.has(fileId) && attachment.previewUrl) {
|
|
903
|
+
URL.revokeObjectURL(attachment.previewUrl);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
return [...unresolved, ...restored];
|
|
907
|
+
});
|
|
908
|
+
},
|
|
909
|
+
[workspaceId]
|
|
910
|
+
);
|
|
911
|
+
const remove = useCallback3((id) => {
|
|
912
|
+
sources.current.delete(id);
|
|
913
|
+
setAttachments((current) => {
|
|
914
|
+
const removed = current.find((attachment) => attachment.id === id);
|
|
915
|
+
if (removed?.previewUrl) {
|
|
916
|
+
URL.revokeObjectURL(removed.previewUrl);
|
|
917
|
+
}
|
|
918
|
+
return current.filter((attachment) => attachment.id !== id);
|
|
919
|
+
});
|
|
920
|
+
}, []);
|
|
921
|
+
const removeReadyFiles = useCallback3((fileIds) => {
|
|
922
|
+
const accepted = new Set(fileIds);
|
|
923
|
+
if (accepted.size === 0) return;
|
|
924
|
+
setAttachments(
|
|
925
|
+
(current) => current.filter((attachment) => {
|
|
926
|
+
const removeAccepted = attachment.status === "ready" && attachment.file !== void 0 && accepted.has(attachment.file.id);
|
|
927
|
+
if (removeAccepted) {
|
|
928
|
+
sources.current.delete(attachment.id);
|
|
929
|
+
if (attachment.previewUrl) URL.revokeObjectURL(attachment.previewUrl);
|
|
930
|
+
}
|
|
931
|
+
return !removeAccepted;
|
|
932
|
+
})
|
|
933
|
+
);
|
|
934
|
+
}, []);
|
|
935
|
+
const clear = useCallback3(() => {
|
|
936
|
+
sources.current.clear();
|
|
937
|
+
setAttachments((current) => {
|
|
938
|
+
for (const attachment of current) {
|
|
939
|
+
if (attachment.previewUrl) {
|
|
940
|
+
URL.revokeObjectURL(attachment.previewUrl);
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
return [];
|
|
944
|
+
});
|
|
945
|
+
}, []);
|
|
946
|
+
return {
|
|
947
|
+
attachments,
|
|
948
|
+
readyResources: attachments.flatMap(
|
|
949
|
+
(attachment) => attachment.status === "ready" && attachment.file ? [{ kind: "file", fileId: attachment.file.id }] : []
|
|
950
|
+
),
|
|
951
|
+
uploading: attachments.some((attachment) => attachment.status === "uploading"),
|
|
952
|
+
hasUnresolved: attachments.some((attachment) => attachment.status !== "ready"),
|
|
953
|
+
addFiles,
|
|
954
|
+
addFromPaste,
|
|
955
|
+
restoreReadyFiles,
|
|
956
|
+
retry,
|
|
957
|
+
remove,
|
|
958
|
+
removeReadyFiles,
|
|
959
|
+
clear
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
|
|
1595
963
|
// src/hooks/use-turn-queue.ts
|
|
1596
|
-
import { useCallback as
|
|
964
|
+
import { useCallback as useCallback4, useEffect as useEffect3, useLayoutEffect, useMemo as useMemo3, useRef as useRef3, useState as useState4 } from "react";
|
|
1597
965
|
function isTurnQueueEvent(event) {
|
|
1598
966
|
return event.type.startsWith("turn.") || event.type.startsWith("session.queue.") || event.type.startsWith("session.control.") || event.type.startsWith("workspace.inference.");
|
|
1599
967
|
}
|
|
@@ -1607,16 +975,16 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1607
975
|
const { client, workspaceId, workspaceControlEvent, registerSessionReconciler } = useEmbeddedSession(options);
|
|
1608
976
|
const enabled = (options.enabled ?? true) && Boolean(sessionId);
|
|
1609
977
|
const targetKey = `${workspaceId}\0${sessionId ?? ""}`;
|
|
1610
|
-
const [snapshot, setSnapshot] =
|
|
1611
|
-
const [stateTargetKey, setStateTargetKey] =
|
|
1612
|
-
const [loading, setLoading] =
|
|
1613
|
-
const [error, setError] =
|
|
1614
|
-
const [mutationError, setMutationError] =
|
|
1615
|
-
const [pendingByTurn, setPendingByTurn] =
|
|
1616
|
-
const pendingRef =
|
|
1617
|
-
const readGeneration =
|
|
1618
|
-
const targetKeyRef =
|
|
1619
|
-
const snapshotRef =
|
|
978
|
+
const [snapshot, setSnapshot] = useState4(null);
|
|
979
|
+
const [stateTargetKey, setStateTargetKey] = useState4(targetKey);
|
|
980
|
+
const [loading, setLoading] = useState4(enabled);
|
|
981
|
+
const [error, setError] = useState4(null);
|
|
982
|
+
const [mutationError, setMutationError] = useState4(null);
|
|
983
|
+
const [pendingByTurn, setPendingByTurn] = useState4({});
|
|
984
|
+
const pendingRef = useRef3({});
|
|
985
|
+
const readGeneration = useRef3(0);
|
|
986
|
+
const targetKeyRef = useRef3(targetKey);
|
|
987
|
+
const snapshotRef = useRef3(null);
|
|
1620
988
|
useLayoutEffect(() => {
|
|
1621
989
|
if (targetKeyRef.current === targetKey) return;
|
|
1622
990
|
targetKeyRef.current = targetKey;
|
|
@@ -1630,7 +998,7 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1630
998
|
setMutationError(null);
|
|
1631
999
|
setPendingByTurn({});
|
|
1632
1000
|
}, [enabled, targetKey]);
|
|
1633
|
-
const acceptSnapshot =
|
|
1001
|
+
const acceptSnapshot = useCallback4(
|
|
1634
1002
|
(ownedTargetKey, next) => {
|
|
1635
1003
|
if (targetKeyRef.current !== ownedTargetKey) return false;
|
|
1636
1004
|
const current = snapshotRef.current;
|
|
@@ -1644,7 +1012,7 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1644
1012
|
},
|
|
1645
1013
|
[]
|
|
1646
1014
|
);
|
|
1647
|
-
const load =
|
|
1015
|
+
const load = useCallback4(
|
|
1648
1016
|
async (rejectOnFailure = false) => {
|
|
1649
1017
|
if (!sessionId) return;
|
|
1650
1018
|
const ownedTargetKey = `${workspaceId}\0${sessionId}`;
|
|
@@ -1675,7 +1043,7 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1675
1043
|
},
|
|
1676
1044
|
[client, workspaceId, sessionId, acceptSnapshot]
|
|
1677
1045
|
);
|
|
1678
|
-
|
|
1046
|
+
useEffect3(() => {
|
|
1679
1047
|
if (!enabled) {
|
|
1680
1048
|
setLoading(false);
|
|
1681
1049
|
return;
|
|
@@ -1694,10 +1062,10 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1694
1062
|
readGeneration.current += 1;
|
|
1695
1063
|
};
|
|
1696
1064
|
}, [load, enabled, options.pollIntervalMs]);
|
|
1697
|
-
|
|
1065
|
+
useEffect3(() => {
|
|
1698
1066
|
if (enabled && workspaceControlEvent) void load();
|
|
1699
1067
|
}, [enabled, load, workspaceControlEvent]);
|
|
1700
|
-
|
|
1068
|
+
useEffect3(() => {
|
|
1701
1069
|
if (!sessionId || !enabled) return;
|
|
1702
1070
|
return registerSessionReconciler(sessionId, "queue", load);
|
|
1703
1071
|
}, [enabled, load, registerSessionReconciler, sessionId]);
|
|
@@ -1714,7 +1082,7 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1714
1082
|
},
|
|
1715
1083
|
async () => await load(true)
|
|
1716
1084
|
);
|
|
1717
|
-
const mutate =
|
|
1085
|
+
const mutate = useCallback4(
|
|
1718
1086
|
async (turnId, kind, command) => {
|
|
1719
1087
|
const ownedTargetKey = targetKey;
|
|
1720
1088
|
if (!sessionId || targetKeyRef.current !== ownedTargetKey || pendingRef.current[turnId]) {
|
|
@@ -1749,7 +1117,7 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1749
1117
|
},
|
|
1750
1118
|
[acceptSnapshot, load, sessionId, targetKey]
|
|
1751
1119
|
);
|
|
1752
|
-
const moveTurn =
|
|
1120
|
+
const moveTurn = useCallback4(
|
|
1753
1121
|
async (turnId, beforeTurnId) => {
|
|
1754
1122
|
const result = await mutate(
|
|
1755
1123
|
turnId,
|
|
@@ -1764,7 +1132,7 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1764
1132
|
},
|
|
1765
1133
|
[client, mutate, sessionId, workspaceId]
|
|
1766
1134
|
);
|
|
1767
|
-
const editTurn =
|
|
1135
|
+
const editTurn = useCallback4(
|
|
1768
1136
|
async (turnId, edit) => {
|
|
1769
1137
|
const result = await mutate(
|
|
1770
1138
|
turnId,
|
|
@@ -1780,7 +1148,7 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1780
1148
|
},
|
|
1781
1149
|
[client, mutate, sessionId, workspaceId]
|
|
1782
1150
|
);
|
|
1783
|
-
const steerTurn =
|
|
1151
|
+
const steerTurn = useCallback4(
|
|
1784
1152
|
async (turnId) => {
|
|
1785
1153
|
const result = await mutate(
|
|
1786
1154
|
turnId,
|
|
@@ -1795,7 +1163,7 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1795
1163
|
},
|
|
1796
1164
|
[client, mutate, sessionId, workspaceId]
|
|
1797
1165
|
);
|
|
1798
|
-
const removeTurn =
|
|
1166
|
+
const removeTurn = useCallback4(
|
|
1799
1167
|
async (turnId) => {
|
|
1800
1168
|
const result = await mutate(
|
|
1801
1169
|
turnId,
|
|
@@ -1813,11 +1181,11 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1813
1181
|
const identityMatches = stateTargetKey === targetKey;
|
|
1814
1182
|
const visibleSnapshot = identityMatches ? snapshot : null;
|
|
1815
1183
|
const visiblePendingByTurn = identityMatches ? pendingByTurn : EMPTY_PENDING_BY_TURN;
|
|
1816
|
-
const mutating =
|
|
1184
|
+
const mutating = useMemo3(
|
|
1817
1185
|
() => Object.keys(visiblePendingByTurn).length > 0,
|
|
1818
1186
|
[visiblePendingByTurn]
|
|
1819
1187
|
);
|
|
1820
|
-
const mutationFor =
|
|
1188
|
+
const mutationFor = useCallback4(
|
|
1821
1189
|
(turnId) => visiblePendingByTurn[turnId] ?? null,
|
|
1822
1190
|
[visiblePendingByTurn]
|
|
1823
1191
|
);
|
|
@@ -1837,7 +1205,7 @@ function useTurnQueue(sessionId, options = {}) {
|
|
|
1837
1205
|
mutationFor,
|
|
1838
1206
|
mutating,
|
|
1839
1207
|
mutationError: identityMatches ? mutationError : null,
|
|
1840
|
-
clearMutationError:
|
|
1208
|
+
clearMutationError: useCallback4(() => {
|
|
1841
1209
|
if (targetKeyRef.current === targetKey) setMutationError(null);
|
|
1842
1210
|
}, [targetKey])
|
|
1843
1211
|
};
|
|
@@ -1849,12 +1217,154 @@ function asError(cause) {
|
|
|
1849
1217
|
return cause instanceof Error ? cause : new Error(String(cause));
|
|
1850
1218
|
}
|
|
1851
1219
|
|
|
1220
|
+
// src/hooks/use-goal.ts
|
|
1221
|
+
import { OpenGeniApiError } from "@opengeni/sdk";
|
|
1222
|
+
import { useCallback as useCallback5, useEffect as useEffect4, useRef as useRef4, useState as useState5 } from "react";
|
|
1223
|
+
function isGoalEvent(event) {
|
|
1224
|
+
return event.type.startsWith("goal.");
|
|
1225
|
+
}
|
|
1226
|
+
function isGoalRefreshEvent(event) {
|
|
1227
|
+
const type = event.type;
|
|
1228
|
+
return isGoalEvent(event) || type.startsWith("turn.") || type.startsWith("session.") || type.startsWith("system.update.") || type.startsWith("workspace.inference.") || type.startsWith("user.");
|
|
1229
|
+
}
|
|
1230
|
+
function useGoal(sessionId, options = {}) {
|
|
1231
|
+
const { client, workspaceId } = useOpenGeni(options);
|
|
1232
|
+
const enabled = (options.enabled ?? true) && Boolean(sessionId);
|
|
1233
|
+
const sharedEvents = options.events;
|
|
1234
|
+
const sharedFeed = sharedEvents !== void 0;
|
|
1235
|
+
const [goal, setGoal] = useState5(null);
|
|
1236
|
+
const [loading, setLoading] = useState5(enabled);
|
|
1237
|
+
const [error, setError] = useState5(null);
|
|
1238
|
+
const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
|
|
1239
|
+
const generation = useRef4(0);
|
|
1240
|
+
const targetKeyRef = useRef4(null);
|
|
1241
|
+
const load = useCallback5(async () => {
|
|
1242
|
+
if (!sessionId) {
|
|
1243
|
+
return;
|
|
1244
|
+
}
|
|
1245
|
+
const ticket = ++generation.current;
|
|
1246
|
+
try {
|
|
1247
|
+
const fetched = await client.getGoal(workspaceId, sessionId);
|
|
1248
|
+
if (ticket === generation.current) {
|
|
1249
|
+
setGoal(fetched);
|
|
1250
|
+
setError(null);
|
|
1251
|
+
setLoading(false);
|
|
1252
|
+
}
|
|
1253
|
+
} catch (cause) {
|
|
1254
|
+
if (ticket !== generation.current) {
|
|
1255
|
+
return;
|
|
1256
|
+
}
|
|
1257
|
+
if (cause instanceof OpenGeniApiError && cause.status === 404) {
|
|
1258
|
+
setGoal(null);
|
|
1259
|
+
setError(null);
|
|
1260
|
+
} else {
|
|
1261
|
+
setError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
1262
|
+
}
|
|
1263
|
+
setLoading(false);
|
|
1264
|
+
}
|
|
1265
|
+
}, [client, workspaceId, sessionId]);
|
|
1266
|
+
useEffect4(() => {
|
|
1267
|
+
const targetKey = `${workspaceId} ${sessionId ?? ""}`;
|
|
1268
|
+
if (targetKeyRef.current !== targetKey) {
|
|
1269
|
+
targetKeyRef.current = targetKey;
|
|
1270
|
+
setGoal(null);
|
|
1271
|
+
setError(null);
|
|
1272
|
+
}
|
|
1273
|
+
if (!enabled) {
|
|
1274
|
+
setLoading(false);
|
|
1275
|
+
return;
|
|
1276
|
+
}
|
|
1277
|
+
if (sharedFeed) {
|
|
1278
|
+
setLoading(false);
|
|
1279
|
+
return () => {
|
|
1280
|
+
generation.current += 1;
|
|
1281
|
+
};
|
|
1282
|
+
}
|
|
1283
|
+
setLoading(true);
|
|
1284
|
+
void load();
|
|
1285
|
+
const pollIntervalMs = options.pollIntervalMs;
|
|
1286
|
+
if (pollIntervalMs === void 0 || pollIntervalMs <= 0) {
|
|
1287
|
+
return () => {
|
|
1288
|
+
generation.current += 1;
|
|
1289
|
+
};
|
|
1290
|
+
}
|
|
1291
|
+
const timer = setInterval(() => void load(), pollIntervalMs);
|
|
1292
|
+
return () => {
|
|
1293
|
+
clearInterval(timer);
|
|
1294
|
+
generation.current += 1;
|
|
1295
|
+
};
|
|
1296
|
+
}, [load, enabled, workspaceId, sessionId, options.pollIntervalMs, sharedFeed]);
|
|
1297
|
+
const scheduleRefresh = useDebouncedCallback(() => void load());
|
|
1298
|
+
useSessionEventTrigger(client, workspaceId, sessionId, isGoalRefreshEvent, scheduleRefresh, {
|
|
1299
|
+
enabled,
|
|
1300
|
+
...sharedEvents !== void 0 ? { events: sharedEvents } : {}
|
|
1301
|
+
});
|
|
1302
|
+
const pause = useCallback5(
|
|
1303
|
+
async (rationale) => {
|
|
1304
|
+
if (!sessionId) {
|
|
1305
|
+
return null;
|
|
1306
|
+
}
|
|
1307
|
+
const result = await run(
|
|
1308
|
+
() => client.updateGoal(workspaceId, sessionId, {
|
|
1309
|
+
status: "paused",
|
|
1310
|
+
...rationale !== void 0 ? { rationale } : {}
|
|
1311
|
+
})
|
|
1312
|
+
);
|
|
1313
|
+
if (result) {
|
|
1314
|
+
setGoal(result);
|
|
1315
|
+
}
|
|
1316
|
+
return result;
|
|
1317
|
+
},
|
|
1318
|
+
[client, workspaceId, sessionId, run]
|
|
1319
|
+
);
|
|
1320
|
+
const resume = useCallback5(async () => {
|
|
1321
|
+
if (!sessionId) {
|
|
1322
|
+
return null;
|
|
1323
|
+
}
|
|
1324
|
+
const result = await run(() => client.updateGoal(workspaceId, sessionId, { status: "active" }));
|
|
1325
|
+
if (result) {
|
|
1326
|
+
setGoal(result);
|
|
1327
|
+
}
|
|
1328
|
+
return result;
|
|
1329
|
+
}, [client, workspaceId, sessionId, run]);
|
|
1330
|
+
const clearGoal = useCallback5(async () => {
|
|
1331
|
+
if (!sessionId) {
|
|
1332
|
+
return;
|
|
1333
|
+
}
|
|
1334
|
+
const ok = await run(async () => {
|
|
1335
|
+
await client.deleteGoal(workspaceId, sessionId);
|
|
1336
|
+
return true;
|
|
1337
|
+
});
|
|
1338
|
+
if (ok) {
|
|
1339
|
+
generation.current += 1;
|
|
1340
|
+
setGoal(null);
|
|
1341
|
+
setError(null);
|
|
1342
|
+
}
|
|
1343
|
+
}, [client, workspaceId, sessionId, run]);
|
|
1344
|
+
return {
|
|
1345
|
+
goal,
|
|
1346
|
+
isActive: goal?.status === "active",
|
|
1347
|
+
isPaused: goal?.status === "paused",
|
|
1348
|
+
isCompleted: goal?.status === "completed",
|
|
1349
|
+
loading,
|
|
1350
|
+
error,
|
|
1351
|
+
refresh: load,
|
|
1352
|
+
pause,
|
|
1353
|
+
resume,
|
|
1354
|
+
clearGoal,
|
|
1355
|
+
deleteGoal: clearGoal,
|
|
1356
|
+
updating: mutating,
|
|
1357
|
+
mutationError,
|
|
1358
|
+
clearMutationError
|
|
1359
|
+
};
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1852
1362
|
// src/hooks/use-session-control.ts
|
|
1853
|
-
import { useCallback as
|
|
1363
|
+
import { useCallback as useCallback6, useLayoutEffect as useLayoutEffect2, useRef as useRef5 } from "react";
|
|
1854
1364
|
function useSessionControl(sessionId, options = {}) {
|
|
1855
1365
|
const { client, workspaceId } = useEmbeddedSession(options);
|
|
1856
1366
|
const approvalTargetKey = `${workspaceId}\0${sessionId ?? ""}`;
|
|
1857
|
-
const pendingApproval =
|
|
1367
|
+
const pendingApproval = useRef5(null);
|
|
1858
1368
|
useLayoutEffect2(() => {
|
|
1859
1369
|
if (pendingApproval.current?.targetKey !== approvalTargetKey) {
|
|
1860
1370
|
pendingApproval.current = null;
|
|
@@ -1872,7 +1382,7 @@ function useSessionControl(sessionId, options = {}) {
|
|
|
1872
1382
|
mutationError: approvalError,
|
|
1873
1383
|
clearMutationError: clearApprovalError
|
|
1874
1384
|
} = useMutationRunner(approvalTargetKey);
|
|
1875
|
-
const pause =
|
|
1385
|
+
const pause = useCallback6(
|
|
1876
1386
|
async (reason) => {
|
|
1877
1387
|
if (!sessionId) {
|
|
1878
1388
|
return null;
|
|
@@ -1883,7 +1393,7 @@ function useSessionControl(sessionId, options = {}) {
|
|
|
1883
1393
|
},
|
|
1884
1394
|
[client, workspaceId, sessionId, runControl]
|
|
1885
1395
|
);
|
|
1886
|
-
const resume =
|
|
1396
|
+
const resume = useCallback6(
|
|
1887
1397
|
async (reason) => {
|
|
1888
1398
|
if (!sessionId) return null;
|
|
1889
1399
|
return await runControl(
|
|
@@ -1892,7 +1402,7 @@ function useSessionControl(sessionId, options = {}) {
|
|
|
1892
1402
|
},
|
|
1893
1403
|
[client, workspaceId, sessionId, runControl]
|
|
1894
1404
|
);
|
|
1895
|
-
const decide =
|
|
1405
|
+
const decide = useCallback6(
|
|
1896
1406
|
async (approvalId, decision, message) => {
|
|
1897
1407
|
if (!sessionId) {
|
|
1898
1408
|
return null;
|
|
@@ -1921,16 +1431,16 @@ function useSessionControl(sessionId, options = {}) {
|
|
|
1921
1431
|
},
|
|
1922
1432
|
[approvalTargetKey, client, workspaceId, sessionId, runApproval]
|
|
1923
1433
|
);
|
|
1924
|
-
const approve =
|
|
1434
|
+
const approve = useCallback6(
|
|
1925
1435
|
async (approvalId, message) => await decide(approvalId, "approve", message),
|
|
1926
1436
|
[decide]
|
|
1927
1437
|
);
|
|
1928
|
-
const reject =
|
|
1438
|
+
const reject = useCallback6(
|
|
1929
1439
|
async (approvalId, message) => await decide(approvalId, "reject", message),
|
|
1930
1440
|
[decide]
|
|
1931
1441
|
);
|
|
1932
1442
|
const error = approvalError ?? controlError;
|
|
1933
|
-
const clearError =
|
|
1443
|
+
const clearError = useCallback6(() => {
|
|
1934
1444
|
clearControlError();
|
|
1935
1445
|
clearApprovalError();
|
|
1936
1446
|
}, [clearControlError, clearApprovalError]);
|
|
@@ -1947,7 +1457,7 @@ function useSessionControl(sessionId, options = {}) {
|
|
|
1947
1457
|
}
|
|
1948
1458
|
|
|
1949
1459
|
// src/hooks/use-session-mcp-approval-policy.ts
|
|
1950
|
-
import { useCallback as
|
|
1460
|
+
import { useCallback as useCallback7, useEffect as useEffect5, useLayoutEffect as useLayoutEffect3, useMemo as useMemo4, useRef as useRef6, useState as useState6 } from "react";
|
|
1951
1461
|
function isSessionMcpApprovalPolicyEvent(event, serverId) {
|
|
1952
1462
|
if (event.type !== "session.mcp.approval_policy.updated") {
|
|
1953
1463
|
return false;
|
|
@@ -1961,16 +1471,16 @@ function isSessionMcpApprovalPolicyEvent(event, serverId) {
|
|
|
1961
1471
|
function useSessionMcpApprovalPolicy(sessionId, serverId, options = {}) {
|
|
1962
1472
|
const { client, workspaceId } = useEmbeddedSessionMcpApprovalPolicy(options);
|
|
1963
1473
|
const enabled = (options.enabled ?? true) && Boolean(sessionId && serverId);
|
|
1964
|
-
const [override, setOverride] =
|
|
1474
|
+
const [override, setOverride] = useState6(null);
|
|
1965
1475
|
const targetKey = `${workspaceId}\0${sessionId ?? ""}\0${serverId ?? ""}`;
|
|
1966
|
-
const authoritativeGeneration =
|
|
1967
|
-
const mutationIdentity =
|
|
1476
|
+
const authoritativeGeneration = useRef6(0);
|
|
1477
|
+
const mutationIdentity = useMemo4(() => ({ client, targetKey }), [client, targetKey]);
|
|
1968
1478
|
const { run, mutating, mutationError, clearMutationError } = useMutationRunner(mutationIdentity);
|
|
1969
1479
|
useLayoutEffect3(() => {
|
|
1970
1480
|
authoritativeGeneration.current += 1;
|
|
1971
1481
|
setOverride(null);
|
|
1972
1482
|
}, [client, targetKey]);
|
|
1973
|
-
const load =
|
|
1483
|
+
const load = useCallback7(async () => {
|
|
1974
1484
|
const generation = authoritativeGeneration.current;
|
|
1975
1485
|
if (!sessionId || !serverId) {
|
|
1976
1486
|
return { targetKey, generation, server: null };
|
|
@@ -1991,13 +1501,13 @@ function useSessionMcpApprovalPolicy(sessionId, serverId, options = {}) {
|
|
|
1991
1501
|
enabled,
|
|
1992
1502
|
pollIntervalMs: options.pollIntervalMs
|
|
1993
1503
|
});
|
|
1994
|
-
|
|
1504
|
+
useEffect5(() => {
|
|
1995
1505
|
if (data?.targetKey === targetKey && data.generation === authoritativeGeneration.current) {
|
|
1996
1506
|
setOverride(null);
|
|
1997
1507
|
}
|
|
1998
1508
|
}, [data, targetKey]);
|
|
1999
1509
|
const scheduleRefresh = useDebouncedCallback(() => void refresh());
|
|
2000
|
-
const matchesPolicyEvent =
|
|
1510
|
+
const matchesPolicyEvent = useCallback7(
|
|
2001
1511
|
(event) => isSessionMcpApprovalPolicyEvent(event, serverId ?? void 0),
|
|
2002
1512
|
[serverId]
|
|
2003
1513
|
);
|
|
@@ -2013,7 +1523,7 @@ function useSessionMcpApprovalPolicy(sessionId, serverId, options = {}) {
|
|
|
2013
1523
|
},
|
|
2014
1524
|
async () => await refresh()
|
|
2015
1525
|
);
|
|
2016
|
-
const update =
|
|
1526
|
+
const update = useCallback7(
|
|
2017
1527
|
async (policy) => {
|
|
2018
1528
|
if (!sessionId || !serverId) {
|
|
2019
1529
|
return null;
|
|
@@ -2047,6 +1557,91 @@ function useSessionMcpApprovalPolicy(sessionId, serverId, options = {}) {
|
|
|
2047
1557
|
};
|
|
2048
1558
|
}
|
|
2049
1559
|
|
|
1560
|
+
// src/hooks/use-session-lineage.ts
|
|
1561
|
+
import { useCallback as useCallback8, useEffect as useEffect6, useRef as useRef7 } from "react";
|
|
1562
|
+
function isLineageRefreshEvent(event) {
|
|
1563
|
+
if (event.type === "session.status.changed" || event.type === "session.created") {
|
|
1564
|
+
return true;
|
|
1565
|
+
}
|
|
1566
|
+
if (event.type === "agent.toolCall.output") {
|
|
1567
|
+
const payload = event.payload;
|
|
1568
|
+
const name = typeof payload?.name === "string" ? payload.name : typeof payload?.toolName === "string" ? payload.toolName : "";
|
|
1569
|
+
return name === "session_create" || name.endsWith("__session_create");
|
|
1570
|
+
}
|
|
1571
|
+
return false;
|
|
1572
|
+
}
|
|
1573
|
+
function isSessionCreateToolCallCreated(event) {
|
|
1574
|
+
if (event.type !== "agent.toolCall.created") {
|
|
1575
|
+
return false;
|
|
1576
|
+
}
|
|
1577
|
+
const payload = event.payload;
|
|
1578
|
+
const name = typeof payload?.name === "string" ? payload.name : typeof payload?.toolName === "string" ? payload.toolName : "";
|
|
1579
|
+
return name === "session_create" || name.endsWith("__session_create");
|
|
1580
|
+
}
|
|
1581
|
+
function useSessionLineage(sessionId, options = {}) {
|
|
1582
|
+
const { client, workspaceId, workspaceControlEvent, registerSessionReconciler } = useOpenGeni(options);
|
|
1583
|
+
const enabled = (options.enabled ?? true) && Boolean(sessionId);
|
|
1584
|
+
const load = useCallback8(
|
|
1585
|
+
async () => sessionId ? await client.getSessionLineage(workspaceId, sessionId) : { ancestors: [], children: [], truncated: false },
|
|
1586
|
+
[client, workspaceId, sessionId]
|
|
1587
|
+
);
|
|
1588
|
+
const state = usePolledValue(load, { pollIntervalMs: options.pollIntervalMs, enabled });
|
|
1589
|
+
const refresh = state.refresh;
|
|
1590
|
+
useEffect6(() => {
|
|
1591
|
+
if (enabled && workspaceControlEvent) void refresh();
|
|
1592
|
+
}, [enabled, refresh, workspaceControlEvent]);
|
|
1593
|
+
useEffect6(() => {
|
|
1594
|
+
if (!sessionId || !enabled) return;
|
|
1595
|
+
return registerSessionReconciler(sessionId, "lineage", refresh);
|
|
1596
|
+
}, [enabled, refresh, registerSessionReconciler, sessionId]);
|
|
1597
|
+
const refreshSoon = useDebouncedCallback(() => void refresh(), 150);
|
|
1598
|
+
const delayedChildRefreshRef = useRef7(null);
|
|
1599
|
+
useEffect6(() => {
|
|
1600
|
+
return () => {
|
|
1601
|
+
if (delayedChildRefreshRef.current !== null) {
|
|
1602
|
+
clearTimeout(delayedChildRefreshRef.current);
|
|
1603
|
+
delayedChildRefreshRef.current = null;
|
|
1604
|
+
}
|
|
1605
|
+
};
|
|
1606
|
+
}, [sessionId, workspaceId]);
|
|
1607
|
+
const refreshAfterChildCreate = useCallback8(() => {
|
|
1608
|
+
void refresh();
|
|
1609
|
+
if (delayedChildRefreshRef.current !== null) {
|
|
1610
|
+
clearTimeout(delayedChildRefreshRef.current);
|
|
1611
|
+
}
|
|
1612
|
+
delayedChildRefreshRef.current = setTimeout(() => {
|
|
1613
|
+
delayedChildRefreshRef.current = null;
|
|
1614
|
+
void refresh();
|
|
1615
|
+
}, 2500);
|
|
1616
|
+
}, [refresh]);
|
|
1617
|
+
useSessionEventTrigger(
|
|
1618
|
+
client,
|
|
1619
|
+
workspaceId,
|
|
1620
|
+
sessionId,
|
|
1621
|
+
isLineageRefreshEvent,
|
|
1622
|
+
refreshSoon,
|
|
1623
|
+
// SHARED-FEED ONLY: without a caller-provided events log the trigger would
|
|
1624
|
+
// open its OWN streamEvents tail — a second live SSE connection next to the
|
|
1625
|
+
// session route's useSessionEvents. A caller with no feed opts into polling
|
|
1626
|
+
// (pollIntervalMs), never a duplicate stream.
|
|
1627
|
+
{ events: options.events, enabled: enabled && options.events !== void 0 }
|
|
1628
|
+
);
|
|
1629
|
+
useSessionEventTrigger(
|
|
1630
|
+
client,
|
|
1631
|
+
workspaceId,
|
|
1632
|
+
sessionId,
|
|
1633
|
+
isSessionCreateToolCallCreated,
|
|
1634
|
+
refreshAfterChildCreate,
|
|
1635
|
+
{ events: options.events, enabled: enabled && options.events !== void 0 }
|
|
1636
|
+
);
|
|
1637
|
+
return {
|
|
1638
|
+
lineage: state.data,
|
|
1639
|
+
loading: state.loading,
|
|
1640
|
+
error: state.error,
|
|
1641
|
+
refresh
|
|
1642
|
+
};
|
|
1643
|
+
}
|
|
1644
|
+
|
|
2050
1645
|
// src/approvals.ts
|
|
2051
1646
|
function approvalsFromRequiresAction(payload) {
|
|
2052
1647
|
const approvals = payload && typeof payload === "object" ? payload.approvals : void 0;
|
|
@@ -2141,14 +1736,14 @@ function isRecord(value) {
|
|
|
2141
1736
|
}
|
|
2142
1737
|
|
|
2143
1738
|
// src/hooks/use-human-input.ts
|
|
2144
|
-
import { useCallback as
|
|
1739
|
+
import { useCallback as useCallback9, useEffect as useEffect7, useRef as useRef8, useState as useState7 } from "react";
|
|
2145
1740
|
function isHumanInputEvent(event) {
|
|
2146
1741
|
return event.type === "session.humanInput.requested" || event.type === "user.humanInputResponse" || event.type === "turn.completed" || event.type === "turn.failed" || event.type === "turn.cancelled";
|
|
2147
1742
|
}
|
|
2148
1743
|
function useHumanInputRequests(sessionId, options = {}) {
|
|
2149
1744
|
const { client, workspaceId, registerSessionReconciler } = useEmbeddedHumanInputSession(options);
|
|
2150
1745
|
const enabled = (options.enabled ?? true) && Boolean(sessionId);
|
|
2151
|
-
const load =
|
|
1746
|
+
const load = useCallback9(
|
|
2152
1747
|
async () => sessionId ? await client.listHumanInputRequests(workspaceId, sessionId, { status: "pending" }) : [],
|
|
2153
1748
|
[client, sessionId, workspaceId]
|
|
2154
1749
|
);
|
|
@@ -2157,13 +1752,13 @@ function useHumanInputRequests(sessionId, options = {}) {
|
|
|
2157
1752
|
pollIntervalMs: options.pollIntervalMs
|
|
2158
1753
|
});
|
|
2159
1754
|
const mutation = useMutationRunner(`${workspaceId}\0${sessionId ?? ""}`);
|
|
2160
|
-
const [respondingRequestId, setRespondingRequestId] =
|
|
2161
|
-
const respondingRequestRef =
|
|
2162
|
-
|
|
1755
|
+
const [respondingRequestId, setRespondingRequestId] = useState7(null);
|
|
1756
|
+
const respondingRequestRef = useRef8(null);
|
|
1757
|
+
useEffect7(() => {
|
|
2163
1758
|
respondingRequestRef.current = null;
|
|
2164
1759
|
setRespondingRequestId(null);
|
|
2165
1760
|
}, [sessionId, workspaceId]);
|
|
2166
|
-
|
|
1761
|
+
useEffect7(() => {
|
|
2167
1762
|
if (!sessionId || !enabled) return;
|
|
2168
1763
|
return registerSessionReconciler(sessionId, "human-input", state.refresh);
|
|
2169
1764
|
}, [enabled, registerSessionReconciler, sessionId, state.refresh]);
|
|
@@ -2172,7 +1767,7 @@ function useHumanInputRequests(sessionId, options = {}) {
|
|
|
2172
1767
|
enabled,
|
|
2173
1768
|
...options.events !== void 0 ? { events: options.events } : {}
|
|
2174
1769
|
});
|
|
2175
|
-
const respond =
|
|
1770
|
+
const respond = useCallback9(
|
|
2176
1771
|
async (requestId, response) => {
|
|
2177
1772
|
if (!sessionId || respondingRequestRef.current !== null) return null;
|
|
2178
1773
|
respondingRequestRef.current = requestId;
|
|
@@ -2209,12 +1804,8 @@ function useHumanInputRequests(sessionId, options = {}) {
|
|
|
2209
1804
|
}
|
|
2210
1805
|
|
|
2211
1806
|
export {
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
sessionStatusFromEvents,
|
|
2215
|
-
groupTimeline,
|
|
2216
|
-
extractSessionRef,
|
|
2217
|
-
toolDisplayName,
|
|
1807
|
+
isTitleEvent,
|
|
1808
|
+
useSession,
|
|
2218
1809
|
SESSION_EVENT_BROWSER_MAX_BYTES,
|
|
2219
1810
|
SESSION_EVENT_BROWSER_MAX_COUNT,
|
|
2220
1811
|
SESSION_EVENT_BROWSER_SINGLE_EVENT_MAX_BYTES,
|
|
@@ -2222,11 +1813,16 @@ export {
|
|
|
2222
1813
|
SESSION_EVENT_BROWSER_PENDING_MAX_COUNT,
|
|
2223
1814
|
useSessionEvents,
|
|
2224
1815
|
boundBrowserSessionEventWindow,
|
|
1816
|
+
useFileAttachments,
|
|
2225
1817
|
isTurnQueueEvent,
|
|
2226
1818
|
useTurnQueue,
|
|
1819
|
+
isGoalEvent,
|
|
1820
|
+
useGoal,
|
|
2227
1821
|
useSessionControl,
|
|
2228
1822
|
isSessionMcpApprovalPolicyEvent,
|
|
2229
1823
|
useSessionMcpApprovalPolicy,
|
|
1824
|
+
isLineageRefreshEvent,
|
|
1825
|
+
useSessionLineage,
|
|
2230
1826
|
approvalsFromRequiresAction,
|
|
2231
1827
|
projectPendingApprovals,
|
|
2232
1828
|
humanInputRequestFromEvent,
|
|
@@ -2234,4 +1830,4 @@ export {
|
|
|
2234
1830
|
isHumanInputEvent,
|
|
2235
1831
|
useHumanInputRequests
|
|
2236
1832
|
};
|
|
2237
|
-
//# sourceMappingURL=chunk-
|
|
1833
|
+
//# sourceMappingURL=chunk-MP6237JB.js.map
|