@standardagents/react 0.9.17 → 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/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}/message`, {
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 ThreadContext = createContext(null);
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 wsRef = useRef(null);
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 clearTimers = useCallback(() => {
337
- if (reconnectTimeoutRef.current) {
338
- clearTimeout(reconnectTimeoutRef.current);
339
- reconnectTimeoutRef.current = null;
340
- }
341
- if (heartbeatIntervalRef.current) {
342
- clearInterval(heartbeatIntervalRef.current);
343
- heartbeatIntervalRef.current = null;
344
- }
345
- }, []);
346
- const startHeartbeat = useCallback((ws) => {
347
- if (heartbeatIntervalRef.current) {
348
- clearInterval(heartbeatIntervalRef.current);
349
- }
350
- heartbeatIntervalRef.current = setInterval(() => {
351
- if (ws.readyState === WebSocket.OPEN) {
352
- ws.send("ping");
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
- }, 3e4);
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
- setConnectionStatus("connecting");
379
- const ws = clientRef.current.connectMessageWebSocket(
248
+ const manager = new ThreadConnectionManager(
249
+ clientRef.current,
380
250
  threadId,
381
251
  {
382
- onOpen: () => {
383
- console.log(
384
- `[ThreadProvider] WebSocket connected for thread ${threadId}`
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
- wsRef.current = ws;
452
- }, [
453
- threadId,
454
- live,
455
- depth,
456
- includeSilent,
457
- dispatchEvent,
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
- useEffect(() => {
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
- messages,
494
- loading,
495
- error,
496
- connectionStatus,
325
+ options: { preload, live, useWorkblocks, depth, includeSilent },
326
+ sendMessage: sendMessage2,
327
+ stopExecution,
497
328
  subscribeToEvent,
498
- options: {
499
- depth,
500
- includeSilent,
501
- ...options
502
- }
329
+ addFiles,
330
+ removeFile,
331
+ getFileUrl,
332
+ getThumbnailUrl,
333
+ getPreviewUrl
503
334
  }),
504
335
  [
505
336
  threadId,
506
- messages,
507
- loading,
508
- error,
509
- connectionStatus,
510
- subscribeToEvent,
337
+ preload,
338
+ live,
339
+ useWorkblocks,
511
340
  depth,
512
341
  includeSilent,
513
- options
342
+ sendMessage2,
343
+ stopExecution,
344
+ subscribeToEvent,
345
+ addFiles,
346
+ removeFile,
347
+ getFileUrl,
348
+ getThumbnailUrl,
349
+ getPreviewUrl
514
350
  ]
515
351
  );
516
- return /* @__PURE__ */ jsx(ThreadContext.Provider, { value: contextValue, children });
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 useThreadContext() {
519
- const context = useContext(ThreadContext);
375
+ function useStaticContext() {
376
+ const context = useContext(StaticContext);
520
377
  if (!context) {
521
- throw new Error("useThreadContext must be used within a ThreadProvider");
378
+ throw new Error("useThread must be used within a ThreadProvider");
522
379
  }
523
380
  return context;
524
381
  }
525
- function useThreadId() {
526
- return useThreadContext().threadId;
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
- // src/utils/workblocks.ts
530
- function transformToWorkblocks(messages) {
531
- if (messages.length === 0) {
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
- const result = [];
535
- let i = 0;
536
- while (i < messages.length) {
537
- const message = messages[i];
538
- if (message.role === "assistant" && message.tool_calls) {
539
- let toolCalls;
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 result;
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
- function useThread(options = {}) {
613
- const {
614
- useWorkblocks = true
615
- } = options;
616
- const context = useContext(ThreadContext);
617
- if (!context) {
618
- throw new Error(
619
- 'useThread must be used within a ThreadProvider. Wrap your component with <ThreadProvider threadId="...">.'
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
- const { messages } = context;
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 context = useContext(ThreadContext);
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 context = useContext(ThreadContext);
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();