@standardagents/react 0.10.0 → 0.10.1-dev.616ec2e
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 +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 +4 -1
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, and listen for custom events.
|
|
3
|
+
React hooks and components for Standard Agents - connect to AI agent threads with real-time updates, send messages, manage files, and listen for custom events.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -16,41 +16,37 @@ yarn add @standardagents/react
|
|
|
16
16
|
|
|
17
17
|
```tsx
|
|
18
18
|
import {
|
|
19
|
-
|
|
19
|
+
AgentBuilderProvider,
|
|
20
20
|
ThreadProvider,
|
|
21
21
|
useThread,
|
|
22
|
-
sendMessage,
|
|
23
22
|
} from "@standardagents/react"
|
|
24
23
|
|
|
25
24
|
function App() {
|
|
26
25
|
return (
|
|
27
|
-
<
|
|
26
|
+
<AgentBuilderProvider config={{ endpoint: "https://your-api.com" }}>
|
|
28
27
|
<ThreadProvider threadId="thread-123">
|
|
29
28
|
<ChatInterface />
|
|
30
29
|
</ThreadProvider>
|
|
31
|
-
</
|
|
30
|
+
</AgentBuilderProvider>
|
|
32
31
|
)
|
|
33
32
|
}
|
|
34
33
|
|
|
35
34
|
function ChatInterface() {
|
|
36
|
-
|
|
37
|
-
const messages = useThread()
|
|
35
|
+
const { messages, sendMessage, status } = useThread()
|
|
38
36
|
|
|
39
37
|
const handleSend = async (text: string) => {
|
|
40
|
-
await sendMessage("
|
|
41
|
-
role: "user",
|
|
42
|
-
content: text,
|
|
43
|
-
})
|
|
38
|
+
await sendMessage({ role: "user", content: text })
|
|
44
39
|
}
|
|
45
40
|
|
|
46
41
|
return (
|
|
47
42
|
<div>
|
|
43
|
+
<p>Status: {status}</p>
|
|
48
44
|
{messages.map((msg) => (
|
|
49
45
|
<div key={msg.id}>
|
|
50
46
|
<strong>{msg.role}:</strong> {msg.content}
|
|
51
47
|
</div>
|
|
52
48
|
))}
|
|
53
|
-
<input
|
|
49
|
+
<input onKeyDown={(e) => e.key === "Enter" && handleSend(e.currentTarget.value)} />
|
|
54
50
|
</div>
|
|
55
51
|
)
|
|
56
52
|
}
|
|
@@ -69,292 +65,247 @@ All API requests and WebSocket connections will automatically include this token
|
|
|
69
65
|
|
|
70
66
|
## API Reference
|
|
71
67
|
|
|
72
|
-
### `
|
|
68
|
+
### `AgentBuilderProvider`
|
|
73
69
|
|
|
74
70
|
Context provider that configures the Standard Agents client for all child components.
|
|
75
71
|
|
|
76
72
|
**Props:**
|
|
77
73
|
|
|
78
|
-
- `config.endpoint: string` - The API endpoint URL
|
|
74
|
+
- `config.endpoint: string` - The API endpoint URL
|
|
79
75
|
|
|
80
76
|
```tsx
|
|
81
|
-
<
|
|
77
|
+
<AgentBuilderProvider config={{ endpoint: "https://api.example.com" }}>
|
|
82
78
|
{children}
|
|
83
|
-
</
|
|
79
|
+
</AgentBuilderProvider>
|
|
84
80
|
```
|
|
85
81
|
|
|
86
82
|
---
|
|
87
83
|
|
|
88
84
|
### `ThreadProvider`
|
|
89
85
|
|
|
90
|
-
Context provider that establishes a WebSocket connection to a specific thread. Must be nested inside `
|
|
86
|
+
Context provider that establishes a WebSocket connection to a specific thread. Must be nested inside `AgentBuilderProvider`.
|
|
91
87
|
|
|
92
88
|
**Props:**
|
|
93
89
|
|
|
94
90
|
- `threadId: string` - The thread ID to connect to
|
|
95
91
|
- `preload?: boolean` - Fetch existing messages on mount (default: `true`)
|
|
96
92
|
- `live?: boolean` - Enable WebSocket for real-time updates (default: `true`)
|
|
93
|
+
- `useWorkblocks?: boolean` - Transform tool calls into workblocks (default: `false`)
|
|
97
94
|
- `depth?: number` - Message depth level for nested conversations (default: `0`)
|
|
98
95
|
- `includeSilent?: boolean` - Include silent messages (default: `false`)
|
|
99
96
|
- `endpoint?: string` - Override the endpoint from context
|
|
100
97
|
|
|
101
98
|
```tsx
|
|
102
|
-
<
|
|
103
|
-
<ThreadProvider threadId="thread-123" live={true}
|
|
99
|
+
<AgentBuilderProvider config={{ endpoint: "https://api.example.com" }}>
|
|
100
|
+
<ThreadProvider threadId="thread-123" live={true}>
|
|
104
101
|
<YourComponents />
|
|
105
102
|
</ThreadProvider>
|
|
106
|
-
</
|
|
103
|
+
</AgentBuilderProvider>
|
|
107
104
|
```
|
|
108
105
|
|
|
109
106
|
---
|
|
110
107
|
|
|
111
|
-
### `useThread(
|
|
108
|
+
### `useThread()`
|
|
112
109
|
|
|
113
|
-
Hook to
|
|
110
|
+
Hook to access the full thread context. Must be used within a `ThreadProvider`.
|
|
114
111
|
|
|
115
|
-
**
|
|
116
|
-
|
|
117
|
-
- `options?: UseThreadOptions` - Configuration options
|
|
118
|
-
|
|
119
|
-
**Options:**
|
|
120
|
-
|
|
121
|
-
- `useWorkblocks?: boolean` - Group tool calls into workblocks (default: `true`)
|
|
112
|
+
**Returns:** `ThreadContextValue`
|
|
122
113
|
|
|
123
|
-
|
|
114
|
+
- `threadId: string` - The thread ID
|
|
115
|
+
- `messages: Message[]` - Array of messages
|
|
116
|
+
- `workblocks: ThreadMessage[]` - Messages transformed to workblocks (if `useWorkblocks` is true)
|
|
117
|
+
- `status: ConnectionStatus` - WebSocket connection status (`"connecting"` | `"connected"` | `"disconnected"` | `"reconnecting"`)
|
|
118
|
+
- `loading: boolean` - Whether messages are loading (alias: `isLoading`)
|
|
119
|
+
- `error: Error | null` - Any error that occurred
|
|
120
|
+
- `options: ThreadProviderOptions` - Options passed to the provider
|
|
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
|
|
124
130
|
|
|
125
131
|
**Example:**
|
|
126
132
|
|
|
127
133
|
```tsx
|
|
128
|
-
function
|
|
129
|
-
|
|
130
|
-
|
|
134
|
+
function ChatView() {
|
|
135
|
+
const {
|
|
136
|
+
messages,
|
|
137
|
+
sendMessage,
|
|
138
|
+
stopExecution,
|
|
139
|
+
status,
|
|
140
|
+
isLoading,
|
|
141
|
+
files,
|
|
142
|
+
addFiles,
|
|
143
|
+
} = useThread()
|
|
131
144
|
|
|
132
145
|
return (
|
|
133
146
|
<div>
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
return <WorkblockView key={item.id} workblock={item} />
|
|
137
|
-
}
|
|
138
|
-
return <MessageView key={item.id} message={item} />
|
|
139
|
-
})}
|
|
140
|
-
</div>
|
|
141
|
-
)
|
|
142
|
-
}
|
|
147
|
+
<p>Status: {status}</p>
|
|
148
|
+
{isLoading && <p>Loading...</p>}
|
|
143
149
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
---
|
|
152
|
-
|
|
153
|
-
### `useThreadId()`
|
|
154
|
-
|
|
155
|
-
Hook to get the current thread ID from context. Must be used within a `ThreadProvider`.
|
|
150
|
+
{messages.map((msg) => (
|
|
151
|
+
<div key={msg.id}>
|
|
152
|
+
<strong>{msg.role}:</strong> {msg.content}
|
|
153
|
+
</div>
|
|
154
|
+
))}
|
|
156
155
|
|
|
157
|
-
|
|
156
|
+
<input
|
|
157
|
+
type="file"
|
|
158
|
+
multiple
|
|
159
|
+
onChange={(e) => e.target.files && addFiles(e.target.files)}
|
|
160
|
+
/>
|
|
158
161
|
|
|
159
|
-
|
|
162
|
+
<div>
|
|
163
|
+
{files.map((file) => (
|
|
164
|
+
<span key={file.id}>{file.name} ({file.status})</span>
|
|
165
|
+
))}
|
|
166
|
+
</div>
|
|
160
167
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
168
|
+
<button onClick={() => sendMessage({ role: "user", content: "Hello!" })}>Send</button>
|
|
169
|
+
<button onClick={stopExecution}>Stop</button>
|
|
170
|
+
</div>
|
|
171
|
+
)
|
|
165
172
|
}
|
|
166
173
|
```
|
|
167
174
|
|
|
168
175
|
---
|
|
169
176
|
|
|
170
|
-
### `
|
|
177
|
+
### `useThreadEvent<T>(eventType)`
|
|
171
178
|
|
|
172
|
-
|
|
179
|
+
Hook to listen for custom events emitted by the agent. Must be used within a `ThreadProvider`.
|
|
173
180
|
|
|
174
181
|
**Parameters:**
|
|
175
182
|
|
|
176
|
-
- `
|
|
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
|
|
183
|
+
- `eventType: string` - The custom event type to listen for
|
|
182
184
|
|
|
183
|
-
**Returns:** `
|
|
185
|
+
**Returns:** `T | null` - The latest event value, or `null` if no event received yet
|
|
184
186
|
|
|
185
187
|
**Example:**
|
|
186
188
|
|
|
187
189
|
```tsx
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
function SendButton() {
|
|
191
|
-
const threadId = useThreadId()
|
|
190
|
+
function TodoProgress() {
|
|
191
|
+
const todos = useThreadEvent<{ todos: string[]; completed: number }>("todo-updated")
|
|
192
192
|
|
|
193
|
-
|
|
194
|
-
await sendMessage(threadId, {
|
|
195
|
-
role: "user",
|
|
196
|
-
content: "Hello, agent!",
|
|
197
|
-
})
|
|
198
|
-
}
|
|
193
|
+
if (!todos) return <div>Waiting for updates...</div>
|
|
199
194
|
|
|
200
|
-
return
|
|
195
|
+
return (
|
|
196
|
+
<div>
|
|
197
|
+
<p>Progress: {todos.completed} / {todos.todos.length}</p>
|
|
198
|
+
<ul>
|
|
199
|
+
{todos.todos.map((todo, i) => (
|
|
200
|
+
<li key={i}>{todo}</li>
|
|
201
|
+
))}
|
|
202
|
+
</ul>
|
|
203
|
+
</div>
|
|
204
|
+
)
|
|
201
205
|
}
|
|
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
|
-
})
|
|
215
206
|
```
|
|
216
207
|
|
|
217
208
|
---
|
|
218
209
|
|
|
219
|
-
### `
|
|
210
|
+
### `onThreadEvent<T>(eventType, callback)`
|
|
220
211
|
|
|
221
|
-
|
|
212
|
+
Hook to listen for custom events with a callback. Must be used within a `ThreadProvider`.
|
|
222
213
|
|
|
223
214
|
**Parameters:**
|
|
224
215
|
|
|
225
|
-
- `
|
|
226
|
-
- `
|
|
227
|
-
|
|
228
|
-
**Returns:** `Promise<void>`
|
|
216
|
+
- `eventType: string` - The custom event type to listen for
|
|
217
|
+
- `callback: (data: T) => void` - Called when event is received
|
|
229
218
|
|
|
230
219
|
**Example:**
|
|
231
220
|
|
|
232
221
|
```tsx
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
-
}
|
|
222
|
+
function Notifications() {
|
|
223
|
+
onThreadEvent<{ message: string }>("notification", (data) => {
|
|
224
|
+
alert(data.message)
|
|
225
|
+
})
|
|
249
226
|
|
|
250
|
-
return
|
|
251
|
-
<button onClick={handleStop} disabled={stopping}>
|
|
252
|
-
{stopping ? "Stopping..." : "Stop Execution"}
|
|
253
|
-
</button>
|
|
254
|
-
)
|
|
227
|
+
return null
|
|
255
228
|
}
|
|
256
229
|
```
|
|
257
230
|
|
|
258
231
|
---
|
|
259
232
|
|
|
260
|
-
|
|
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:**
|
|
233
|
+
## File Management
|
|
269
234
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
**Returns:** `T | null` - The latest event value, or `null` if no event received yet
|
|
273
|
-
|
|
274
|
-
**Example:**
|
|
235
|
+
The `useThread()` hook provides file management capabilities:
|
|
275
236
|
|
|
276
237
|
```tsx
|
|
277
|
-
|
|
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>
|
|
238
|
+
function FileUploader() {
|
|
239
|
+
const { files, addFiles, removeFile, getFileUrl, getPreviewUrl } = useThread()
|
|
286
240
|
|
|
287
241
|
return (
|
|
288
242
|
<div>
|
|
289
|
-
<
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
{
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
243
|
+
<input
|
|
244
|
+
type="file"
|
|
245
|
+
multiple
|
|
246
|
+
accept="image/*,.pdf,.txt"
|
|
247
|
+
onChange={(e) => e.target.files && addFiles(e.target.files)}
|
|
248
|
+
/>
|
|
249
|
+
|
|
250
|
+
{files.map((file) => (
|
|
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
|
+
))}
|
|
297
264
|
</div>
|
|
298
265
|
)
|
|
299
266
|
}
|
|
300
267
|
```
|
|
301
268
|
|
|
302
|
-
|
|
269
|
+
### File States
|
|
303
270
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
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
|
-
```
|
|
271
|
+
- `uploading` - File is being uploaded
|
|
272
|
+
- `ready` - Upload complete, file ready to attach to message
|
|
273
|
+
- `committed` - File is attached to a sent message
|
|
274
|
+
- `error` - Upload failed
|
|
314
275
|
|
|
315
276
|
---
|
|
316
277
|
|
|
317
|
-
##
|
|
318
|
-
|
|
319
|
-
### Regular Message
|
|
278
|
+
## Types
|
|
320
279
|
|
|
321
280
|
```typescript
|
|
322
281
|
interface Message {
|
|
323
282
|
id: string
|
|
324
283
|
role: "user" | "assistant" | "system" | "tool"
|
|
325
284
|
content: string | null
|
|
326
|
-
created_at: number
|
|
327
|
-
|
|
328
|
-
silent?: boolean
|
|
329
|
-
depth?: number
|
|
285
|
+
created_at: number
|
|
286
|
+
attachments?: string // JSON array of AttachmentRef
|
|
330
287
|
}
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
### Workblock
|
|
334
288
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
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
|
|
289
|
+
interface SendMessagePayload {
|
|
290
|
+
role: "user" | "assistant" | "system"
|
|
291
|
+
content: string
|
|
292
|
+
silent?: boolean
|
|
293
|
+
attachments?: string[] // Paths of files to attach
|
|
347
294
|
}
|
|
348
295
|
|
|
349
|
-
interface
|
|
296
|
+
interface ThreadFile {
|
|
350
297
|
id: string
|
|
351
|
-
type: "tool_call" | "tool_result"
|
|
352
298
|
name: string
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
299
|
+
mimeType: string
|
|
300
|
+
size: number
|
|
301
|
+
isImage: boolean
|
|
302
|
+
status: "uploading" | "ready" | "committed" | "error"
|
|
303
|
+
error?: string
|
|
304
|
+
path?: string
|
|
305
|
+
localPreviewUrl: string | null
|
|
357
306
|
}
|
|
307
|
+
|
|
308
|
+
type ConnectionStatus = "connecting" | "connected" | "disconnected" | "reconnecting"
|
|
358
309
|
```
|
|
359
310
|
|
|
360
311
|
---
|
|
@@ -362,74 +313,53 @@ interface WorkItem {
|
|
|
362
313
|
## Complete Example
|
|
363
314
|
|
|
364
315
|
```tsx
|
|
316
|
+
import { useState, useEffect } from "react"
|
|
365
317
|
import {
|
|
366
|
-
|
|
318
|
+
AgentBuilderProvider,
|
|
367
319
|
ThreadProvider,
|
|
368
320
|
useThread,
|
|
369
|
-
|
|
370
|
-
sendMessage,
|
|
371
|
-
stopThread,
|
|
372
|
-
onThreadEvent,
|
|
321
|
+
useThreadEvent,
|
|
373
322
|
} from "@standardagents/react"
|
|
374
|
-
import { useState, useEffect } from "react"
|
|
375
323
|
|
|
376
324
|
function App() {
|
|
377
|
-
// Set auth token
|
|
378
325
|
useEffect(() => {
|
|
379
326
|
localStorage.setItem("standardagents_auth_token", "your-token")
|
|
380
327
|
}, [])
|
|
381
328
|
|
|
382
329
|
return (
|
|
383
|
-
<
|
|
384
|
-
<ThreadProvider threadId="thread-123"
|
|
330
|
+
<AgentBuilderProvider config={{ endpoint: "https://api.example.com" }}>
|
|
331
|
+
<ThreadProvider threadId="thread-123">
|
|
385
332
|
<AgentChat />
|
|
386
333
|
</ThreadProvider>
|
|
387
|
-
</
|
|
334
|
+
</AgentBuilderProvider>
|
|
388
335
|
)
|
|
389
336
|
}
|
|
390
337
|
|
|
391
338
|
function AgentChat() {
|
|
392
339
|
const [input, setInput] = useState("")
|
|
393
|
-
const [isExecuting, setIsExecuting] = useState(false)
|
|
394
340
|
|
|
395
|
-
|
|
396
|
-
|
|
341
|
+
const {
|
|
342
|
+
messages,
|
|
343
|
+
sendMessage,
|
|
344
|
+
stopExecution,
|
|
345
|
+
status,
|
|
346
|
+
isLoading,
|
|
347
|
+
files,
|
|
348
|
+
addFiles,
|
|
349
|
+
removeFile,
|
|
350
|
+
getPreviewUrl,
|
|
351
|
+
} = useThread()
|
|
397
352
|
|
|
398
|
-
|
|
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")
|
|
353
|
+
const progress = useThreadEvent<{ step: string; percent: number }>("progress")
|
|
403
354
|
|
|
404
355
|
const handleSend = async () => {
|
|
405
356
|
if (!input.trim()) return
|
|
406
|
-
|
|
407
|
-
|
|
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
|
-
}
|
|
357
|
+
await sendMessage({ role: "user", content: input })
|
|
358
|
+
setInput("")
|
|
428
359
|
}
|
|
429
360
|
|
|
430
361
|
return (
|
|
431
362
|
<div>
|
|
432
|
-
{/* Progress indicator */}
|
|
433
363
|
{progress && (
|
|
434
364
|
<div>
|
|
435
365
|
<p>{progress.step}</p>
|
|
@@ -437,43 +367,39 @@ function AgentChat() {
|
|
|
437
367
|
</div>
|
|
438
368
|
)}
|
|
439
369
|
|
|
440
|
-
{
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
</div>
|
|
370
|
+
{isLoading && <p>Loading messages...</p>}
|
|
371
|
+
|
|
372
|
+
{messages.map((msg) => (
|
|
373
|
+
<div key={msg.id}>
|
|
374
|
+
<strong>{msg.role}:</strong> {msg.content}
|
|
375
|
+
</div>
|
|
376
|
+
))}
|
|
377
|
+
|
|
378
|
+
<input
|
|
379
|
+
type="file"
|
|
380
|
+
multiple
|
|
381
|
+
onChange={(e) => e.target.files && addFiles(e.target.files)}
|
|
382
|
+
/>
|
|
383
|
+
|
|
384
|
+
{files.filter(f => f.status !== 'committed').map((file) => (
|
|
385
|
+
<div key={file.id}>
|
|
386
|
+
{file.isImage && getPreviewUrl(file) && (
|
|
387
|
+
<img src={getPreviewUrl(file)!} alt={file.name} width={50} />
|
|
388
|
+
)}
|
|
389
|
+
<span>{file.name} ({file.status})</span>
|
|
390
|
+
<button onClick={() => removeFile(file.id)}>x</button>
|
|
391
|
+
</div>
|
|
392
|
+
))}
|
|
464
393
|
|
|
465
|
-
{/* Input */}
|
|
466
394
|
<div>
|
|
467
395
|
<input
|
|
468
396
|
value={input}
|
|
469
397
|
onChange={(e) => setInput(e.target.value)}
|
|
470
398
|
onKeyDown={(e) => e.key === "Enter" && handleSend()}
|
|
471
|
-
disabled={isExecuting}
|
|
472
399
|
/>
|
|
473
|
-
<button onClick={handleSend}
|
|
474
|
-
|
|
475
|
-
</
|
|
476
|
-
{isExecuting && <button onClick={handleStop}>Stop</button>}
|
|
400
|
+
<button onClick={handleSend}>Send</button>
|
|
401
|
+
<button onClick={stopExecution}>Stop</button>
|
|
402
|
+
<span>Status: {status}</span>
|
|
477
403
|
</div>
|
|
478
404
|
</div>
|
|
479
405
|
)
|
|
@@ -484,61 +410,14 @@ function AgentChat() {
|
|
|
484
410
|
|
|
485
411
|
## TypeScript Support
|
|
486
412
|
|
|
487
|
-
The package
|
|
413
|
+
The package includes full TypeScript definitions:
|
|
488
414
|
|
|
489
415
|
```tsx
|
|
490
416
|
import type {
|
|
491
417
|
Message,
|
|
492
|
-
WorkMessage,
|
|
493
|
-
ThreadMessage,
|
|
494
418
|
SendMessagePayload,
|
|
495
|
-
|
|
496
|
-
|
|
419
|
+
ThreadFile,
|
|
420
|
+
ConnectionStatus,
|
|
421
|
+
ThreadContextValue,
|
|
497
422
|
} from "@standardagents/react"
|
|
498
423
|
```
|
|
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
|
-
```
|