@standardagents/react 0.10.1-dev.616ec2e → 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/README.md +312 -191
- package/dist/index.d.ts +170 -75
- package/dist/index.js +399 -237
- package/dist/index.js.map +1 -1
- package/package.json +1 -4
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @standardagents/react
|
|
2
2
|
|
|
3
|
-
React hooks and components for Standard Agents - connect to AI agent threads with real-time updates, send messages,
|
|
3
|
+
React hooks and components for Standard Agents - connect to AI agent threads with real-time updates, send messages, and listen for custom events.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -16,37 +16,41 @@ yarn add @standardagents/react
|
|
|
16
16
|
|
|
17
17
|
```tsx
|
|
18
18
|
import {
|
|
19
|
-
|
|
19
|
+
StandardAgentsProvider,
|
|
20
20
|
ThreadProvider,
|
|
21
21
|
useThread,
|
|
22
|
+
sendMessage,
|
|
22
23
|
} from "@standardagents/react"
|
|
23
24
|
|
|
24
25
|
function App() {
|
|
25
26
|
return (
|
|
26
|
-
<
|
|
27
|
+
<StandardAgentsProvider config={{ endpoint: "https://your-api.com" }}>
|
|
27
28
|
<ThreadProvider threadId="thread-123">
|
|
28
29
|
<ChatInterface />
|
|
29
30
|
</ThreadProvider>
|
|
30
|
-
</
|
|
31
|
+
</StandardAgentsProvider>
|
|
31
32
|
)
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
function ChatInterface() {
|
|
35
|
-
|
|
36
|
+
// No thread ID needed - inherited from ThreadProvider context
|
|
37
|
+
const messages = useThread()
|
|
36
38
|
|
|
37
39
|
const handleSend = async (text: string) => {
|
|
38
|
-
await sendMessage(
|
|
40
|
+
await sendMessage("thread-123", {
|
|
41
|
+
role: "user",
|
|
42
|
+
content: text,
|
|
43
|
+
})
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
return (
|
|
42
47
|
<div>
|
|
43
|
-
<p>Status: {status}</p>
|
|
44
48
|
{messages.map((msg) => (
|
|
45
49
|
<div key={msg.id}>
|
|
46
50
|
<strong>{msg.role}:</strong> {msg.content}
|
|
47
51
|
</div>
|
|
48
52
|
))}
|
|
49
|
-
<input
|
|
53
|
+
<input onSubmit={(e) => handleSend(e.currentTarget.value)} />
|
|
50
54
|
</div>
|
|
51
55
|
)
|
|
52
56
|
}
|
|
@@ -65,247 +69,292 @@ All API requests and WebSocket connections will automatically include this token
|
|
|
65
69
|
|
|
66
70
|
## API Reference
|
|
67
71
|
|
|
68
|
-
### `
|
|
72
|
+
### `StandardAgentsProvider`
|
|
69
73
|
|
|
70
74
|
Context provider that configures the Standard Agents client for all child components.
|
|
71
75
|
|
|
72
76
|
**Props:**
|
|
73
77
|
|
|
74
|
-
- `config.endpoint: string` - The API endpoint URL
|
|
78
|
+
- `config.endpoint: string` - The API endpoint URL (e.g., `https://api.example.com`)
|
|
75
79
|
|
|
76
80
|
```tsx
|
|
77
|
-
<
|
|
81
|
+
<StandardAgentsProvider config={{ endpoint: "https://api.example.com" }}>
|
|
78
82
|
{children}
|
|
79
|
-
</
|
|
83
|
+
</StandardAgentsProvider>
|
|
80
84
|
```
|
|
81
85
|
|
|
82
86
|
---
|
|
83
87
|
|
|
84
88
|
### `ThreadProvider`
|
|
85
89
|
|
|
86
|
-
Context provider that establishes a WebSocket connection to a specific thread. Must be nested inside `
|
|
90
|
+
Context provider that establishes a WebSocket connection to a specific thread. Must be nested inside `StandardAgentsProvider`. Provides thread context to child hooks like `useThread` and `onThreadEvent`.
|
|
87
91
|
|
|
88
92
|
**Props:**
|
|
89
93
|
|
|
90
94
|
- `threadId: string` - The thread ID to connect to
|
|
91
95
|
- `preload?: boolean` - Fetch existing messages on mount (default: `true`)
|
|
92
96
|
- `live?: boolean` - Enable WebSocket for real-time updates (default: `true`)
|
|
93
|
-
- `useWorkblocks?: boolean` - Transform tool calls into workblocks (default: `false`)
|
|
94
97
|
- `depth?: number` - Message depth level for nested conversations (default: `0`)
|
|
95
98
|
- `includeSilent?: boolean` - Include silent messages (default: `false`)
|
|
96
99
|
- `endpoint?: string` - Override the endpoint from context
|
|
97
100
|
|
|
98
101
|
```tsx
|
|
99
|
-
<
|
|
100
|
-
<ThreadProvider threadId="thread-123" live={true}>
|
|
102
|
+
<StandardAgentsProvider config={{ endpoint: "https://api.example.com" }}>
|
|
103
|
+
<ThreadProvider threadId="thread-123" live={true} depth={0}>
|
|
101
104
|
<YourComponents />
|
|
102
105
|
</ThreadProvider>
|
|
103
|
-
</
|
|
106
|
+
</StandardAgentsProvider>
|
|
104
107
|
```
|
|
105
108
|
|
|
106
109
|
---
|
|
107
110
|
|
|
108
|
-
### `useThread()`
|
|
111
|
+
### `useThread(options?)`
|
|
109
112
|
|
|
110
|
-
Hook to
|
|
113
|
+
Hook to subscribe to thread messages. Must be used within a `ThreadProvider`.
|
|
111
114
|
|
|
112
|
-
**
|
|
115
|
+
**Parameters:**
|
|
113
116
|
|
|
114
|
-
- `
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
- `
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
- `sendMessage: (payload: SendMessagePayload) => Promise<Message>` - Send a message
|
|
122
|
-
- `stopExecution: () => Promise<void>` - Stop current execution
|
|
123
|
-
- `onEvent: <T>(eventType, listener) => () => void` - Subscribe to custom events (alias: `subscribeToEvent`)
|
|
124
|
-
- `files: ThreadFile[]` - All files (pending + committed)
|
|
125
|
-
- `addFiles: (files: File[] | FileList) => void` - Upload files
|
|
126
|
-
- `removeFile: (id: string) => void` - Remove a pending file
|
|
127
|
-
- `getFileUrl: (file: ThreadFile) => string` - Get file URL
|
|
128
|
-
- `getThumbnailUrl: (file: ThreadFile) => string` - Get thumbnail URL
|
|
129
|
-
- `getPreviewUrl: (file: ThreadFile) => string | null` - Get preview URL
|
|
117
|
+
- `options?: UseThreadOptions` - Configuration options
|
|
118
|
+
|
|
119
|
+
**Options:**
|
|
120
|
+
|
|
121
|
+
- `useWorkblocks?: boolean` - Group tool calls into workblocks (default: `true`)
|
|
122
|
+
|
|
123
|
+
**Returns:** `ThreadMessage[]` - Array of messages or workblocks
|
|
130
124
|
|
|
131
125
|
**Example:**
|
|
132
126
|
|
|
133
127
|
```tsx
|
|
134
|
-
function
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
sendMessage,
|
|
138
|
-
stopExecution,
|
|
139
|
-
status,
|
|
140
|
-
isLoading,
|
|
141
|
-
files,
|
|
142
|
-
addFiles,
|
|
143
|
-
} = useThread()
|
|
128
|
+
function ThreadView() {
|
|
129
|
+
// Thread ID inherited from ThreadProvider context
|
|
130
|
+
const messages = useThread()
|
|
144
131
|
|
|
145
132
|
return (
|
|
146
133
|
<div>
|
|
147
|
-
|
|
148
|
-
|
|
134
|
+
{messages.map((item) => {
|
|
135
|
+
if (item.type === "workblock") {
|
|
136
|
+
return <WorkblockView key={item.id} workblock={item} />
|
|
137
|
+
}
|
|
138
|
+
return <MessageView key={item.id} message={item} />
|
|
139
|
+
})}
|
|
140
|
+
</div>
|
|
141
|
+
)
|
|
142
|
+
}
|
|
149
143
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
144
|
+
// Disable workblocks transformation
|
|
145
|
+
function RawMessagesView() {
|
|
146
|
+
const messages = useThread({ useWorkblocks: false })
|
|
147
|
+
// Returns raw messages without workblock grouping
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
155
152
|
|
|
156
|
-
|
|
157
|
-
type="file"
|
|
158
|
-
multiple
|
|
159
|
-
onChange={(e) => e.target.files && addFiles(e.target.files)}
|
|
160
|
-
/>
|
|
153
|
+
### `useThreadId()`
|
|
161
154
|
|
|
162
|
-
|
|
163
|
-
{files.map((file) => (
|
|
164
|
-
<span key={file.id}>{file.name} ({file.status})</span>
|
|
165
|
-
))}
|
|
166
|
-
</div>
|
|
155
|
+
Hook to get the current thread ID from context. Must be used within a `ThreadProvider`.
|
|
167
156
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
157
|
+
**Returns:** `string` - The thread ID
|
|
158
|
+
|
|
159
|
+
**Example:**
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
function CurrentThreadId() {
|
|
163
|
+
const threadId = useThreadId()
|
|
164
|
+
return <span>Thread: {threadId}</span>
|
|
172
165
|
}
|
|
173
166
|
```
|
|
174
167
|
|
|
175
168
|
---
|
|
176
169
|
|
|
177
|
-
### `
|
|
170
|
+
### `sendMessage(threadId, payload, options?)`
|
|
178
171
|
|
|
179
|
-
|
|
172
|
+
Send a message to a thread. This is a standalone function that works outside of React components.
|
|
180
173
|
|
|
181
174
|
**Parameters:**
|
|
182
175
|
|
|
183
|
-
- `
|
|
176
|
+
- `threadId: string` - The thread ID
|
|
177
|
+
- `payload: SendMessagePayload` - The message to send
|
|
178
|
+
- `role: 'user' | 'assistant'` - Message role
|
|
179
|
+
- `content: string | null` - Message content
|
|
180
|
+
- `silent?: boolean` - Silent message (not shown to user, default: `false`)
|
|
181
|
+
- `options?: { endpoint?: string }` - Override endpoint
|
|
184
182
|
|
|
185
|
-
**Returns:** `
|
|
183
|
+
**Returns:** `Promise<Message>` - The created message
|
|
186
184
|
|
|
187
185
|
**Example:**
|
|
188
186
|
|
|
189
187
|
```tsx
|
|
190
|
-
|
|
191
|
-
const todos = useThreadEvent<{ todos: string[]; completed: number }>("todo-updated")
|
|
188
|
+
import { sendMessage, useThreadId } from "@standardagents/react"
|
|
192
189
|
|
|
193
|
-
|
|
190
|
+
function SendButton() {
|
|
191
|
+
const threadId = useThreadId()
|
|
194
192
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
</div>
|
|
204
|
-
)
|
|
193
|
+
const handleSend = async () => {
|
|
194
|
+
await sendMessage(threadId, {
|
|
195
|
+
role: "user",
|
|
196
|
+
content: "Hello, agent!",
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return <button onClick={handleSend}>Send</button>
|
|
205
201
|
}
|
|
202
|
+
|
|
203
|
+
// Send a silent message (for context injection)
|
|
204
|
+
await sendMessage("thread-123", {
|
|
205
|
+
role: "user",
|
|
206
|
+
content: "Additional context",
|
|
207
|
+
silent: true,
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
// Send an assistant message (for injecting responses)
|
|
211
|
+
await sendMessage("thread-123", {
|
|
212
|
+
role: "assistant",
|
|
213
|
+
content: "Custom assistant response",
|
|
214
|
+
})
|
|
206
215
|
```
|
|
207
216
|
|
|
208
217
|
---
|
|
209
218
|
|
|
210
|
-
### `
|
|
219
|
+
### `stopThread(threadId, options?)`
|
|
211
220
|
|
|
212
|
-
|
|
221
|
+
Cancel an in-flight thread execution.
|
|
213
222
|
|
|
214
223
|
**Parameters:**
|
|
215
224
|
|
|
216
|
-
- `
|
|
217
|
-
- `
|
|
225
|
+
- `threadId: string` - The thread ID
|
|
226
|
+
- `options?: { endpoint?: string }` - Override endpoint
|
|
227
|
+
|
|
228
|
+
**Returns:** `Promise<void>`
|
|
218
229
|
|
|
219
230
|
**Example:**
|
|
220
231
|
|
|
221
232
|
```tsx
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
233
|
+
import { stopThread, useThreadId } from "@standardagents/react"
|
|
234
|
+
|
|
235
|
+
function StopButton() {
|
|
236
|
+
const threadId = useThreadId()
|
|
237
|
+
const [stopping, setStopping] = useState(false)
|
|
238
|
+
|
|
239
|
+
const handleStop = async () => {
|
|
240
|
+
setStopping(true)
|
|
241
|
+
try {
|
|
242
|
+
await stopThread(threadId)
|
|
243
|
+
} catch (error) {
|
|
244
|
+
console.error("Failed to stop thread:", error)
|
|
245
|
+
} finally {
|
|
246
|
+
setStopping(false)
|
|
247
|
+
}
|
|
248
|
+
}
|
|
226
249
|
|
|
227
|
-
return
|
|
250
|
+
return (
|
|
251
|
+
<button onClick={handleStop} disabled={stopping}>
|
|
252
|
+
{stopping ? "Stopping..." : "Stop Execution"}
|
|
253
|
+
</button>
|
|
254
|
+
)
|
|
228
255
|
}
|
|
229
256
|
```
|
|
230
257
|
|
|
231
258
|
---
|
|
232
259
|
|
|
233
|
-
|
|
260
|
+
### `onThreadEvent<T>(eventType)`
|
|
261
|
+
|
|
262
|
+
Hook to listen for custom events emitted by the agent via WebSocket. Must be used within a `ThreadProvider`.
|
|
263
|
+
|
|
264
|
+
**Parameters:**
|
|
265
|
+
|
|
266
|
+
- `eventType: string` - The custom event type to listen for
|
|
267
|
+
|
|
268
|
+
**Type Parameter:**
|
|
234
269
|
|
|
235
|
-
|
|
270
|
+
- `T` - The expected shape of the event data
|
|
271
|
+
|
|
272
|
+
**Returns:** `T | null` - The latest event value, or `null` if no event received yet
|
|
273
|
+
|
|
274
|
+
**Example:**
|
|
236
275
|
|
|
237
276
|
```tsx
|
|
238
|
-
|
|
239
|
-
|
|
277
|
+
import { onThreadEvent } from "@standardagents/react"
|
|
278
|
+
|
|
279
|
+
function TodoProgress() {
|
|
280
|
+
// Thread ID inherited from ThreadProvider context
|
|
281
|
+
const todos = onThreadEvent<{ todos: string[]; completed: number }>(
|
|
282
|
+
"todo-updated"
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
if (!todos) return <div>Waiting for updates...</div>
|
|
240
286
|
|
|
241
287
|
return (
|
|
242
288
|
<div>
|
|
243
|
-
<
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
<div key={file.id}>
|
|
252
|
-
{file.isImage && file.status !== 'uploading' && (
|
|
253
|
-
<img src={getPreviewUrl(file) || ''} alt={file.name} />
|
|
254
|
-
)}
|
|
255
|
-
<span>{file.name}</span>
|
|
256
|
-
<span>{file.status}</span>
|
|
257
|
-
{file.status === 'uploading' && <span>Uploading...</span>}
|
|
258
|
-
{file.status === 'error' && <span>Error: {file.error}</span>}
|
|
259
|
-
{file.status !== 'committed' && (
|
|
260
|
-
<button onClick={() => removeFile(file.id)}>Remove</button>
|
|
261
|
-
)}
|
|
262
|
-
</div>
|
|
263
|
-
))}
|
|
289
|
+
<p>
|
|
290
|
+
Progress: {todos.completed} / {todos.todos.length}
|
|
291
|
+
</p>
|
|
292
|
+
<ul>
|
|
293
|
+
{todos.todos.map((todo, i) => (
|
|
294
|
+
<li key={i}>{todo}</li>
|
|
295
|
+
))}
|
|
296
|
+
</ul>
|
|
264
297
|
</div>
|
|
265
298
|
)
|
|
266
299
|
}
|
|
267
300
|
```
|
|
268
301
|
|
|
269
|
-
|
|
302
|
+
**Backend Event Emission:**
|
|
270
303
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
304
|
+
```typescript
|
|
305
|
+
// In your agent's tool or hook (using @standardagents/builder)
|
|
306
|
+
import { emitThreadEvent } from "@standardagents/builder"
|
|
307
|
+
|
|
308
|
+
// Emit an event to all connected clients
|
|
309
|
+
emitThreadEvent(flow, "todo-updated", {
|
|
310
|
+
todos: ["Task 1", "Task 2"],
|
|
311
|
+
completed: 1,
|
|
312
|
+
})
|
|
313
|
+
```
|
|
275
314
|
|
|
276
315
|
---
|
|
277
316
|
|
|
278
|
-
## Types
|
|
317
|
+
## Message Types
|
|
318
|
+
|
|
319
|
+
### Regular Message
|
|
279
320
|
|
|
280
321
|
```typescript
|
|
281
322
|
interface Message {
|
|
282
323
|
id: string
|
|
283
324
|
role: "user" | "assistant" | "system" | "tool"
|
|
284
325
|
content: string | null
|
|
285
|
-
created_at: number
|
|
286
|
-
|
|
326
|
+
created_at: number // microseconds
|
|
327
|
+
reasoning_content?: string | null
|
|
328
|
+
silent?: boolean
|
|
329
|
+
depth?: number
|
|
287
330
|
}
|
|
331
|
+
```
|
|
288
332
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
333
|
+
### Workblock
|
|
334
|
+
|
|
335
|
+
When `useWorkblocks: true`, tool calls and results are grouped:
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
interface WorkMessage {
|
|
339
|
+
type: "workblock"
|
|
340
|
+
id: string
|
|
341
|
+
content: string | null
|
|
342
|
+
reasoning_content?: string | null
|
|
343
|
+
status: "pending" | "completed"
|
|
344
|
+
workItems: WorkItem[]
|
|
345
|
+
created_at: number
|
|
346
|
+
depth?: number
|
|
294
347
|
}
|
|
295
348
|
|
|
296
|
-
interface
|
|
349
|
+
interface WorkItem {
|
|
297
350
|
id: string
|
|
351
|
+
type: "tool_call" | "tool_result"
|
|
298
352
|
name: string
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
error?: string
|
|
304
|
-
path?: string
|
|
305
|
-
localPreviewUrl: string | null
|
|
353
|
+
input?: string
|
|
354
|
+
content?: string
|
|
355
|
+
status: "success" | "error" | null
|
|
356
|
+
tool_call_id?: string
|
|
306
357
|
}
|
|
307
|
-
|
|
308
|
-
type ConnectionStatus = "connecting" | "connected" | "disconnected" | "reconnecting"
|
|
309
358
|
```
|
|
310
359
|
|
|
311
360
|
---
|
|
@@ -313,53 +362,74 @@ type ConnectionStatus = "connecting" | "connected" | "disconnected" | "reconnect
|
|
|
313
362
|
## Complete Example
|
|
314
363
|
|
|
315
364
|
```tsx
|
|
316
|
-
import { useState, useEffect } from "react"
|
|
317
365
|
import {
|
|
318
|
-
|
|
366
|
+
StandardAgentsProvider,
|
|
319
367
|
ThreadProvider,
|
|
320
368
|
useThread,
|
|
321
|
-
|
|
369
|
+
useThreadId,
|
|
370
|
+
sendMessage,
|
|
371
|
+
stopThread,
|
|
372
|
+
onThreadEvent,
|
|
322
373
|
} from "@standardagents/react"
|
|
374
|
+
import { useState, useEffect } from "react"
|
|
323
375
|
|
|
324
376
|
function App() {
|
|
377
|
+
// Set auth token
|
|
325
378
|
useEffect(() => {
|
|
326
379
|
localStorage.setItem("standardagents_auth_token", "your-token")
|
|
327
380
|
}, [])
|
|
328
381
|
|
|
329
382
|
return (
|
|
330
|
-
<
|
|
331
|
-
<ThreadProvider threadId="thread-123">
|
|
383
|
+
<StandardAgentsProvider config={{ endpoint: "https://api.example.com" }}>
|
|
384
|
+
<ThreadProvider threadId="thread-123" live={true}>
|
|
332
385
|
<AgentChat />
|
|
333
386
|
</ThreadProvider>
|
|
334
|
-
</
|
|
387
|
+
</StandardAgentsProvider>
|
|
335
388
|
)
|
|
336
389
|
}
|
|
337
390
|
|
|
338
391
|
function AgentChat() {
|
|
339
392
|
const [input, setInput] = useState("")
|
|
393
|
+
const [isExecuting, setIsExecuting] = useState(false)
|
|
340
394
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
sendMessage,
|
|
344
|
-
stopExecution,
|
|
345
|
-
status,
|
|
346
|
-
isLoading,
|
|
347
|
-
files,
|
|
348
|
-
addFiles,
|
|
349
|
-
removeFile,
|
|
350
|
-
getPreviewUrl,
|
|
351
|
-
} = useThread()
|
|
395
|
+
// Get thread ID from context
|
|
396
|
+
const threadId = useThreadId()
|
|
352
397
|
|
|
353
|
-
|
|
398
|
+
// Subscribe to thread messages (no threadId needed)
|
|
399
|
+
const messages = useThread({ useWorkblocks: true })
|
|
400
|
+
|
|
401
|
+
// Listen for custom progress events (no threadId needed)
|
|
402
|
+
const progress = onThreadEvent<{ step: string; percent: number }>("progress")
|
|
354
403
|
|
|
355
404
|
const handleSend = async () => {
|
|
356
405
|
if (!input.trim()) return
|
|
357
|
-
|
|
358
|
-
|
|
406
|
+
|
|
407
|
+
setIsExecuting(true)
|
|
408
|
+
try {
|
|
409
|
+
await sendMessage(threadId, {
|
|
410
|
+
role: "user",
|
|
411
|
+
content: input,
|
|
412
|
+
})
|
|
413
|
+
setInput("")
|
|
414
|
+
} catch (error) {
|
|
415
|
+
console.error("Failed to send message:", error)
|
|
416
|
+
} finally {
|
|
417
|
+
setIsExecuting(false)
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const handleStop = async () => {
|
|
422
|
+
try {
|
|
423
|
+
await stopThread(threadId)
|
|
424
|
+
setIsExecuting(false)
|
|
425
|
+
} catch (error) {
|
|
426
|
+
console.error("Failed to stop thread:", error)
|
|
427
|
+
}
|
|
359
428
|
}
|
|
360
429
|
|
|
361
430
|
return (
|
|
362
431
|
<div>
|
|
432
|
+
{/* Progress indicator */}
|
|
363
433
|
{progress && (
|
|
364
434
|
<div>
|
|
365
435
|
<p>{progress.step}</p>
|
|
@@ -367,39 +437,43 @@ function AgentChat() {
|
|
|
367
437
|
</div>
|
|
368
438
|
)}
|
|
369
439
|
|
|
370
|
-
{
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
440
|
+
{/* Message list */}
|
|
441
|
+
<div>
|
|
442
|
+
{messages.map((item) => {
|
|
443
|
+
if (item.type === "workblock") {
|
|
444
|
+
return (
|
|
445
|
+
<div key={item.id}>
|
|
446
|
+
<p>Executing tools...</p>
|
|
447
|
+
{item.workItems.map((work) => (
|
|
448
|
+
<div key={work.id}>
|
|
449
|
+
{work.type === "tool_call" && `🔧 ${work.name}`}
|
|
450
|
+
{work.type === "tool_result" && `✅ ${work.status}`}
|
|
451
|
+
</div>
|
|
452
|
+
))}
|
|
453
|
+
</div>
|
|
454
|
+
)
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return (
|
|
458
|
+
<div key={item.id}>
|
|
459
|
+
<strong>{item.role}:</strong> {item.content}
|
|
460
|
+
</div>
|
|
461
|
+
)
|
|
462
|
+
})}
|
|
463
|
+
</div>
|
|
393
464
|
|
|
465
|
+
{/* Input */}
|
|
394
466
|
<div>
|
|
395
467
|
<input
|
|
396
468
|
value={input}
|
|
397
469
|
onChange={(e) => setInput(e.target.value)}
|
|
398
470
|
onKeyDown={(e) => e.key === "Enter" && handleSend()}
|
|
471
|
+
disabled={isExecuting}
|
|
399
472
|
/>
|
|
400
|
-
<button onClick={handleSend}>
|
|
401
|
-
|
|
402
|
-
|
|
473
|
+
<button onClick={handleSend} disabled={isExecuting}>
|
|
474
|
+
Send
|
|
475
|
+
</button>
|
|
476
|
+
{isExecuting && <button onClick={handleStop}>Stop</button>}
|
|
403
477
|
</div>
|
|
404
478
|
</div>
|
|
405
479
|
)
|
|
@@ -410,14 +484,61 @@ function AgentChat() {
|
|
|
410
484
|
|
|
411
485
|
## TypeScript Support
|
|
412
486
|
|
|
413
|
-
The package includes full
|
|
487
|
+
The package is written in TypeScript and includes full type definitions.
|
|
414
488
|
|
|
415
489
|
```tsx
|
|
416
490
|
import type {
|
|
417
491
|
Message,
|
|
492
|
+
WorkMessage,
|
|
493
|
+
ThreadMessage,
|
|
418
494
|
SendMessagePayload,
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
ThreadContextValue,
|
|
495
|
+
UseThreadOptions,
|
|
496
|
+
ThreadProviderOptions,
|
|
422
497
|
} from "@standardagents/react"
|
|
423
498
|
```
|
|
499
|
+
|
|
500
|
+
---
|
|
501
|
+
|
|
502
|
+
## Local Development
|
|
503
|
+
|
|
504
|
+
### Building the Package
|
|
505
|
+
|
|
506
|
+
```bash
|
|
507
|
+
# Install dependencies
|
|
508
|
+
pnpm install
|
|
509
|
+
|
|
510
|
+
# Build package
|
|
511
|
+
pnpm build
|
|
512
|
+
|
|
513
|
+
# Watch mode (rebuilds on changes)
|
|
514
|
+
pnpm dev
|
|
515
|
+
|
|
516
|
+
# Type check
|
|
517
|
+
pnpm typecheck
|
|
518
|
+
|
|
519
|
+
# Run tests
|
|
520
|
+
pnpm test
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
### Linking for Local Development
|
|
524
|
+
|
|
525
|
+
Using `file:` protocol in your app's `package.json`:
|
|
526
|
+
|
|
527
|
+
```json
|
|
528
|
+
{
|
|
529
|
+
"dependencies": {
|
|
530
|
+
"@standardagents/react": "file:../path/to/packages/react"
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
Or using `pnpm link`:
|
|
536
|
+
|
|
537
|
+
```bash
|
|
538
|
+
# In this package
|
|
539
|
+
cd packages/react
|
|
540
|
+
pnpm link --global
|
|
541
|
+
|
|
542
|
+
# In your app
|
|
543
|
+
pnpm link --global @standardagents/react
|
|
544
|
+
```
|