@opengeni/react 3.7.0-canary.0 → 3.7.0-canary.1
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/artifacts.js +24 -5
- package/dist/artifacts.js.map +1 -1
- package/dist/{browser-viewer-E5DDEFQ4.js → browser-viewer-NTD6UIPR.js} +3 -3
- package/dist/{chunk-W2RGYBCV.js → chunk-24M4YBCL.js} +4 -4
- package/dist/{chunk-DOK4KZF2.js → chunk-5IXTJTK3.js} +1322 -392
- package/dist/chunk-5IXTJTK3.js.map +1 -0
- package/dist/chunk-5PQJOCX5.js +902 -0
- package/dist/chunk-5PQJOCX5.js.map +1 -0
- package/dist/{chunk-R55HFTT3.js → chunk-INGB2N5R.js} +4305 -4019
- package/dist/chunk-INGB2N5R.js.map +1 -0
- package/dist/{chunk-FJBOU2TH.js → chunk-MOWYEBCQ.js} +1728 -12
- package/dist/chunk-MOWYEBCQ.js.map +1 -0
- package/dist/chunk-PAJU5KKL.js +331 -0
- package/dist/chunk-PAJU5KKL.js.map +1 -0
- package/dist/{chunk-VTOREEXF.js → chunk-PLYRL5ER.js} +24 -2
- package/dist/{chunk-VTOREEXF.js.map → chunk-PLYRL5ER.js.map} +1 -1
- package/dist/{chunk-YVCHAQZY.js → chunk-ZFOFY5ST.js} +27 -349
- package/dist/chunk-ZFOFY5ST.js.map +1 -0
- package/dist/components/artifacts/published-html-artifact-frame.d.ts +3 -2
- package/dist/components/session-chrome.d.ts +37 -4
- package/dist/components/session-commands-panel.d.ts +6 -0
- package/dist/components/session-conversation.d.ts +14 -0
- package/dist/components/timeline-anchor.d.ts +21 -0
- package/dist/composer.js +2 -3
- package/dist/conversation-timeline.d.ts +5 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +716 -914
- package/dist/index.js.map +1 -1
- package/dist/interaction.js +2 -2
- package/dist/lib/format.d.ts +5 -0
- package/dist/machines.js +3 -2
- package/dist/session-ui.d.ts +3 -0
- package/dist/session-ui.js +14 -4
- package/dist/session.js +19 -19
- package/dist/timeline/types.d.ts +3 -1
- package/package.json +2 -2
- package/src/components/artifacts/published-html-artifact-frame.tsx +27 -4
- package/src/components/machine-input-display.ts +6 -0
- package/src/components/message-timeline.tsx +320 -197
- package/src/components/session-chrome.tsx +498 -125
- package/src/components/session-commands-panel.tsx +113 -0
- package/src/components/session-conversation.tsx +141 -0
- package/src/components/timeline-anchor.tsx +91 -0
- package/src/conversation-timeline.ts +83 -0
- package/src/hooks/use-session-background-commands.ts +21 -10
- package/src/hooks/use-session-events.ts +36 -14
- package/src/hooks/use-session.ts +23 -2
- package/src/index.ts +4 -0
- package/src/lib/format.ts +32 -0
- package/src/session-ui.ts +3 -0
- package/src/timeline/projection.ts +6 -12
- package/src/timeline/types.ts +4 -0
- package/styles/compiled.css +495 -411
- package/styles/index.css +23 -0
- package/dist/chunk-7EAFGICR.js +0 -302
- package/dist/chunk-7EAFGICR.js.map +0 -1
- package/dist/chunk-C6WDLHBA.js +0 -2590
- package/dist/chunk-C6WDLHBA.js.map +0 -1
- package/dist/chunk-DOK4KZF2.js.map +0 -1
- package/dist/chunk-FJBOU2TH.js.map +0 -1
- package/dist/chunk-R55HFTT3.js.map +0 -1
- package/dist/chunk-YVCHAQZY.js.map +0 -1
- /package/dist/{browser-viewer-E5DDEFQ4.js.map → browser-viewer-NTD6UIPR.js.map} +0 -0
- /package/dist/{chunk-W2RGYBCV.js.map → chunk-24M4YBCL.js.map} +0 -0
|
@@ -0,0 +1,902 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useDebouncedCallback,
|
|
3
|
+
useMutationRunner,
|
|
4
|
+
usePageLiveActivity,
|
|
5
|
+
usePolledValue,
|
|
6
|
+
useSessionEventTrigger
|
|
7
|
+
} from "./chunk-KZ73RL76.js";
|
|
8
|
+
import {
|
|
9
|
+
useEmbeddedFileAttachments,
|
|
10
|
+
useEmbeddedGoal,
|
|
11
|
+
useEmbeddedSession,
|
|
12
|
+
useEmbeddedSessionLineage,
|
|
13
|
+
useEmbeddedSessionMcpApprovalPolicy
|
|
14
|
+
} from "./chunk-NEXLLTP4.js";
|
|
15
|
+
|
|
16
|
+
// src/embedded-session-client.ts
|
|
17
|
+
var requiredMethods = [
|
|
18
|
+
"getSession",
|
|
19
|
+
"listEvents",
|
|
20
|
+
"streamEvents",
|
|
21
|
+
"getComposerDraft",
|
|
22
|
+
"saveComposerDraft",
|
|
23
|
+
"submitComposerDraft",
|
|
24
|
+
"sendMessage",
|
|
25
|
+
"steerMessage",
|
|
26
|
+
"getQueue",
|
|
27
|
+
"moveQueueItem",
|
|
28
|
+
"editQueueItem",
|
|
29
|
+
"steerQueueItem",
|
|
30
|
+
"deleteQueueItem",
|
|
31
|
+
"pauseSession",
|
|
32
|
+
"resumeSession",
|
|
33
|
+
"sendApprovalDecision"
|
|
34
|
+
];
|
|
35
|
+
function createEmbeddedSessionClient(base, options = {}) {
|
|
36
|
+
const overrides = options.overrides ?? {};
|
|
37
|
+
const client = {};
|
|
38
|
+
for (const key of requiredMethods) {
|
|
39
|
+
const override = overrides[key];
|
|
40
|
+
const method = override ?? base[key];
|
|
41
|
+
if (typeof method !== "function") {
|
|
42
|
+
throw new TypeError(`Embedded session client method is required: ${key}`);
|
|
43
|
+
}
|
|
44
|
+
client[key] = method.bind(override === void 0 ? base : overrides);
|
|
45
|
+
}
|
|
46
|
+
const inferenceOverride = overrides.setWorkspaceInferenceState;
|
|
47
|
+
const inferenceBase = base.setWorkspaceInferenceState;
|
|
48
|
+
const inferenceMethod = inferenceOverride ?? inferenceBase;
|
|
49
|
+
if (typeof inferenceMethod === "function") {
|
|
50
|
+
client.setWorkspaceInferenceState = inferenceMethod.bind(
|
|
51
|
+
inferenceOverride === void 0 ? base : overrides
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
const mapDraft = options.mapComposerDraft;
|
|
55
|
+
if (!mapDraft) return client;
|
|
56
|
+
const getComposerDraft = client.getComposerDraft;
|
|
57
|
+
client.getComposerDraft = async (workspaceId, sessionId) => mapDraft(await getComposerDraft(workspaceId, sessionId), {
|
|
58
|
+
operation: "read",
|
|
59
|
+
workspaceId,
|
|
60
|
+
sessionId
|
|
61
|
+
});
|
|
62
|
+
const saveComposerDraft = client.saveComposerDraft;
|
|
63
|
+
client.saveComposerDraft = async (workspaceId, sessionId, request) => mapDraft(await saveComposerDraft(workspaceId, sessionId, request), {
|
|
64
|
+
operation: "save",
|
|
65
|
+
workspaceId,
|
|
66
|
+
sessionId
|
|
67
|
+
});
|
|
68
|
+
const submitComposerDraft = client.submitComposerDraft;
|
|
69
|
+
client.submitComposerDraft = async (workspaceId, sessionId, request) => {
|
|
70
|
+
const response = await submitComposerDraft(workspaceId, sessionId, request);
|
|
71
|
+
return {
|
|
72
|
+
...response,
|
|
73
|
+
draft: mapDraft(response.draft, {
|
|
74
|
+
operation: "submit",
|
|
75
|
+
workspaceId,
|
|
76
|
+
sessionId
|
|
77
|
+
})
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
return client;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/hooks/use-file-attachments.ts
|
|
84
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
85
|
+
var isImage = (file) => file.type.startsWith("image/");
|
|
86
|
+
var fallbackAttachmentId = 0;
|
|
87
|
+
function createAttachmentId() {
|
|
88
|
+
const cryptoSource = globalThis.crypto;
|
|
89
|
+
if (typeof cryptoSource?.randomUUID === "function") return cryptoSource.randomUUID();
|
|
90
|
+
fallbackAttachmentId += 1;
|
|
91
|
+
return `attachment:${Date.now().toString(36)}:${fallbackAttachmentId.toString(36)}`;
|
|
92
|
+
}
|
|
93
|
+
function secureContextRequiredErrorCode(error) {
|
|
94
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "secure_context_required" ? error.code : void 0;
|
|
95
|
+
}
|
|
96
|
+
function useFileAttachments(options = {}) {
|
|
97
|
+
const { client, workspaceId } = useEmbeddedFileAttachments(options);
|
|
98
|
+
const pasteFilter = options.pasteFilter ?? isImage;
|
|
99
|
+
const [attachments, setAttachments] = useState([]);
|
|
100
|
+
const attachmentsRef = useRef(attachments);
|
|
101
|
+
attachmentsRef.current = attachments;
|
|
102
|
+
const sources = useRef(/* @__PURE__ */ new Map());
|
|
103
|
+
const previewUrls = useRef(/* @__PURE__ */ new Map());
|
|
104
|
+
const previewRetainers = useRef(/* @__PURE__ */ new Map());
|
|
105
|
+
const pendingPreviewRevocations = useRef(/* @__PURE__ */ new Set());
|
|
106
|
+
const revokePreview = useCallback((id) => {
|
|
107
|
+
const previewUrl = previewUrls.current.get(id);
|
|
108
|
+
if (!previewUrl) return;
|
|
109
|
+
if ((previewRetainers.current.get(id) ?? 0) > 0) {
|
|
110
|
+
pendingPreviewRevocations.current.add(id);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
previewUrls.current.delete(id);
|
|
114
|
+
pendingPreviewRevocations.current.delete(id);
|
|
115
|
+
URL.revokeObjectURL(previewUrl);
|
|
116
|
+
}, []);
|
|
117
|
+
const revokeAllPreviews = useCallback(() => {
|
|
118
|
+
for (const id of previewUrls.current.keys()) revokePreview(id);
|
|
119
|
+
}, [revokePreview]);
|
|
120
|
+
const retainPreview = useCallback(
|
|
121
|
+
(id) => {
|
|
122
|
+
if (!previewUrls.current.has(id)) return void 0;
|
|
123
|
+
previewRetainers.current.set(id, (previewRetainers.current.get(id) ?? 0) + 1);
|
|
124
|
+
let released = false;
|
|
125
|
+
return () => {
|
|
126
|
+
if (released) return;
|
|
127
|
+
released = true;
|
|
128
|
+
const remaining = (previewRetainers.current.get(id) ?? 1) - 1;
|
|
129
|
+
if (remaining > 0) {
|
|
130
|
+
previewRetainers.current.set(id, remaining);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
previewRetainers.current.delete(id);
|
|
134
|
+
if (pendingPreviewRevocations.current.has(id)) revokePreview(id);
|
|
135
|
+
};
|
|
136
|
+
},
|
|
137
|
+
[revokePreview]
|
|
138
|
+
);
|
|
139
|
+
const committedAttachmentIds = useRef(/* @__PURE__ */ new Set());
|
|
140
|
+
useEffect(() => {
|
|
141
|
+
const currentIds = new Set(attachments.map((attachment) => attachment.id));
|
|
142
|
+
for (const id of committedAttachmentIds.current) {
|
|
143
|
+
if (!currentIds.has(id)) revokePreview(id);
|
|
144
|
+
}
|
|
145
|
+
committedAttachmentIds.current = currentIds;
|
|
146
|
+
}, [attachments, revokePreview]);
|
|
147
|
+
const scopeGeneration = useRef(0);
|
|
148
|
+
const previousScope = useRef({ client, workspaceId });
|
|
149
|
+
useEffect(() => {
|
|
150
|
+
const previous = previousScope.current;
|
|
151
|
+
if (previous.client === client && previous.workspaceId === workspaceId) return;
|
|
152
|
+
previousScope.current = { client, workspaceId };
|
|
153
|
+
scopeGeneration.current += 1;
|
|
154
|
+
sources.current.clear();
|
|
155
|
+
revokeAllPreviews();
|
|
156
|
+
setAttachments([]);
|
|
157
|
+
}, [client, revokeAllPreviews, workspaceId]);
|
|
158
|
+
useEffect(
|
|
159
|
+
() => () => {
|
|
160
|
+
scopeGeneration.current += 1;
|
|
161
|
+
sources.current.clear();
|
|
162
|
+
revokeAllPreviews();
|
|
163
|
+
},
|
|
164
|
+
[revokeAllPreviews]
|
|
165
|
+
);
|
|
166
|
+
const startUpload = useCallback(
|
|
167
|
+
(id, file) => {
|
|
168
|
+
const generation = scopeGeneration.current;
|
|
169
|
+
void client.uploadFile(workspaceId, {
|
|
170
|
+
filename: file.name || "file",
|
|
171
|
+
contentType: file.type || "application/octet-stream",
|
|
172
|
+
data: file
|
|
173
|
+
}).then((asset) => {
|
|
174
|
+
if (scopeGeneration.current !== generation) return;
|
|
175
|
+
sources.current.delete(id);
|
|
176
|
+
setAttachments(
|
|
177
|
+
(current) => current.map(
|
|
178
|
+
(attachment) => attachment.id === id ? {
|
|
179
|
+
...attachment,
|
|
180
|
+
status: "ready",
|
|
181
|
+
file: asset,
|
|
182
|
+
name: asset.filename,
|
|
183
|
+
contentType: asset.contentType,
|
|
184
|
+
sizeBytes: asset.sizeBytes,
|
|
185
|
+
errorCode: void 0,
|
|
186
|
+
error: void 0
|
|
187
|
+
} : attachment
|
|
188
|
+
)
|
|
189
|
+
);
|
|
190
|
+
}).catch((error) => {
|
|
191
|
+
if (scopeGeneration.current !== generation) return;
|
|
192
|
+
setAttachments(
|
|
193
|
+
(current) => current.map(
|
|
194
|
+
(attachment) => attachment.id === id ? {
|
|
195
|
+
...attachment,
|
|
196
|
+
status: "failed",
|
|
197
|
+
errorCode: secureContextRequiredErrorCode(error),
|
|
198
|
+
error: error instanceof Error ? error.message : String(error)
|
|
199
|
+
} : attachment
|
|
200
|
+
)
|
|
201
|
+
);
|
|
202
|
+
});
|
|
203
|
+
},
|
|
204
|
+
[client, workspaceId]
|
|
205
|
+
);
|
|
206
|
+
const addFiles = useCallback(
|
|
207
|
+
(files) => {
|
|
208
|
+
for (const file of files) {
|
|
209
|
+
const id = createAttachmentId();
|
|
210
|
+
sources.current.set(id, file);
|
|
211
|
+
const previewUrl = isImage(file) ? URL.createObjectURL(file) : void 0;
|
|
212
|
+
if (previewUrl) previewUrls.current.set(id, previewUrl);
|
|
213
|
+
setAttachments((current) => [
|
|
214
|
+
...current,
|
|
215
|
+
{
|
|
216
|
+
id,
|
|
217
|
+
name: file.name || "image",
|
|
218
|
+
contentType: file.type || "application/octet-stream",
|
|
219
|
+
sizeBytes: file.size,
|
|
220
|
+
status: "uploading",
|
|
221
|
+
...previewUrl ? { previewUrl } : {}
|
|
222
|
+
}
|
|
223
|
+
]);
|
|
224
|
+
startUpload(id, file);
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
[startUpload]
|
|
228
|
+
);
|
|
229
|
+
const retry = useCallback(
|
|
230
|
+
(id) => {
|
|
231
|
+
const file = sources.current.get(id);
|
|
232
|
+
if (!file) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
setAttachments(
|
|
236
|
+
(current) => current.map(
|
|
237
|
+
(attachment) => attachment.id === id ? { ...attachment, status: "uploading", errorCode: void 0, error: void 0 } : attachment
|
|
238
|
+
)
|
|
239
|
+
);
|
|
240
|
+
startUpload(id, file);
|
|
241
|
+
},
|
|
242
|
+
[startUpload]
|
|
243
|
+
);
|
|
244
|
+
const addFromPaste = useCallback(
|
|
245
|
+
(event) => {
|
|
246
|
+
const clipboardFiles = event.clipboardData?.files;
|
|
247
|
+
if (!clipboardFiles) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const files = [...clipboardFiles].filter(pasteFilter);
|
|
251
|
+
if (files.length > 0) {
|
|
252
|
+
addFiles(files);
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
[addFiles, pasteFilter]
|
|
256
|
+
);
|
|
257
|
+
const restoreReadyFiles = useCallback(
|
|
258
|
+
(files) => {
|
|
259
|
+
const incoming = /* @__PURE__ */ new Map();
|
|
260
|
+
for (const file of files) {
|
|
261
|
+
if (file.status === "ready" && file.workspaceId === workspaceId) {
|
|
262
|
+
incoming.set(file.id, file);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
setAttachments((current) => {
|
|
266
|
+
const unresolved = current.filter((attachment) => attachment.status !== "ready");
|
|
267
|
+
const existingReady = new Map(
|
|
268
|
+
current.flatMap(
|
|
269
|
+
(attachment) => attachment.status === "ready" && attachment.file ? [[attachment.file.id, attachment]] : []
|
|
270
|
+
)
|
|
271
|
+
);
|
|
272
|
+
const restored = [...incoming.values()].map((file) => {
|
|
273
|
+
const existing = existingReady.get(file.id);
|
|
274
|
+
return existing ? {
|
|
275
|
+
...existing,
|
|
276
|
+
name: file.filename,
|
|
277
|
+
contentType: file.contentType,
|
|
278
|
+
sizeBytes: file.sizeBytes,
|
|
279
|
+
status: "ready",
|
|
280
|
+
file,
|
|
281
|
+
errorCode: void 0,
|
|
282
|
+
error: void 0
|
|
283
|
+
} : {
|
|
284
|
+
id: `restored:${file.id}`,
|
|
285
|
+
name: file.filename,
|
|
286
|
+
contentType: file.contentType,
|
|
287
|
+
sizeBytes: file.sizeBytes,
|
|
288
|
+
status: "ready",
|
|
289
|
+
file
|
|
290
|
+
// No source File and no object URL: server metadata is the
|
|
291
|
+
// only authority restored across page/device boundaries.
|
|
292
|
+
};
|
|
293
|
+
});
|
|
294
|
+
return [...unresolved, ...restored];
|
|
295
|
+
});
|
|
296
|
+
},
|
|
297
|
+
[workspaceId]
|
|
298
|
+
);
|
|
299
|
+
const loadPreview = useCallback(
|
|
300
|
+
async (id, signal) => {
|
|
301
|
+
const generation = scopeGeneration.current;
|
|
302
|
+
const attachment = attachmentsRef.current.find((candidate) => candidate.id === id);
|
|
303
|
+
const createDownloadUrl = client.createFileDownloadUrl;
|
|
304
|
+
if (!attachment || attachment.status !== "ready" || !attachment.file || !attachment.contentType.startsWith("image/") || typeof createDownloadUrl !== "function" || signal?.aborted) {
|
|
305
|
+
return void 0;
|
|
306
|
+
}
|
|
307
|
+
const fileId = attachment.file.id;
|
|
308
|
+
const signed = await createDownloadUrl.call(client, workspaceId, fileId, { signal });
|
|
309
|
+
if (scopeGeneration.current !== generation || signal?.aborted) return void 0;
|
|
310
|
+
const current = attachmentsRef.current.find((candidate) => candidate.id === id);
|
|
311
|
+
if (current?.status !== "ready" || current.file?.id !== fileId) return void 0;
|
|
312
|
+
return signed.url;
|
|
313
|
+
},
|
|
314
|
+
[client, workspaceId]
|
|
315
|
+
);
|
|
316
|
+
const remove = useCallback(
|
|
317
|
+
(id) => {
|
|
318
|
+
sources.current.delete(id);
|
|
319
|
+
revokePreview(id);
|
|
320
|
+
setAttachments((current) => current.filter((attachment) => attachment.id !== id));
|
|
321
|
+
},
|
|
322
|
+
[revokePreview]
|
|
323
|
+
);
|
|
324
|
+
const removeReadyFiles = useCallback((fileIds) => {
|
|
325
|
+
const accepted = new Set(fileIds);
|
|
326
|
+
if (accepted.size === 0) return;
|
|
327
|
+
setAttachments(
|
|
328
|
+
(current) => current.filter((attachment) => {
|
|
329
|
+
return !(attachment.status === "ready" && attachment.file !== void 0 && accepted.has(attachment.file.id));
|
|
330
|
+
})
|
|
331
|
+
);
|
|
332
|
+
}, []);
|
|
333
|
+
const clear = useCallback(() => {
|
|
334
|
+
sources.current.clear();
|
|
335
|
+
revokeAllPreviews();
|
|
336
|
+
setAttachments([]);
|
|
337
|
+
}, [revokeAllPreviews]);
|
|
338
|
+
return {
|
|
339
|
+
attachments,
|
|
340
|
+
readyResources: attachments.flatMap(
|
|
341
|
+
(attachment) => attachment.status === "ready" && attachment.file ? [{ kind: "file", fileId: attachment.file.id }] : []
|
|
342
|
+
),
|
|
343
|
+
uploading: attachments.some((attachment) => attachment.status === "uploading"),
|
|
344
|
+
hasUnresolved: attachments.some((attachment) => attachment.status !== "ready"),
|
|
345
|
+
addFiles,
|
|
346
|
+
addFromPaste,
|
|
347
|
+
restoreReadyFiles,
|
|
348
|
+
...typeof client.createFileDownloadUrl === "function" ? { loadPreview } : {},
|
|
349
|
+
retry,
|
|
350
|
+
retainPreview,
|
|
351
|
+
remove,
|
|
352
|
+
removeReadyFiles,
|
|
353
|
+
clear
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// src/hooks/use-goal.ts
|
|
358
|
+
import { OpenGeniApiError } from "@opengeni/sdk";
|
|
359
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
360
|
+
function isGoalEvent(event) {
|
|
361
|
+
return event.type.startsWith("goal.");
|
|
362
|
+
}
|
|
363
|
+
function isGoalRefreshEvent(event) {
|
|
364
|
+
const type = event.type;
|
|
365
|
+
return isGoalEvent(event) || type.startsWith("turn.") || type.startsWith("session.") || type.startsWith("system.update.") || type.startsWith("workspace.inference.") || type.startsWith("user.");
|
|
366
|
+
}
|
|
367
|
+
function useGoal(sessionId, options = {}) {
|
|
368
|
+
const { client, workspaceId } = useEmbeddedGoal(options);
|
|
369
|
+
const enabled = (options.enabled ?? true) && Boolean(sessionId);
|
|
370
|
+
const sharedEvents = options.events;
|
|
371
|
+
const sharedFeed = sharedEvents !== void 0;
|
|
372
|
+
const pageLive = usePageLiveActivity();
|
|
373
|
+
const [goal, setGoal] = useState2(null);
|
|
374
|
+
const [loading, setLoading] = useState2(enabled);
|
|
375
|
+
const [error, setError] = useState2(null);
|
|
376
|
+
const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
|
|
377
|
+
const generation = useRef2(0);
|
|
378
|
+
const targetKeyRef = useRef2(null);
|
|
379
|
+
const loadAbort = useRef2(null);
|
|
380
|
+
const retireLoads = useCallback2(() => {
|
|
381
|
+
generation.current += 1;
|
|
382
|
+
loadAbort.current?.abort();
|
|
383
|
+
loadAbort.current = null;
|
|
384
|
+
}, []);
|
|
385
|
+
const load = useCallback2(async () => {
|
|
386
|
+
if (!sessionId) {
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
const ticket = ++generation.current;
|
|
390
|
+
loadAbort.current?.abort();
|
|
391
|
+
const controller = new AbortController();
|
|
392
|
+
loadAbort.current = controller;
|
|
393
|
+
try {
|
|
394
|
+
const fetched = await client.getGoal(workspaceId, sessionId, {
|
|
395
|
+
signal: controller.signal
|
|
396
|
+
});
|
|
397
|
+
if (ticket === generation.current) {
|
|
398
|
+
setGoal(fetched);
|
|
399
|
+
setError(null);
|
|
400
|
+
setLoading(false);
|
|
401
|
+
}
|
|
402
|
+
} catch (cause) {
|
|
403
|
+
if (ticket !== generation.current) {
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
if (cause instanceof OpenGeniApiError && cause.status === 404) {
|
|
407
|
+
setGoal(null);
|
|
408
|
+
setError(null);
|
|
409
|
+
} else {
|
|
410
|
+
setError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
411
|
+
}
|
|
412
|
+
setLoading(false);
|
|
413
|
+
} finally {
|
|
414
|
+
if (loadAbort.current === controller) loadAbort.current = null;
|
|
415
|
+
}
|
|
416
|
+
}, [client, workspaceId, sessionId]);
|
|
417
|
+
useEffect2(() => {
|
|
418
|
+
const targetKey = `${workspaceId} ${sessionId ?? ""}`;
|
|
419
|
+
if (targetKeyRef.current !== targetKey) {
|
|
420
|
+
targetKeyRef.current = targetKey;
|
|
421
|
+
setGoal(null);
|
|
422
|
+
setError(null);
|
|
423
|
+
}
|
|
424
|
+
if (!enabled || !pageLive) {
|
|
425
|
+
retireLoads();
|
|
426
|
+
setLoading(false);
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
if (sharedFeed) {
|
|
430
|
+
setLoading(false);
|
|
431
|
+
return () => {
|
|
432
|
+
retireLoads();
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
setLoading(true);
|
|
436
|
+
const pollIntervalMs = options.pollIntervalMs;
|
|
437
|
+
if (pollIntervalMs === void 0 || pollIntervalMs <= 0) {
|
|
438
|
+
void load();
|
|
439
|
+
return () => {
|
|
440
|
+
retireLoads();
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
let cancelled = false;
|
|
444
|
+
let timer = null;
|
|
445
|
+
const schedule = () => {
|
|
446
|
+
if (cancelled) return;
|
|
447
|
+
timer = setTimeout(() => {
|
|
448
|
+
timer = null;
|
|
449
|
+
void load().finally(schedule);
|
|
450
|
+
}, pollIntervalMs);
|
|
451
|
+
};
|
|
452
|
+
void load().finally(schedule);
|
|
453
|
+
return () => {
|
|
454
|
+
cancelled = true;
|
|
455
|
+
if (timer !== null) clearTimeout(timer);
|
|
456
|
+
retireLoads();
|
|
457
|
+
};
|
|
458
|
+
}, [
|
|
459
|
+
load,
|
|
460
|
+
enabled,
|
|
461
|
+
pageLive,
|
|
462
|
+
workspaceId,
|
|
463
|
+
sessionId,
|
|
464
|
+
options.pollIntervalMs,
|
|
465
|
+
retireLoads,
|
|
466
|
+
sharedFeed
|
|
467
|
+
]);
|
|
468
|
+
const scheduleRefresh = useDebouncedCallback(() => void load());
|
|
469
|
+
useSessionEventTrigger(client, workspaceId, sessionId, isGoalRefreshEvent, scheduleRefresh, {
|
|
470
|
+
enabled,
|
|
471
|
+
...sharedEvents !== void 0 ? { events: sharedEvents } : {}
|
|
472
|
+
});
|
|
473
|
+
const pause = useCallback2(
|
|
474
|
+
async (rationale) => {
|
|
475
|
+
if (!sessionId) {
|
|
476
|
+
return null;
|
|
477
|
+
}
|
|
478
|
+
const result = await run(
|
|
479
|
+
() => client.updateGoal(workspaceId, sessionId, {
|
|
480
|
+
status: "paused",
|
|
481
|
+
...rationale !== void 0 ? { rationale } : {}
|
|
482
|
+
})
|
|
483
|
+
);
|
|
484
|
+
if (result) {
|
|
485
|
+
setGoal(result);
|
|
486
|
+
}
|
|
487
|
+
return result;
|
|
488
|
+
},
|
|
489
|
+
[client, workspaceId, sessionId, run]
|
|
490
|
+
);
|
|
491
|
+
const resume = useCallback2(async () => {
|
|
492
|
+
if (!sessionId) {
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
const result = await run(() => client.updateGoal(workspaceId, sessionId, { status: "active" }));
|
|
496
|
+
if (result) {
|
|
497
|
+
setGoal(result);
|
|
498
|
+
}
|
|
499
|
+
return result;
|
|
500
|
+
}, [client, workspaceId, sessionId, run]);
|
|
501
|
+
const clearGoal = useCallback2(async () => {
|
|
502
|
+
if (!sessionId) {
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
const ok = await run(async () => {
|
|
506
|
+
await client.deleteGoal(workspaceId, sessionId);
|
|
507
|
+
return true;
|
|
508
|
+
});
|
|
509
|
+
if (ok) {
|
|
510
|
+
retireLoads();
|
|
511
|
+
setGoal(null);
|
|
512
|
+
setError(null);
|
|
513
|
+
}
|
|
514
|
+
}, [client, workspaceId, sessionId, run, retireLoads]);
|
|
515
|
+
return {
|
|
516
|
+
goal,
|
|
517
|
+
isActive: goal?.status === "active",
|
|
518
|
+
isPaused: goal?.status === "paused",
|
|
519
|
+
isCompleted: goal?.status === "completed",
|
|
520
|
+
loading,
|
|
521
|
+
error,
|
|
522
|
+
refresh: load,
|
|
523
|
+
pause,
|
|
524
|
+
resume,
|
|
525
|
+
clearGoal,
|
|
526
|
+
deleteGoal: clearGoal,
|
|
527
|
+
updating: mutating,
|
|
528
|
+
mutationError,
|
|
529
|
+
clearMutationError
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// src/hooks/use-session-control.ts
|
|
534
|
+
import { useCallback as useCallback3, useLayoutEffect, useRef as useRef3 } from "react";
|
|
535
|
+
function useSessionControl(sessionId, options = {}) {
|
|
536
|
+
const { client, workspaceId } = useEmbeddedSession(options);
|
|
537
|
+
const approvalTargetKey = `${workspaceId}\0${sessionId ?? ""}`;
|
|
538
|
+
const pendingApproval = useRef3(null);
|
|
539
|
+
useLayoutEffect(() => {
|
|
540
|
+
if (pendingApproval.current?.targetKey !== approvalTargetKey) {
|
|
541
|
+
pendingApproval.current = null;
|
|
542
|
+
}
|
|
543
|
+
}, [approvalTargetKey]);
|
|
544
|
+
const {
|
|
545
|
+
run: runControl,
|
|
546
|
+
mutating: controlling,
|
|
547
|
+
mutationError: controlError,
|
|
548
|
+
clearMutationError: clearControlError
|
|
549
|
+
} = useMutationRunner(approvalTargetKey);
|
|
550
|
+
const {
|
|
551
|
+
run: runApproval,
|
|
552
|
+
mutating: responding,
|
|
553
|
+
mutationError: approvalError,
|
|
554
|
+
clearMutationError: clearApprovalError
|
|
555
|
+
} = useMutationRunner(approvalTargetKey);
|
|
556
|
+
const pause = useCallback3(
|
|
557
|
+
async (reason) => {
|
|
558
|
+
if (!sessionId) {
|
|
559
|
+
return null;
|
|
560
|
+
}
|
|
561
|
+
return await runControl(
|
|
562
|
+
() => client.pauseSession(workspaceId, sessionId, reason !== void 0 ? { reason } : {})
|
|
563
|
+
);
|
|
564
|
+
},
|
|
565
|
+
[client, workspaceId, sessionId, runControl]
|
|
566
|
+
);
|
|
567
|
+
const resume = useCallback3(
|
|
568
|
+
async (reason) => {
|
|
569
|
+
if (!sessionId) return null;
|
|
570
|
+
return await runControl(
|
|
571
|
+
() => client.resumeSession(workspaceId, sessionId, reason !== void 0 ? { reason } : {})
|
|
572
|
+
);
|
|
573
|
+
},
|
|
574
|
+
[client, workspaceId, sessionId, runControl]
|
|
575
|
+
);
|
|
576
|
+
const decide = useCallback3(
|
|
577
|
+
async (approvalId, decision, message) => {
|
|
578
|
+
if (!sessionId) {
|
|
579
|
+
return null;
|
|
580
|
+
}
|
|
581
|
+
const decisionKey = JSON.stringify([approvalId, decision, message ?? null]);
|
|
582
|
+
if (pendingApproval.current?.decisionKey !== decisionKey) {
|
|
583
|
+
pendingApproval.current = {
|
|
584
|
+
targetKey: approvalTargetKey,
|
|
585
|
+
decisionKey,
|
|
586
|
+
clientEventId: crypto.randomUUID()
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
const clientEventId = pendingApproval.current.clientEventId;
|
|
590
|
+
const accepted = await runApproval(
|
|
591
|
+
() => client.sendApprovalDecision(workspaceId, sessionId, {
|
|
592
|
+
approvalId,
|
|
593
|
+
decision,
|
|
594
|
+
...message !== void 0 ? { message } : {},
|
|
595
|
+
clientEventId
|
|
596
|
+
})
|
|
597
|
+
);
|
|
598
|
+
if (accepted !== null && pendingApproval.current?.targetKey === approvalTargetKey && pendingApproval.current.clientEventId === clientEventId) {
|
|
599
|
+
pendingApproval.current = null;
|
|
600
|
+
}
|
|
601
|
+
return accepted;
|
|
602
|
+
},
|
|
603
|
+
[approvalTargetKey, client, workspaceId, sessionId, runApproval]
|
|
604
|
+
);
|
|
605
|
+
const approve = useCallback3(
|
|
606
|
+
async (approvalId, message) => await decide(approvalId, "approve", message),
|
|
607
|
+
[decide]
|
|
608
|
+
);
|
|
609
|
+
const reject = useCallback3(
|
|
610
|
+
async (approvalId, message) => await decide(approvalId, "reject", message),
|
|
611
|
+
[decide]
|
|
612
|
+
);
|
|
613
|
+
const error = approvalError ?? controlError;
|
|
614
|
+
const clearError = useCallback3(() => {
|
|
615
|
+
clearControlError();
|
|
616
|
+
clearApprovalError();
|
|
617
|
+
}, [clearControlError, clearApprovalError]);
|
|
618
|
+
return {
|
|
619
|
+
pause,
|
|
620
|
+
resume,
|
|
621
|
+
controlling,
|
|
622
|
+
approve,
|
|
623
|
+
reject,
|
|
624
|
+
responding,
|
|
625
|
+
error,
|
|
626
|
+
clearError
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// src/hooks/use-session-mcp-approval-policy.ts
|
|
631
|
+
import { useCallback as useCallback4, useEffect as useEffect3, useLayoutEffect as useLayoutEffect2, useMemo, useRef as useRef4, useState as useState3 } from "react";
|
|
632
|
+
function isSessionMcpApprovalPolicyEvent(event, serverId) {
|
|
633
|
+
if (event.type !== "session.mcp.approval_policy.updated") {
|
|
634
|
+
return false;
|
|
635
|
+
}
|
|
636
|
+
if (serverId === void 0) {
|
|
637
|
+
return true;
|
|
638
|
+
}
|
|
639
|
+
const payload = event.payload;
|
|
640
|
+
return payload?.serverId === serverId;
|
|
641
|
+
}
|
|
642
|
+
function useSessionMcpApprovalPolicy(sessionId, serverId, options = {}) {
|
|
643
|
+
const { client, workspaceId } = useEmbeddedSessionMcpApprovalPolicy(options);
|
|
644
|
+
const enabled = (options.enabled ?? true) && Boolean(sessionId && serverId);
|
|
645
|
+
const [override, setOverride] = useState3(null);
|
|
646
|
+
const targetKey = `${workspaceId}\0${sessionId ?? ""}\0${serverId ?? ""}`;
|
|
647
|
+
const authoritativeGeneration = useRef4(0);
|
|
648
|
+
const mutationIdentity = useMemo(() => ({ client, targetKey }), [client, targetKey]);
|
|
649
|
+
const { run, mutating, mutationError, clearMutationError } = useMutationRunner(mutationIdentity);
|
|
650
|
+
useLayoutEffect2(() => {
|
|
651
|
+
authoritativeGeneration.current += 1;
|
|
652
|
+
setOverride(null);
|
|
653
|
+
}, [client, targetKey]);
|
|
654
|
+
const load = useCallback4(async () => {
|
|
655
|
+
const generation = authoritativeGeneration.current;
|
|
656
|
+
if (!sessionId || !serverId) {
|
|
657
|
+
return { targetKey, generation, server: null };
|
|
658
|
+
}
|
|
659
|
+
const session = await client.getSession(workspaceId, sessionId);
|
|
660
|
+
return {
|
|
661
|
+
targetKey,
|
|
662
|
+
generation,
|
|
663
|
+
server: session.mcpServers.find((server2) => server2.id === serverId) ?? null
|
|
664
|
+
};
|
|
665
|
+
}, [client, serverId, sessionId, targetKey, workspaceId]);
|
|
666
|
+
const {
|
|
667
|
+
data,
|
|
668
|
+
loading,
|
|
669
|
+
error: loadError,
|
|
670
|
+
refresh
|
|
671
|
+
} = usePolledValue(load, {
|
|
672
|
+
enabled,
|
|
673
|
+
pollIntervalMs: options.pollIntervalMs
|
|
674
|
+
});
|
|
675
|
+
useEffect3(() => {
|
|
676
|
+
if (data?.targetKey === targetKey && data.generation === authoritativeGeneration.current) {
|
|
677
|
+
setOverride(null);
|
|
678
|
+
}
|
|
679
|
+
}, [data, targetKey]);
|
|
680
|
+
const scheduleRefresh = useDebouncedCallback(() => void refresh());
|
|
681
|
+
const matchesPolicyEvent = useCallback4(
|
|
682
|
+
(event) => isSessionMcpApprovalPolicyEvent(event, serverId ?? void 0),
|
|
683
|
+
[serverId]
|
|
684
|
+
);
|
|
685
|
+
useSessionEventTrigger(
|
|
686
|
+
client,
|
|
687
|
+
workspaceId,
|
|
688
|
+
sessionId,
|
|
689
|
+
matchesPolicyEvent,
|
|
690
|
+
scheduleRefresh,
|
|
691
|
+
{
|
|
692
|
+
enabled,
|
|
693
|
+
...options.events !== void 0 ? { events: options.events } : {}
|
|
694
|
+
},
|
|
695
|
+
async () => await refresh()
|
|
696
|
+
);
|
|
697
|
+
const update = useCallback4(
|
|
698
|
+
async (policy) => {
|
|
699
|
+
if (!sessionId || !serverId) {
|
|
700
|
+
return null;
|
|
701
|
+
}
|
|
702
|
+
const response = await run(
|
|
703
|
+
() => client.updateSessionMcpApprovalPolicy(workspaceId, sessionId, serverId, {
|
|
704
|
+
requireApproval: policy
|
|
705
|
+
})
|
|
706
|
+
);
|
|
707
|
+
if (response) {
|
|
708
|
+
authoritativeGeneration.current += 1;
|
|
709
|
+
setOverride(response.server);
|
|
710
|
+
void refresh();
|
|
711
|
+
}
|
|
712
|
+
return response;
|
|
713
|
+
},
|
|
714
|
+
[client, refresh, run, serverId, sessionId, workspaceId]
|
|
715
|
+
);
|
|
716
|
+
const server = override ?? data?.server ?? null;
|
|
717
|
+
return {
|
|
718
|
+
server,
|
|
719
|
+
policy: server?.requireApproval ?? null,
|
|
720
|
+
loading,
|
|
721
|
+
error: mutationError ?? loadError,
|
|
722
|
+
refresh: async () => {
|
|
723
|
+
await refresh();
|
|
724
|
+
},
|
|
725
|
+
update,
|
|
726
|
+
updating: mutating,
|
|
727
|
+
clearError: clearMutationError
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// src/hooks/use-session-lineage.ts
|
|
732
|
+
import { useCallback as useCallback5, useEffect as useEffect4, useRef as useRef5 } from "react";
|
|
733
|
+
function isLineageRefreshEvent(event) {
|
|
734
|
+
if (event.type === "session.status.changed" || event.type === "session.created") {
|
|
735
|
+
return true;
|
|
736
|
+
}
|
|
737
|
+
if (event.type === "agent.toolCall.output") {
|
|
738
|
+
const payload = event.payload;
|
|
739
|
+
const name = typeof payload?.name === "string" ? payload.name : typeof payload?.toolName === "string" ? payload.toolName : "";
|
|
740
|
+
return name === "session_create" || name.endsWith("__session_create");
|
|
741
|
+
}
|
|
742
|
+
return false;
|
|
743
|
+
}
|
|
744
|
+
function isSessionCreateToolCallCreated(event) {
|
|
745
|
+
if (event.type !== "agent.toolCall.created") {
|
|
746
|
+
return false;
|
|
747
|
+
}
|
|
748
|
+
const payload = event.payload;
|
|
749
|
+
const name = typeof payload?.name === "string" ? payload.name : typeof payload?.toolName === "string" ? payload.toolName : "";
|
|
750
|
+
return name === "session_create" || name.endsWith("__session_create");
|
|
751
|
+
}
|
|
752
|
+
function useSessionLineage(sessionId, options = {}) {
|
|
753
|
+
const { client, workspaceId, workspaceControlEvent, registerSessionReconciler } = useEmbeddedSessionLineage(options);
|
|
754
|
+
const enabled = (options.enabled ?? true) && Boolean(sessionId);
|
|
755
|
+
const nextReadGeneration = useRef5(0);
|
|
756
|
+
const beginRead = options.beginRead;
|
|
757
|
+
const load = useCallback5(
|
|
758
|
+
async (signal) => {
|
|
759
|
+
if (!sessionId) {
|
|
760
|
+
return {
|
|
761
|
+
lineage: { ancestors: [], children: [], truncated: false },
|
|
762
|
+
readGeneration: 0
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
let readGeneration = 0;
|
|
766
|
+
const lineage = await client.getSessionLineage(workspaceId, sessionId, {
|
|
767
|
+
signal,
|
|
768
|
+
onRequestStart: (sharedReadGeneration) => {
|
|
769
|
+
readGeneration = sharedReadGeneration ?? beginRead?.() ?? ++nextReadGeneration.current;
|
|
770
|
+
}
|
|
771
|
+
});
|
|
772
|
+
return {
|
|
773
|
+
lineage,
|
|
774
|
+
readGeneration
|
|
775
|
+
};
|
|
776
|
+
},
|
|
777
|
+
[beginRead, client, workspaceId, sessionId]
|
|
778
|
+
);
|
|
779
|
+
const state = usePolledValue(load, { pollIntervalMs: options.pollIntervalMs, enabled });
|
|
780
|
+
const refresh = state.refresh;
|
|
781
|
+
useEffect4(() => {
|
|
782
|
+
if (enabled && workspaceControlEvent) void refresh();
|
|
783
|
+
}, [enabled, refresh, workspaceControlEvent]);
|
|
784
|
+
useEffect4(() => {
|
|
785
|
+
if (!sessionId || !enabled) return;
|
|
786
|
+
return registerSessionReconciler(sessionId, "lineage", refresh);
|
|
787
|
+
}, [enabled, refresh, registerSessionReconciler, sessionId]);
|
|
788
|
+
const refreshSoon = useDebouncedCallback(() => void refresh(), 150);
|
|
789
|
+
const delayedChildRefreshRef = useRef5(null);
|
|
790
|
+
useEffect4(() => {
|
|
791
|
+
return () => {
|
|
792
|
+
if (delayedChildRefreshRef.current !== null) {
|
|
793
|
+
clearTimeout(delayedChildRefreshRef.current);
|
|
794
|
+
delayedChildRefreshRef.current = null;
|
|
795
|
+
}
|
|
796
|
+
};
|
|
797
|
+
}, [sessionId, workspaceId]);
|
|
798
|
+
const refreshAfterChildCreate = useCallback5(() => {
|
|
799
|
+
void refresh();
|
|
800
|
+
if (delayedChildRefreshRef.current !== null) {
|
|
801
|
+
clearTimeout(delayedChildRefreshRef.current);
|
|
802
|
+
}
|
|
803
|
+
delayedChildRefreshRef.current = setTimeout(() => {
|
|
804
|
+
delayedChildRefreshRef.current = null;
|
|
805
|
+
void refresh();
|
|
806
|
+
}, 2500);
|
|
807
|
+
}, [refresh]);
|
|
808
|
+
useSessionEventTrigger(
|
|
809
|
+
client,
|
|
810
|
+
workspaceId,
|
|
811
|
+
sessionId,
|
|
812
|
+
isLineageRefreshEvent,
|
|
813
|
+
refreshSoon,
|
|
814
|
+
// SHARED-FEED ONLY: without a caller-provided events log the trigger would
|
|
815
|
+
// open its OWN streamEvents tail — a second live SSE connection next to the
|
|
816
|
+
// session route's useSessionEvents. A caller with no feed opts into polling
|
|
817
|
+
// (pollIntervalMs), never a duplicate stream.
|
|
818
|
+
{ events: options.events, enabled: enabled && options.events !== void 0 }
|
|
819
|
+
);
|
|
820
|
+
useSessionEventTrigger(
|
|
821
|
+
client,
|
|
822
|
+
workspaceId,
|
|
823
|
+
sessionId,
|
|
824
|
+
isSessionCreateToolCallCreated,
|
|
825
|
+
refreshAfterChildCreate,
|
|
826
|
+
{ events: options.events, enabled: enabled && options.events !== void 0 }
|
|
827
|
+
);
|
|
828
|
+
return {
|
|
829
|
+
lineage: state.data?.lineage ?? null,
|
|
830
|
+
loading: state.loading,
|
|
831
|
+
error: state.error,
|
|
832
|
+
readGeneration: state.data?.readGeneration ?? 0,
|
|
833
|
+
refresh
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// src/approvals.ts
|
|
838
|
+
function approvalsFromRequiresAction(payload) {
|
|
839
|
+
const approvals = payload && typeof payload === "object" ? payload.approvals : void 0;
|
|
840
|
+
if (!Array.isArray(approvals)) {
|
|
841
|
+
return [];
|
|
842
|
+
}
|
|
843
|
+
return approvals.map((approval, index) => {
|
|
844
|
+
const raw = approval && typeof approval === "object" ? approval : {};
|
|
845
|
+
const rawItem = raw.rawItem && typeof raw.rawItem === "object" ? raw.rawItem : {};
|
|
846
|
+
return {
|
|
847
|
+
// Mirror the runtime's canonical interruption identity before retaining
|
|
848
|
+
// the legacy top-level callId fallback for already-normalized payloads.
|
|
849
|
+
id: String(rawItem.callId ?? rawItem.id ?? raw.id ?? raw.callId ?? raw.name ?? index),
|
|
850
|
+
name: String(raw.name ?? raw.toolName ?? rawItem.name ?? "approval"),
|
|
851
|
+
arguments: raw.arguments ?? rawItem.arguments,
|
|
852
|
+
raw: approval
|
|
853
|
+
};
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
function projectPendingApprovals(events) {
|
|
857
|
+
let pending = [];
|
|
858
|
+
let owningTurnId = null;
|
|
859
|
+
for (const event of events) {
|
|
860
|
+
switch (event.type) {
|
|
861
|
+
case "session.requiresAction": {
|
|
862
|
+
pending = approvalsFromRequiresAction(event.payload);
|
|
863
|
+
owningTurnId = event.turnId ?? null;
|
|
864
|
+
break;
|
|
865
|
+
}
|
|
866
|
+
case "user.approvalDecision": {
|
|
867
|
+
const payload = event.payload && typeof event.payload === "object" ? event.payload : {};
|
|
868
|
+
if (typeof payload.approvalId === "string") {
|
|
869
|
+
pending = pending.filter((approval) => approval.id !== payload.approvalId);
|
|
870
|
+
}
|
|
871
|
+
break;
|
|
872
|
+
}
|
|
873
|
+
case "turn.completed":
|
|
874
|
+
case "turn.failed":
|
|
875
|
+
case "turn.cancelled": {
|
|
876
|
+
if (owningTurnId === null || event.turnId == null || event.turnId === owningTurnId) {
|
|
877
|
+
pending = [];
|
|
878
|
+
owningTurnId = null;
|
|
879
|
+
}
|
|
880
|
+
break;
|
|
881
|
+
}
|
|
882
|
+
default:
|
|
883
|
+
break;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
return pending;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
export {
|
|
890
|
+
createEmbeddedSessionClient,
|
|
891
|
+
useFileAttachments,
|
|
892
|
+
isGoalEvent,
|
|
893
|
+
useGoal,
|
|
894
|
+
useSessionControl,
|
|
895
|
+
isSessionMcpApprovalPolicyEvent,
|
|
896
|
+
useSessionMcpApprovalPolicy,
|
|
897
|
+
isLineageRefreshEvent,
|
|
898
|
+
useSessionLineage,
|
|
899
|
+
approvalsFromRequiresAction,
|
|
900
|
+
projectPendingApprovals
|
|
901
|
+
};
|
|
902
|
+
//# sourceMappingURL=chunk-5PQJOCX5.js.map
|