@standardagents/react 0.10.0 → 0.10.1-dev.114898
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/LICENSE.txt +48 -0
- package/README.md +191 -312
- package/dist/index.d.ts +75 -170
- package/dist/index.js +237 -399
- package/dist/index.js.map +1 -1
- package/package.json +13 -10
package/dist/index.js
CHANGED
|
@@ -1,183 +1,10 @@
|
|
|
1
1
|
import { createContext, useMemo, useEffect, useRef, useState, useCallback, useContext } from 'react';
|
|
2
|
+
import { FileUploadManager, AgentBuilderClient, messagesToFiles, transformToWorkblocks, ThreadConnectionManager } from '@standardagents/client';
|
|
3
|
+
export { AgentBuilderClient, FileUploadManager, ThreadConnectionManager, generatePendingFileId, isImageMimeType, messagesToFiles, parseAttachments, readFileAsDataUrl, transformToWorkblocks } from '@standardagents/client';
|
|
2
4
|
import { jsx } from 'react/jsx-runtime';
|
|
3
5
|
|
|
4
6
|
// src/context/AgentBuilderProvider.tsx
|
|
5
7
|
|
|
6
|
-
// src/services/client.ts
|
|
7
|
-
var AgentBuilderClient = class {
|
|
8
|
-
endpoint;
|
|
9
|
-
token;
|
|
10
|
-
constructor(endpoint) {
|
|
11
|
-
this.endpoint = endpoint.replace(/\/$/, "");
|
|
12
|
-
this.token = typeof localStorage !== "undefined" ? localStorage.getItem("agentbuilder_auth_token") : null;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Get thread metadata
|
|
16
|
-
*/
|
|
17
|
-
async getThread(id) {
|
|
18
|
-
const response = await fetch(`${this.endpoint}/threads/${id}`, {
|
|
19
|
-
method: "GET",
|
|
20
|
-
headers: this.getHeaders()
|
|
21
|
-
});
|
|
22
|
-
if (!response.ok) {
|
|
23
|
-
throw new Error(`Failed to get thread: ${response.statusText}`);
|
|
24
|
-
}
|
|
25
|
-
return response.json();
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Get messages from a thread with optional pagination and filtering
|
|
29
|
-
*/
|
|
30
|
-
async getMessages(id, options = {}) {
|
|
31
|
-
const params = new URLSearchParams();
|
|
32
|
-
if (options.limit !== void 0) params.set("limit", String(options.limit));
|
|
33
|
-
if (options.offset !== void 0) params.set("offset", String(options.offset));
|
|
34
|
-
if (options.depth !== void 0) params.set("depth", String(options.depth));
|
|
35
|
-
if (options.includeSilent !== void 0) params.set("includeSilent", String(options.includeSilent));
|
|
36
|
-
const queryString = params.toString();
|
|
37
|
-
const url = `${this.endpoint}/threads/${id}/messages${queryString ? `?${queryString}` : ""}`;
|
|
38
|
-
const response = await fetch(url, {
|
|
39
|
-
method: "GET",
|
|
40
|
-
headers: this.getHeaders()
|
|
41
|
-
});
|
|
42
|
-
if (!response.ok) {
|
|
43
|
-
throw new Error(`Failed to get messages: ${response.statusText}`);
|
|
44
|
-
}
|
|
45
|
-
const data = await response.json();
|
|
46
|
-
return data.messages || [];
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Send a message to a thread
|
|
50
|
-
*/
|
|
51
|
-
async sendMessage(id, payload) {
|
|
52
|
-
const response = await fetch(`${this.endpoint}/threads/${id}/message`, {
|
|
53
|
-
method: "POST",
|
|
54
|
-
headers: {
|
|
55
|
-
...this.getHeaders(),
|
|
56
|
-
"Content-Type": "application/json"
|
|
57
|
-
},
|
|
58
|
-
body: JSON.stringify(payload)
|
|
59
|
-
});
|
|
60
|
-
if (!response.ok) {
|
|
61
|
-
throw new Error(`Failed to send message: ${response.statusText}`);
|
|
62
|
-
}
|
|
63
|
-
return response.json();
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Stop execution of a thread
|
|
67
|
-
*/
|
|
68
|
-
async stopExecution(id) {
|
|
69
|
-
const response = await fetch(`${this.endpoint}/threads/${id}/stop`, {
|
|
70
|
-
method: "POST",
|
|
71
|
-
headers: this.getHeaders()
|
|
72
|
-
});
|
|
73
|
-
if (!response.ok) {
|
|
74
|
-
throw new Error(`Failed to stop execution: ${response.statusText}`);
|
|
75
|
-
}
|
|
76
|
-
await response.json();
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Connect to message WebSocket for real-time message updates
|
|
80
|
-
*/
|
|
81
|
-
connectMessageWebSocket(id, callbacks = {}, options = {}) {
|
|
82
|
-
const params = new URLSearchParams();
|
|
83
|
-
if (this.token) params.set("token", this.token);
|
|
84
|
-
if (options.includeSilent !== void 0) params.set("includeSilent", String(options.includeSilent));
|
|
85
|
-
if (options.depth !== void 0) params.set("depth", String(options.depth));
|
|
86
|
-
const wsProtocol = this.endpoint.startsWith("https") ? "wss" : "ws";
|
|
87
|
-
const wsEndpoint = this.endpoint.replace(/^https?/, wsProtocol);
|
|
88
|
-
const url = `${wsEndpoint}/threads/${id}/stream?${params.toString()}`;
|
|
89
|
-
const ws = new WebSocket(url);
|
|
90
|
-
ws.onopen = () => {
|
|
91
|
-
callbacks.onOpen?.();
|
|
92
|
-
};
|
|
93
|
-
ws.onmessage = (event) => {
|
|
94
|
-
try {
|
|
95
|
-
if (typeof event.data === "string" && event.data === "pong") {
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
const data = JSON.parse(event.data);
|
|
99
|
-
switch (data.type) {
|
|
100
|
-
case "message_data":
|
|
101
|
-
callbacks.onMessage?.(data);
|
|
102
|
-
break;
|
|
103
|
-
case "message_chunk":
|
|
104
|
-
callbacks.onChunk?.(data);
|
|
105
|
-
break;
|
|
106
|
-
case "event":
|
|
107
|
-
callbacks.onEvent?.(data);
|
|
108
|
-
break;
|
|
109
|
-
case "error":
|
|
110
|
-
callbacks.onError?.(data);
|
|
111
|
-
break;
|
|
112
|
-
}
|
|
113
|
-
} catch (error) {
|
|
114
|
-
console.error("Failed to parse WebSocket message:", error);
|
|
115
|
-
}
|
|
116
|
-
};
|
|
117
|
-
ws.onerror = (event) => {
|
|
118
|
-
console.error("WebSocket error:", event);
|
|
119
|
-
callbacks.onError?.({ type: "error", error: "WebSocket connection error" });
|
|
120
|
-
};
|
|
121
|
-
ws.onclose = (event) => {
|
|
122
|
-
console.log(`[AgentBuilderClient] Message WebSocket closed - code: ${event.code}, reason: ${event.reason || "none"}, wasClean: ${event.wasClean}`);
|
|
123
|
-
callbacks.onClose?.();
|
|
124
|
-
};
|
|
125
|
-
return ws;
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* Connect to log WebSocket for custom events
|
|
129
|
-
*/
|
|
130
|
-
connectLogWebSocket(id, callbacks = {}) {
|
|
131
|
-
const params = new URLSearchParams();
|
|
132
|
-
if (this.token) params.set("token", this.token);
|
|
133
|
-
const wsProtocol = this.endpoint.startsWith("https") ? "wss" : "ws";
|
|
134
|
-
const wsEndpoint = this.endpoint.replace(/^https?/, wsProtocol);
|
|
135
|
-
const url = `${wsEndpoint}/threads/${id}?${params.toString()}`;
|
|
136
|
-
const ws = new WebSocket(url);
|
|
137
|
-
ws.onopen = () => {
|
|
138
|
-
callbacks.onOpen?.();
|
|
139
|
-
};
|
|
140
|
-
ws.onmessage = (event) => {
|
|
141
|
-
try {
|
|
142
|
-
if (typeof event.data === "string" && event.data === "pong") {
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
const data = JSON.parse(event.data);
|
|
146
|
-
switch (data.type) {
|
|
147
|
-
case "log_data":
|
|
148
|
-
callbacks.onLog?.(data);
|
|
149
|
-
break;
|
|
150
|
-
case "custom":
|
|
151
|
-
callbacks.onCustom?.(data);
|
|
152
|
-
break;
|
|
153
|
-
case "stopped_by_user":
|
|
154
|
-
callbacks.onStopped?.(data);
|
|
155
|
-
break;
|
|
156
|
-
}
|
|
157
|
-
} catch (error) {
|
|
158
|
-
console.error("Failed to parse WebSocket message:", error);
|
|
159
|
-
}
|
|
160
|
-
};
|
|
161
|
-
ws.onerror = (event) => {
|
|
162
|
-
console.error("WebSocket error:", event);
|
|
163
|
-
};
|
|
164
|
-
ws.onclose = () => {
|
|
165
|
-
callbacks.onClose?.();
|
|
166
|
-
};
|
|
167
|
-
return ws;
|
|
168
|
-
}
|
|
169
|
-
/**
|
|
170
|
-
* Get headers for HTTP requests
|
|
171
|
-
*/
|
|
172
|
-
getHeaders() {
|
|
173
|
-
const headers = {};
|
|
174
|
-
if (this.token) {
|
|
175
|
-
headers["Authorization"] = `Bearer ${this.token}`;
|
|
176
|
-
}
|
|
177
|
-
return headers;
|
|
178
|
-
}
|
|
179
|
-
};
|
|
180
|
-
|
|
181
8
|
// src/services/sendMessage.ts
|
|
182
9
|
var globalEndpoint = null;
|
|
183
10
|
function __setGlobalEndpoint(endpoint) {
|
|
@@ -196,7 +23,7 @@ async function sendMessage(id, payload) {
|
|
|
196
23
|
if (token) {
|
|
197
24
|
headers["Authorization"] = `Bearer ${token}`;
|
|
198
25
|
}
|
|
199
|
-
const response = await fetch(`${globalEndpoint}/threads/${id}/
|
|
26
|
+
const response = await fetch(`${globalEndpoint}/threads/${id}/messages`, {
|
|
200
27
|
method: "POST",
|
|
201
28
|
headers,
|
|
202
29
|
body: JSON.stringify(payload)
|
|
@@ -266,12 +93,18 @@ function useAgentBuilderConfig() {
|
|
|
266
93
|
}
|
|
267
94
|
return context.config;
|
|
268
95
|
}
|
|
269
|
-
var
|
|
96
|
+
var uploadManager = new FileUploadManager();
|
|
97
|
+
var StaticContext = createContext(null);
|
|
98
|
+
var MessagesContext = createContext(null);
|
|
99
|
+
var FilesContext = createContext(null);
|
|
100
|
+
var ConnectionContext = createContext(null);
|
|
101
|
+
createContext(null);
|
|
270
102
|
function ThreadProvider({
|
|
271
103
|
threadId,
|
|
272
104
|
options = {},
|
|
273
105
|
preload = true,
|
|
274
106
|
live = true,
|
|
107
|
+
useWorkblocks = false,
|
|
275
108
|
depth = 0,
|
|
276
109
|
includeSilent = false,
|
|
277
110
|
endpoint: endpointOverride,
|
|
@@ -296,13 +129,21 @@ function ThreadProvider({
|
|
|
296
129
|
const [connectionStatus, setConnectionStatus] = useState(
|
|
297
130
|
live ? "connecting" : "disconnected"
|
|
298
131
|
);
|
|
132
|
+
const [pendingFiles, setPendingFiles] = useState([]);
|
|
133
|
+
const committedFiles = useMemo(() => {
|
|
134
|
+
return messagesToFiles(messages);
|
|
135
|
+
}, [messages]);
|
|
136
|
+
const files = useMemo(() => {
|
|
137
|
+
return [...pendingFiles, ...committedFiles];
|
|
138
|
+
}, [pendingFiles, committedFiles]);
|
|
139
|
+
const workblocks = useMemo(() => {
|
|
140
|
+
if (!useWorkblocks) {
|
|
141
|
+
return messages;
|
|
142
|
+
}
|
|
143
|
+
return transformToWorkblocks(messages);
|
|
144
|
+
}, [messages, useWorkblocks]);
|
|
299
145
|
const eventListenersRef = useRef(/* @__PURE__ */ new Map());
|
|
300
|
-
const
|
|
301
|
-
const reconnectAttempts = useRef(0);
|
|
302
|
-
const reconnectTimeoutRef = useRef(null);
|
|
303
|
-
const heartbeatIntervalRef = useRef(null);
|
|
304
|
-
const isReconnectingRef = useRef(false);
|
|
305
|
-
const isMountedRef = useRef(true);
|
|
146
|
+
const connectionManagerRef = useRef(null);
|
|
306
147
|
const subscribeToEvent = useCallback(
|
|
307
148
|
(eventType, listener) => {
|
|
308
149
|
if (!eventListenersRef.current.has(eventType)) {
|
|
@@ -333,60 +174,84 @@ function ThreadProvider({
|
|
|
333
174
|
});
|
|
334
175
|
}
|
|
335
176
|
}, []);
|
|
336
|
-
const
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
177
|
+
const addFiles = useCallback(
|
|
178
|
+
(filesToAdd) => {
|
|
179
|
+
const items = uploadManager.queueFiles(filesToAdd);
|
|
180
|
+
setPendingFiles((prev) => [...prev, ...items.map((i) => i.pending)]);
|
|
181
|
+
for (const { pending, file } of items) {
|
|
182
|
+
uploadManager.executeUpload(
|
|
183
|
+
threadId,
|
|
184
|
+
file,
|
|
185
|
+
pending.id,
|
|
186
|
+
clientRef.current,
|
|
187
|
+
(updates) => {
|
|
188
|
+
setPendingFiles(
|
|
189
|
+
(prev) => prev.map(
|
|
190
|
+
(f) => f.id === pending.id ? { ...f, ...updates } : f
|
|
191
|
+
)
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
).catch(() => {
|
|
195
|
+
});
|
|
353
196
|
}
|
|
354
|
-
},
|
|
197
|
+
},
|
|
198
|
+
[threadId]
|
|
199
|
+
);
|
|
200
|
+
const removeFile = useCallback((id) => {
|
|
201
|
+
setPendingFiles((prev) => prev.filter((f) => f.id !== id));
|
|
355
202
|
}, []);
|
|
203
|
+
const getFileUrl = useCallback(
|
|
204
|
+
(file) => {
|
|
205
|
+
if (!file.path) return "";
|
|
206
|
+
return clientRef.current.getFileUrl(threadId, file.path);
|
|
207
|
+
},
|
|
208
|
+
[threadId]
|
|
209
|
+
);
|
|
210
|
+
const getThumbnailUrl = useCallback(
|
|
211
|
+
(file) => {
|
|
212
|
+
if (!file.path) return "";
|
|
213
|
+
return clientRef.current.getThumbnailUrl(threadId, file.path);
|
|
214
|
+
},
|
|
215
|
+
[threadId]
|
|
216
|
+
);
|
|
217
|
+
const getPreviewUrl = useCallback(
|
|
218
|
+
(file) => {
|
|
219
|
+
if (!file.isImage) return null;
|
|
220
|
+
if (file.localPreviewUrl) return file.localPreviewUrl;
|
|
221
|
+
if (file.path) return clientRef.current.getThumbnailUrl(threadId, file.path);
|
|
222
|
+
return null;
|
|
223
|
+
},
|
|
224
|
+
[threadId]
|
|
225
|
+
);
|
|
226
|
+
const sendMessage2 = useCallback(
|
|
227
|
+
async (payload) => {
|
|
228
|
+
try {
|
|
229
|
+
return await clientRef.current.sendMessage(threadId, payload);
|
|
230
|
+
} catch (err) {
|
|
231
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
232
|
+
throw err;
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
[threadId]
|
|
236
|
+
);
|
|
237
|
+
const stopExecution = useCallback(async () => {
|
|
238
|
+
try {
|
|
239
|
+
await clientRef.current.stopExecution(threadId);
|
|
240
|
+
} catch (err) {
|
|
241
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
242
|
+
throw err;
|
|
243
|
+
}
|
|
244
|
+
}, [threadId]);
|
|
356
245
|
useEffect(() => {
|
|
357
246
|
if (!live || !threadId) return;
|
|
358
|
-
console.log(
|
|
359
|
-
`[ThreadProvider] useEffect running - threadId: ${threadId}, live: ${live}`
|
|
360
|
-
);
|
|
361
|
-
connectWebSocket();
|
|
362
|
-
return () => {
|
|
363
|
-
console.log(
|
|
364
|
-
`[ThreadProvider] useEffect cleanup - threadId: ${threadId}, live: ${live}`
|
|
365
|
-
);
|
|
366
|
-
clearTimers();
|
|
367
|
-
if (wsRef.current) {
|
|
368
|
-
wsRef.current.close();
|
|
369
|
-
wsRef.current = null;
|
|
370
|
-
}
|
|
371
|
-
reconnectAttempts.current = 0;
|
|
372
|
-
isReconnectingRef.current = false;
|
|
373
|
-
};
|
|
374
|
-
}, [threadId, live]);
|
|
375
|
-
const connectWebSocket = useCallback(() => {
|
|
376
|
-
if (!live || !threadId || !isMountedRef.current) return;
|
|
377
247
|
console.log(`[ThreadProvider] Connecting WebSocket for thread ${threadId}`);
|
|
378
|
-
|
|
379
|
-
|
|
248
|
+
const manager = new ThreadConnectionManager(
|
|
249
|
+
clientRef.current,
|
|
380
250
|
threadId,
|
|
381
251
|
{
|
|
382
|
-
|
|
383
|
-
console.log(
|
|
384
|
-
|
|
385
|
-
);
|
|
386
|
-
setConnectionStatus("connected");
|
|
387
|
-
reconnectAttempts.current = 0;
|
|
388
|
-
isReconnectingRef.current = false;
|
|
389
|
-
startHeartbeat(ws);
|
|
252
|
+
onStatusChange: (status) => {
|
|
253
|
+
console.log(`[ThreadProvider] Connection status: ${status}`);
|
|
254
|
+
setConnectionStatus(status);
|
|
390
255
|
},
|
|
391
256
|
onMessage: (event) => {
|
|
392
257
|
setMessages((prev) => {
|
|
@@ -417,30 +282,6 @@ function ThreadProvider({
|
|
|
417
282
|
onError: (event) => {
|
|
418
283
|
console.error("[ThreadProvider] WebSocket error:", event.error);
|
|
419
284
|
setError(new Error(event.error));
|
|
420
|
-
setConnectionStatus("disconnected");
|
|
421
|
-
},
|
|
422
|
-
onClose: () => {
|
|
423
|
-
console.log("[ThreadProvider] WebSocket closed");
|
|
424
|
-
clearTimers();
|
|
425
|
-
if (isMountedRef.current && !isReconnectingRef.current) {
|
|
426
|
-
isReconnectingRef.current = true;
|
|
427
|
-
setConnectionStatus("connecting");
|
|
428
|
-
const delay = Math.min(
|
|
429
|
-
1e3 * Math.pow(2, reconnectAttempts.current),
|
|
430
|
-
3e4
|
|
431
|
-
);
|
|
432
|
-
reconnectAttempts.current++;
|
|
433
|
-
console.log(
|
|
434
|
-
`[ThreadProvider] Reconnecting in ${delay}ms (attempt ${reconnectAttempts.current})`
|
|
435
|
-
);
|
|
436
|
-
reconnectTimeoutRef.current = setTimeout(() => {
|
|
437
|
-
if (isMountedRef.current) {
|
|
438
|
-
connectWebSocket();
|
|
439
|
-
}
|
|
440
|
-
}, delay);
|
|
441
|
-
} else {
|
|
442
|
-
setConnectionStatus("disconnected");
|
|
443
|
-
}
|
|
444
285
|
}
|
|
445
286
|
},
|
|
446
287
|
{
|
|
@@ -448,16 +289,13 @@ function ThreadProvider({
|
|
|
448
289
|
includeSilent
|
|
449
290
|
}
|
|
450
291
|
);
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
startHeartbeat,
|
|
459
|
-
clearTimers
|
|
460
|
-
]);
|
|
292
|
+
connectionManagerRef.current = manager;
|
|
293
|
+
manager.connect();
|
|
294
|
+
return () => {
|
|
295
|
+
manager.disconnect();
|
|
296
|
+
connectionManagerRef.current = null;
|
|
297
|
+
};
|
|
298
|
+
}, [threadId, live, depth, includeSilent, dispatchEvent]);
|
|
461
299
|
useEffect(() => {
|
|
462
300
|
if (!preload || !threadId) return;
|
|
463
301
|
const fetchMessages = async () => {
|
|
@@ -481,172 +319,158 @@ function ThreadProvider({
|
|
|
481
319
|
};
|
|
482
320
|
fetchMessages();
|
|
483
321
|
}, [threadId, preload, depth, includeSilent]);
|
|
484
|
-
|
|
485
|
-
isMountedRef.current = true;
|
|
486
|
-
return () => {
|
|
487
|
-
isMountedRef.current = false;
|
|
488
|
-
};
|
|
489
|
-
}, []);
|
|
490
|
-
const contextValue = useMemo(
|
|
322
|
+
const staticValue = useMemo(
|
|
491
323
|
() => ({
|
|
492
324
|
threadId,
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
connectionStatus,
|
|
325
|
+
options: { preload, live, useWorkblocks, depth, includeSilent },
|
|
326
|
+
sendMessage: sendMessage2,
|
|
327
|
+
stopExecution,
|
|
497
328
|
subscribeToEvent,
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
329
|
+
addFiles,
|
|
330
|
+
removeFile,
|
|
331
|
+
getFileUrl,
|
|
332
|
+
getThumbnailUrl,
|
|
333
|
+
getPreviewUrl
|
|
503
334
|
}),
|
|
504
335
|
[
|
|
505
336
|
threadId,
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
connectionStatus,
|
|
510
|
-
subscribeToEvent,
|
|
337
|
+
preload,
|
|
338
|
+
live,
|
|
339
|
+
useWorkblocks,
|
|
511
340
|
depth,
|
|
512
341
|
includeSilent,
|
|
513
|
-
|
|
342
|
+
sendMessage2,
|
|
343
|
+
stopExecution,
|
|
344
|
+
subscribeToEvent,
|
|
345
|
+
addFiles,
|
|
346
|
+
removeFile,
|
|
347
|
+
getFileUrl,
|
|
348
|
+
getThumbnailUrl,
|
|
349
|
+
getPreviewUrl
|
|
514
350
|
]
|
|
515
351
|
);
|
|
516
|
-
|
|
352
|
+
const messagesValue = useMemo(
|
|
353
|
+
() => ({
|
|
354
|
+
messages,
|
|
355
|
+
workblocks,
|
|
356
|
+
loading
|
|
357
|
+
}),
|
|
358
|
+
[messages, workblocks, loading]
|
|
359
|
+
);
|
|
360
|
+
const filesValue = useMemo(
|
|
361
|
+
() => ({
|
|
362
|
+
files
|
|
363
|
+
}),
|
|
364
|
+
[files]
|
|
365
|
+
);
|
|
366
|
+
const connectionValue = useMemo(
|
|
367
|
+
() => ({
|
|
368
|
+
connectionStatus,
|
|
369
|
+
error
|
|
370
|
+
}),
|
|
371
|
+
[connectionStatus, error]
|
|
372
|
+
);
|
|
373
|
+
return /* @__PURE__ */ jsx(StaticContext.Provider, { value: staticValue, children: /* @__PURE__ */ jsx(MessagesContext.Provider, { value: messagesValue, children: /* @__PURE__ */ jsx(FilesContext.Provider, { value: filesValue, children: /* @__PURE__ */ jsx(ConnectionContext.Provider, { value: connectionValue, children }) }) }) });
|
|
517
374
|
}
|
|
518
|
-
function
|
|
519
|
-
const context = useContext(
|
|
375
|
+
function useStaticContext() {
|
|
376
|
+
const context = useContext(StaticContext);
|
|
520
377
|
if (!context) {
|
|
521
|
-
throw new Error("
|
|
378
|
+
throw new Error("useThread must be used within a ThreadProvider");
|
|
522
379
|
}
|
|
523
380
|
return context;
|
|
524
381
|
}
|
|
525
|
-
function
|
|
526
|
-
|
|
382
|
+
function useMessagesContext() {
|
|
383
|
+
const context = useContext(MessagesContext);
|
|
384
|
+
if (!context) {
|
|
385
|
+
throw new Error("useThread must be used within a ThreadProvider");
|
|
386
|
+
}
|
|
387
|
+
return context;
|
|
527
388
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
return [];
|
|
389
|
+
function useFilesContext() {
|
|
390
|
+
const context = useContext(FilesContext);
|
|
391
|
+
if (!context) {
|
|
392
|
+
throw new Error("useThread must be used within a ThreadProvider");
|
|
533
393
|
}
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
try {
|
|
541
|
-
toolCalls = JSON.parse(message.tool_calls);
|
|
542
|
-
} catch (error) {
|
|
543
|
-
result.push(message);
|
|
544
|
-
i++;
|
|
545
|
-
continue;
|
|
546
|
-
}
|
|
547
|
-
const workItems = [];
|
|
548
|
-
for (const toolCall of toolCalls) {
|
|
549
|
-
workItems.push({
|
|
550
|
-
id: toolCall.id || message.id,
|
|
551
|
-
type: "tool_call",
|
|
552
|
-
name: toolCall.function?.name,
|
|
553
|
-
content: toolCall.function?.arguments || null,
|
|
554
|
-
status: null,
|
|
555
|
-
// Will be updated below based on matching results
|
|
556
|
-
tool_call_id: toolCall.id
|
|
557
|
-
});
|
|
558
|
-
}
|
|
559
|
-
let j = i + 1;
|
|
560
|
-
while (j < messages.length && messages[j].role === "tool") {
|
|
561
|
-
const toolMessage = messages[j];
|
|
562
|
-
const resultStatus = toolMessage.tool_status || "pending";
|
|
563
|
-
workItems.push({
|
|
564
|
-
id: toolMessage.id,
|
|
565
|
-
type: "tool_result",
|
|
566
|
-
name: toolMessage.name || void 0,
|
|
567
|
-
content: toolMessage.content,
|
|
568
|
-
status: resultStatus,
|
|
569
|
-
tool_call_id: toolMessage.tool_call_id || void 0
|
|
570
|
-
});
|
|
571
|
-
j++;
|
|
572
|
-
}
|
|
573
|
-
for (const item of workItems) {
|
|
574
|
-
if (item.type === "tool_call" && item.tool_call_id) {
|
|
575
|
-
const matchingResult = workItems.find(
|
|
576
|
-
(wi) => wi.type === "tool_result" && wi.tool_call_id === item.tool_call_id
|
|
577
|
-
);
|
|
578
|
-
if (matchingResult) {
|
|
579
|
-
item.status = matchingResult.status;
|
|
580
|
-
} else {
|
|
581
|
-
item.status = "pending";
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
let status = "completed";
|
|
586
|
-
if (message.status === "pending") {
|
|
587
|
-
status = "pending";
|
|
588
|
-
} else if (message.status === "failed") {
|
|
589
|
-
status = "failed";
|
|
590
|
-
}
|
|
591
|
-
const workblock = {
|
|
592
|
-
id: message.id,
|
|
593
|
-
type: "workblock",
|
|
594
|
-
content: message.content,
|
|
595
|
-
reasoning_content: message.reasoning_content,
|
|
596
|
-
workItems,
|
|
597
|
-
status,
|
|
598
|
-
created_at: message.created_at,
|
|
599
|
-
depth: message.depth
|
|
600
|
-
};
|
|
601
|
-
result.push(workblock);
|
|
602
|
-
i = j;
|
|
603
|
-
} else {
|
|
604
|
-
result.push(message);
|
|
605
|
-
i++;
|
|
606
|
-
}
|
|
394
|
+
return context;
|
|
395
|
+
}
|
|
396
|
+
function useConnectionContext() {
|
|
397
|
+
const context = useContext(ConnectionContext);
|
|
398
|
+
if (!context) {
|
|
399
|
+
throw new Error("useThread must be used within a ThreadProvider");
|
|
607
400
|
}
|
|
608
|
-
return
|
|
401
|
+
return context;
|
|
402
|
+
}
|
|
403
|
+
function useThreadContextInternal() {
|
|
404
|
+
const static_ = useStaticContext();
|
|
405
|
+
const messages_ = useMessagesContext();
|
|
406
|
+
const files_ = useFilesContext();
|
|
407
|
+
const connection_ = useConnectionContext();
|
|
408
|
+
return {
|
|
409
|
+
threadId: static_.threadId,
|
|
410
|
+
options: static_.options,
|
|
411
|
+
sendMessage: static_.sendMessage,
|
|
412
|
+
stopExecution: static_.stopExecution,
|
|
413
|
+
subscribeToEvent: static_.subscribeToEvent,
|
|
414
|
+
onEvent: static_.subscribeToEvent,
|
|
415
|
+
addFiles: static_.addFiles,
|
|
416
|
+
removeFile: static_.removeFile,
|
|
417
|
+
getFileUrl: static_.getFileUrl,
|
|
418
|
+
getThumbnailUrl: static_.getThumbnailUrl,
|
|
419
|
+
getPreviewUrl: static_.getPreviewUrl,
|
|
420
|
+
messages: messages_.messages,
|
|
421
|
+
workblocks: messages_.workblocks,
|
|
422
|
+
loading: messages_.loading,
|
|
423
|
+
isLoading: messages_.loading,
|
|
424
|
+
files: files_.files,
|
|
425
|
+
connectionStatus: connection_.connectionStatus,
|
|
426
|
+
status: connection_.connectionStatus,
|
|
427
|
+
error: connection_.error
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
var hasWarnedAboutUseThreadContext = false;
|
|
431
|
+
function useThreadContext() {
|
|
432
|
+
if (!hasWarnedAboutUseThreadContext && process.env.NODE_ENV !== "production") {
|
|
433
|
+
hasWarnedAboutUseThreadContext = true;
|
|
434
|
+
console.warn(
|
|
435
|
+
"[DEPRECATED] useThreadContext() is deprecated.\nUse: const { messages, sendMessage, ... } = useThread()"
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
return useThreadContextInternal();
|
|
439
|
+
}
|
|
440
|
+
function useThreadId() {
|
|
441
|
+
return useStaticContext().threadId;
|
|
609
442
|
}
|
|
610
443
|
|
|
611
444
|
// src/hooks/useThread.ts
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
445
|
+
var hasWarnedAboutArrayUsage = false;
|
|
446
|
+
function useThread() {
|
|
447
|
+
const context = useThreadContextInternal();
|
|
448
|
+
if (process.env.NODE_ENV !== "production") {
|
|
449
|
+
const arrayMethods = ["map", "filter", "forEach", "find", "some", "every", "reduce", "length", "push", "pop", "slice", "splice"];
|
|
450
|
+
return new Proxy(context, {
|
|
451
|
+
get(target, prop) {
|
|
452
|
+
if (arrayMethods.includes(prop) && !hasWarnedAboutArrayUsage) {
|
|
453
|
+
hasWarnedAboutArrayUsage = true;
|
|
454
|
+
console.warn(
|
|
455
|
+
"[BREAKING CHANGE] useThread() now returns an object, not an array.\nChange: const messages = useThread()\nTo: const { messages } = useThread()\n\nThe returned object includes: messages, sendMessage, stopExecution, files, and more."
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
return target[prop];
|
|
459
|
+
}
|
|
460
|
+
});
|
|
621
461
|
}
|
|
622
|
-
|
|
623
|
-
const transformedMessages = useMemo(() => {
|
|
624
|
-
return useWorkblocks ? transformToWorkblocks(messages) : messages;
|
|
625
|
-
}, [messages, useWorkblocks]);
|
|
626
|
-
return transformedMessages;
|
|
462
|
+
return context;
|
|
627
463
|
}
|
|
628
464
|
function onThreadEvent(type, callback) {
|
|
629
|
-
const
|
|
630
|
-
if (!context) {
|
|
631
|
-
throw new Error(
|
|
632
|
-
'onThreadEvent must be used within a ThreadProvider. Wrap your component with <ThreadProvider threadId="...">.'
|
|
633
|
-
);
|
|
634
|
-
}
|
|
635
|
-
const { subscribeToEvent } = context;
|
|
465
|
+
const { subscribeToEvent } = useThreadContextInternal();
|
|
636
466
|
useEffect(() => {
|
|
637
467
|
const unsubscribe = subscribeToEvent(type, callback);
|
|
638
468
|
return unsubscribe;
|
|
639
469
|
}, [subscribeToEvent, type, callback]);
|
|
640
470
|
}
|
|
641
471
|
function useThreadEvent(type) {
|
|
642
|
-
const
|
|
472
|
+
const { subscribeToEvent } = useThreadContextInternal();
|
|
643
473
|
const [eventData, setEventData] = useState(null);
|
|
644
|
-
if (!context) {
|
|
645
|
-
throw new Error(
|
|
646
|
-
'useThreadEvent must be used within a ThreadProvider. Wrap your component with <ThreadProvider threadId="...">.'
|
|
647
|
-
);
|
|
648
|
-
}
|
|
649
|
-
const { subscribeToEvent } = context;
|
|
650
474
|
useEffect(() => {
|
|
651
475
|
const unsubscribe = subscribeToEvent(type, (data) => {
|
|
652
476
|
setEventData(data);
|
|
@@ -655,7 +479,14 @@ function useThreadEvent(type) {
|
|
|
655
479
|
}, [subscribeToEvent, type]);
|
|
656
480
|
return eventData;
|
|
657
481
|
}
|
|
482
|
+
var hasWarned = false;
|
|
658
483
|
function useSendMessage() {
|
|
484
|
+
if (!hasWarned && process.env.NODE_ENV !== "production") {
|
|
485
|
+
hasWarned = true;
|
|
486
|
+
console.warn(
|
|
487
|
+
'[DEPRECATED] useSendMessage() is deprecated.\nUse: const { sendMessage } = useThread()\nThen: await sendMessage({ role: "user", content: "Hello!" })'
|
|
488
|
+
);
|
|
489
|
+
}
|
|
659
490
|
let context;
|
|
660
491
|
try {
|
|
661
492
|
context = useThreadContext();
|
|
@@ -670,7 +501,14 @@ function useSendMessage() {
|
|
|
670
501
|
[threadId]
|
|
671
502
|
);
|
|
672
503
|
}
|
|
504
|
+
var hasWarned2 = false;
|
|
673
505
|
function useStopThread() {
|
|
506
|
+
if (!hasWarned2 && process.env.NODE_ENV !== "production") {
|
|
507
|
+
hasWarned2 = true;
|
|
508
|
+
console.warn(
|
|
509
|
+
"[DEPRECATED] useStopThread() is deprecated.\nUse: const { stopExecution } = useThread()"
|
|
510
|
+
);
|
|
511
|
+
}
|
|
674
512
|
let context;
|
|
675
513
|
try {
|
|
676
514
|
context = useThreadContext();
|