@standardagents/react 0.10.1-dev.b8746e9 → 0.10.1-next.bbd142a

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,10 +1,183 @@
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';
4
2
  import { jsx } from 'react/jsx-runtime';
5
3
 
6
4
  // src/context/AgentBuilderProvider.tsx
7
5
 
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
+
8
181
  // src/services/sendMessage.ts
9
182
  var globalEndpoint = null;
10
183
  function __setGlobalEndpoint(endpoint) {
@@ -23,7 +196,7 @@ async function sendMessage(id, payload) {
23
196
  if (token) {
24
197
  headers["Authorization"] = `Bearer ${token}`;
25
198
  }
26
- const response = await fetch(`${globalEndpoint}/threads/${id}/messages`, {
199
+ const response = await fetch(`${globalEndpoint}/threads/${id}/message`, {
27
200
  method: "POST",
28
201
  headers,
29
202
  body: JSON.stringify(payload)
@@ -93,18 +266,12 @@ function useAgentBuilderConfig() {
93
266
  }
94
267
  return context.config;
95
268
  }
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);
269
+ var ThreadContext = createContext(null);
102
270
  function ThreadProvider({
103
271
  threadId,
104
272
  options = {},
105
273
  preload = true,
106
274
  live = true,
107
- useWorkblocks = false,
108
275
  depth = 0,
109
276
  includeSilent = false,
110
277
  endpoint: endpointOverride,
@@ -129,21 +296,13 @@ function ThreadProvider({
129
296
  const [connectionStatus, setConnectionStatus] = useState(
130
297
  live ? "connecting" : "disconnected"
131
298
  );
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]);
145
299
  const eventListenersRef = useRef(/* @__PURE__ */ new Map());
146
- const connectionManagerRef = useRef(null);
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);
147
306
  const subscribeToEvent = useCallback(
148
307
  (eventType, listener) => {
149
308
  if (!eventListenersRef.current.has(eventType)) {
@@ -174,84 +333,60 @@ function ThreadProvider({
174
333
  });
175
334
  }
176
335
  }, []);
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
- });
196
- }
197
- },
198
- [threadId]
199
- );
200
- const removeFile = useCallback((id) => {
201
- setPendingFiles((prev) => prev.filter((f) => f.id !== id));
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
+ }
202
345
  }, []);
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;
346
+ const startHeartbeat = useCallback((ws) => {
347
+ if (heartbeatIntervalRef.current) {
348
+ clearInterval(heartbeatIntervalRef.current);
243
349
  }
244
- }, [threadId]);
350
+ heartbeatIntervalRef.current = setInterval(() => {
351
+ if (ws.readyState === WebSocket.OPEN) {
352
+ ws.send("ping");
353
+ }
354
+ }, 3e4);
355
+ }, []);
245
356
  useEffect(() => {
246
357
  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;
247
377
  console.log(`[ThreadProvider] Connecting WebSocket for thread ${threadId}`);
248
- const manager = new ThreadConnectionManager(
249
- clientRef.current,
378
+ setConnectionStatus("connecting");
379
+ const ws = clientRef.current.connectMessageWebSocket(
250
380
  threadId,
251
381
  {
252
- onStatusChange: (status) => {
253
- console.log(`[ThreadProvider] Connection status: ${status}`);
254
- setConnectionStatus(status);
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);
255
390
  },
256
391
  onMessage: (event) => {
257
392
  setMessages((prev) => {
@@ -282,6 +417,30 @@ function ThreadProvider({
282
417
  onError: (event) => {
283
418
  console.error("[ThreadProvider] WebSocket error:", event.error);
284
419
  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
+ }
285
444
  }
286
445
  },
287
446
  {
@@ -289,13 +448,16 @@ function ThreadProvider({
289
448
  includeSilent
290
449
  }
291
450
  );
292
- connectionManagerRef.current = manager;
293
- manager.connect();
294
- return () => {
295
- manager.disconnect();
296
- connectionManagerRef.current = null;
297
- };
298
- }, [threadId, live, depth, includeSilent, dispatchEvent]);
451
+ wsRef.current = ws;
452
+ }, [
453
+ threadId,
454
+ live,
455
+ depth,
456
+ includeSilent,
457
+ dispatchEvent,
458
+ startHeartbeat,
459
+ clearTimers
460
+ ]);
299
461
  useEffect(() => {
300
462
  if (!preload || !threadId) return;
301
463
  const fetchMessages = async () => {
@@ -319,158 +481,172 @@ function ThreadProvider({
319
481
  };
320
482
  fetchMessages();
321
483
  }, [threadId, preload, depth, includeSilent]);
322
- const staticValue = useMemo(
484
+ useEffect(() => {
485
+ isMountedRef.current = true;
486
+ return () => {
487
+ isMountedRef.current = false;
488
+ };
489
+ }, []);
490
+ const contextValue = useMemo(
323
491
  () => ({
324
492
  threadId,
325
- options: { preload, live, useWorkblocks, depth, includeSilent },
326
- sendMessage: sendMessage2,
327
- stopExecution,
493
+ messages,
494
+ loading,
495
+ error,
496
+ connectionStatus,
328
497
  subscribeToEvent,
329
- addFiles,
330
- removeFile,
331
- getFileUrl,
332
- getThumbnailUrl,
333
- getPreviewUrl
498
+ options: {
499
+ depth,
500
+ includeSilent,
501
+ ...options
502
+ }
334
503
  }),
335
504
  [
336
505
  threadId,
337
- preload,
338
- live,
339
- useWorkblocks,
506
+ messages,
507
+ loading,
508
+ error,
509
+ connectionStatus,
510
+ subscribeToEvent,
340
511
  depth,
341
512
  includeSilent,
342
- sendMessage2,
343
- stopExecution,
344
- subscribeToEvent,
345
- addFiles,
346
- removeFile,
347
- getFileUrl,
348
- getThumbnailUrl,
349
- getPreviewUrl
513
+ options
350
514
  ]
351
515
  );
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 }) }) }) });
374
- }
375
- function useStaticContext() {
376
- const context = useContext(StaticContext);
377
- if (!context) {
378
- throw new Error("useThread must be used within a ThreadProvider");
379
- }
380
- return context;
516
+ return /* @__PURE__ */ jsx(ThreadContext.Provider, { value: contextValue, children });
381
517
  }
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;
388
- }
389
- function useFilesContext() {
390
- const context = useContext(FilesContext);
391
- if (!context) {
392
- throw new Error("useThread must be used within a ThreadProvider");
393
- }
394
- return context;
395
- }
396
- function useConnectionContext() {
397
- const context = useContext(ConnectionContext);
518
+ function useThreadContext() {
519
+ const context = useContext(ThreadContext);
398
520
  if (!context) {
399
- throw new Error("useThread must be used within a ThreadProvider");
521
+ throw new Error("useThreadContext must be used within a ThreadProvider");
400
522
  }
401
523
  return context;
402
524
  }
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
525
  function useThreadId() {
441
- return useStaticContext().threadId;
526
+ return useThreadContext().threadId;
442
527
  }
443
528
 
444
- // src/hooks/useThread.ts
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."
529
+ // src/utils/workblocks.ts
530
+ function transformToWorkblocks(messages) {
531
+ if (messages.length === 0) {
532
+ return [];
533
+ }
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
456
577
  );
578
+ if (matchingResult) {
579
+ item.status = matchingResult.status;
580
+ } else {
581
+ item.status = "pending";
582
+ }
457
583
  }
458
- return target[prop];
459
584
  }
460
- });
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
+ }
461
607
  }
462
- return context;
608
+ return result;
609
+ }
610
+
611
+ // 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
+ );
621
+ }
622
+ const { messages } = context;
623
+ const transformedMessages = useMemo(() => {
624
+ return useWorkblocks ? transformToWorkblocks(messages) : messages;
625
+ }, [messages, useWorkblocks]);
626
+ return transformedMessages;
463
627
  }
464
628
  function onThreadEvent(type, callback) {
465
- const { subscribeToEvent } = useThreadContextInternal();
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;
466
636
  useEffect(() => {
467
637
  const unsubscribe = subscribeToEvent(type, callback);
468
638
  return unsubscribe;
469
639
  }, [subscribeToEvent, type, callback]);
470
640
  }
471
641
  function useThreadEvent(type) {
472
- const { subscribeToEvent } = useThreadContextInternal();
642
+ const context = useContext(ThreadContext);
473
643
  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;
474
650
  useEffect(() => {
475
651
  const unsubscribe = subscribeToEvent(type, (data) => {
476
652
  setEventData(data);
@@ -479,14 +655,7 @@ function useThreadEvent(type) {
479
655
  }, [subscribeToEvent, type]);
480
656
  return eventData;
481
657
  }
482
- var hasWarned = false;
483
658
  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
- }
490
659
  let context;
491
660
  try {
492
661
  context = useThreadContext();
@@ -501,14 +670,7 @@ function useSendMessage() {
501
670
  [threadId]
502
671
  );
503
672
  }
504
- var hasWarned2 = false;
505
673
  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
- }
512
674
  let context;
513
675
  try {
514
676
  context = useThreadContext();