@standardagents/react 0.8.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/index.js ADDED
@@ -0,0 +1,575 @@
1
+ import { createContext, useMemo, useEffect, useRef, useState, useCallback, useContext } from 'react';
2
+ import { jsx } from 'react/jsx-runtime';
3
+
4
+ // src/context/AgentBuilderProvider.tsx
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
+ const data = JSON.parse(event.data);
96
+ switch (data.type) {
97
+ case "message_data":
98
+ callbacks.onMessage?.(data);
99
+ break;
100
+ case "message_chunk":
101
+ callbacks.onChunk?.(data);
102
+ break;
103
+ case "event":
104
+ callbacks.onEvent?.(data);
105
+ break;
106
+ case "error":
107
+ callbacks.onError?.(data);
108
+ break;
109
+ }
110
+ } catch (error) {
111
+ console.error("Failed to parse WebSocket message:", error);
112
+ }
113
+ };
114
+ ws.onerror = (event) => {
115
+ console.error("WebSocket error:", event);
116
+ };
117
+ ws.onclose = () => {
118
+ callbacks.onClose?.();
119
+ };
120
+ return ws;
121
+ }
122
+ /**
123
+ * Connect to log WebSocket for custom events
124
+ */
125
+ connectLogWebSocket(id, callbacks = {}) {
126
+ const params = new URLSearchParams();
127
+ if (this.token) params.set("token", this.token);
128
+ const wsProtocol = this.endpoint.startsWith("https") ? "wss" : "ws";
129
+ const wsEndpoint = this.endpoint.replace(/^https?/, wsProtocol);
130
+ const url = `${wsEndpoint}/threads/${id}?${params.toString()}`;
131
+ const ws = new WebSocket(url);
132
+ ws.onopen = () => {
133
+ callbacks.onOpen?.();
134
+ };
135
+ ws.onmessage = (event) => {
136
+ try {
137
+ const data = JSON.parse(event.data);
138
+ switch (data.type) {
139
+ case "log_data":
140
+ callbacks.onLog?.(data);
141
+ break;
142
+ case "custom":
143
+ callbacks.onCustom?.(data);
144
+ break;
145
+ case "stopped_by_user":
146
+ callbacks.onStopped?.(data);
147
+ break;
148
+ }
149
+ } catch (error) {
150
+ console.error("Failed to parse WebSocket message:", error);
151
+ }
152
+ };
153
+ ws.onerror = (event) => {
154
+ console.error("WebSocket error:", event);
155
+ };
156
+ ws.onclose = () => {
157
+ callbacks.onClose?.();
158
+ };
159
+ return ws;
160
+ }
161
+ /**
162
+ * Get headers for HTTP requests
163
+ */
164
+ getHeaders() {
165
+ const headers = {};
166
+ if (this.token) {
167
+ headers["Authorization"] = `Bearer ${this.token}`;
168
+ }
169
+ return headers;
170
+ }
171
+ };
172
+
173
+ // src/services/sendMessage.ts
174
+ var globalEndpoint = null;
175
+ function __setGlobalEndpoint(endpoint) {
176
+ globalEndpoint = endpoint;
177
+ }
178
+ async function sendMessage(id, payload) {
179
+ if (!globalEndpoint) {
180
+ throw new Error(
181
+ "sendMessage requires AgentBuilderProvider to be mounted in your app"
182
+ );
183
+ }
184
+ const token = typeof localStorage !== "undefined" ? localStorage.getItem("agentbuilder_auth_token") : null;
185
+ const headers = {
186
+ "Content-Type": "application/json"
187
+ };
188
+ if (token) {
189
+ headers["Authorization"] = `Bearer ${token}`;
190
+ }
191
+ const response = await fetch(`${globalEndpoint}/threads/${id}/message`, {
192
+ method: "POST",
193
+ headers,
194
+ body: JSON.stringify(payload)
195
+ });
196
+ if (!response.ok) {
197
+ throw new Error(`Failed to send message: ${response.statusText}`);
198
+ }
199
+ return response.json();
200
+ }
201
+
202
+ // src/services/stopThread.ts
203
+ var globalEndpoint2 = null;
204
+ function __setGlobalEndpointForStop(endpoint) {
205
+ globalEndpoint2 = endpoint;
206
+ }
207
+ async function stopThread(id, options) {
208
+ const endpoint = options?.endpoint ?? globalEndpoint2;
209
+ if (!endpoint) {
210
+ throw new Error(
211
+ "stopThread requires AgentBuilderProvider to be mounted or endpoint option to be provided"
212
+ );
213
+ }
214
+ const normalizedEndpoint = endpoint.replace(/\/$/, "");
215
+ const token = typeof localStorage !== "undefined" ? localStorage.getItem("agentbuilder_auth_token") : null;
216
+ const headers = {};
217
+ if (token) {
218
+ headers["Authorization"] = `Bearer ${token}`;
219
+ }
220
+ const response = await fetch(`${normalizedEndpoint}/threads/${id}/stop`, {
221
+ method: "POST",
222
+ headers
223
+ });
224
+ if (!response.ok) {
225
+ throw new Error(`Failed to stop thread: ${response.statusText}`);
226
+ }
227
+ await response.json();
228
+ }
229
+ var AgentBuilderContext = createContext(null);
230
+ function AgentBuilderProvider({ config, children }) {
231
+ const client = useMemo(() => {
232
+ return new AgentBuilderClient(config.endpoint);
233
+ }, [config.endpoint]);
234
+ const value = useMemo(
235
+ () => ({
236
+ client,
237
+ config
238
+ }),
239
+ [client, config]
240
+ );
241
+ useEffect(() => {
242
+ __setGlobalEndpoint(config.endpoint);
243
+ __setGlobalEndpointForStop(config.endpoint);
244
+ }, [config.endpoint]);
245
+ return /* @__PURE__ */ jsx(AgentBuilderContext.Provider, { value, children });
246
+ }
247
+ function useAgentBuilderClient() {
248
+ const context = useContext(AgentBuilderContext);
249
+ if (!context) {
250
+ throw new Error("useAgentBuilderClient must be used within AgentBuilderProvider");
251
+ }
252
+ return context.client;
253
+ }
254
+ function useAgentBuilderConfig() {
255
+ const context = useContext(AgentBuilderContext);
256
+ if (!context) {
257
+ throw new Error("useAgentBuilderConfig must be used within AgentBuilderProvider");
258
+ }
259
+ return context.config;
260
+ }
261
+ var ThreadContext = createContext(null);
262
+ function ThreadProvider({
263
+ threadId,
264
+ options = {},
265
+ preload = true,
266
+ live = true,
267
+ depth = 0,
268
+ includeSilent = false,
269
+ endpoint: endpointOverride,
270
+ children
271
+ }) {
272
+ const contextClient = useAgentBuilderClient();
273
+ const contextConfig = useAgentBuilderConfig();
274
+ endpointOverride || contextConfig.endpoint;
275
+ const clientRef = useRef(
276
+ endpointOverride ? new AgentBuilderClient(endpointOverride) : contextClient
277
+ );
278
+ useEffect(() => {
279
+ if (endpointOverride) {
280
+ clientRef.current = new AgentBuilderClient(endpointOverride);
281
+ } else {
282
+ clientRef.current = contextClient;
283
+ }
284
+ }, [endpointOverride, contextClient]);
285
+ const [messages, setMessages] = useState([]);
286
+ const [loading, setLoading] = useState(preload);
287
+ const [error, setError] = useState(null);
288
+ const eventListenersRef = useRef(/* @__PURE__ */ new Map());
289
+ const wsRef = useRef(null);
290
+ const subscribeToEvent = useCallback((eventType, listener) => {
291
+ if (!eventListenersRef.current.has(eventType)) {
292
+ eventListenersRef.current.set(eventType, /* @__PURE__ */ new Set());
293
+ }
294
+ eventListenersRef.current.get(eventType).add(listener);
295
+ return () => {
296
+ const listeners = eventListenersRef.current.get(eventType);
297
+ if (listeners) {
298
+ listeners.delete(listener);
299
+ if (listeners.size === 0) {
300
+ eventListenersRef.current.delete(eventType);
301
+ }
302
+ }
303
+ };
304
+ }, []);
305
+ const dispatchEvent = useCallback((eventType, data) => {
306
+ const listeners = eventListenersRef.current.get(eventType);
307
+ if (listeners) {
308
+ listeners.forEach((listener) => {
309
+ try {
310
+ listener(data);
311
+ } catch (err) {
312
+ console.error(`Error in event listener for "${eventType}":`, err);
313
+ }
314
+ });
315
+ }
316
+ }, []);
317
+ useEffect(() => {
318
+ if (!preload || !threadId) return;
319
+ const fetchMessages = async () => {
320
+ setLoading(true);
321
+ setError(null);
322
+ try {
323
+ const fetchedMessages = await clientRef.current.getMessages(threadId, {
324
+ depth,
325
+ includeSilent
326
+ });
327
+ setMessages(fetchedMessages);
328
+ } catch (err) {
329
+ console.error("Failed to fetch messages:", err);
330
+ setError(err instanceof Error ? err : new Error("Failed to fetch messages"));
331
+ setMessages([]);
332
+ } finally {
333
+ setLoading(false);
334
+ }
335
+ };
336
+ fetchMessages();
337
+ }, [threadId, preload, depth, includeSilent]);
338
+ useEffect(() => {
339
+ if (!live || !threadId) return;
340
+ const ws = clientRef.current.connectMessageWebSocket(
341
+ threadId,
342
+ {
343
+ onMessage: (event) => {
344
+ setMessages((prev) => {
345
+ const exists = prev.some((m) => m.id === event.data.id);
346
+ if (exists) {
347
+ return prev.map((m) => m.id === event.data.id ? event.data : m);
348
+ } else {
349
+ return [...prev, event.data];
350
+ }
351
+ });
352
+ },
353
+ onChunk: (event) => {
354
+ setMessages((prev) => {
355
+ return prev.map((m) => {
356
+ if (m.id === event.message_id) {
357
+ return {
358
+ ...m,
359
+ content: (m.content || "") + event.chunk
360
+ };
361
+ }
362
+ return m;
363
+ });
364
+ });
365
+ },
366
+ onEvent: (event) => {
367
+ dispatchEvent(event.eventType, event.data);
368
+ },
369
+ onError: (event) => {
370
+ console.error("WebSocket error:", event.error);
371
+ setError(new Error(event.error));
372
+ }
373
+ },
374
+ {
375
+ depth,
376
+ includeSilent
377
+ }
378
+ );
379
+ wsRef.current = ws;
380
+ return () => {
381
+ ws.close();
382
+ wsRef.current = null;
383
+ };
384
+ }, [threadId, live, depth, includeSilent, dispatchEvent]);
385
+ const contextValue = useMemo(
386
+ () => ({
387
+ threadId,
388
+ messages,
389
+ loading,
390
+ error,
391
+ subscribeToEvent,
392
+ options: {
393
+ depth,
394
+ includeSilent,
395
+ ...options
396
+ }
397
+ }),
398
+ [threadId, messages, loading, error, subscribeToEvent, depth, includeSilent, options]
399
+ );
400
+ return /* @__PURE__ */ jsx(ThreadContext.Provider, { value: contextValue, children });
401
+ }
402
+ function useThreadContext() {
403
+ const context = useContext(ThreadContext);
404
+ if (!context) {
405
+ throw new Error("useThreadContext must be used within a ThreadProvider");
406
+ }
407
+ return context;
408
+ }
409
+ function useThreadId() {
410
+ return useThreadContext().threadId;
411
+ }
412
+
413
+ // src/utils/workblocks.ts
414
+ function transformToWorkblocks(messages) {
415
+ if (messages.length === 0) {
416
+ return [];
417
+ }
418
+ const result = [];
419
+ let i = 0;
420
+ while (i < messages.length) {
421
+ const message = messages[i];
422
+ if (message.role === "assistant" && message.tool_calls) {
423
+ let toolCalls;
424
+ try {
425
+ toolCalls = JSON.parse(message.tool_calls);
426
+ } catch (error) {
427
+ result.push(message);
428
+ i++;
429
+ continue;
430
+ }
431
+ const workItems = [];
432
+ for (const toolCall of toolCalls) {
433
+ workItems.push({
434
+ id: toolCall.id || message.id,
435
+ type: "tool_call",
436
+ name: toolCall.function?.name,
437
+ content: toolCall.function?.arguments || null,
438
+ status: null,
439
+ // Will be updated below based on matching results
440
+ tool_call_id: toolCall.id
441
+ });
442
+ }
443
+ let j = i + 1;
444
+ while (j < messages.length && messages[j].role === "tool") {
445
+ const toolMessage = messages[j];
446
+ const resultStatus = toolMessage.tool_status || "pending";
447
+ workItems.push({
448
+ id: toolMessage.id,
449
+ type: "tool_result",
450
+ name: toolMessage.name || void 0,
451
+ content: toolMessage.content,
452
+ status: resultStatus,
453
+ tool_call_id: toolMessage.tool_call_id || void 0
454
+ });
455
+ j++;
456
+ }
457
+ for (const item of workItems) {
458
+ if (item.type === "tool_call" && item.tool_call_id) {
459
+ const matchingResult = workItems.find(
460
+ (wi) => wi.type === "tool_result" && wi.tool_call_id === item.tool_call_id
461
+ );
462
+ if (matchingResult) {
463
+ item.status = matchingResult.status;
464
+ } else {
465
+ item.status = "pending";
466
+ }
467
+ }
468
+ }
469
+ let status = "completed";
470
+ if (message.status === "pending") {
471
+ status = "pending";
472
+ } else if (message.status === "failed") {
473
+ status = "failed";
474
+ }
475
+ const workblock = {
476
+ id: message.id,
477
+ type: "workblock",
478
+ content: message.content,
479
+ reasoning_content: message.reasoning_content,
480
+ workItems,
481
+ status,
482
+ created_at: message.created_at,
483
+ depth: message.depth
484
+ };
485
+ result.push(workblock);
486
+ i = j;
487
+ } else {
488
+ result.push(message);
489
+ i++;
490
+ }
491
+ }
492
+ return result;
493
+ }
494
+
495
+ // src/hooks/useThread.ts
496
+ function useThread(options = {}) {
497
+ const {
498
+ useWorkblocks = true
499
+ } = options;
500
+ const context = useContext(ThreadContext);
501
+ if (!context) {
502
+ throw new Error(
503
+ 'useThread must be used within a ThreadProvider. Wrap your component with <ThreadProvider threadId="...">.'
504
+ );
505
+ }
506
+ const { messages } = context;
507
+ const transformedMessages = useMemo(() => {
508
+ return useWorkblocks ? transformToWorkblocks(messages) : messages;
509
+ }, [messages, useWorkblocks]);
510
+ return transformedMessages;
511
+ }
512
+ function onThreadEvent(type, callback) {
513
+ const context = useContext(ThreadContext);
514
+ if (!context) {
515
+ throw new Error(
516
+ 'onThreadEvent must be used within a ThreadProvider. Wrap your component with <ThreadProvider threadId="...">.'
517
+ );
518
+ }
519
+ const { subscribeToEvent } = context;
520
+ useEffect(() => {
521
+ const unsubscribe = subscribeToEvent(type, callback);
522
+ return unsubscribe;
523
+ }, [subscribeToEvent, type, callback]);
524
+ }
525
+ function useThreadEvent(type) {
526
+ const context = useContext(ThreadContext);
527
+ const [eventData, setEventData] = useState(null);
528
+ if (!context) {
529
+ throw new Error(
530
+ 'useThreadEvent must be used within a ThreadProvider. Wrap your component with <ThreadProvider threadId="...">.'
531
+ );
532
+ }
533
+ const { subscribeToEvent } = context;
534
+ useEffect(() => {
535
+ const unsubscribe = subscribeToEvent(type, (data) => {
536
+ setEventData(data);
537
+ });
538
+ return unsubscribe;
539
+ }, [subscribeToEvent, type]);
540
+ return eventData;
541
+ }
542
+ function useSendMessage() {
543
+ let context;
544
+ try {
545
+ context = useThreadContext();
546
+ } catch {
547
+ throw new Error("useSendMessage must be used within a ThreadProvider");
548
+ }
549
+ const { threadId } = context;
550
+ return useCallback(
551
+ (payload) => {
552
+ return sendMessage(threadId, payload);
553
+ },
554
+ [threadId]
555
+ );
556
+ }
557
+ function useStopThread() {
558
+ let context;
559
+ try {
560
+ context = useThreadContext();
561
+ } catch {
562
+ throw new Error("useStopThread must be used within a ThreadProvider");
563
+ }
564
+ const { threadId } = context;
565
+ return useCallback(
566
+ () => {
567
+ return stopThread(threadId);
568
+ },
569
+ [threadId]
570
+ );
571
+ }
572
+
573
+ export { AgentBuilderProvider, ThreadProvider, onThreadEvent, sendMessage, stopThread, useSendMessage, useStopThread, useThread, useThreadContext, useThreadEvent, useThreadId };
574
+ //# sourceMappingURL=index.js.map
575
+ //# sourceMappingURL=index.js.map