bs-agent 0.0.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/LICENSE +21 -0
- package/README.md +558 -0
- package/dist/index.cjs +911 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +196 -0
- package/dist/index.d.ts +196 -0
- package/dist/index.js +882 -0
- package/dist/index.js.map +1 -0
- package/package.json +46 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 BuildShip
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,558 @@
|
|
|
1
|
+
# @buildship/agent
|
|
2
|
+
|
|
3
|
+
A React library for integrating BuildShip AI agents into your frontend
|
|
4
|
+
applications with support for streaming responses, file handling, and
|
|
5
|
+
client-side widgets.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Real-time Streaming**: Built on Server-Sent Events (SSE) for live agent
|
|
10
|
+
responses
|
|
11
|
+
- **Session Management**: Automatic conversation persistence with localStorage
|
|
12
|
+
- **File Support**: Upload and send files to your agents
|
|
13
|
+
- **Client Tools/Widgets**: Render interactive components from agent responses
|
|
14
|
+
- **TypeScript**: Full type safety with comprehensive TypeScript definitions
|
|
15
|
+
- **Multi-Agent**: Support for multiple agents in a single application
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @buildship/agent
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
import { AgentContextProvider, useAgentContext } from "@buildship/agent";
|
|
27
|
+
|
|
28
|
+
// 1. Wrap your app with the provider
|
|
29
|
+
function App() {
|
|
30
|
+
return (
|
|
31
|
+
<AgentContextProvider>
|
|
32
|
+
<YourApp />
|
|
33
|
+
</AgentContextProvider>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 2. Use the agent in your components
|
|
38
|
+
function ChatComponent() {
|
|
39
|
+
const agent = useAgentContext("your-agent-id", "https://your-agent-url.com");
|
|
40
|
+
|
|
41
|
+
const handleSend = () => {
|
|
42
|
+
agent.handleSend("Hello, agent!");
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<div>
|
|
47
|
+
{agent.messages.map((msg, idx) => (
|
|
48
|
+
<div key={idx}>
|
|
49
|
+
<strong>{msg.role}:</strong> {msg.content}
|
|
50
|
+
</div>
|
|
51
|
+
))}
|
|
52
|
+
<button onClick={handleSend} disabled={agent.inProgress}>
|
|
53
|
+
Send Message
|
|
54
|
+
</button>
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Setup
|
|
61
|
+
|
|
62
|
+
### AgentContextProvider
|
|
63
|
+
|
|
64
|
+
The `AgentContextProvider` must wrap your application to enable agent
|
|
65
|
+
functionality. It manages:
|
|
66
|
+
|
|
67
|
+
- Global session state across all agents
|
|
68
|
+
- Automatic localStorage persistence
|
|
69
|
+
- Agent runner registry
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
import { AgentContextProvider } from "@buildship/agent";
|
|
73
|
+
|
|
74
|
+
function App() {
|
|
75
|
+
return (
|
|
76
|
+
<AgentContextProvider>{/* Your app components */}</AgentContextProvider>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Basic Usage
|
|
82
|
+
|
|
83
|
+
### Using the Agent Hook
|
|
84
|
+
|
|
85
|
+
The `useAgentContext` hook is the primary interface for interacting with agents:
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
import { useAgentContext } from "@buildship/agent";
|
|
89
|
+
|
|
90
|
+
function AgentChat() {
|
|
91
|
+
const agent = useAgentContext(
|
|
92
|
+
"my-agent-id", // Unique identifier for your agent
|
|
93
|
+
"https://agent-url.com", // Your agent's endpoint URL
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// Send a message
|
|
97
|
+
const sendMessage = async () => {
|
|
98
|
+
await agent.handleSend("What's the weather today?");
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// Display messages
|
|
102
|
+
return (
|
|
103
|
+
<div>
|
|
104
|
+
{agent.messages.map((message, idx) => (
|
|
105
|
+
<div key={idx}>
|
|
106
|
+
<strong>{message.role === "user" ? "You" : "Agent"}:</strong>
|
|
107
|
+
<p>{message.content}</p>
|
|
108
|
+
</div>
|
|
109
|
+
))}
|
|
110
|
+
|
|
111
|
+
{agent.inProgress && <div>Agent is thinking...</div>}
|
|
112
|
+
|
|
113
|
+
<button onClick={sendMessage} disabled={agent.inProgress}>
|
|
114
|
+
Send
|
|
115
|
+
</button>
|
|
116
|
+
</div>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### AgentRunner API
|
|
122
|
+
|
|
123
|
+
The `useAgentContext` hook returns an `AgentRunner` object with:
|
|
124
|
+
|
|
125
|
+
| Property | Type | Description |
|
|
126
|
+
| ---------------------- | ------------------------------- | ----------------------------------------- |
|
|
127
|
+
| `messages` | `Message[]` | Array of conversation messages |
|
|
128
|
+
| `inProgress` | `boolean` | Whether the agent is currently processing |
|
|
129
|
+
| `sessionId` | `string` | Current conversation session ID |
|
|
130
|
+
| `sessions` | `Session[]` | All sessions for this agent |
|
|
131
|
+
| `debugData` | `Record<string, DebugDataType>` | Debug information indexed by executionId |
|
|
132
|
+
| `handleSend` | `Function` | Send a message to the agent |
|
|
133
|
+
| `switchSession` | `Function` | Switch to a different session |
|
|
134
|
+
| `deleteSession` | `Function` | Delete a session |
|
|
135
|
+
| `addOptimisticMessage` | `Function` | Add message to UI without sending |
|
|
136
|
+
| `abort` | `Function` | Cancel current agent execution |
|
|
137
|
+
|
|
138
|
+
### Session Management
|
|
139
|
+
|
|
140
|
+
```tsx
|
|
141
|
+
function SessionList() {
|
|
142
|
+
const agent = useAgentContext("agent-id", "agent-url");
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<div>
|
|
146
|
+
{agent.sessions.map((session) => (
|
|
147
|
+
<div key={session.id}>
|
|
148
|
+
<button onClick={() => agent.switchSession(session.id)}>
|
|
149
|
+
{session.name || "Untitled Session"}
|
|
150
|
+
</button>
|
|
151
|
+
<button onClick={() => agent.deleteSession(session.id)}>
|
|
152
|
+
Delete
|
|
153
|
+
</button>
|
|
154
|
+
</div>
|
|
155
|
+
))}
|
|
156
|
+
|
|
157
|
+
{/* Create new session */}
|
|
158
|
+
<button onClick={() => agent.switchSession()}>New Session</button>
|
|
159
|
+
</div>
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## File Handling
|
|
165
|
+
|
|
166
|
+
To send files to your agent:
|
|
167
|
+
|
|
168
|
+
### 1. Upload Files to Storage
|
|
169
|
+
|
|
170
|
+
First, upload files to a publicly accessible URL (e.g., Firebase Storage, AWS
|
|
171
|
+
S3):
|
|
172
|
+
|
|
173
|
+
```tsx
|
|
174
|
+
async function uploadFiles(files: File[]): Promise<Record<string, string>> {
|
|
175
|
+
// Upload to your storage provider
|
|
176
|
+
const fileMap: Record<string, string> = {};
|
|
177
|
+
|
|
178
|
+
for (const file of files) {
|
|
179
|
+
const url = await uploadToStorage(file); // Your upload logic
|
|
180
|
+
const fileId = file.name.replace(/[^a-zA-Z0-9]/g, "_").toLowerCase();
|
|
181
|
+
fileMap[fileId] = url;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return fileMap;
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### 2. Send Files with Message
|
|
189
|
+
|
|
190
|
+
```tsx
|
|
191
|
+
function FileUpload() {
|
|
192
|
+
const agent = useAgentContext("agent-id", "agent-url");
|
|
193
|
+
const [files, setFiles] = useState<File[]>([]);
|
|
194
|
+
|
|
195
|
+
const handleSendWithFiles = async () => {
|
|
196
|
+
// Upload files and get URL mapping
|
|
197
|
+
const fileMap = await uploadFiles(files);
|
|
198
|
+
|
|
199
|
+
// Create input with file references
|
|
200
|
+
const fileIds = Object.keys(fileMap).join(", ");
|
|
201
|
+
const input = `Analyze these files: ${fileIds}`;
|
|
202
|
+
|
|
203
|
+
// Send to agent with file context
|
|
204
|
+
await agent.handleSend(input, {
|
|
205
|
+
context: {
|
|
206
|
+
mapped_file_ids_with_url: fileMap,
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
return (
|
|
212
|
+
<div>
|
|
213
|
+
<input
|
|
214
|
+
type="file"
|
|
215
|
+
multiple
|
|
216
|
+
onChange={(e) => setFiles(Array.from(e.target.files || []))}
|
|
217
|
+
/>
|
|
218
|
+
<button onClick={handleSendWithFiles}>Send with Files</button>
|
|
219
|
+
</div>
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### File Context Structure
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
{
|
|
228
|
+
context: {
|
|
229
|
+
mapped_file_ids_with_url: {
|
|
230
|
+
"file_name_pdf": "https://storage.url/file1.pdf",
|
|
231
|
+
"image_png": "https://storage.url/image.png"
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Client Tools / Widgets
|
|
238
|
+
|
|
239
|
+
Client tools allow your agent to render interactive widgets directly in the chat
|
|
240
|
+
interface.
|
|
241
|
+
|
|
242
|
+
### 1. Define Tool Configuration
|
|
243
|
+
|
|
244
|
+
Create tool configurations with Zod schemas:
|
|
245
|
+
|
|
246
|
+
```tsx
|
|
247
|
+
import { z } from "zod";
|
|
248
|
+
|
|
249
|
+
export const chartToolConfig = {
|
|
250
|
+
name: "render_chart",
|
|
251
|
+
description: "Renders a bar chart with the provided data",
|
|
252
|
+
schema: z.object({
|
|
253
|
+
title: z.string().describe("Chart title"),
|
|
254
|
+
data: z
|
|
255
|
+
.array(
|
|
256
|
+
z.object({
|
|
257
|
+
label: z.string(),
|
|
258
|
+
value: z.number(),
|
|
259
|
+
}),
|
|
260
|
+
)
|
|
261
|
+
.describe("Data points for the chart"),
|
|
262
|
+
}),
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
// Create your widget component
|
|
266
|
+
export function ChartWidget({ title, data }) {
|
|
267
|
+
return (
|
|
268
|
+
<div>
|
|
269
|
+
<h3>{title}</h3>
|
|
270
|
+
{/* Your chart rendering logic */}
|
|
271
|
+
</div>
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### 2. Create Widget Registry
|
|
277
|
+
|
|
278
|
+
```tsx
|
|
279
|
+
import type { ComponentType } from "react";
|
|
280
|
+
import type { ClientToolDefinition } from "@buildship/agent";
|
|
281
|
+
import { z } from "zod";
|
|
282
|
+
|
|
283
|
+
// Import all your widgets
|
|
284
|
+
import { ChartWidget, chartToolConfig } from "./widgets/chart";
|
|
285
|
+
import { MapWidget, mapToolConfig } from "./widgets/map";
|
|
286
|
+
|
|
287
|
+
const allConfigs = [chartToolConfig, mapToolConfig];
|
|
288
|
+
|
|
289
|
+
// Registry for rendering widgets
|
|
290
|
+
export const widgetRegistry: Record<string, ComponentType<any>> = {
|
|
291
|
+
[chartToolConfig.name]: ChartWidget,
|
|
292
|
+
[mapToolConfig.name]: MapWidget,
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
// Convert to agent tool definitions
|
|
296
|
+
export function getToolDefinitions(): ClientToolDefinition[] {
|
|
297
|
+
return allConfigs.map((config) => {
|
|
298
|
+
const parameters = z.toJSONSchema(config.schema);
|
|
299
|
+
|
|
300
|
+
// Remove $schema property for LLM compatibility
|
|
301
|
+
if (
|
|
302
|
+
parameters &&
|
|
303
|
+
typeof parameters === "object" &&
|
|
304
|
+
"$schema" in parameters
|
|
305
|
+
) {
|
|
306
|
+
const { $schema, ...rest } = parameters as any;
|
|
307
|
+
return {
|
|
308
|
+
name: config.name,
|
|
309
|
+
description: config.description,
|
|
310
|
+
parameters: rest,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return {
|
|
315
|
+
name: config.name,
|
|
316
|
+
description: config.description,
|
|
317
|
+
parameters,
|
|
318
|
+
};
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### 3. Pass Tools to Agent
|
|
324
|
+
|
|
325
|
+
```tsx
|
|
326
|
+
import { getToolDefinitions } from "./widget-registry";
|
|
327
|
+
|
|
328
|
+
function ChatWithWidgets() {
|
|
329
|
+
const agent = useAgentContext("agent-id", "agent-url");
|
|
330
|
+
const tools = getToolDefinitions();
|
|
331
|
+
|
|
332
|
+
const handleSend = async (input: string) => {
|
|
333
|
+
await agent.handleSend(input, {
|
|
334
|
+
clientTools: tools,
|
|
335
|
+
});
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
return (
|
|
339
|
+
<div>
|
|
340
|
+
<button onClick={() => handleSend("Show me a chart")}>
|
|
341
|
+
Ask for Chart
|
|
342
|
+
</button>
|
|
343
|
+
</div>
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### 4. Render Widgets in Messages
|
|
349
|
+
|
|
350
|
+
```tsx
|
|
351
|
+
import { widgetRegistry } from "./widget-registry";
|
|
352
|
+
|
|
353
|
+
function MessageDisplay() {
|
|
354
|
+
const agent = useAgentContext("agent-id", "agent-url");
|
|
355
|
+
|
|
356
|
+
return (
|
|
357
|
+
<div>
|
|
358
|
+
{agent.messages.map((message) => (
|
|
359
|
+
<div key={message.executionId}>
|
|
360
|
+
{/* Render message parts (text + widgets) */}
|
|
361
|
+
{message.parts?.map((part, idx) => {
|
|
362
|
+
if (part.type === "text") {
|
|
363
|
+
return <p key={idx}>{part.text}</p>;
|
|
364
|
+
} else if (part.type === "widget") {
|
|
365
|
+
const Widget = widgetRegistry[part.toolName];
|
|
366
|
+
if (!Widget) return null;
|
|
367
|
+
return <Widget key={idx} {...part.inputs} />;
|
|
368
|
+
}
|
|
369
|
+
return null;
|
|
370
|
+
})}
|
|
371
|
+
|
|
372
|
+
{/* Fallback: render plain content if no parts */}
|
|
373
|
+
{!message.parts && <p>{message.content}</p>}
|
|
374
|
+
</div>
|
|
375
|
+
))}
|
|
376
|
+
</div>
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
## API Reference
|
|
382
|
+
|
|
383
|
+
### handleSend Options
|
|
384
|
+
|
|
385
|
+
```typescript
|
|
386
|
+
agent.handleSend(input: string, options?: {
|
|
387
|
+
// Custom context data for the agent
|
|
388
|
+
context?: Record<string, unknown>;
|
|
389
|
+
|
|
390
|
+
// Don't add user message to UI (for optimistic updates)
|
|
391
|
+
skipUserMessage?: boolean;
|
|
392
|
+
|
|
393
|
+
// Additional HTTP headers
|
|
394
|
+
additionalHeaders?: Record<string, string>;
|
|
395
|
+
|
|
396
|
+
// Server-side tool definitions
|
|
397
|
+
tools?: ClientToolDefinition[];
|
|
398
|
+
|
|
399
|
+
// Client-side widget definitions
|
|
400
|
+
clientTools?: ClientToolDefinition[];
|
|
401
|
+
})
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### Types
|
|
405
|
+
|
|
406
|
+
#### Message
|
|
407
|
+
|
|
408
|
+
```typescript
|
|
409
|
+
type Message = {
|
|
410
|
+
role: "user" | "agent";
|
|
411
|
+
content: string; // Full text content
|
|
412
|
+
parts?: MessagePart[]; // Structured parts (text + widgets)
|
|
413
|
+
executionId?: string; // Links to debug data
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
type MessagePart =
|
|
417
|
+
| { type: "text"; text: string; firstSequence: number; lastSequence: number }
|
|
418
|
+
| {
|
|
419
|
+
type: "widget";
|
|
420
|
+
toolName: string;
|
|
421
|
+
callId: string;
|
|
422
|
+
inputs: any;
|
|
423
|
+
sequence: number;
|
|
424
|
+
};
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
#### Session
|
|
428
|
+
|
|
429
|
+
```typescript
|
|
430
|
+
type Session = {
|
|
431
|
+
id: string;
|
|
432
|
+
createdAt: number;
|
|
433
|
+
updatedAt: number;
|
|
434
|
+
messages: Message[];
|
|
435
|
+
name?: string;
|
|
436
|
+
};
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
#### ClientToolDefinition
|
|
440
|
+
|
|
441
|
+
```typescript
|
|
442
|
+
type ClientToolDefinition = {
|
|
443
|
+
name: string; // Unique tool identifier
|
|
444
|
+
description: string; // Human-readable description for LLM
|
|
445
|
+
parameters: unknown; // JSON Schema object
|
|
446
|
+
};
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
## Advanced Features
|
|
450
|
+
|
|
451
|
+
### Debug Data
|
|
452
|
+
|
|
453
|
+
Access detailed execution information:
|
|
454
|
+
|
|
455
|
+
```tsx
|
|
456
|
+
function DebugView() {
|
|
457
|
+
const agent = useAgentContext("agent-id", "agent-url");
|
|
458
|
+
|
|
459
|
+
return (
|
|
460
|
+
<div>
|
|
461
|
+
{Object.entries(agent.debugData).map(([executionId, debug]) => (
|
|
462
|
+
<details key={executionId}>
|
|
463
|
+
<summary>Execution {executionId}</summary>
|
|
464
|
+
<pre>{JSON.stringify(debug, null, 2)}</pre>
|
|
465
|
+
</details>
|
|
466
|
+
))}
|
|
467
|
+
</div>
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
### Custom Headers
|
|
473
|
+
|
|
474
|
+
```tsx
|
|
475
|
+
await agent.handleSend("message", {
|
|
476
|
+
additionalHeaders: {
|
|
477
|
+
"X-Custom-Header": "value",
|
|
478
|
+
Authorization: "Bearer token",
|
|
479
|
+
},
|
|
480
|
+
});
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### Abort Requests
|
|
484
|
+
|
|
485
|
+
```tsx
|
|
486
|
+
function CancellableRequest() {
|
|
487
|
+
const agent = useAgentContext("agent-id", "agent-url");
|
|
488
|
+
|
|
489
|
+
return (
|
|
490
|
+
<div>
|
|
491
|
+
<button onClick={() => agent.handleSend("Long task...")}>Start</button>
|
|
492
|
+
<button onClick={() => agent.abort()} disabled={!agent.inProgress}>
|
|
493
|
+
Cancel
|
|
494
|
+
</button>
|
|
495
|
+
</div>
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
### Optimistic Updates
|
|
501
|
+
|
|
502
|
+
```tsx
|
|
503
|
+
function OptimisticMessage() {
|
|
504
|
+
const agent = useAgentContext("agent-id", "agent-url");
|
|
505
|
+
|
|
506
|
+
const handleSend = async (input: string) => {
|
|
507
|
+
// Add message to UI immediately
|
|
508
|
+
agent.addOptimisticMessage(input);
|
|
509
|
+
|
|
510
|
+
// Send to agent without adding duplicate
|
|
511
|
+
await agent.handleSend(input, { skipUserMessage: true });
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
return <button onClick={() => handleSend("Hello")}>Send</button>;
|
|
515
|
+
}
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
## Storage and Persistence
|
|
519
|
+
|
|
520
|
+
The library automatically persists conversations to localStorage:
|
|
521
|
+
|
|
522
|
+
- **Sessions**: Stored under `buildship:agent:conversations`
|
|
523
|
+
- **Debug Data**: Stored under `buildship:agent:debug`
|
|
524
|
+
|
|
525
|
+
Sessions are automatically synced across browser tabs and survive page
|
|
526
|
+
refreshes.
|
|
527
|
+
|
|
528
|
+
## TypeScript Support
|
|
529
|
+
|
|
530
|
+
The package is written in TypeScript and exports all types:
|
|
531
|
+
|
|
532
|
+
```tsx
|
|
533
|
+
import type {
|
|
534
|
+
Message,
|
|
535
|
+
Session,
|
|
536
|
+
ClientToolDefinition,
|
|
537
|
+
AgentRunner,
|
|
538
|
+
DebugDataType,
|
|
539
|
+
MessagePart,
|
|
540
|
+
} from "@buildship/agent";
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
## Best Practices
|
|
544
|
+
|
|
545
|
+
1. **Single Provider**: Only use one `AgentContextProvider` at the root of your
|
|
546
|
+
app
|
|
547
|
+
2. **Tool Descriptions**: Write clear, detailed descriptions for client tools to
|
|
548
|
+
help the LLM understand when to use them
|
|
549
|
+
3. **File URLs**: Ensure file URLs are publicly accessible or use signed URLs
|
|
550
|
+
with sufficient expiration
|
|
551
|
+
4. **Error Handling**: Wrap `handleSend` calls in try-catch blocks for error
|
|
552
|
+
handling
|
|
553
|
+
5. **Widget Registry**: Keep your widget registry centralized for easier
|
|
554
|
+
maintenance
|
|
555
|
+
|
|
556
|
+
## License
|
|
557
|
+
|
|
558
|
+
MIT
|