@useago/sdk 1.4.3 → 1.4.4
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/AgoClient-CLmtYTJx.cjs +8 -0
- package/dist/{AgoClient-S8oeNnAE.js → AgoClient-Ozz2b1dQ.js} +104 -80
- package/dist/angular.cjs +1 -1
- package/dist/angular.d.cts +30 -2
- package/dist/angular.d.ts +30 -2
- package/dist/angular.js +1 -1
- package/dist/{createConversationSession-CmBq7uGi.cjs → createConversationSession-CUkNp6mZ.cjs} +1 -1
- package/dist/{createConversationSession-DoUHdVNR.js → createConversationSession-QIBN0gLx.js} +1 -1
- package/dist/{createFormCollector-Bz_0IJ0Q.cjs → createFormCollector-DHT1ObOb.cjs} +1 -1
- package/dist/{createFormCollector-vy-QvFja.js → createFormCollector-F3YJfsbg.js} +1 -1
- package/dist/devtools.d.cts +29 -1
- package/dist/devtools.d.ts +29 -1
- package/dist/helpers.d.cts +6 -0
- package/dist/helpers.d.ts +6 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +49 -2
- package/dist/index.d.ts +49 -2
- package/dist/index.js +3 -3
- package/dist/react.cjs +2 -2
- package/dist/react.d.cts +35 -1
- package/dist/react.d.ts +35 -1
- package/dist/react.js +100 -98
- package/dist/testing.d.cts +29 -1
- package/dist/testing.d.ts +29 -1
- package/dist/vue.cjs +1 -1
- package/dist/vue.d.cts +35 -1
- package/dist/vue.d.ts +35 -1
- package/dist/vue.js +22 -21
- package/dist/widget.cjs +1 -1
- package/dist/widget.d.cts +29 -1
- package/dist/widget.d.ts +29 -1
- package/dist/widget.js +3 -3
- package/llms-full.txt +1910 -0
- package/package.json +5 -3
- package/dist/AgoClient-CMDVqNaH.cjs +0 -8
package/llms-full.txt
ADDED
|
@@ -0,0 +1,1910 @@
|
|
|
1
|
+
# AGO SDK (@useago/sdk) — coding-agent guide
|
|
2
|
+
|
|
3
|
+
Single-file guide for a coding agent (Cursor, Claude Code, Copilot, Windsurf, ...)
|
|
4
|
+
integrating AGO chat into an app. It has two parts:
|
|
5
|
+
|
|
6
|
+
1. SKILLS — one self-contained section per stack. Jump to the one that matches
|
|
7
|
+
the project, follow it exactly. Each is everything you need to wire AGO in.
|
|
8
|
+
2. CONCEPTS — cross-cutting reference (functions/context, events/streaming,
|
|
9
|
+
configuration) to understand what the skills do.
|
|
10
|
+
|
|
11
|
+
Pick your stack:
|
|
12
|
+
- Embeddable <script> widget, no build step -> "AGO SDK skill: Widget"
|
|
13
|
+
- Plain JavaScript / TypeScript -> "AGO SDK skill: Vanilla"
|
|
14
|
+
- React -> "AGO SDK skill: React"
|
|
15
|
+
- Vue 3 -> "AGO SDK skill: Vue"
|
|
16
|
+
- Angular -> "AGO SDK skill: Angular"
|
|
17
|
+
|
|
18
|
+
Runnable snippets hit the live demo (baseUrl https://playground.api.useago.com,
|
|
19
|
+
agent generic-guide) so they work with zero setup. For a real tenant, swap in
|
|
20
|
+
https://YOUR-DOMAIN.api.useago.com and your own agent slug, and read baseUrl from
|
|
21
|
+
an env var (never hardcode a tenant URL).
|
|
22
|
+
|
|
23
|
+
Source: https://github.com/useago/ago-sdk
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
# SKILLS
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
<!-- source: docs/agents/ago-widget.md -->
|
|
32
|
+
|
|
33
|
+
# AGO SDK skill: embeddable `<script>` widget
|
|
34
|
+
|
|
35
|
+
You are adding the AGO chat widget to a website with a `<script>` tag. No build
|
|
36
|
+
step and no npm install. This is the right approach for a marketing site, a CMS
|
|
37
|
+
(WordPress, Webflow), or any server-rendered page where you don't control a JS
|
|
38
|
+
bundle. For a React/Vue/Angular app, use the framework binding instead. This file
|
|
39
|
+
is everything you need. Follow it exactly.
|
|
40
|
+
|
|
41
|
+
## Endpoint
|
|
42
|
+
|
|
43
|
+
Set `basepath` to the project's AGO instance, `https://YOUR-DOMAIN.api.useago.com`.
|
|
44
|
+
Grab the widget API key and the recommended `integrity` hash from the AGO
|
|
45
|
+
dashboard, and add the embedding domain to the dashboard's allowed-domains list.
|
|
46
|
+
|
|
47
|
+
## 1. Add the widget script
|
|
48
|
+
|
|
49
|
+
Configure `window.AGO` BEFORE loading the script, and place both tags just before
|
|
50
|
+
the closing `</body>` tag (not in `<head>`).
|
|
51
|
+
|
|
52
|
+
```html
|
|
53
|
+
<script>
|
|
54
|
+
window.AGO = {
|
|
55
|
+
basepath: "https://YOUR-DOMAIN.api.useago.com",
|
|
56
|
+
widgetApiKey: "YOUR-WIDGET-API-KEY",
|
|
57
|
+
defaultAgent: "support-bot",
|
|
58
|
+
title: "Support",
|
|
59
|
+
prompt: "Hi! How can I help?",
|
|
60
|
+
};
|
|
61
|
+
</script>
|
|
62
|
+
<script
|
|
63
|
+
async
|
|
64
|
+
src="https://useago.github.io/widgetjs/frame.js"
|
|
65
|
+
crossorigin="anonymous"
|
|
66
|
+
></script>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
A floating chat bubble appears in the bottom-right corner. The panel itself is an
|
|
70
|
+
iframe, so it cannot leak styles into the host page.
|
|
71
|
+
|
|
72
|
+
## 2. Customize appearance
|
|
73
|
+
|
|
74
|
+
```html
|
|
75
|
+
<script>
|
|
76
|
+
window.AGO = {
|
|
77
|
+
basepath: "https://YOUR-DOMAIN.api.useago.com",
|
|
78
|
+
widgetApiKey: "YOUR-WIDGET-API-KEY",
|
|
79
|
+
title: "Support",
|
|
80
|
+
icon: "https://example.com/logo.svg",
|
|
81
|
+
colors: {
|
|
82
|
+
button: "#4F46E5",
|
|
83
|
+
header: "#4F46E5",
|
|
84
|
+
userMessage: "#4F46E5",
|
|
85
|
+
userMessageFont: "#FFFFFF",
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
</script>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## 3. Identify the user (optional)
|
|
92
|
+
|
|
93
|
+
```html
|
|
94
|
+
<script>
|
|
95
|
+
window.AGO = {
|
|
96
|
+
basepath: "https://YOUR-DOMAIN.api.useago.com",
|
|
97
|
+
widgetApiKey: "YOUR-WIDGET-API-KEY",
|
|
98
|
+
email: "user@example.com",
|
|
99
|
+
jwt: "jwt-token-here",
|
|
100
|
+
metadata: { plan: "pro", company: "Acme Inc" },
|
|
101
|
+
};
|
|
102
|
+
</script>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## 4. Framework-specific placement
|
|
106
|
+
|
|
107
|
+
**Next.js (App Router)**, in `app/layout.tsx`:
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
export default function RootLayout({ children }) {
|
|
111
|
+
return (
|
|
112
|
+
<html>
|
|
113
|
+
<body>
|
|
114
|
+
{children}
|
|
115
|
+
<script
|
|
116
|
+
dangerouslySetInnerHTML={{
|
|
117
|
+
__html: `window.AGO = { basepath: "${process.env.NEXT_PUBLIC_AGO_BASE_URL}", widgetApiKey: "${process.env.NEXT_PUBLIC_AGO_WIDGET_KEY}" };`,
|
|
118
|
+
}}
|
|
119
|
+
/>
|
|
120
|
+
<script async src="https://useago.github.io/widgetjs/frame.js" crossOrigin="anonymous" />
|
|
121
|
+
</body>
|
|
122
|
+
</html>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**WordPress**: add both tags via the theme's `footer.php` or a "Custom HTML"
|
|
128
|
+
widget placed in a footer area.
|
|
129
|
+
|
|
130
|
+
**Webflow**: Project Settings > Custom Code > Footer Code.
|
|
131
|
+
|
|
132
|
+
## 5. Type the config (TypeScript projects)
|
|
133
|
+
|
|
134
|
+
The SDK ships the types and augments the global `Window`, so `window.AGO` is typed
|
|
135
|
+
anywhere this import is in scope:
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
import type { AgoWidgetConfig } from "@useago/sdk/widget";
|
|
139
|
+
|
|
140
|
+
const config: AgoWidgetConfig = {
|
|
141
|
+
basepath: "https://YOUR-DOMAIN.api.useago.com",
|
|
142
|
+
widgetApiKey: "YOUR-WIDGET-API-KEY",
|
|
143
|
+
defaultAgent: "support-bot",
|
|
144
|
+
};
|
|
145
|
+
window.AGO = config;
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## `AgoWidgetConfig` reference
|
|
149
|
+
|
|
150
|
+
| Field | Type | Description |
|
|
151
|
+
| ---------------- | ------- | ----------- |
|
|
152
|
+
| `basepath` | string | AGO instance URL (required) |
|
|
153
|
+
| `widgetApiKey` | string | Widget API key from the dashboard (required) |
|
|
154
|
+
| `defaultAgent?` | string | Agent id/slug to start conversations with |
|
|
155
|
+
| `email?` | string | Identify the end user |
|
|
156
|
+
| `title?` | string | Header title |
|
|
157
|
+
| `icon?` | string | Launcher/header icon URL |
|
|
158
|
+
| `prompt?` | string | Greeting / opening message |
|
|
159
|
+
| `notifications?` | boolean | Enable a proactive notification bubble |
|
|
160
|
+
| `colors?` | object | `button`, `header`, `agentMessage`, `background`, `font`, `userMessage`, ... |
|
|
161
|
+
| `hideFooter?` | boolean | Hide the "powered by" footer |
|
|
162
|
+
| `jwt?` | string | Authenticated-session token |
|
|
163
|
+
| `metadata?` | object | Arbitrary key-value pairs sent with the session |
|
|
164
|
+
|
|
165
|
+
## When to use `mountChatWidget` instead
|
|
166
|
+
|
|
167
|
+
If the project is a pure TS/JS app where you DO control the DOM (but use no
|
|
168
|
+
framework), prefer `mountChatWidget` from `@useago/sdk/widget`: it renders an
|
|
169
|
+
inline panel instead of a floating iframe and supports conversational forms. See
|
|
170
|
+
the plain JS/TS skill for that.
|
|
171
|
+
|
|
172
|
+
## Checklist before you finish
|
|
173
|
+
|
|
174
|
+
1. The `window.AGO` config script comes BEFORE the `frame.js` script.
|
|
175
|
+
2. Both tags are just before `</body>`, not in `<head>`.
|
|
176
|
+
3. `basepath` and `widgetApiKey` are set.
|
|
177
|
+
4. The embedding domain is on the dashboard's allowed-domains list.
|
|
178
|
+
5. Reload the page and confirm the chat bubble appears bottom-right; check the
|
|
179
|
+
browser console for errors if it doesn't.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
<!-- source: docs/agents/ago-vanilla.md -->
|
|
184
|
+
|
|
185
|
+
# AGO SDK skill: plain JavaScript / TypeScript
|
|
186
|
+
|
|
187
|
+
You are integrating the AGO SDK (`@useago/sdk`) into a plain JS/TS app (no React,
|
|
188
|
+
Vue, or Angular), a Node service, or a web component. AGO is a chat agent that can
|
|
189
|
+
answer questions, run functions in the user's browser, and navigate your app. The
|
|
190
|
+
core `AgoClient` documented here is what every framework binding wraps. This file
|
|
191
|
+
is everything you need. Follow it exactly.
|
|
192
|
+
|
|
193
|
+
## Endpoints
|
|
194
|
+
|
|
195
|
+
Use the live demo for anything runnable so it answers with zero setup:
|
|
196
|
+
|
|
197
|
+
```
|
|
198
|
+
baseUrl: https://playground.api.useago.com
|
|
199
|
+
agent: generic-guide
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
When the project has its own tenant, swap to `https://YOUR-DOMAIN.api.useago.com`
|
|
203
|
+
and the project's own agent slug. Read `baseUrl` from an env var, never hardcode a
|
|
204
|
+
real tenant URL.
|
|
205
|
+
|
|
206
|
+
## Install
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
npm install @useago/sdk
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## 1. Create a client
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
import { AgoClient } from "@useago/sdk";
|
|
216
|
+
|
|
217
|
+
const ago = new AgoClient({
|
|
218
|
+
baseUrl: "https://playground.api.useago.com", // required
|
|
219
|
+
agent: "generic-guide", // optional default agent
|
|
220
|
+
userEmail: "jane@acme.com", // optional end-user identity
|
|
221
|
+
debug: true, // optional verbose logging
|
|
222
|
+
});
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
If the page already exposes config (a `window.AGO` object, `<meta name="ago-base-url">`
|
|
226
|
+
tags, or `data-ago-*` attributes), let the SDK find it with `createAgo()` (throws
|
|
227
|
+
if it can't resolve a `baseUrl`).
|
|
228
|
+
|
|
229
|
+
## 2. Send a message and stream the reply
|
|
230
|
+
|
|
231
|
+
`sendMessage` resolves with the final assistant message, but the text arrives
|
|
232
|
+
incrementally through events. Subscribe BEFORE you send.
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
const outputEl = document.getElementById("output");
|
|
236
|
+
|
|
237
|
+
ago.on("message:start", ({ messageId }) => console.log("New message", messageId));
|
|
238
|
+
ago.on("message:chunk", ({ content }) => { outputEl.textContent += content; }); // token-by-token
|
|
239
|
+
ago.on("message:complete", (message) => console.log("sources:", message.sources));
|
|
240
|
+
ago.on("message:error", ({ error }) => console.error(error));
|
|
241
|
+
|
|
242
|
+
const reply = await ago.sendMessage("How do I reset my password?");
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Continue a conversation by reusing `conversationId`:
|
|
246
|
+
|
|
247
|
+
```ts
|
|
248
|
+
const first = await ago.sendMessage("Hi");
|
|
249
|
+
await ago.sendMessage("Tell me more", { conversationId: first.conversationId });
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Attach files (`File[]`, sent as multipart) and override the agent per message:
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
await ago.sendMessage("Summarise this", { files: [fileInput.files[0]], conversationId });
|
|
256
|
+
await ago.sendMessage("Escalate this", { agentId: "human-handoff" });
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## 3. Conversations
|
|
260
|
+
|
|
261
|
+
```ts
|
|
262
|
+
const conversations = await ago.getConversations(); // [{ id, title, lastMessageDate }]
|
|
263
|
+
const thread = await ago.getConversation(conversations[0].id); // includes messages
|
|
264
|
+
const messages = await ago.getMessages(conversations[0].id);
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## 4. Let the agent run code in the browser
|
|
268
|
+
|
|
269
|
+
```ts
|
|
270
|
+
ago.registerFunction({
|
|
271
|
+
name: "lookupOrder",
|
|
272
|
+
description: "Look up an order by its ID",
|
|
273
|
+
parameters: {
|
|
274
|
+
type: "object",
|
|
275
|
+
properties: { id: { type: "string", description: "Order ID" } },
|
|
276
|
+
required: ["id"],
|
|
277
|
+
},
|
|
278
|
+
handler: async (args) => fetchOrder(args.id as string),
|
|
279
|
+
});
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Rules for functions:
|
|
283
|
+
- Every parameter property needs a `description`. The agent reads it to decide
|
|
284
|
+
what to pass.
|
|
285
|
+
- `required` lists only the params the function truly needs.
|
|
286
|
+
- The handler must return serializable data (no DOM nodes, no circular refs).
|
|
287
|
+
|
|
288
|
+
Navigation is a built-in convenience:
|
|
289
|
+
|
|
290
|
+
```ts
|
|
291
|
+
ago.registerNavigationFunction((path) => (window.location.href = path), [
|
|
292
|
+
{ name: "pricing", path: "/pricing", description: "Pricing page" },
|
|
293
|
+
{ name: "docs", path: "/docs", description: "Documentation" },
|
|
294
|
+
]);
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Page state is the mirror: let the agent change the current page's state
|
|
298
|
+
(filters, sort, view mode…) and read it back via each control's `get()`. One
|
|
299
|
+
synthesized `setPageState` function, one optional property per control.
|
|
300
|
+
|
|
301
|
+
```ts
|
|
302
|
+
ago.registerPageStateFunction([
|
|
303
|
+
{
|
|
304
|
+
name: "statusFilter",
|
|
305
|
+
description: "Filter the list by status",
|
|
306
|
+
schema: { type: "string", enum: ["all", "paid", "overdue"] },
|
|
307
|
+
get: () => filters.status,
|
|
308
|
+
set: (v) => { filters.status = v as string; },
|
|
309
|
+
},
|
|
310
|
+
]);
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## 5. Give the agent context
|
|
314
|
+
|
|
315
|
+
```ts
|
|
316
|
+
ago.setContext("order-page", {
|
|
317
|
+
name: "Order detail",
|
|
318
|
+
description: "User is viewing an order",
|
|
319
|
+
data: { orderId: "123", status: "shipped" },
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
// Re-evaluated on every send, great for live state:
|
|
323
|
+
ago.addDynamicContext("cart", () => ({ name: "Cart", data: { itemCount: cart.length } }));
|
|
324
|
+
|
|
325
|
+
// Attach the current URL + page title:
|
|
326
|
+
ago.enableAutoPageContext();
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Hold shared state both the agent's functions and your UI mutate with
|
|
330
|
+
`createStore` (a tiny `get` / `set` / `subscribe` holder):
|
|
331
|
+
|
|
332
|
+
```ts
|
|
333
|
+
import { createStore } from "@useago/sdk";
|
|
334
|
+
|
|
335
|
+
const store = createStore({ items: [] as string[] });
|
|
336
|
+
store.subscribe((state) => render(state));
|
|
337
|
+
ago.addDynamicContext("cart", () => ({ name: "Cart", data: store.get() }));
|
|
338
|
+
store.set({ items: [...store.get().items, "SKU-1"] });
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
Pass `{ key: "cart" }` as a second arg to persist across reloads (localStorage by
|
|
342
|
+
default; pass `storage: sessionStorage` to swap).
|
|
343
|
+
|
|
344
|
+
## 6. Drop-in chat panel: `mountChatWidget`
|
|
345
|
+
|
|
346
|
+
A complete chat panel (header, streaming messages, input) you mount into any DOM
|
|
347
|
+
element, no framework. Supports conversational forms and clickable suggested
|
|
348
|
+
replies, same as the React `<ChatWidget>`.
|
|
349
|
+
|
|
350
|
+
```ts
|
|
351
|
+
import { mountChatWidget } from "@useago/sdk/widget";
|
|
352
|
+
|
|
353
|
+
const widget = mountChatWidget("#chat", {
|
|
354
|
+
config: { baseUrl: "https://playground.api.useago.com", agent: "generic-guide" },
|
|
355
|
+
title: "Ask me anything",
|
|
356
|
+
welcomeMessage: "Hi! How can I help?",
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
widget.sendMessage("Hello");
|
|
360
|
+
widget.destroy(); // removes listeners, uninstalls forms, clears the DOM
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
Set `placement: "left"` or `"right"` for a fixed full-height side panel with a
|
|
364
|
+
launcher button. Set `persistConversation: true` to resume the visitor's last
|
|
365
|
+
thread across reloads. Theme it with the `theme` option or `--ago-*` CSS variables
|
|
366
|
+
on `.ago-chat-widget`. Message text renders as GitHub-flavored markdown and is
|
|
367
|
+
HTML-escaped before it touches the DOM.
|
|
368
|
+
|
|
369
|
+
For a fully custom UI, build from `sendMessage` + the `message:chunk` event (see
|
|
370
|
+
section 2); `renderMarkdown(source)` is exported if you want the same markdown
|
|
371
|
+
rendering.
|
|
372
|
+
|
|
373
|
+
## 7. Tool calls, feedback, lifecycle
|
|
374
|
+
|
|
375
|
+
```ts
|
|
376
|
+
await ago.submitToolCallForm(toolCallId, { quantity: 3 });
|
|
377
|
+
await ago.confirmToolCall(toolCallId);
|
|
378
|
+
await ago.rejectToolCall(toolCallId);
|
|
379
|
+
await ago.submitFeedback(messageId, "positive");
|
|
380
|
+
ago.updateConfig({ userJwt: token }); // e.g. after login
|
|
381
|
+
ago.destroy(); // clean up listeners, functions, context
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
## Key events
|
|
385
|
+
|
|
386
|
+
`message:start`, `message:chunk` (`{ content }` per token), `message:complete`
|
|
387
|
+
(`AgoMessage`), `message:error`, `conversation:loaded`, `context:changed`,
|
|
388
|
+
`toolCall:received`, `function:invoke`, `function:result`, `connection:status`.
|
|
389
|
+
Use `on` / `off` / `once`, or `await ago.waitFor("message:complete", { timeout })`.
|
|
390
|
+
|
|
391
|
+
## Key types
|
|
392
|
+
|
|
393
|
+
```ts
|
|
394
|
+
interface AgoMessage {
|
|
395
|
+
id: string;
|
|
396
|
+
conversationId: string;
|
|
397
|
+
content: string;
|
|
398
|
+
role: "user" | "assistant";
|
|
399
|
+
status: "IN_PROGRESS" | "DONE" | "ERROR" | "TODO" | "CANCELED";
|
|
400
|
+
sources?: AgoSource[]; // knowledge-base citations { id, title, url? }
|
|
401
|
+
toolCalls?: ToolCallData[];
|
|
402
|
+
followUpReplies?: string[]; // suggested next questions
|
|
403
|
+
createdAt: Date;
|
|
404
|
+
}
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
All types are exported from `@useago/sdk`.
|
|
408
|
+
|
|
409
|
+
## Checklist before you finish
|
|
410
|
+
|
|
411
|
+
1. Event subscriptions are added BEFORE the first `sendMessage`.
|
|
412
|
+
2. `baseUrl` comes from an env var, not a hardcoded tenant URL.
|
|
413
|
+
3. Every registered function's parameters have descriptions and the handler
|
|
414
|
+
returns serializable data.
|
|
415
|
+
4. `ago.destroy()` runs when the client is no longer needed.
|
|
416
|
+
5. Run the project's typecheck and lint.
|
|
417
|
+
|
|
418
|
+
---
|
|
419
|
+
|
|
420
|
+
<!-- source: docs/agents/ago-react.md -->
|
|
421
|
+
|
|
422
|
+
# AGO SDK skill: React
|
|
423
|
+
|
|
424
|
+
You are integrating the AGO SDK (`@useago/sdk`) into a React app. AGO is a chat
|
|
425
|
+
agent that can answer questions, run functions in the user's browser, and
|
|
426
|
+
navigate your app's routes. This file is everything you need. Follow it exactly.
|
|
427
|
+
|
|
428
|
+
## Endpoints
|
|
429
|
+
|
|
430
|
+
Use the live demo for anything runnable so it answers with zero setup:
|
|
431
|
+
|
|
432
|
+
```
|
|
433
|
+
baseUrl: https://playground.api.useago.com
|
|
434
|
+
agent: generic-guide
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
When the project has its own tenant, swap to `https://YOUR-DOMAIN.api.useago.com`
|
|
438
|
+
and the project's own agent slug. Read `baseUrl` from an env var, never hardcode a
|
|
439
|
+
real tenant URL. React `>=17` is an optional peer dependency of the SDK; the app
|
|
440
|
+
already provides it.
|
|
441
|
+
|
|
442
|
+
## Install
|
|
443
|
+
|
|
444
|
+
```bash
|
|
445
|
+
npm install @useago/sdk
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
## 1. Wrap the app in `<AgoProvider>`
|
|
449
|
+
|
|
450
|
+
The provider creates one `AgoClient` and shares it with every hook and component.
|
|
451
|
+
|
|
452
|
+
```tsx
|
|
453
|
+
import { AgoProvider } from "@useago/sdk/react";
|
|
454
|
+
|
|
455
|
+
function Root() {
|
|
456
|
+
return (
|
|
457
|
+
<AgoProvider baseUrl="https://playground.api.useago.com" agent="generic-guide">
|
|
458
|
+
<App />
|
|
459
|
+
</AgoProvider>
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
It can also wire app-wide tools, pre-built helpers, and page context:
|
|
465
|
+
|
|
466
|
+
```tsx
|
|
467
|
+
<AgoProvider
|
|
468
|
+
baseUrl={import.meta.env.VITE_AGO_BASE_URL}
|
|
469
|
+
tools={[lookupOrder, cancelOrder]} // registered app-wide
|
|
470
|
+
helpers={{ copyToClipboard: true, showToast: (a) => toast(a.message) }}
|
|
471
|
+
pageContext="auto" // auto-capture URL + title
|
|
472
|
+
>
|
|
473
|
+
<App />
|
|
474
|
+
</AgoProvider>
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
Pass `client={myClient}` to bring your own client (handy in tests).
|
|
478
|
+
|
|
479
|
+
## 2. Fastest UI: `<ChatWidget>`
|
|
480
|
+
|
|
481
|
+
A complete, styled chat panel. Drop it anywhere under the provider.
|
|
482
|
+
|
|
483
|
+
```tsx
|
|
484
|
+
import { ChatWidget } from "@useago/sdk/react";
|
|
485
|
+
|
|
486
|
+
function Support() {
|
|
487
|
+
return (
|
|
488
|
+
<ChatWidget
|
|
489
|
+
title="Support"
|
|
490
|
+
welcomeMessage="Hi! How can I help?"
|
|
491
|
+
placeholder="Ask anything..."
|
|
492
|
+
height={600}
|
|
493
|
+
allowFiles
|
|
494
|
+
/>
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
Common props: `title`, `welcomeMessage`, `placeholder`, `allowFiles`, `height`,
|
|
500
|
+
`logoUrl`, `showAgentName`, `forms`, `onMessageSent`, `onMessageReceived`,
|
|
501
|
+
`onFollowUpClick`, `className`. When the agent returns follow-up suggestions the
|
|
502
|
+
widget renders them as clickable buttons (clicking sends the reply by default).
|
|
503
|
+
|
|
504
|
+
`ChatWidget` is a high-level component. For a custom interface, use `useChat`.
|
|
505
|
+
|
|
506
|
+
## 3. Custom UI with `useChat`
|
|
507
|
+
|
|
508
|
+
All-in-one state for a custom chat interface (composes `useMessages` +
|
|
509
|
+
`useConversation`).
|
|
510
|
+
|
|
511
|
+
```tsx
|
|
512
|
+
import { useChat } from "@useago/sdk/react";
|
|
513
|
+
|
|
514
|
+
function Chat() {
|
|
515
|
+
const { messages, sendMessage, isLoading, error } = useChat();
|
|
516
|
+
|
|
517
|
+
return (
|
|
518
|
+
<div>
|
|
519
|
+
{messages.map((m) => (
|
|
520
|
+
<p key={m.id}><b>{m.role}:</b> {m.content}</p>
|
|
521
|
+
))}
|
|
522
|
+
<button onClick={() => sendMessage("Hello!")} disabled={isLoading}>Send</button>
|
|
523
|
+
{error && <p role="alert">{error.message}</p>}
|
|
524
|
+
</div>
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
`messages` updates token-by-token as the reply streams (the optimistic user
|
|
530
|
+
message is included). `sendMessage(content, files?)` resolves with the final
|
|
531
|
+
message or `null` on error.
|
|
532
|
+
|
|
533
|
+
Show the knowledge sources the agent retrieved via `m.sources` (an `AgoSource[]`,
|
|
534
|
+
each `{ id, title, url? }`):
|
|
535
|
+
|
|
536
|
+
```tsx
|
|
537
|
+
{m.sources?.map((s) => (
|
|
538
|
+
<a key={s.id} href={s.url} target="_blank" rel="noreferrer">{s.title || s.url}</a>
|
|
539
|
+
))}
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
Finer-grained hooks: `useMessages({ conversationId? })`,
|
|
543
|
+
`useConversation({ autoLoad? })`. All hooks read the client from context; pass
|
|
544
|
+
`{ client }` to override. Get the raw client with `useAgoClient()` (throws
|
|
545
|
+
outside a provider) or `useOptionalAgoClient()`.
|
|
546
|
+
|
|
547
|
+
## 4. Let the agent call your code: `useAgoFunction`
|
|
548
|
+
|
|
549
|
+
Registers a client-side function on mount, cleans it up on unmount.
|
|
550
|
+
|
|
551
|
+
```tsx
|
|
552
|
+
import { useAgoFunction } from "@useago/sdk/react";
|
|
553
|
+
|
|
554
|
+
useAgoFunction({
|
|
555
|
+
name: "lookupOrder",
|
|
556
|
+
description: "Look up an order by ID",
|
|
557
|
+
parameters: {
|
|
558
|
+
type: "object",
|
|
559
|
+
properties: { id: { type: "string", description: "Order ID" } },
|
|
560
|
+
required: ["id"],
|
|
561
|
+
},
|
|
562
|
+
handler: async (args) => fetchOrder(args.id as string),
|
|
563
|
+
});
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
Rules for functions:
|
|
567
|
+
- Every parameter property needs a `description`. The agent reads it to decide
|
|
568
|
+
what to pass.
|
|
569
|
+
- `required` lists only the params the function truly needs.
|
|
570
|
+
- The handler must return serializable data (no DOM nodes, no circular refs).
|
|
571
|
+
|
|
572
|
+
Reuse a definition made with `defineFunction` from `@useago/sdk`:
|
|
573
|
+
|
|
574
|
+
```tsx
|
|
575
|
+
const lookupOrder = defineFunction({ name: "lookupOrder", /* ... */ });
|
|
576
|
+
useAgoFunction(lookupOrder);
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
## 5. Let the agent navigate: `useAgoNavigation`
|
|
580
|
+
|
|
581
|
+
Wire AGO into your router. The agent calls your `navigate`, so guards, auth
|
|
582
|
+
redirects, and lazy loading keep working.
|
|
583
|
+
|
|
584
|
+
Define each route once. React Router's `<Route>` has no `description` prop, so
|
|
585
|
+
keep one route table and read both the router and `useAgoNavigation` off it
|
|
586
|
+
instead of duplicating paths.
|
|
587
|
+
|
|
588
|
+
```tsx
|
|
589
|
+
import { Routes, Route, useNavigate } from "react-router-dom";
|
|
590
|
+
import { useAgoNavigation } from "@useago/sdk/react";
|
|
591
|
+
|
|
592
|
+
const ROUTES = {
|
|
593
|
+
dashboard: { name: "dashboard", path: "/dashboard", description: "KPIs and recent activity" },
|
|
594
|
+
invoices: { name: "invoices", path: "/invoices", description: "List and download invoices" },
|
|
595
|
+
} as const;
|
|
596
|
+
|
|
597
|
+
function AppShell() {
|
|
598
|
+
const navigate = useNavigate();
|
|
599
|
+
useAgoNavigation(navigate, Object.values(ROUTES)); // agent reads paths + descriptions
|
|
600
|
+
|
|
601
|
+
return (
|
|
602
|
+
<Routes>
|
|
603
|
+
<Route path={ROUTES.dashboard.path} element={<Dashboard />} />
|
|
604
|
+
<Route path={ROUTES.invoices.path} element={<Invoices />} />
|
|
605
|
+
</Routes>
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
The `description` is what the agent reads to pick a page. Write it like you'd
|
|
611
|
+
explain the page to a colleague. A `/invoices/:id` detail page can be registered
|
|
612
|
+
as-is: `:id` becomes a top-level `id` argument of `navigateToPage`, so one
|
|
613
|
+
route covers every record.
|
|
614
|
+
|
|
615
|
+
## 5b. Let the agent change the page: `useAgoPageState`
|
|
616
|
+
|
|
617
|
+
The mirror of navigation. Let the agent change the current page's state
|
|
618
|
+
(filters, sort, view mode…) and read it back. Each control becomes one optional
|
|
619
|
+
property of a single synthesized `setPageState` function, and every control's
|
|
620
|
+
`get()` value is sent as context so the agent knows the state before changing it.
|
|
621
|
+
|
|
622
|
+
```tsx
|
|
623
|
+
import { useAgoPageState } from "@useago/sdk/react";
|
|
624
|
+
|
|
625
|
+
const [status, setStatus] = useState("all");
|
|
626
|
+
useAgoPageState([
|
|
627
|
+
{
|
|
628
|
+
name: "statusFilter",
|
|
629
|
+
description: "Filter the list by invoice status",
|
|
630
|
+
schema: { type: "string", enum: ["all", "paid", "overdue"] },
|
|
631
|
+
get: () => status,
|
|
632
|
+
set: setStatus,
|
|
633
|
+
},
|
|
634
|
+
]);
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
## 6. Give the agent context: `useAgoContext`
|
|
638
|
+
|
|
639
|
+
Expose what the user is looking at, sent with every message.
|
|
640
|
+
|
|
641
|
+
```tsx
|
|
642
|
+
// Static, captured from props/state
|
|
643
|
+
useAgoContext({
|
|
644
|
+
name: "Order detail",
|
|
645
|
+
description: "The user is viewing a specific order",
|
|
646
|
+
data: { orderId: order.id, status: order.status },
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
// Dynamic, re-evaluated on every send (fresh data from a store)
|
|
650
|
+
useAgoContext(() => ({ name: "App shell", data: { userId: store.getState().auth.userId } }));
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
## 7. Reactive external state: `useAgoStore`
|
|
654
|
+
|
|
655
|
+
If shared state lives in a core `createStore`, `useAgoStore` reads it reactively
|
|
656
|
+
(re-renders on every `store.set`). It is SSR-safe.
|
|
657
|
+
|
|
658
|
+
```tsx
|
|
659
|
+
import { createStore } from "@useago/sdk";
|
|
660
|
+
import { useAgoStore } from "@useago/sdk/react";
|
|
661
|
+
|
|
662
|
+
const cart = createStore({ items: [] as string[] });
|
|
663
|
+
|
|
664
|
+
function CartBadge() {
|
|
665
|
+
const { items } = useAgoStore(cart);
|
|
666
|
+
return <span>{items.length}</span>;
|
|
667
|
+
}
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
## 8. Events
|
|
671
|
+
|
|
672
|
+
Use the client from `useAgoClient()` with `on` / `off` in an effect:
|
|
673
|
+
|
|
674
|
+
```tsx
|
|
675
|
+
useEffect(() => {
|
|
676
|
+
const handler = (m) => toast(`AGO: ${m.content}`);
|
|
677
|
+
client.on("message:complete", handler);
|
|
678
|
+
return () => client.off("message:complete", handler);
|
|
679
|
+
}, [client]);
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
Key events: `message:start`, `message:chunk` (`{ content }` per token),
|
|
683
|
+
`message:complete` (`AgoMessage`), `message:error`, `toolCall:received`,
|
|
684
|
+
`function:invoke`, `function:result`.
|
|
685
|
+
|
|
686
|
+
## Exports cheat-sheet (`@useago/sdk/react`)
|
|
687
|
+
|
|
688
|
+
- Provider/context: `AgoProvider`, `useAgoClient`, `useOptionalAgoClient`
|
|
689
|
+
- Hooks: `useAgo`, `useChat`, `useMessages`, `useConversation`, `useAgoFunction`,
|
|
690
|
+
`useAgoNavigation`, `useAgoPageState`, `useAgoContext`, `useAgoStore`, `useFormCollector`
|
|
691
|
+
- Components: `ChatWidget`, `Message`, `ChatInput`, `Markdown`
|
|
692
|
+
- Forms: `createFormCollector`
|
|
693
|
+
- Testing: `createMockClient`
|
|
694
|
+
- Types: `AgoConfig`, `AgoMessage`, `Conversation`, `AgoAgent`, `AgoSource`,
|
|
695
|
+
`ToolCallData` (import `AgoAttachment` from `@useago/sdk`, not the `/react` subpath)
|
|
696
|
+
|
|
697
|
+
## Checklist before you finish
|
|
698
|
+
|
|
699
|
+
1. `AgoProvider` wraps the component tree.
|
|
700
|
+
2. `baseUrl` comes from an env var, not a hardcoded tenant URL.
|
|
701
|
+
3. Every registered function's parameters have descriptions and the handler
|
|
702
|
+
returns serializable data.
|
|
703
|
+
4. Run `npm run typecheck` and `npm run lint` (or the project's equivalents).
|
|
704
|
+
|
|
705
|
+
---
|
|
706
|
+
|
|
707
|
+
<!-- source: docs/agents/ago-vue.md -->
|
|
708
|
+
|
|
709
|
+
# AGO SDK skill: Vue 3
|
|
710
|
+
|
|
711
|
+
You are integrating the AGO SDK (`@useago/sdk`) into a Vue 3 app. AGO is a chat
|
|
712
|
+
agent that can answer questions, run functions in the user's browser, and
|
|
713
|
+
navigate your app's routes. This file is everything you need. Follow it exactly.
|
|
714
|
+
|
|
715
|
+
## Endpoints
|
|
716
|
+
|
|
717
|
+
Use the live demo for anything runnable so it answers with zero setup:
|
|
718
|
+
|
|
719
|
+
```
|
|
720
|
+
baseUrl: https://playground.api.useago.com
|
|
721
|
+
agent: generic-guide
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
When the project has its own tenant, swap to `https://YOUR-DOMAIN.api.useago.com`
|
|
725
|
+
and the project's own agent slug. Read `baseUrl` from an env var, never hardcode a
|
|
726
|
+
real tenant URL. Vue `>=3.3` is an optional peer dependency. Composables use the
|
|
727
|
+
Composition API and must be called from `setup` / `<script setup>`.
|
|
728
|
+
|
|
729
|
+
## Install
|
|
730
|
+
|
|
731
|
+
```bash
|
|
732
|
+
npm install @useago/sdk
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
## 1. Install the plugin
|
|
736
|
+
|
|
737
|
+
`AgoPlugin` creates one `AgoClient` and provides it to the whole app.
|
|
738
|
+
|
|
739
|
+
```ts
|
|
740
|
+
import { createApp } from "vue";
|
|
741
|
+
import { AgoPlugin } from "@useago/sdk/vue";
|
|
742
|
+
import App from "./App.vue";
|
|
743
|
+
|
|
744
|
+
createApp(App)
|
|
745
|
+
.use(AgoPlugin, {
|
|
746
|
+
baseUrl: "https://playground.api.useago.com",
|
|
747
|
+
agent: "generic-guide",
|
|
748
|
+
})
|
|
749
|
+
.mount("#app");
|
|
750
|
+
```
|
|
751
|
+
|
|
752
|
+
Prefer no plugin? Every composable accepts an explicit client, or create one
|
|
753
|
+
standalone with `useAgo`:
|
|
754
|
+
|
|
755
|
+
```ts
|
|
756
|
+
import { useAgo } from "@useago/sdk/vue";
|
|
757
|
+
|
|
758
|
+
const client = useAgo(); // from plugin
|
|
759
|
+
const standalone = useAgo({ baseUrl: "https://..." }); // no plugin needed
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
There is no polished `<ChatWidget>` component in Vue (it is React-only). Build the
|
|
763
|
+
UI from `useChat`; it is a handful of lines, shown below.
|
|
764
|
+
|
|
765
|
+
## 2. Build a chat with `useChat`
|
|
766
|
+
|
|
767
|
+
All-in-one reactive state (messages + conversations). Everything returned is a
|
|
768
|
+
`ref`/`computed`, so it stays reactive. `messages` updates token-by-token.
|
|
769
|
+
|
|
770
|
+
```vue
|
|
771
|
+
<script setup lang="ts">
|
|
772
|
+
import { ref } from "vue";
|
|
773
|
+
import { useChat } from "@useago/sdk/vue";
|
|
774
|
+
|
|
775
|
+
const { messages, sendMessage, isLoading, error } = useChat();
|
|
776
|
+
const draft = ref("");
|
|
777
|
+
|
|
778
|
+
async function onSend() {
|
|
779
|
+
if (!draft.value.trim()) return;
|
|
780
|
+
const text = draft.value;
|
|
781
|
+
draft.value = "";
|
|
782
|
+
await sendMessage(text);
|
|
783
|
+
}
|
|
784
|
+
</script>
|
|
785
|
+
|
|
786
|
+
<template>
|
|
787
|
+
<div v-for="m in messages" :key="m.id">
|
|
788
|
+
<b>{{ m.role }}:</b> {{ m.content }}
|
|
789
|
+
</div>
|
|
790
|
+
<form @submit.prevent="onSend">
|
|
791
|
+
<input v-model="draft" :disabled="isLoading" placeholder="Ask anything..." />
|
|
792
|
+
<button :disabled="isLoading">Send</button>
|
|
793
|
+
</form>
|
|
794
|
+
<p v-if="error" role="alert">{{ error.message }}</p>
|
|
795
|
+
</template>
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
Each assistant message carries the knowledge sources it used in `m.sources` (an
|
|
799
|
+
`AgoSource[]`, each `{ id, title, url? }`). Render them as links.
|
|
800
|
+
|
|
801
|
+
Finer-grained composables: `useMessages({ conversationId? })`,
|
|
802
|
+
`useConversation({ autoLoad? })`. Each accepts `{ client }` to override the
|
|
803
|
+
injected client.
|
|
804
|
+
|
|
805
|
+
## 3. Let the agent call your code: `useAgoFunction`
|
|
806
|
+
|
|
807
|
+
Registers on mount, unregisters on unmount.
|
|
808
|
+
|
|
809
|
+
```vue
|
|
810
|
+
<script setup lang="ts">
|
|
811
|
+
import { useAgoFunction } from "@useago/sdk/vue";
|
|
812
|
+
|
|
813
|
+
useAgoFunction("lookupOrder", {
|
|
814
|
+
description: "Look up an order by ID",
|
|
815
|
+
parameters: {
|
|
816
|
+
type: "object",
|
|
817
|
+
properties: { id: { type: "string", description: "Order ID" } },
|
|
818
|
+
required: ["id"],
|
|
819
|
+
},
|
|
820
|
+
handler: async (args) => fetchOrder(args.id as string),
|
|
821
|
+
});
|
|
822
|
+
</script>
|
|
823
|
+
```
|
|
824
|
+
|
|
825
|
+
Rules for functions:
|
|
826
|
+
- Every parameter property needs a `description`. The agent reads it to decide
|
|
827
|
+
what to pass.
|
|
828
|
+
- `required` lists only the params the function truly needs.
|
|
829
|
+
- The handler must return serializable data (no DOM nodes, no circular refs).
|
|
830
|
+
|
|
831
|
+
## 4. Let the agent navigate: `useAgoNavigation`
|
|
832
|
+
|
|
833
|
+
Works with `vue-router`.
|
|
834
|
+
|
|
835
|
+
```vue
|
|
836
|
+
<script setup lang="ts">
|
|
837
|
+
import { useRouter } from "vue-router";
|
|
838
|
+
import { useAgoNavigation } from "@useago/sdk/vue";
|
|
839
|
+
|
|
840
|
+
const router = useRouter();
|
|
841
|
+
useAgoNavigation((path) => router.push(path), [
|
|
842
|
+
{ name: "dashboard", path: "/dashboard", description: "Main dashboard" },
|
|
843
|
+
{ name: "settings", path: "/settings", description: "User settings" },
|
|
844
|
+
]);
|
|
845
|
+
</script>
|
|
846
|
+
```
|
|
847
|
+
|
|
848
|
+
The `description` is what the agent reads to pick a page. Write it like you'd
|
|
849
|
+
explain the page to a colleague.
|
|
850
|
+
|
|
851
|
+
## 4b. Let the agent change the page: `useAgoPageState`
|
|
852
|
+
|
|
853
|
+
The mirror of navigation. Let the agent change the current page's state
|
|
854
|
+
(filters, sort, view mode…) and read it back. Each control becomes one optional
|
|
855
|
+
property of a single synthesized `setPageState` function, and every control's
|
|
856
|
+
`get()` value is sent as context.
|
|
857
|
+
|
|
858
|
+
```vue
|
|
859
|
+
<script setup lang="ts">
|
|
860
|
+
import { ref } from "vue";
|
|
861
|
+
import { useAgoPageState } from "@useago/sdk/vue";
|
|
862
|
+
|
|
863
|
+
const status = ref("all");
|
|
864
|
+
useAgoPageState([
|
|
865
|
+
{
|
|
866
|
+
name: "statusFilter",
|
|
867
|
+
description: "Filter the list by status",
|
|
868
|
+
schema: { type: "string", enum: ["all", "paid", "overdue"] },
|
|
869
|
+
get: () => status.value,
|
|
870
|
+
set: (v) => { status.value = v as string; },
|
|
871
|
+
},
|
|
872
|
+
]);
|
|
873
|
+
</script>
|
|
874
|
+
```
|
|
875
|
+
|
|
876
|
+
## 5. Subscribe to events: `useAgoEvents`
|
|
877
|
+
|
|
878
|
+
Auto-cleans up on unmount.
|
|
879
|
+
|
|
880
|
+
```vue
|
|
881
|
+
<script setup lang="ts">
|
|
882
|
+
import { useAgoEvents } from "@useago/sdk/vue";
|
|
883
|
+
|
|
884
|
+
useAgoEvents("message:complete", (msg) => console.log("Got reply:", msg.content));
|
|
885
|
+
</script>
|
|
886
|
+
```
|
|
887
|
+
|
|
888
|
+
Key events: `message:start`, `message:chunk` (`{ content }` per token),
|
|
889
|
+
`message:complete` (`AgoMessage`), `message:error`.
|
|
890
|
+
|
|
891
|
+
## 6. Client context
|
|
892
|
+
|
|
893
|
+
There is no dedicated Vue composable for context. Use the core client API, which
|
|
894
|
+
is reactive-friendly:
|
|
895
|
+
|
|
896
|
+
```vue
|
|
897
|
+
<script setup lang="ts">
|
|
898
|
+
import { onMounted, onUnmounted } from "vue";
|
|
899
|
+
import { useAgo } from "@useago/sdk/vue";
|
|
900
|
+
|
|
901
|
+
const client = useAgo();
|
|
902
|
+
|
|
903
|
+
onMounted(() => {
|
|
904
|
+
// Re-evaluated on every send, so it always reflects current state
|
|
905
|
+
client.addDynamicContext("cart", () => ({ name: "Cart", data: { itemCount: cart.value.length } }));
|
|
906
|
+
});
|
|
907
|
+
onUnmounted(() => client.removeDynamicContext("cart"));
|
|
908
|
+
</script>
|
|
909
|
+
```
|
|
910
|
+
|
|
911
|
+
For a static snapshot use `client.setContext(key, entry)`. Attach the current URL
|
|
912
|
+
+ title with `client.enableAutoPageContext()`.
|
|
913
|
+
|
|
914
|
+
## 7. Reactive external state: `useAgoStore`
|
|
915
|
+
|
|
916
|
+
Hold shared state in a core `createStore` and expose it as a reactive ref.
|
|
917
|
+
|
|
918
|
+
```vue
|
|
919
|
+
<script setup lang="ts">
|
|
920
|
+
import { createStore } from "@useago/sdk";
|
|
921
|
+
import { useAgoStore } from "@useago/sdk/vue";
|
|
922
|
+
|
|
923
|
+
const cart = createStore({ items: [] as string[] });
|
|
924
|
+
const state = useAgoStore(cart);
|
|
925
|
+
</script>
|
|
926
|
+
|
|
927
|
+
<template><span>{{ state.items.length }}</span></template>
|
|
928
|
+
```
|
|
929
|
+
|
|
930
|
+
## Exports cheat-sheet (`@useago/sdk/vue`)
|
|
931
|
+
|
|
932
|
+
- Setup: `AgoPlugin`, `useAgo`, `AGO_CLIENT_KEY`
|
|
933
|
+
- Composables: `useChat`, `useMessages`, `useConversation`, `useAgoFunction`,
|
|
934
|
+
`useAgoNavigation`, `useAgoPageState`, `useAgoEvents`, `useAgoStore`
|
|
935
|
+
- Types: `AgoConfig`, `AgoMessage`, `Conversation`, `AgoAgent`, `AgoSource`,
|
|
936
|
+
`ToolCallData`, `AgoPluginOptions`
|
|
937
|
+
|
|
938
|
+
## Checklist before you finish
|
|
939
|
+
|
|
940
|
+
1. `AgoPlugin` is installed on the app (or a client is passed explicitly).
|
|
941
|
+
2. `baseUrl` comes from an env var, not a hardcoded tenant URL.
|
|
942
|
+
3. Every registered function's parameters have descriptions and the handler
|
|
943
|
+
returns serializable data.
|
|
944
|
+
4. Run `npm run typecheck` and `npm run lint` (or the project's equivalents).
|
|
945
|
+
|
|
946
|
+
---
|
|
947
|
+
|
|
948
|
+
<!-- source: docs/agents/ago-angular.md -->
|
|
949
|
+
|
|
950
|
+
# AGO SDK skill: Angular
|
|
951
|
+
|
|
952
|
+
You are integrating the AGO SDK (`@useago/sdk`) into an Angular app. AGO is a chat
|
|
953
|
+
agent that can answer questions, run functions in the user's browser, and
|
|
954
|
+
navigate your app's routes. This file is everything you need. Follow it exactly.
|
|
955
|
+
|
|
956
|
+
## Endpoints
|
|
957
|
+
|
|
958
|
+
Use the live demo for anything runnable so it answers with zero setup:
|
|
959
|
+
|
|
960
|
+
```
|
|
961
|
+
baseUrl: https://playground.api.useago.com
|
|
962
|
+
agent: generic-guide
|
|
963
|
+
```
|
|
964
|
+
|
|
965
|
+
When the project has its own tenant, swap to `https://YOUR-DOMAIN.api.useago.com`
|
|
966
|
+
and the project's own agent slug. Read `baseUrl` from the environment, never
|
|
967
|
+
hardcode a real tenant URL.
|
|
968
|
+
|
|
969
|
+
The Angular bindings have no hard dependency on Angular: they ship a minimal
|
|
970
|
+
Observable shape compatible with RxJS, so streaming events arrive as Observables.
|
|
971
|
+
|
|
972
|
+
## Install
|
|
973
|
+
|
|
974
|
+
```bash
|
|
975
|
+
npm install @useago/sdk
|
|
976
|
+
```
|
|
977
|
+
|
|
978
|
+
## 1. Provide the service
|
|
979
|
+
|
|
980
|
+
Standalone bootstrap:
|
|
981
|
+
|
|
982
|
+
```ts
|
|
983
|
+
import { bootstrapApplication } from "@angular/platform-browser";
|
|
984
|
+
import { provideAgo } from "@useago/sdk/angular";
|
|
985
|
+
import { AppComponent } from "./app.component";
|
|
986
|
+
|
|
987
|
+
bootstrapApplication(AppComponent, {
|
|
988
|
+
providers: [
|
|
989
|
+
provideAgo({
|
|
990
|
+
baseUrl: "https://playground.api.useago.com",
|
|
991
|
+
agent: "generic-guide",
|
|
992
|
+
}),
|
|
993
|
+
],
|
|
994
|
+
});
|
|
995
|
+
```
|
|
996
|
+
|
|
997
|
+
`provideAgo(config)` returns `{ provide: AgoService, useValue: <singleton> }`,
|
|
998
|
+
which Angular's injector accepts directly. In an `NgModule`, add it to the
|
|
999
|
+
`providers` array the same way. With no DI, construct it:
|
|
1000
|
+
`new AgoService({ baseUrl: "..." })`.
|
|
1001
|
+
|
|
1002
|
+
## 2. Inject and use
|
|
1003
|
+
|
|
1004
|
+
Subscribe to the streaming Observables to render messages token-by-token.
|
|
1005
|
+
|
|
1006
|
+
```ts
|
|
1007
|
+
import { Component, inject, OnDestroy } from "@angular/core";
|
|
1008
|
+
import { AgoService } from "@useago/sdk/angular";
|
|
1009
|
+
import type { AgoMessage } from "@useago/sdk/angular";
|
|
1010
|
+
|
|
1011
|
+
@Component({
|
|
1012
|
+
selector: "app-chat",
|
|
1013
|
+
standalone: true,
|
|
1014
|
+
template: `
|
|
1015
|
+
<div *ngFor="let m of messages"><b>{{ m.role }}:</b> {{ m.content }}</div>
|
|
1016
|
+
<form (submit)="send(input.value); input.value = ''">
|
|
1017
|
+
<input #input placeholder="Ask anything..." />
|
|
1018
|
+
<button>Send</button>
|
|
1019
|
+
</form>
|
|
1020
|
+
`,
|
|
1021
|
+
})
|
|
1022
|
+
export class ChatComponent implements OnDestroy {
|
|
1023
|
+
private ago = inject(AgoService);
|
|
1024
|
+
messages: AgoMessage[] = [];
|
|
1025
|
+
private streaming?: AgoMessage;
|
|
1026
|
+
|
|
1027
|
+
private subs = [
|
|
1028
|
+
// A new assistant message starts: push a placeholder to stream into
|
|
1029
|
+
this.ago.messageStart$.subscribe(({ messageId }) => {
|
|
1030
|
+
this.streaming = {
|
|
1031
|
+
id: messageId, conversationId: "", content: "",
|
|
1032
|
+
role: "assistant", status: "IN_PROGRESS", createdAt: new Date(),
|
|
1033
|
+
};
|
|
1034
|
+
this.messages = [...this.messages, this.streaming];
|
|
1035
|
+
}),
|
|
1036
|
+
// Append streamed tokens
|
|
1037
|
+
this.ago.chunks$.subscribe(({ content, messageId }) => {
|
|
1038
|
+
this.messages = this.messages.map((m) =>
|
|
1039
|
+
m.id === messageId ? { ...m, content: m.content + content } : m);
|
|
1040
|
+
}),
|
|
1041
|
+
// Final message replaces the placeholder
|
|
1042
|
+
this.ago.messages$.subscribe((msg) => {
|
|
1043
|
+
this.messages = this.messages.map((m) => (m.id === msg.id ? msg : m));
|
|
1044
|
+
}),
|
|
1045
|
+
this.ago.errors$.subscribe(({ error }) => console.error(error)),
|
|
1046
|
+
];
|
|
1047
|
+
|
|
1048
|
+
async send(content: string) {
|
|
1049
|
+
if (!content.trim()) return;
|
|
1050
|
+
this.messages = [...this.messages, {
|
|
1051
|
+
id: `u-${Date.now()}`, conversationId: "", content,
|
|
1052
|
+
role: "user", status: "DONE", createdAt: new Date(),
|
|
1053
|
+
}];
|
|
1054
|
+
await this.ago.sendMessage(content);
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
ngOnDestroy() {
|
|
1058
|
+
this.subs.forEach((s) => s.unsubscribe());
|
|
1059
|
+
this.ago.destroy();
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
```
|
|
1063
|
+
|
|
1064
|
+
## 3. Observables
|
|
1065
|
+
|
|
1066
|
+
| Observable | Emits |
|
|
1067
|
+
| --------------- | ----- |
|
|
1068
|
+
| `messageStart$` | `{ conversationId, messageId }` when a reply begins |
|
|
1069
|
+
| `chunks$` | `{ content, conversationId, messageId }` per streamed token |
|
|
1070
|
+
| `messages$` | the final `AgoMessage` on completion |
|
|
1071
|
+
| `errors$` | `{ error, conversationId?, messageId? }` |
|
|
1072
|
+
|
|
1073
|
+
Each `subscribe()` returns an object with `unsubscribe()`. For any other event,
|
|
1074
|
+
use `ago.on(event, handler)` / `ago.off(event, handler)`.
|
|
1075
|
+
|
|
1076
|
+
## 4. Methods
|
|
1077
|
+
|
|
1078
|
+
`AgoService` forwards the full client surface:
|
|
1079
|
+
|
|
1080
|
+
```ts
|
|
1081
|
+
ago.sendMessage(content, options?) // Promise<AgoMessage>
|
|
1082
|
+
ago.getConversations() // Promise<Conversation[]>
|
|
1083
|
+
ago.getConversation(id)
|
|
1084
|
+
ago.getMessages(conversationId)
|
|
1085
|
+
|
|
1086
|
+
ago.registerFunction(definition) // or (name, handler, schema)
|
|
1087
|
+
ago.unregisterFunction(name)
|
|
1088
|
+
ago.registerNavigationFunction(navigate, routes)
|
|
1089
|
+
ago.registerPageStateFunction(controls, opts?) / ago.unregisterPageStateFunction(functionName?)
|
|
1090
|
+
|
|
1091
|
+
ago.submitToolCallForm(toolCallId, formData)
|
|
1092
|
+
ago.confirmToolCall(toolCallId)
|
|
1093
|
+
ago.rejectToolCall(toolCallId)
|
|
1094
|
+
ago.submitFeedback(messageId, "positive" | "negative")
|
|
1095
|
+
|
|
1096
|
+
ago.on(event, handler) / ago.off(event, handler)
|
|
1097
|
+
ago.updateConfig(partialConfig)
|
|
1098
|
+
ago.getClient() // underlying AgoClient
|
|
1099
|
+
ago.destroy()
|
|
1100
|
+
```
|
|
1101
|
+
|
|
1102
|
+
## 5. Functions, navigation and context
|
|
1103
|
+
|
|
1104
|
+
Register a client-side function the agent can call:
|
|
1105
|
+
|
|
1106
|
+
```ts
|
|
1107
|
+
this.ago.registerFunction({
|
|
1108
|
+
name: "lookupOrder",
|
|
1109
|
+
description: "Look up an order by ID",
|
|
1110
|
+
parameters: {
|
|
1111
|
+
type: "object",
|
|
1112
|
+
properties: { id: { type: "string", description: "Order ID" } },
|
|
1113
|
+
required: ["id"],
|
|
1114
|
+
},
|
|
1115
|
+
handler: async (args) => this.orders.fetch(args.id as string),
|
|
1116
|
+
});
|
|
1117
|
+
```
|
|
1118
|
+
|
|
1119
|
+
Rules for functions:
|
|
1120
|
+
- Every parameter property needs a `description`. The agent reads it to decide
|
|
1121
|
+
what to pass.
|
|
1122
|
+
- `required` lists only the params the function truly needs.
|
|
1123
|
+
- The handler must return serializable data (no DOM nodes, no circular refs).
|
|
1124
|
+
|
|
1125
|
+
Register navigation (works with Angular's `Router`):
|
|
1126
|
+
|
|
1127
|
+
```ts
|
|
1128
|
+
const router = inject(Router);
|
|
1129
|
+
this.ago.registerNavigationFunction((path) => router.navigateByUrl(path), [
|
|
1130
|
+
{ name: "dashboard", path: "/dashboard", description: "Main dashboard" },
|
|
1131
|
+
]);
|
|
1132
|
+
```
|
|
1133
|
+
|
|
1134
|
+
Register page state, the mirror of navigation (change the current page's state
|
|
1135
|
+
and read it back):
|
|
1136
|
+
|
|
1137
|
+
```ts
|
|
1138
|
+
this.ago.registerPageStateFunction([
|
|
1139
|
+
{
|
|
1140
|
+
name: "statusFilter",
|
|
1141
|
+
description: "Filter the list by status",
|
|
1142
|
+
schema: { type: "string", enum: ["all", "paid", "overdue"] },
|
|
1143
|
+
get: () => this.status,
|
|
1144
|
+
set: (v) => { this.status = v as string; },
|
|
1145
|
+
},
|
|
1146
|
+
]);
|
|
1147
|
+
```
|
|
1148
|
+
|
|
1149
|
+
For client context, reach the client via `getClient()`:
|
|
1150
|
+
|
|
1151
|
+
```ts
|
|
1152
|
+
this.ago.getClient().enableAutoPageContext();
|
|
1153
|
+
this.ago.getClient().setContext("order", { name: "Order", data: { id } });
|
|
1154
|
+
```
|
|
1155
|
+
|
|
1156
|
+
## Exports cheat-sheet (`@useago/sdk/angular`)
|
|
1157
|
+
|
|
1158
|
+
- `AgoService`, `provideAgo`, `AgoProvideOptions`
|
|
1159
|
+
- Types: `AgoConfig`, `AgoMessage`, `Conversation`, `AgoAgent`, `AgoSource`,
|
|
1160
|
+
`ToolCallData`, `AgoClientEvents`, `AgoEventName`
|
|
1161
|
+
|
|
1162
|
+
## Checklist before you finish
|
|
1163
|
+
|
|
1164
|
+
1. `provideAgo(...)` is registered (or `AgoService` constructed directly).
|
|
1165
|
+
2. `baseUrl` comes from the environment, not a hardcoded tenant URL.
|
|
1166
|
+
3. Every subscription is torn down in `ngOnDestroy`, and `ago.destroy()` is called.
|
|
1167
|
+
4. Every registered function's parameters have descriptions and the handler
|
|
1168
|
+
returns serializable data.
|
|
1169
|
+
5. Run the project's typecheck and lint.
|
|
1170
|
+
|
|
1171
|
+
---
|
|
1172
|
+
|
|
1173
|
+
# CONCEPTS
|
|
1174
|
+
|
|
1175
|
+
---
|
|
1176
|
+
|
|
1177
|
+
<!-- source: docs/general/functions-and-context.md -->
|
|
1178
|
+
|
|
1179
|
+
# Client-side functions & context
|
|
1180
|
+
|
|
1181
|
+
Two capabilities that turn AGO from a chatbot into an agent that acts inside
|
|
1182
|
+
**your** app. Both work in every framework; this page is the canonical
|
|
1183
|
+
reference, and the framework guides show the idiomatic wrappers.
|
|
1184
|
+
|
|
1185
|
+
- [Client-side functions](#client-side-functions): the agent runs code in the browser
|
|
1186
|
+
- [Pre-built helpers](#pre-built-helpers): a catalogue of ready-made functions
|
|
1187
|
+
- [Client context](#client-context): tell the agent what the user is doing
|
|
1188
|
+
|
|
1189
|
+
---
|
|
1190
|
+
|
|
1191
|
+
## Client-side functions
|
|
1192
|
+
|
|
1193
|
+
You expose functions (with a name, description and JSON-Schema parameters). The
|
|
1194
|
+
SDK sends those schemas with each message; when the agent decides to call one,
|
|
1195
|
+
the SDK runs your handler in the browser, then submits the result back so the
|
|
1196
|
+
agent can continue. Great for: looking up data, mutating UI, navigation,
|
|
1197
|
+
triggering app actions.
|
|
1198
|
+
|
|
1199
|
+
### Define a function
|
|
1200
|
+
|
|
1201
|
+
```ts
|
|
1202
|
+
import { defineFunction } from "@useago/sdk";
|
|
1203
|
+
|
|
1204
|
+
export const lookupOrder = defineFunction({
|
|
1205
|
+
name: "lookupOrder",
|
|
1206
|
+
description: "Look up an order by its ID and return its status.",
|
|
1207
|
+
parameters: {
|
|
1208
|
+
type: "object",
|
|
1209
|
+
properties: {
|
|
1210
|
+
id: { type: "string", description: "The order ID" },
|
|
1211
|
+
},
|
|
1212
|
+
required: ["id"],
|
|
1213
|
+
},
|
|
1214
|
+
handler: async (args) => {
|
|
1215
|
+
// fetchOrder: your own API call
|
|
1216
|
+
const order = await fetchOrder(args.id as string);
|
|
1217
|
+
return { status: order.status, total: order.total }; // returned to the agent
|
|
1218
|
+
},
|
|
1219
|
+
});
|
|
1220
|
+
```
|
|
1221
|
+
|
|
1222
|
+
`defineFunction` is an identity helper: it just gives you typing and a reusable
|
|
1223
|
+
object. The shape is:
|
|
1224
|
+
|
|
1225
|
+
```ts
|
|
1226
|
+
interface ClientFunctionDefinition {
|
|
1227
|
+
name: string; // unique
|
|
1228
|
+
description: string; // the LLM reads this to decide when to call it
|
|
1229
|
+
parameters: {
|
|
1230
|
+
type: "object";
|
|
1231
|
+
properties: Record<string, {
|
|
1232
|
+
type: string; // "string" | "number" | "boolean" | "array" | ...
|
|
1233
|
+
description?: string;
|
|
1234
|
+
enum?: string[];
|
|
1235
|
+
default?: unknown;
|
|
1236
|
+
}>;
|
|
1237
|
+
required?: string[];
|
|
1238
|
+
};
|
|
1239
|
+
handler: (args: Record<string, unknown>) => unknown | Promise<unknown>;
|
|
1240
|
+
}
|
|
1241
|
+
```
|
|
1242
|
+
|
|
1243
|
+
> **Tip:** write descriptions for the model. A clear `description` (and per-field
|
|
1244
|
+
> `description`) is what makes the agent call the function correctly. Return a
|
|
1245
|
+
> small, structured object; it's fed straight back into the conversation.
|
|
1246
|
+
|
|
1247
|
+
### Result size limit
|
|
1248
|
+
|
|
1249
|
+
Everything a handler returns goes into the LLM context, so the SDK caps the
|
|
1250
|
+
serialized result at 50 KB by default. Over the cap, the agent receives a
|
|
1251
|
+
flagged preview instead of the full payload:
|
|
1252
|
+
|
|
1253
|
+
```json
|
|
1254
|
+
{ "truncated": true, "originalBytes": 412031, "maxBytes": 50000, "preview": "...", "hint": "..." }
|
|
1255
|
+
```
|
|
1256
|
+
|
|
1257
|
+
A console warning fires at 20 KB (with `debug: true`) so you catch it in dev.
|
|
1258
|
+
Tune the cap globally with `maxFunctionResultBytes` on the client config, or
|
|
1259
|
+
per function:
|
|
1260
|
+
|
|
1261
|
+
```ts
|
|
1262
|
+
defineFunction({
|
|
1263
|
+
name: "exportReport",
|
|
1264
|
+
maxResultBytes: 200_000, // this one is allowed to be big (Infinity disables)
|
|
1265
|
+
// ...
|
|
1266
|
+
});
|
|
1267
|
+
```
|
|
1268
|
+
|
|
1269
|
+
If you hit the cap, the fix is usually in the handler: return only the fields
|
|
1270
|
+
the agent needs, add `limit`/filter parameters to the schema, or push the data
|
|
1271
|
+
to the UI (page state) and return a short summary like `{ displayed: 25, total: 1200 }`.
|
|
1272
|
+
|
|
1273
|
+
### Register a function
|
|
1274
|
+
|
|
1275
|
+
```ts
|
|
1276
|
+
// Single object (preferred)
|
|
1277
|
+
client.registerFunction(lookupOrder);
|
|
1278
|
+
|
|
1279
|
+
// Short alias, also accepts an array
|
|
1280
|
+
client.register([lookupOrder, cancelOrder]);
|
|
1281
|
+
|
|
1282
|
+
// Classic 3-arg form
|
|
1283
|
+
client.registerFunction("lookupOrder", handler, { description, parameters });
|
|
1284
|
+
|
|
1285
|
+
// Remove it
|
|
1286
|
+
client.unregisterFunction("lookupOrder");
|
|
1287
|
+
```
|
|
1288
|
+
|
|
1289
|
+
Per framework:
|
|
1290
|
+
|
|
1291
|
+
| Framework | Idiomatic API |
|
|
1292
|
+
| --- | --- |
|
|
1293
|
+
| Core | `client.registerFunction(def)` / `client.register([...])` |
|
|
1294
|
+
| React | `useAgoFunction(def)` (auto-cleanup) or `<AgoProvider tools={[…]}>` |
|
|
1295
|
+
| Vue | `useAgoFunction(name, options)` (auto-cleanup) |
|
|
1296
|
+
| Angular | `agoService.registerFunction(def)` |
|
|
1297
|
+
|
|
1298
|
+
### Navigation shortcut
|
|
1299
|
+
|
|
1300
|
+
A common case (letting the agent move the user around) has a dedicated helper
|
|
1301
|
+
that builds the function for you:
|
|
1302
|
+
|
|
1303
|
+
```ts
|
|
1304
|
+
client.registerNavigationFunction(
|
|
1305
|
+
(path) => router.push(path), // your navigate fn
|
|
1306
|
+
[
|
|
1307
|
+
{ name: "pricing", path: "/pricing", description: "Pricing page" },
|
|
1308
|
+
{ name: "docs", path: "/docs", description: "Documentation" },
|
|
1309
|
+
{ name: "orderDetail", path: "/orders/:id", description: "One order's detail page" },
|
|
1310
|
+
],
|
|
1311
|
+
);
|
|
1312
|
+
```
|
|
1313
|
+
|
|
1314
|
+
This registers a `navigateToPage` function whose `page` parameter is an enum of
|
|
1315
|
+
your route names. React/Vue offer `useAgoNavigation(navigate, routes)` with
|
|
1316
|
+
lifecycle cleanup.
|
|
1317
|
+
|
|
1318
|
+
Paths can contain `:param` placeholders. Each placeholder becomes an explicit
|
|
1319
|
+
top-level argument of `navigateToPage` (`{ page: "orderDetail", id: "42" }`
|
|
1320
|
+
navigates to `/orders/42`), so one route covers every detail page of an entity.
|
|
1321
|
+
Values are URL-encoded. A call that misses a required param navigates nowhere
|
|
1322
|
+
and returns an error telling the agent what to retry with. Placeholders cannot
|
|
1323
|
+
be named `page` (that argument selects the route).
|
|
1324
|
+
|
|
1325
|
+
It also registers a dynamic context entry ("Current page") that reports the page
|
|
1326
|
+
the user is on (by route name, plus URL and title) on every message, so the agent
|
|
1327
|
+
knows where it is after it navigates, not just how to navigate away. Call
|
|
1328
|
+
`client.unregisterNavigationFunction()` to remove both the function and that
|
|
1329
|
+
context.
|
|
1330
|
+
|
|
1331
|
+
### Page state shortcut
|
|
1332
|
+
|
|
1333
|
+
The mirror of navigation: instead of moving the user to another page, let the
|
|
1334
|
+
agent change the state of the page they're on (filters, sort, view mode,
|
|
1335
|
+
selection…) and read the current state back. One helper builds both halves.
|
|
1336
|
+
|
|
1337
|
+
```ts
|
|
1338
|
+
client.registerPageStateFunction([
|
|
1339
|
+
{
|
|
1340
|
+
name: "statusFilter",
|
|
1341
|
+
description: "Filter the list by review status",
|
|
1342
|
+
schema: { type: "string", enum: ["all", "pending", "approved"] },
|
|
1343
|
+
get: () => filters.status, // current value → context
|
|
1344
|
+
set: (v) => setFilters({ ...filters, status: v }), // apply the change
|
|
1345
|
+
},
|
|
1346
|
+
{
|
|
1347
|
+
name: "sort",
|
|
1348
|
+
description: "Sort order of the list",
|
|
1349
|
+
schema: { type: "string", enum: ["newest", "oldest"] },
|
|
1350
|
+
get: () => filters.sort,
|
|
1351
|
+
set: (v) => setFilters({ ...filters, sort: v }),
|
|
1352
|
+
},
|
|
1353
|
+
]);
|
|
1354
|
+
|
|
1355
|
+
client.unregisterPageStateFunction(); // pass the functionName if you customised it
|
|
1356
|
+
```
|
|
1357
|
+
|
|
1358
|
+
This does two things:
|
|
1359
|
+
|
|
1360
|
+
- Synthesizes **one** client function (`setPageState` by default; override with
|
|
1361
|
+
`{ functionName }`). Each control becomes one optional property, so the agent
|
|
1362
|
+
sets only the controls the user asked for and leaves the rest untouched.
|
|
1363
|
+
- Registers a dynamic context entry ("Page state") reporting each control's
|
|
1364
|
+
current `get()` value, so the agent knows what to change. Controls without a
|
|
1365
|
+
`get()` are write-only and don't appear in context.
|
|
1366
|
+
|
|
1367
|
+
It's fire-and-forget: the handler applies each `set()` locally and returns
|
|
1368
|
+
`{ success, applied }`. The new state reaches the agent through the context on
|
|
1369
|
+
the next message. React/Vue offer `useAgoPageState(controls, opts?)` with
|
|
1370
|
+
lifecycle cleanup; Angular exposes `agoService.registerPageStateFunction(...)`.
|
|
1371
|
+
|
|
1372
|
+
### Navigate then change the page in one go
|
|
1373
|
+
|
|
1374
|
+
A request like "open the invoices page and show only the overdue ones" needs two
|
|
1375
|
+
steps: the agent navigates, then changes the new page's state. But the
|
|
1376
|
+
destination page only registers its `setPageState` (and current state) once it
|
|
1377
|
+
mounts, so the agent can't do both in a single turn.
|
|
1378
|
+
|
|
1379
|
+
`useAgoAutoContinueAfterNavigation()` closes the gap. Mount it once inside
|
|
1380
|
+
`AgoProvider`:
|
|
1381
|
+
|
|
1382
|
+
```tsx
|
|
1383
|
+
function AppShell() {
|
|
1384
|
+
useAgoNavigation(navigate, routes);
|
|
1385
|
+
useAgoPageState(controls); // on each pilotable page
|
|
1386
|
+
useAgoAutoContinueAfterNavigation(); // once, near the provider
|
|
1387
|
+
}
|
|
1388
|
+
```
|
|
1389
|
+
|
|
1390
|
+
How the gap is bridged depends on the client-functions mode:
|
|
1391
|
+
|
|
1392
|
+
- **Pause mode** (recommended, `clientFunctionsMode: "pause"` in the config —
|
|
1393
|
+
needs a backend with pause/resume support): the backend pauses the turn on the
|
|
1394
|
+
navigation call. The SDK submits the function result and resumes the SAME turn
|
|
1395
|
+
via `POST /messages/{id}/continue`; the hook only delays that resume until the
|
|
1396
|
+
destination's `useAgoPageState` registered. No extra prompt, no second turn,
|
|
1397
|
+
and the agent sees the real function results.
|
|
1398
|
+
|
|
1399
|
+
- **Placeholder mode** (legacy default): when the navigating turn ends, the hook
|
|
1400
|
+
waits for the destination page to register, then sends a continuation message
|
|
1401
|
+
with `{ hidden: true }` — kept in the model's context, never displayed
|
|
1402
|
+
(`useMessages` and `ChatWidget` filter hidden messages). Without backend
|
|
1403
|
+
support for `hidden` the mechanism still works but the nudge shows as a message.
|
|
1404
|
+
|
|
1405
|
+
A paused turn also survives a full page reload: after `getConversation(...)`,
|
|
1406
|
+
call `client.resumePendingClientFunctions(conversation)` to re-run the waiting
|
|
1407
|
+
functions, submit their results, and resume the turn (opt-in — re-running a
|
|
1408
|
+
navigation on reload navigates again).
|
|
1409
|
+
|
|
1410
|
+
### Observe invocations
|
|
1411
|
+
|
|
1412
|
+
```ts
|
|
1413
|
+
client.on("function:invoke", ({ functionName, arguments: args }) =>
|
|
1414
|
+
console.log("Agent called", functionName, args),
|
|
1415
|
+
);
|
|
1416
|
+
client.on("function:result", ({ result, error }) =>
|
|
1417
|
+
console.log("Returned", result, error),
|
|
1418
|
+
);
|
|
1419
|
+
```
|
|
1420
|
+
|
|
1421
|
+
---
|
|
1422
|
+
|
|
1423
|
+
## Pre-built helpers
|
|
1424
|
+
|
|
1425
|
+
The SDK ships a catalogue of common browser actions as ready-made function
|
|
1426
|
+
definitions. Import them from `@useago/sdk` (or `@useago/sdk/helpers`), wire up a
|
|
1427
|
+
handler where needed, and register.
|
|
1428
|
+
|
|
1429
|
+
| Helper | What it does | Handler needed? |
|
|
1430
|
+
| --- | --- | --- |
|
|
1431
|
+
| `showToast` | Show a toast notification | ✅ wire to your toast lib |
|
|
1432
|
+
| `showNotification` | Browser `Notification` (asks permission) | built-in |
|
|
1433
|
+
| `openUrl` | Open a URL in a new tab | built-in |
|
|
1434
|
+
| `copyToClipboard` | Copy text to clipboard | built-in |
|
|
1435
|
+
| `setTheme` | Set `data-theme` on `<html>` (light/dark/system) | built-in |
|
|
1436
|
+
| `showConfirmDialog` | `window.confirm` and return the choice | built-in |
|
|
1437
|
+
| `getUserLocation` | Geolocation (asks permission) | built-in |
|
|
1438
|
+
| `scrollToElement` | Scroll to a CSS selector | built-in |
|
|
1439
|
+
| `setLocalStorage` / `getLocalStorage` | Read/write `localStorage` | built-in |
|
|
1440
|
+
| `highlightElement` | Outline an element (guided tours) | built-in |
|
|
1441
|
+
| `submitForm` | Fill & submit an HTML form | built-in |
|
|
1442
|
+
| `trackEvent` | Custom analytics event | ➖ logs to console by default |
|
|
1443
|
+
|
|
1444
|
+
### Wiring a handler
|
|
1445
|
+
|
|
1446
|
+
Some helpers ship a no-op/console default (`showToast`, `trackEvent`). Attach
|
|
1447
|
+
your implementation without mutating the original with `withHandler`:
|
|
1448
|
+
|
|
1449
|
+
```ts
|
|
1450
|
+
import { showToast, withHandler } from "@useago/sdk";
|
|
1451
|
+
|
|
1452
|
+
client.register(
|
|
1453
|
+
withHandler(showToast, (args) => {
|
|
1454
|
+
myToast(args.message as string); // myToast: your own toast handler
|
|
1455
|
+
return { shown: true };
|
|
1456
|
+
}),
|
|
1457
|
+
);
|
|
1458
|
+
```
|
|
1459
|
+
|
|
1460
|
+
In React you can wire helpers declaratively on the provider:
|
|
1461
|
+
|
|
1462
|
+
```tsx
|
|
1463
|
+
<AgoProvider
|
|
1464
|
+
baseUrl="…"
|
|
1465
|
+
helpers={{
|
|
1466
|
+
copyToClipboard: true, // built-in handler
|
|
1467
|
+
showToast: (args) => toast(args.message as string), // custom
|
|
1468
|
+
}}
|
|
1469
|
+
>
|
|
1470
|
+
```
|
|
1471
|
+
|
|
1472
|
+
---
|
|
1473
|
+
|
|
1474
|
+
## Client context
|
|
1475
|
+
|
|
1476
|
+
Context is structured data describing the user's current situation. It's sent
|
|
1477
|
+
with **every** message so the agent answers in context, without the user having
|
|
1478
|
+
to explain where they are.
|
|
1479
|
+
|
|
1480
|
+
### Static context
|
|
1481
|
+
|
|
1482
|
+
```ts
|
|
1483
|
+
client.setContext("order-page", {
|
|
1484
|
+
name: "Order detail",
|
|
1485
|
+
description: "User is viewing an order",
|
|
1486
|
+
data: { orderId: "123", status: "shipped" },
|
|
1487
|
+
});
|
|
1488
|
+
|
|
1489
|
+
client.removeContext("order-page");
|
|
1490
|
+
```
|
|
1491
|
+
|
|
1492
|
+
Shape:
|
|
1493
|
+
|
|
1494
|
+
```ts
|
|
1495
|
+
interface ContextEntry {
|
|
1496
|
+
name?: string; // human label, e.g. "Order detail"
|
|
1497
|
+
description?: string; // what this context represents
|
|
1498
|
+
data?: Record<string, unknown>; // structured data for the AI
|
|
1499
|
+
}
|
|
1500
|
+
```
|
|
1501
|
+
|
|
1502
|
+
### Dynamic context
|
|
1503
|
+
|
|
1504
|
+
A function evaluated on every send, perfect for data outside your render state
|
|
1505
|
+
(global stores, refs, computed values). Return `null` to skip.
|
|
1506
|
+
|
|
1507
|
+
```ts
|
|
1508
|
+
// cart / cartTotal(): a variable and helper from your outer scope or store
|
|
1509
|
+
client.addDynamicContext("cart", () => ({
|
|
1510
|
+
name: "Cart",
|
|
1511
|
+
data: { itemCount: cart.length, total: cartTotal() },
|
|
1512
|
+
}));
|
|
1513
|
+
|
|
1514
|
+
client.removeDynamicContext("cart");
|
|
1515
|
+
```
|
|
1516
|
+
|
|
1517
|
+
### Auto page context
|
|
1518
|
+
|
|
1519
|
+
One call attaches the current URL + document title on every message:
|
|
1520
|
+
|
|
1521
|
+
```ts
|
|
1522
|
+
client.enableAutoPageContext();
|
|
1523
|
+
```
|
|
1524
|
+
|
|
1525
|
+
(In React, set `<AgoProvider pageContext="auto">`.)
|
|
1526
|
+
|
|
1527
|
+
### Per framework
|
|
1528
|
+
|
|
1529
|
+
| Framework | Idiomatic API |
|
|
1530
|
+
| --- | --- |
|
|
1531
|
+
| Core | `setContext` / `addDynamicContext` / `enableAutoPageContext` |
|
|
1532
|
+
| React | `useAgoContext(entryOrFn, key?)` + `pageContext="auto"` |
|
|
1533
|
+
| Vue | core API inside `onMounted` / `onUnmounted` |
|
|
1534
|
+
| Angular | `agoService.getClient().setContext(…)` |
|
|
1535
|
+
|
|
1536
|
+
### Inspect what's being sent
|
|
1537
|
+
|
|
1538
|
+
```ts
|
|
1539
|
+
const snapshot = client.getContextSnapshot();
|
|
1540
|
+
// → { entries: { "order-page": { name, description, data }, … } } | null
|
|
1541
|
+
```
|
|
1542
|
+
|
|
1543
|
+
---
|
|
1544
|
+
|
|
1545
|
+
See also: [Events & streaming](events-and-streaming.md) ·
|
|
1546
|
+
[Core API](core.md) · [Testing](testing.md)
|
|
1547
|
+
|
|
1548
|
+
---
|
|
1549
|
+
|
|
1550
|
+
<!-- source: docs/general/events-and-streaming.md -->
|
|
1551
|
+
|
|
1552
|
+
# Events & streaming
|
|
1553
|
+
|
|
1554
|
+
`AgoClient` streams responses over Server-Sent Events and re-emits them as typed
|
|
1555
|
+
events. You can consume them three ways, from lowest to highest level:
|
|
1556
|
+
|
|
1557
|
+
1. Raw events: `client.on(event, handler)`
|
|
1558
|
+
2. Callback helpers: `onMessage`, `onMessageChunk`, …
|
|
1559
|
+
3. An async generator: `createMessageStream`
|
|
1560
|
+
|
|
1561
|
+
All three are framework-agnostic and live in `@useago/sdk`.
|
|
1562
|
+
|
|
1563
|
+
---
|
|
1564
|
+
|
|
1565
|
+
## 1. Raw events
|
|
1566
|
+
|
|
1567
|
+
```ts
|
|
1568
|
+
client.on("message:chunk", ({ content }) => append(content)); // append: your own render fn
|
|
1569
|
+
client.off("message:chunk", handler);
|
|
1570
|
+
client.once("message:complete", (m) => console.log(m.content));
|
|
1571
|
+
|
|
1572
|
+
const final = await client.waitFor("message:complete", { timeout: 10_000 });
|
|
1573
|
+
```
|
|
1574
|
+
|
|
1575
|
+
| Event | Payload |
|
|
1576
|
+
| --- | --- |
|
|
1577
|
+
| `message:start` | `{ conversationId, messageId }` |
|
|
1578
|
+
| `message:chunk` | `{ content, conversationId, messageId }` |
|
|
1579
|
+
| `message:answer-complete` | `AgoMessage`: the main answer text is done, but follow-up replies may still be pending; fires once, before `message:complete` |
|
|
1580
|
+
| `message:complete` | `AgoMessage` |
|
|
1581
|
+
| `message:waiting-client` | `{ conversationId, messageId, waitingToolCallIds }`: the turn paused on client function call(s) (`clientFunctionsMode: "pause"`). `message:complete` does not fire for the paused stream — it fires when the resumed turn concludes. The SDK submits the results and resumes on its own; listen to this to keep a "working…" indicator up |
|
|
1582
|
+
| `message:empty` | `{ conversationId, messageId }`: the reply completed as `DONE` with no content, tool calls, or follow-ups (usually an unknown `agent` slug). Fires after `message:complete` AND after `sendMessage` resolves, so subscribing right after the `await` still catches it. Both ids are `""` when the stream carried no message data at all. See [Empty replies](configuration.md#empty-replies). |
|
|
1583
|
+
| `message:error` | `{ error, code?, conversationId?, messageId? }`: `code` is the stable [error code](configuration.md#error-codes) when the failure was an `AgoError` |
|
|
1584
|
+
| `conversation:loaded` | `Conversation`: a full conversation was loaded from the server (e.g. after a page reload), with messages and persisted tool calls |
|
|
1585
|
+
| `context:changed` | `ContextSnapshot \| null`: the client-side context changed (entry added/removed, or a stateful helper updated its store) |
|
|
1586
|
+
| `toolCall:received` | `ToolCallData` |
|
|
1587
|
+
| `toolCall:form` | `ToolCallData` (only when the tool call is a form) |
|
|
1588
|
+
| `function:invoke` | `{ invocationId, functionName, arguments, conversationId }` |
|
|
1589
|
+
| `function:result` | `{ invocationId, result, error? }` |
|
|
1590
|
+
| `form:submitted` | `{ name, values, result }`: a form collector submitted successfully; `result` is the submit response (the third-party API's answer) |
|
|
1591
|
+
| `form:error` | `{ name, values, error }`: a form collector submit failed at the network/server level (HTTP non-2xx or a thrown handler/fetch). Validation pre-checks (missing fields, no submit target) do not fire this |
|
|
1592
|
+
| `connection:status` | `{ connected }` |
|
|
1593
|
+
|
|
1594
|
+
`waitFor` rejects if the optional `timeout` (ms) elapses first.
|
|
1595
|
+
|
|
1596
|
+
---
|
|
1597
|
+
|
|
1598
|
+
## 2. Callback helpers
|
|
1599
|
+
|
|
1600
|
+
Thin wrappers that return an **unsubscribe** function, handy when you don't want
|
|
1601
|
+
to keep a reference to the handler.
|
|
1602
|
+
|
|
1603
|
+
```ts
|
|
1604
|
+
import {
|
|
1605
|
+
onMessage,
|
|
1606
|
+
onMessageChunk,
|
|
1607
|
+
onMessageStart,
|
|
1608
|
+
onMessageError,
|
|
1609
|
+
onToolCall,
|
|
1610
|
+
onFunctionInvoke,
|
|
1611
|
+
} from "@useago/sdk";
|
|
1612
|
+
|
|
1613
|
+
const unsub = onMessageChunk(client, ({ content }) => {
|
|
1614
|
+
outputEl.textContent += content;
|
|
1615
|
+
});
|
|
1616
|
+
// later
|
|
1617
|
+
unsub();
|
|
1618
|
+
|
|
1619
|
+
onMessage(client, (msg) => console.log("complete:", msg.content));
|
|
1620
|
+
```
|
|
1621
|
+
|
|
1622
|
+
| Helper | Subscribes to |
|
|
1623
|
+
| --- | --- |
|
|
1624
|
+
| `onMessage(client, cb)` | `message:complete` |
|
|
1625
|
+
| `onMessageChunk(client, cb)` | `message:chunk` |
|
|
1626
|
+
| `onMessageStart(client, cb)` | `message:start` |
|
|
1627
|
+
| `onMessageError(client, cb)` | `message:error` |
|
|
1628
|
+
| `onToolCall(client, cb)` | `toolCall:received` |
|
|
1629
|
+
| `onFunctionInvoke(client, cb)` | `function:invoke` |
|
|
1630
|
+
| `onFormSubmitted(client, cb)` | `form:submitted` |
|
|
1631
|
+
| `onFormError(client, cb)` | `form:error` |
|
|
1632
|
+
|
|
1633
|
+
---
|
|
1634
|
+
|
|
1635
|
+
## 3. Async generator
|
|
1636
|
+
|
|
1637
|
+
`createMessageStream` fires a message and yields a typed event for each step:
|
|
1638
|
+
no manual subscribe/unsubscribe, just `for await`.
|
|
1639
|
+
|
|
1640
|
+
```ts
|
|
1641
|
+
import { createMessageStream } from "@useago/sdk";
|
|
1642
|
+
|
|
1643
|
+
for await (const ev of createMessageStream(client, "Hello!", { conversationId })) {
|
|
1644
|
+
switch (ev.type) {
|
|
1645
|
+
case "start": console.log("started", ev.data.messageId); break;
|
|
1646
|
+
case "chunk": process.stdout.write(ev.data.content); break;
|
|
1647
|
+
case "toolCall": console.log("tool call", ev.data); break;
|
|
1648
|
+
case "complete": console.log("\ndone:", ev.data.content); break;
|
|
1649
|
+
case "error": console.error(ev.data.error); break;
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
```
|
|
1653
|
+
|
|
1654
|
+
Options: `{ conversationId?, agentId?, files? }`. The generator cleans up its
|
|
1655
|
+
listeners automatically when the loop ends.
|
|
1656
|
+
|
|
1657
|
+
---
|
|
1658
|
+
|
|
1659
|
+
## Tool calls
|
|
1660
|
+
|
|
1661
|
+
When the agent needs structured input (a form), a confirmation, or shows a
|
|
1662
|
+
status/progress indicator, it emits a tool call. Listen and respond:
|
|
1663
|
+
|
|
1664
|
+
```ts
|
|
1665
|
+
client.on("toolCall:form", async (toolCall) => {
|
|
1666
|
+
// toolCall.formSchema describes the fields to render
|
|
1667
|
+
const values = await renderFormSomehow(toolCall.formSchema); // render your own form UI here
|
|
1668
|
+
await client.submitToolCallForm(toolCall.id, values);
|
|
1669
|
+
});
|
|
1670
|
+
|
|
1671
|
+
client.on("toolCall:received", async (toolCall) => {
|
|
1672
|
+
if (toolCall.type === "confirmation_input") {
|
|
1673
|
+
const ok = window.confirm(toolCall.message ?? "Confirm?");
|
|
1674
|
+
ok ? await client.confirmToolCall(toolCall.id)
|
|
1675
|
+
: await client.rejectToolCall(toolCall.id);
|
|
1676
|
+
}
|
|
1677
|
+
});
|
|
1678
|
+
```
|
|
1679
|
+
|
|
1680
|
+
`ToolCallType` is one of: `form`, `confirmation_input`, `status_message`,
|
|
1681
|
+
`progress_indicator`, `client_function`, `reasoning`, `mcp_ui_resource`.
|
|
1682
|
+
|
|
1683
|
+
---
|
|
1684
|
+
|
|
1685
|
+
## Framework equivalents
|
|
1686
|
+
|
|
1687
|
+
| Framework | How to subscribe |
|
|
1688
|
+
| --- | --- |
|
|
1689
|
+
| Core | `client.on(...)` or the helpers above |
|
|
1690
|
+
| React | `client.on(...)` in `useEffect` (get the client via `useAgoClient()`), or rely on `useChat`/`useMessages` which manage this for you |
|
|
1691
|
+
| Vue | `useAgoEvents(event, handler)` (auto-cleanup) |
|
|
1692
|
+
| Angular | `messages$`, `chunks$`, `errors$`, `messageStart$` Observables, or `ago.on(...)` |
|
|
1693
|
+
|
|
1694
|
+
---
|
|
1695
|
+
|
|
1696
|
+
See also: [Core API](core.md) · [Functions & context](functions-and-context.md)
|
|
1697
|
+
|
|
1698
|
+
---
|
|
1699
|
+
|
|
1700
|
+
<!-- source: docs/general/configuration.md -->
|
|
1701
|
+
|
|
1702
|
+
# Configuration & auth
|
|
1703
|
+
|
|
1704
|
+
Every entry point ultimately builds an `AgoClient` from an `AgoConfig`. This
|
|
1705
|
+
page documents each option, how it maps to request headers, runtime updates,
|
|
1706
|
+
auto-detection, and error handling.
|
|
1707
|
+
|
|
1708
|
+
---
|
|
1709
|
+
|
|
1710
|
+
## `AgoConfig`
|
|
1711
|
+
|
|
1712
|
+
```ts
|
|
1713
|
+
interface AgoConfig {
|
|
1714
|
+
baseUrl: string; // required, e.g. https://YOUR-DOMAIN.useago.com
|
|
1715
|
+
widgetId?: string; // X-User-Anon-Id header; auto-generated + persisted if omitted
|
|
1716
|
+
agent?: string; // default agent (id or slug) for new conversations
|
|
1717
|
+
defaultAgentId?: string; // same as `agent`; `agent` is preferred
|
|
1718
|
+
permission?: string; // X-Widget-Permission header
|
|
1719
|
+
userEmail?: string; // X-User-Email header, identify the end user
|
|
1720
|
+
userJwt?: string; // Authorization: Bearer <jwt>, authenticated users
|
|
1721
|
+
debug?: boolean; // enable verbose console logging
|
|
1722
|
+
warnOnEmptyReply?: boolean; // default true; see "Empty replies" below
|
|
1723
|
+
clientFunctionsMode?: "placeholder" | "pause"; // default "placeholder"
|
|
1724
|
+
maxFunctionResultBytes?: number; // default 50000; cap on client function results
|
|
1725
|
+
}
|
|
1726
|
+
```
|
|
1727
|
+
|
|
1728
|
+
| Option | Required | Maps to | Notes |
|
|
1729
|
+
| --- | :---: | --- | --- |
|
|
1730
|
+
| `baseUrl` | ✅ | request URL | Trailing slash is trimmed automatically. Missing/empty throws `config_missing_base_url` at construct time. `/path` values (same-origin proxy) are allowed. |
|
|
1731
|
+
| `widgetId` | — | `X-User-Anon-Id` | Per-visitor anonymous id. If omitted, a UUID is generated and cached in `localStorage` (`ago_widget_id`) so a returning visitor keeps the same id. |
|
|
1732
|
+
| `agent` / `defaultAgentId` | — | `agent_id` in the body | Per-message `agentId` overrides it. `agent` wins if both are set. |
|
|
1733
|
+
| `permission` | — | `X-Widget-Permission` | Mirrors the widget's `window.AGO.permission`. |
|
|
1734
|
+
| `userEmail` | — | `X-User-Email` | Identifies the user to AGO. |
|
|
1735
|
+
| `userJwt` | — | `Authorization: Bearer …` | For authenticated sessions. |
|
|
1736
|
+
| `debug` | — | — | Turns on the SDK's logger. |
|
|
1737
|
+
| `warnOnEmptyReply` | — | — | Default `true`: warn on the console (once per conversation) when a reply completes empty, usually an unknown `agent` slug. The [`message:empty` event](events-and-streaming.md#events) fires regardless. |
|
|
1738
|
+
| `clientFunctionsMode` | — | `client_functions_mode` in the body | `"pause"` makes the agent stop on client function call(s) and resume the SAME turn once the results are submitted (the SDK submits and resumes automatically; see the [`message:waiting-client` event](events-and-streaming.md#events)). Default `"placeholder"`: the turn continues on a placeholder and results are only visible on later turns. Needs a backend with pause/resume support; older backends ignore the flag. Per-message override via `sendMessage(..., { clientFunctionsMode })`. |
|
|
1739
|
+
| `maxFunctionResultBytes` | — | — | Default `50000`. Client function results larger than this (serialized) are replaced with a truncated preview before reaching the LLM. Override per function with `maxResultBytes`; see [result size limit](functions-and-context.md#result-size-limit). |
|
|
1740
|
+
|
|
1741
|
+
---
|
|
1742
|
+
|
|
1743
|
+
## Debug logging
|
|
1744
|
+
|
|
1745
|
+
`debug: true` turns on the SDK's internal logger. While enabled, the client
|
|
1746
|
+
writes verbose messages to the browser console, each prefixed with `[AGO SDK]`:
|
|
1747
|
+
client init, HTTP/stream requests, and registered functions/context. Examples:
|
|
1748
|
+
|
|
1749
|
+
```
|
|
1750
|
+
[AGO SDK] AgoClient initialized
|
|
1751
|
+
[AGO SDK] POST (stream) https://acme.useago.com/api/sdk/v1/messages {…}
|
|
1752
|
+
[AGO SDK] ClientContext set: order-page
|
|
1753
|
+
```
|
|
1754
|
+
|
|
1755
|
+
- **Default is `false`**: all of those messages are silent.
|
|
1756
|
+
- **Errors are always logged** (`console.error`), whether `debug` is on or off.
|
|
1757
|
+
- Use it in development; **leave it off in production** to keep users' consoles
|
|
1758
|
+
clean.
|
|
1759
|
+
- Toggle it at runtime with `client.updateConfig({ debug: true })` (or `false`).
|
|
1760
|
+
|
|
1761
|
+
```ts
|
|
1762
|
+
const ago = new AgoClient({ baseUrl: "https://acme.useago.com", debug: true });
|
|
1763
|
+
|
|
1764
|
+
// Or detect config from the DOM and only override `debug`:
|
|
1765
|
+
import { createAgo } from "@useago/sdk";
|
|
1766
|
+
const ago2 = createAgo({ debug: true });
|
|
1767
|
+
```
|
|
1768
|
+
|
|
1769
|
+
---
|
|
1770
|
+
|
|
1771
|
+
## Examples per framework
|
|
1772
|
+
|
|
1773
|
+
```ts
|
|
1774
|
+
// Core
|
|
1775
|
+
const ago = new AgoClient({ baseUrl: "https://acme.useago.com", agent: "support" });
|
|
1776
|
+
```
|
|
1777
|
+
|
|
1778
|
+
```tsx
|
|
1779
|
+
// React
|
|
1780
|
+
<AgoProvider baseUrl="https://acme.useago.com" agent="support" />
|
|
1781
|
+
```
|
|
1782
|
+
|
|
1783
|
+
```ts
|
|
1784
|
+
// Vue
|
|
1785
|
+
app.use(AgoPlugin, { baseUrl: "https://acme.useago.com", agent: "support" });
|
|
1786
|
+
```
|
|
1787
|
+
|
|
1788
|
+
```ts
|
|
1789
|
+
// Angular
|
|
1790
|
+
provideAgo({ baseUrl: "https://acme.useago.com", agent: "support" });
|
|
1791
|
+
```
|
|
1792
|
+
|
|
1793
|
+
---
|
|
1794
|
+
|
|
1795
|
+
## Zero-config auto-detection
|
|
1796
|
+
|
|
1797
|
+
`createAgo()` and `autoDetectConfig()` resolve config from the page, in priority
|
|
1798
|
+
order:
|
|
1799
|
+
|
|
1800
|
+
1. **`window.AGO`** (the widget config object): `basepath`, `widgetId`,
|
|
1801
|
+
`agent`/`defaultAgent`, `permission`, `email`, `jwt`.
|
|
1802
|
+
2. **`<meta>` tags**: `ago-base-url`, `ago-widget-id`, `ago-agent`
|
|
1803
|
+
(or `ago-agent-id`), `ago-permission`, `ago-user-email`.
|
|
1804
|
+
3. **`data-ago-*` attributes** on `<body>` or a `<script>` tag:
|
|
1805
|
+
`data-ago-base-url`, `data-ago-widget-id`, `data-ago-agent`
|
|
1806
|
+
(or `data-ago-agent-id`), `data-ago-permission`, `data-ago-user-email`.
|
|
1807
|
+
4. **Explicit overrides** passed to the function.
|
|
1808
|
+
|
|
1809
|
+
```html
|
|
1810
|
+
<meta name="ago-base-url" content="https://acme.useago.com" />
|
|
1811
|
+
<meta name="ago-agent" content="support" />
|
|
1812
|
+
```
|
|
1813
|
+
|
|
1814
|
+
```ts
|
|
1815
|
+
import { createAgo, autoDetectConfig } from "@useago/sdk";
|
|
1816
|
+
|
|
1817
|
+
const client = createAgo(); // throws if no baseUrl found
|
|
1818
|
+
const client2 = createAgo({ debug: true }); // detect + override
|
|
1819
|
+
|
|
1820
|
+
const config = autoDetectConfig(); // → AgoConfig | null (no throw)
|
|
1821
|
+
```
|
|
1822
|
+
|
|
1823
|
+
---
|
|
1824
|
+
|
|
1825
|
+
## Updating config at runtime
|
|
1826
|
+
|
|
1827
|
+
Common after login (attaching a JWT) or switching agents:
|
|
1828
|
+
|
|
1829
|
+
```ts
|
|
1830
|
+
client.updateConfig({ userJwt: token, userEmail: "jane@acme.com" });
|
|
1831
|
+
```
|
|
1832
|
+
|
|
1833
|
+
Changing `permission` to a falsy value removes the header. In React, the
|
|
1834
|
+
`useAgo` hook re-applies `agent`, `permission`, `userEmail`, `userJwt`,
|
|
1835
|
+
`debug` and `warnOnEmptyReply` automatically when those props change (changing
|
|
1836
|
+
`baseUrl`/`widgetId` recreates the client).
|
|
1837
|
+
|
|
1838
|
+
`updateConfig` validates the merged result: omitting a key (or passing it as
|
|
1839
|
+
`undefined`) keeps the current value; an explicit empty or non-string `baseUrl`
|
|
1840
|
+
throws `config_missing_base_url` and leaves the client unchanged.
|
|
1841
|
+
|
|
1842
|
+
---
|
|
1843
|
+
|
|
1844
|
+
## Errors
|
|
1845
|
+
|
|
1846
|
+
All SDK errors extend `AgoError` (`code`, optional `statusCode`). Catch and
|
|
1847
|
+
branch on the subclass:
|
|
1848
|
+
|
|
1849
|
+
```ts
|
|
1850
|
+
import {
|
|
1851
|
+
AgoError,
|
|
1852
|
+
AgoApiError, // non-2xx HTTP response (has type, statusCode, param, docUrl)
|
|
1853
|
+
AgoNetworkError, // fetch failed / offline (has originalError)
|
|
1854
|
+
AgoStreamError, // SSE stream failure
|
|
1855
|
+
AgoFunctionError, // a client-side function handler threw (has functionName)
|
|
1856
|
+
} from "@useago/sdk";
|
|
1857
|
+
|
|
1858
|
+
try {
|
|
1859
|
+
await client.sendMessage("hi");
|
|
1860
|
+
} catch (err) {
|
|
1861
|
+
if (err instanceof AgoApiError) {
|
|
1862
|
+
console.error(`API ${err.statusCode}: ${err.message} (${err.code})`);
|
|
1863
|
+
} else if (err instanceof AgoNetworkError) {
|
|
1864
|
+
console.error("Network problem", err.originalError);
|
|
1865
|
+
} else if (err instanceof AgoError) {
|
|
1866
|
+
console.error(err.code, err.message);
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
```
|
|
1870
|
+
|
|
1871
|
+
`isStreamNetworkError(err)` is exported for detecting recoverable stream drops.
|
|
1872
|
+
|
|
1873
|
+
### Error codes
|
|
1874
|
+
|
|
1875
|
+
`code` is the compatibility surface: match on it, never on message text, which
|
|
1876
|
+
may be reworded in any release.
|
|
1877
|
+
|
|
1878
|
+
| Code | Thrown by | Meaning | Fix |
|
|
1879
|
+
| --- | --- | --- | --- |
|
|
1880
|
+
| `config_missing_base_url` | `AgoError` | `baseUrl` missing, empty, or not a string (also from `createAgo()` and `mountChatWidget`) | Pass `baseUrl`, e.g. `https://YOUR-DOMAIN.api.useago.com` (the live demo endpoint `https://playground.api.useago.com` works without a tenant) |
|
|
1881
|
+
| `config_suspect_base_url` | console warning only | `baseUrl` has no protocol and is not a `/path` | Add `https://`, or use a `/path` for a same-origin proxy |
|
|
1882
|
+
| `network_error` | `AgoNetworkError` | fetch failed (offline, DNS, unreachable host) | Check `baseUrl` is reachable and includes the protocol |
|
|
1883
|
+
| `http_error` | `AgoApiError` | non-2xx without a structured error body | 401/403: check `userJwt`. 404: check `baseUrl` points at the API root |
|
|
1884
|
+
| `stream_no_body` | `AgoStreamError` | the endpoint returned no streamable body | Check `baseUrl` targets an AGO API and no proxy/mock strips the SSE stream |
|
|
1885
|
+
| `stream_error` | `AgoStreamError` | the stream failed mid-flight | Usually a dropped connection; retry the message |
|
|
1886
|
+
| `function_invalid_registration` | `AgoError` | `registerFunction` called without a handler or schema | Use `registerFunction({ name, parameters, handler })` |
|
|
1887
|
+
| `function_error` | `AgoFunctionError` | a registered client function threw | Inspect `functionName` and `originalError` |
|
|
1888
|
+
|
|
1889
|
+
Server-supplied errors (`AgoApiError` with a structured body) keep the
|
|
1890
|
+
backend's own `code` and, when provided, link their docs in the message
|
|
1891
|
+
(`docUrl`).
|
|
1892
|
+
|
|
1893
|
+
### Empty replies
|
|
1894
|
+
|
|
1895
|
+
The backend currently answers an unknown `agent` slug with an empty 200, which
|
|
1896
|
+
would otherwise fail silently (an empty bubble, nothing thrown). When a reply
|
|
1897
|
+
completes as `DONE` with no content, no tool calls, no client-function
|
|
1898
|
+
invocations and no follow-up replies, the SDK:
|
|
1899
|
+
|
|
1900
|
+
1. emits [`message:empty`](events-and-streaming.md#events) (always), and
|
|
1901
|
+
2. warns on the console, once per conversation, unless
|
|
1902
|
+
`warnOnEmptyReply: false`.
|
|
1903
|
+
|
|
1904
|
+
Listen for `message:empty` to handle it in your UI. This client-side detection
|
|
1905
|
+
is temporary: it goes away once the backend returns an explicit error for
|
|
1906
|
+
unknown agents.
|
|
1907
|
+
|
|
1908
|
+
---
|
|
1909
|
+
|
|
1910
|
+
See also: [Core API](core.md) · [Events & streaming](events-and-streaming.md) · [Custom domain (reverse proxy)](custom-domain.md)
|