@useago/sdk 1.0.0 → 1.0.2

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/CHANGELOG.md CHANGED
@@ -5,6 +5,50 @@ All notable changes to `@useago/sdk` are documented here.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+
12
+ - `AgoConfig.warnOnEmptyReply` (default `true`) and a `message:empty` event:
13
+ a reply that completes `DONE` with no content, tool calls, or follow-ups
14
+ (usually an unknown `agent` slug) now warns on the console once per
15
+ conversation and emits a host-consumable event instead of failing silently.
16
+ - Stable error codes as the compatibility surface (match on `code`, not
17
+ message text): `config_missing_base_url`, `config_suspect_base_url`
18
+ (warning), `stream_no_body`, `function_invalid_registration`. Code registry
19
+ documented in `docs/general/configuration.md#error-codes`.
20
+ - `message:error` events now carry the stable `code` when the failure was an
21
+ `AgoError`.
22
+ - `validateConfig` is exported.
23
+ - CI now runs lint, typecheck, build and `check:package`, plus a Node 18
24
+ packed-tarball smoke test (ESM import + CJS require + instantiation).
25
+
26
+ ### Changed
27
+
28
+ - `new AgoClient({})` (or missing/empty `baseUrl`) now throws an actionable
29
+ `AgoError` (`config_missing_base_url`) instead of a raw `TypeError`.
30
+ Same-origin `/path` baseUrls remain supported; a baseUrl with no protocol
31
+ and no leading slash logs a one-time warning.
32
+ - `updateConfig` validates the merged config and no longer half-applies an
33
+ invalid update; explicit `undefined` values are treated as absent.
34
+ - Error messages now include a one-line fix hint (network, no-body stream,
35
+ 401/403/404 HTTP errors) and surface the server's `doc_url` when present.
36
+ - `createAgo()` and `mountChatWidget` throw coded `AgoError`s for missing
37
+ config instead of plain `Error`s.
38
+ - The `simple-html` examples import the published package from
39
+ `https://esm.sh/@useago/sdk@1`, so opening the file directly in a browser
40
+ works with no build step.
41
+ - The visitor anonymous id is now sent as the `X-User-Anon-Id` header. The
42
+ `widgetId` config option and the `ago_widget_id` storage key are unchanged.
43
+
44
+ ### Fixed
45
+
46
+ - `generateAnonId` no longer crashes on stock Node 18 (global
47
+ `crypto.randomUUID` is flag-gated there); it feature-detects and falls back.
48
+ - `AgoProvider` no longer constructs a throwaway internal client when an
49
+ external `client` prop is provided.
50
+ - `examples/simple-html/test.html` imported from a wrong-depth path.
51
+
8
52
  ## [1.0.0] - 2026-06-11
9
53
 
10
54
  First stable public release.
package/README.md CHANGED
@@ -1,97 +1,115 @@
1
1
  # @useago/sdk
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@useago/sdk.svg)](https://www.npmjs.com/package/@useago/sdk)
4
- [![license](https://img.shields.io/npm/l/@useago/sdk.svg)](./LICENSE)
5
4
  [![types](https://img.shields.io/npm/types/@useago/sdk.svg)](https://www.npmjs.com/package/@useago/sdk)
6
5
 
7
- Official JavaScript/TypeScript SDK for [AGO](https://useago.com) — embed AGO's AI
8
- agents directly inside your own app. Stream answers, let the agent call **your**
9
- client-side functions, navigate users around your UI, and feed it live context
10
- about what the user is doing.
6
+ **The agent layer for your frontend stack.** AGO's agents plug into React, Vue,
7
+ Angular or plain TypeScript like any other dependency: send a message, stream
8
+ the reply, and let the agent call **your** code and navigate **your** UI.
11
9
 
12
- Works with **plain JS/TS, React, Vue and Angular**. The core is framework-agnostic;
13
- each framework gets idiomatic bindings (hooks, composables, services) on top of the
14
- same `AgoClient`.
10
+ In two minutes you'll have a working chat on your page and an agent that can
11
+ navigate your app. The quickstart below uses **React**; every other stack gets
12
+ the same two steps through its own guide:
13
+
14
+ | Stack | Guide |
15
+ | --- | --- |
16
+ | <img src="https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/react/react-original.svg" width="14" /> React | **you're reading it**, just scroll down |
17
+ | <img src="https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/vuejs/vuejs-original.svg" width="14" /> Vue 3 | [Vue guide](docs/frameworks/vue.md) |
18
+ | <img src="https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/angular/angular-original.svg" width="14" /> Angular | [Angular guide](docs/frameworks/angular.md) |
19
+ | <img src="https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/typescript/typescript-original.svg" width="14" /> Plain JavaScript / TypeScript | [Core guide](docs/general/core.md) |
20
+ | <img src="https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/html5/html5-original.svg" width="14" /> Any website, no build step (`<script>`) | [Widget guide](docs/general/widget.md) |
21
+
22
+ ## <img src="https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/react/react-original.svg" width="22" /> Try it in 30 seconds
15
23
 
16
24
  ```bash
17
25
  npm install @useago/sdk
18
26
  ```
19
27
 
20
- 📚 **Full documentation:** [docs.useago.com](https://docs.useago.com) · the
21
- framework guides below are also published there.
28
+ The SDK has no framework dependency of its own. The React bindings need `react`
29
+ and `react-dom` (>=17): already there in an existing React app, otherwise
30
+ install them too.
31
+
32
+ ```tsx
33
+ // App.tsx
34
+ import { AgoProvider, ChatWidget } from "@useago/sdk/react";
35
+
36
+ export default function App() {
37
+ return (
38
+ <AgoProvider baseUrl="https://playground.api.useago.com" agent="generic-guide">
39
+ <ChatWidget title="AGO" welcomeMessage="Ask me anything!" height={500} />
40
+ </AgoProvider>
41
+ );
42
+ }
43
+ ```
22
44
 
23
- ---
45
+ Start your dev server. A chat panel renders on the page; type a message and
46
+ watch the reply stream in. That's a complete integration. (`generic-guide` is a
47
+ public demo agent; swap `baseUrl` and `agent` for your own once you have a tenant.)
24
48
 
25
- ## Quick start
49
+ ## Let the agent drive your existing routes
26
50
 
27
- Pick your stack — each guide is self-contained.
51
+ This is the part existing apps care about. You already have a router and pages;
52
+ describe them to the agent and *"show me my invoices"* actually navigates there.
53
+ One hook in the layout you already have, nothing to restructure:
28
54
 
29
- | Stack | Guide |
30
- | --- | --- |
31
- | Plain JavaScript / TypeScript | [docs/general/core.md](docs/general/core.md) |
32
- | React | [docs/frameworks/react.md](docs/frameworks/react.md) |
33
- | Vue 3 | [docs/frameworks/vue.md](docs/frameworks/vue.md) |
34
- | Angular | [docs/frameworks/angular.md](docs/frameworks/angular.md) |
35
- | Embeddable widget (`<script>`) | [docs/general/widget.md](docs/general/widget.md) |
55
+ ```tsx
56
+ import { Outlet, useNavigate } from "react-router-dom";
57
+ import { useAgoNavigation } from "@useago/sdk/react";
36
58
 
37
- Cross-cutting topics (apply to every framework):
59
+ // Your existing layout component. Only the hook is new.
60
+ function AppLayout() {
61
+ const navigate = useNavigate();
38
62
 
39
- - [**Client-side functions & helpers**](docs/general/functions-and-context.md#client-side-functions) — let the agent run code in the browser
40
- - [**Client context**](docs/general/functions-and-context.md#client-context) — tell the agent what the user is looking at
41
- - [**Events & streaming**](docs/general/events-and-streaming.md) — low-level hooks into the message stream
42
- - [**Testing**](docs/general/testing.md) — a mock client for unit tests
43
- - [**Configuration & auth**](docs/general/configuration.md) — every `AgoConfig` option, headers, errors
63
+ useAgoNavigation(navigate, [
64
+ { name: "dashboard", path: "/dashboard", description: "KPIs and recent activity" },
65
+ { name: "invoices", path: "/invoices", description: "List, search and download invoices" },
66
+ { name: "settings", path: "/settings", description: "Account, billing and team settings" },
67
+ ]);
44
68
 
45
- The 30-second version (vanilla):
69
+ return <Outlet />; // your existing routes render as before
70
+ }
71
+ ```
46
72
 
47
- ```ts
48
- import { AgoClient } from "@useago/sdk";
73
+ Each route needs a `name`, its existing `path`, and a `description` the agent
74
+ uses to pick the right page. It calls **your** `navigate` function, so route
75
+ guards, auth and layouts keep working exactly as they do today.
76
+ `react-router-dom` here is *your* router, not an SDK dependency:
77
+ `useAgoNavigation` accepts any `(path: string) => void` function.
49
78
 
50
- const ago = new AgoClient({ baseUrl: "https://YOUR-DOMAIN.useago.com" });
79
+ **Next: the [Getting started guide](docs/general/getting-started.md)** has both
80
+ examples assembled into a running app, plus everything else the SDK can do, in
81
+ about 5 minutes.
51
82
 
52
- ago.on("message:chunk", ({ content }) => process.stdout.write(content));
83
+ ## Or clone a running example
53
84
 
54
- const reply = await ago.sendMessage("What can you do?");
55
- console.log("\nDone:", reply.status);
85
+ ```bash
86
+ git clone https://github.com/useago/ago-sdk.git
87
+ cd ago-sdk && npm install && npm run build # build the SDK once
88
+ cd examples/simple-react
89
+ npm install && npm run dev
56
90
  ```
57
91
 
92
+ The examples ship pre-configured against the demo backend, so they answer
93
+ immediately. [`examples/`](examples/) has one per stack: React (with
94
+ react-router navigation), Vue, Angular, plain TypeScript, and no-build HTML.
95
+
58
96
  ---
59
97
 
60
- ## Feature matrix
61
-
62
- What ships out of the box for each entry point. The **core** (`@useago/sdk`)
63
- APIs are usable from *any* framework — the framework columns mark where a more
64
- idiomatic binding (hook / composable / service / component) is also provided.
65
-
66
- | Feature | Core (vanilla) | React | Vue | Angular |
67
- | --- | :---: | :---: | :---: | :---: |
68
- | Create client | `new AgoClient()` | `useAgo` / `<AgoProvider>` | `AgoPlugin` / `useAgo` | `provideAgo` / `AgoService` |
69
- | Zero-config auto-detect | ✅ `createAgo()` | ✅ | ✅ | ✅ |
70
- | Send message (streaming) | ✅ | ✅ `useChat`/`useMessages` | ✅ `useChat`/`useMessages` | ✅ `AgoService.sendMessage` |
71
- | File attachments | ✅ | ✅ | ✅ | ✅ |
72
- | List / load conversations | ✅ | ✅ `useConversation` | ✅ `useConversation` | ✅ `AgoService` |
73
- | All-in-one chat state | — | ✅ `useChat` | ✅ `useChat` | ➖ compose manually |
74
- | Pre-built `<ChatWidget>` UI | — | ✅ | ➖ see example | — |
75
- | Markdown / `<Message>` / `<ChatInput>` | — | ✅ | — | — |
76
- | Client-side functions | ✅ `registerFunction` | ✅ `useAgoFunction` | ✅ `useAgoFunction` | ✅ `AgoService` |
77
- | `defineFunction` / `withHandler` | ✅ | ✅ | ✅ | ✅ |
78
- | Pre-built helpers (`showToast`, …) | ✅ | ✅ `helpers` prop | ✅ | ✅ |
79
- | Navigation function | ✅ `registerNavigationFunction` | ✅ `useAgoNavigation` | ✅ `useAgoNavigation` | ✅ `AgoService` |
80
- | Client context | ✅ `setContext` / `addDynamicContext` | ✅ `useAgoContext` | ✅ core API | ✅ core API |
81
- | Auto page context | ✅ `enableAutoPageContext()` | ✅ `pageContext="auto"` | ✅ core API | ✅ core API |
82
- | Events (`on`/`off`/`once`/`waitFor`) | ✅ | ✅ (via client) | ✅ `useAgoEvents` | ✅ `messages$` Observables |
83
- | Streaming helpers / async generator | ✅ | ✅ | ✅ | ✅ |
84
- | Tool calls (form/confirm/reject) | ✅ | ✅ | ✅ | ✅ `AgoService` |
85
- | Message feedback | ✅ `submitFeedback` | ✅ | ✅ | ✅ `AgoService` |
86
- | Observable store | ✅ `createStore` | ✅ `useAgoStore` | ✅ `useAgoStore` | ➖ core API |
87
- | Dev panel / devtools | ✅ `initDevPanel` (`/devtools`) | ✅ | ✅ | ✅ |
88
- | Testing mock client | ✅ `createMockClient` | ✅ | ✅ | ✅ |
89
-
90
- Legend: ✅ first-class binding · ➖ supported via the core API, no dedicated sugar · — not applicable.
98
+ ## Docs
99
+
100
+ - [**Getting started**](docs/general/getting-started.md): send a message, wire the router, see the feature map
101
+ - [**Feature matrix**](docs/general/feature-matrix.md): everything that ships, per stack
102
+ - [**Functions & context**](docs/general/functions-and-context.md): let the agent run your code and see what the user sees
103
+ - [**Events & streaming**](docs/general/events-and-streaming.md): low-level hooks into the message stream
104
+ - [**Testing**](docs/general/testing.md): mock client for unit tests
105
+ - [**Configuration & auth**](docs/general/configuration.md): every `AgoConfig` option, headers, errors
106
+
107
+ Full documentation: [ago.mintlify.app](https://ago.mintlify.app/)
91
108
 
92
109
  ---
93
110
 
94
- ## Package entry points
111
+ <details>
112
+ <summary><strong>Package entry points</strong> (subpath exports)</summary>
95
113
 
96
114
  The package is published with several subpath exports so bundlers only pull in
97
115
  what you use:
@@ -104,20 +122,31 @@ what you use:
104
122
  | `@useago/sdk/angular` | Angular service and provider |
105
123
  | `@useago/sdk/helpers` | Pre-built client functions only |
106
124
  | `@useago/sdk/widget` | `window.AGO` widget config types |
107
- | `@useago/sdk/devtools` | `initDevPanel` — in-browser debug overlay (DOM/CSS) |
108
- | `@useago/sdk/testing` | `createMockClient` — mock client for tests |
125
+ | `@useago/sdk/devtools` | `initDevPanel`, an in-browser debug overlay (DOM/CSS) |
126
+ | `@useago/sdk/testing` | `createMockClient`, a mock client for tests |
109
127
 
110
128
  ESM and CJS builds are both shipped, with full TypeScript declarations.
111
129
 
112
- ---
130
+ </details>
113
131
 
114
- ## Requirements
132
+ ## Dependencies
115
133
 
116
- - A modern browser environment (uses `fetch` + `ReadableStream` for SSE) or Node 18+.
117
- - `react` / `react-dom` `>=17` for the React bindings (peer dependency, optional).
118
- - `vue` `>=3.3` for the Vue bindings (peer dependency, optional).
119
- - Angular bindings have **no** hard Angular dependency — they work with any DI container.
134
+ The core SDK depends on nothing but the platform: `fetch` and `ReadableStream`,
135
+ so any modern browser or Node 18+. Framework packages are **optional peer
136
+ dependencies**; you only need the one whose bindings you import:
137
+
138
+ | You import | Needs | Notes |
139
+ | --- | --- | --- |
140
+ | `@useago/sdk` (core, widget, helpers, testing) | nothing | works everywhere |
141
+ | `@useago/sdk/react` | `react` + `react-dom` `>=17` | already present in any React app |
142
+ | `@useago/sdk/vue` | `vue` `>=3.3` | already present in any Vue 3 app |
143
+ | `@useago/sdk/angular` | nothing | no hard Angular dependency, works with any DI container |
144
+
145
+ Your router (`react-router-dom`, `vue-router`, Angular Router) is **never** an
146
+ SDK dependency: the navigation helpers just take your navigate function.
147
+
148
+ ---
120
149
 
121
150
  ## License
122
151
 
123
- [Apache 2.0](./LICENSE) · [Documentation](https://docs.useago.com) · [Website](https://useago.com)
152
+ [Apache 2.0](./LICENSE) · [Documentation](https://ago.mintlify.app/) · [Website](https://useago.com)
@@ -0,0 +1,6 @@
1
+ var h=class extends Error{constructor(e,t,s){super(e),this.code=t,this.statusCode=s,this.name="AgoError"}},u=class v extends h{constructor(t,s,r,n,i,a){super(t,s,r),this.type=n,this.param=i,this.docUrl=a,this.name="AgoApiError"}static fromResponse(t,s){const r=t.error;return new v(r.doc_url?`${r.message} See ${r.doc_url}`:r.message,r.code,s,r.type,r.param,r.doc_url)}},d=class extends h{constructor(e,t){super(e,"network_error"),this.originalError=t,this.name="AgoNetworkError"}},g=class extends h{constructor(e,t="stream_error"){super(e,t),this.name="AgoStreamError"}},m=class extends h{constructor(e,t,s){super(e,"function_error"),this.functionName=t,this.originalError=s,this.name="AgoFunctionError"}},A=class{constructor(e=!1){this.enabled=e}enable(){this.enabled=!0}disable(){this.enabled=!1}log(...e){this.enabled&&console.log("[AGO SDK]",...e)}warn(...e){this.enabled&&console.warn("[AGO SDK]",...e)}error(...e){console.error("[AGO SDK]",...e)}debug(...e){this.enabled&&console.debug("[AGO SDK]",...e)}},o=new A,p="ago_widget_id";function x(){if(typeof localStorage<"u"){const t=localStorage.getItem(p);if(t)return t}const e=typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():`ago-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,12)}`;if(typeof localStorage<"u")try{localStorage.setItem(p,e)}catch{}return e}var $=class{constructor(e){this.baseUrl=e.baseUrl.replace(/\/$/,""),this.headers={"X-User-Anon-Id":e.widgetId||x()},e.userEmail&&(this.headers["X-User-Email"]=e.userEmail),e.userJwt&&(this.headers.Authorization=`Bearer ${e.userJwt}`),e.permission&&(this.headers["X-Widget-Permission"]=e.permission)}updateConfig(e){e.baseUrl&&(this.baseUrl=e.baseUrl.replace(/\/$/,"")),e.widgetId&&(this.headers["X-User-Anon-Id"]=e.widgetId),e.userEmail&&(this.headers["X-User-Email"]=e.userEmail),e.userJwt&&(this.headers.Authorization=`Bearer ${e.userJwt}`),e.permission!==void 0&&(e.permission?this.headers["X-Widget-Permission"]=e.permission:delete this.headers["X-Widget-Permission"])}async get(e){return this.request("GET",e)}async post(e,t){return this.request("POST",e,t)}async postStream(e,t){const s=`${this.baseUrl}${e}`;o.debug("POST (stream)",s,t);try{const r=await fetch(s,{method:"POST",headers:{...this.headers,"Content-Type":"application/json"},body:t?JSON.stringify(t):void 0});return r.ok||await this.handleErrorResponse(r),r}catch(r){throw r instanceof u?r:new d(`Network error: ${r instanceof Error?r.message:"Unknown error"}. Check that \`baseUrl\` is reachable and includes the protocol (https://).`,r instanceof Error?r:void 0)}}async postFormData(e,t){const s=`${this.baseUrl}${e}`;o.debug("POST (formData)",s);const r={...this.headers};try{const n=await fetch(s,{method:"POST",headers:r,body:t});return n.ok||await this.handleErrorResponse(n),n}catch(n){throw n instanceof u?n:new d(`Network error: ${n instanceof Error?n.message:"Unknown error"}. Check that \`baseUrl\` is reachable and includes the protocol (https://).`,n instanceof Error?n:void 0)}}async request(e,t,s){const r=`${this.baseUrl}${t}`;o.debug(e,r,s);try{const n=await fetch(r,{method:e,headers:{...this.headers,"Content-Type":"application/json"},body:s?JSON.stringify(s):void 0});return n.ok||await this.handleErrorResponse(n),n.status===204?void 0:n.json()}catch(n){throw n instanceof u?n:new d(`Network error: ${n instanceof Error?n.message:"Unknown error"}. Check that \`baseUrl\` is reachable and includes the protocol (https://).`,n instanceof Error?n:void 0)}}async handleErrorResponse(e){let t;try{t=await e.json()}catch{}if(t?.error)throw u.fromResponse(t,e.status);let s="";throw e.status===401||e.status===403?s=" Check `userJwt` (and that it has not expired).":e.status===404&&(s=" Check that `baseUrl` points at your AGO API root."),new u(`HTTP ${e.status}: ${e.statusText}.${s}`,"http_error",e.status,"api_error")}},b=class{constructor(){this.functions=new Map}register(e,t,s){if(typeof e=="object"){const{name:n,handler:i,description:a,parameters:l}=e;return this.register(n,i,{description:a,parameters:l})}const r=e;if(!t||!s)throw new h(`registerFunction("${r}"): both a handler and a schema are required. Pass them as arguments or use the single-object form: registerFunction({ name, parameters, handler }).`,"function_invalid_registration");this.functions.has(r)&&o.warn(`Function "${r}" is being overwritten`),this.functions.set(r,{schema:{...s,name:r},handler:t}),o.log(`Registered function: ${r}`)}unregister(e){const t=this.functions.delete(e);return t&&o.log(`Unregistered function: ${e}`),t}get(e){return this.functions.get(e)}has(e){return this.functions.has(e)}getSchemas(){return Array.from(this.functions.values()).map(e=>e.schema)}async execute(e,t){const s=this.functions.get(e);if(!s)throw new m(`Function "${e}" is not registered`,e);o.log(`Executing function: ${e}`,t);try{const r=await s.handler(t);return o.log(`Function ${e} completed:`,r),r}catch(r){throw o.error(`Function ${e} failed:`,r),new m(`Function "${e}" execution failed: ${r instanceof Error?r.message:"Unknown error"}`,e,r instanceof Error?r:void 0)}}clear(){this.functions.clear(),o.log("Cleared all registered functions")}get size(){return this.functions.size}},C=class{constructor(){this.entries=new Map,this.dynamicProviders=new Map}set(e,t){this.entries.set(e,t),o.log(`ClientContext set: ${e}`)}remove(e){const t=this.entries.delete(e);return t&&o.log(`ClientContext removed: ${e}`),t}addDynamicProvider(e,t){this.dynamicProviders.set(e,t),o.log(`DynamicContext provider added: ${e}`)}removeDynamicProvider(e){const t=this.dynamicProviders.delete(e);return t&&o.log(`DynamicContext provider removed: ${e}`),t}getSnapshot(){if(this.entries.size===0&&this.dynamicProviders.size===0)return null;const e={};for(const[t,s]of this.entries)e[t]=s;for(const[t,s]of this.dynamicProviders)try{const r=s();r&&(e[t]=r)}catch(r){o.error(`DynamicContext provider "${t}" threw:`,r)}return Object.keys(e).length===0?null:{entries:e}}clear(){this.entries.clear(),this.dynamicProviders.clear(),o.log("ClientContext cleared")}},E=class{constructor(e){this.callbacks=e,this.buffer="",this.message={},this.toolCalls=[],this.sources=[],this.followUpReplies=[],this.isFirstChunk=!0,this.answerCompleteEmitted=!1}async processStream(e){if(!e.body)throw new g("Response has no body. The endpoint did not return a stream: check that `baseUrl` points at an AGO API and nothing (proxy, mock) strips the SSE body.","stream_no_body");const t=e.body.getReader(),s=new TextDecoder;try{for(;;){const{done:r,value:n}=await t.read();if(r)break;const i=s.decode(n,{stream:!0});this.buffer+=i,this.processBuffer()}return this.buffer.trim()&&this.processBuffer(),this.buildFinalMessage()}catch(r){const n=r instanceof Error?r:new g("Stream processing failed mid-flight. The connection may have dropped; retry the message.");throw this.callbacks.onError?.(n),n}finally{t.releaseLock()}}processBuffer(){const e=this.buffer.split(`
2
+
3
+ `);this.buffer=e.pop()||"";for(const t of e){if(!t.trim())continue;const s=t.split(`
4
+ `);for(const r of s)if(!r.startsWith(": ")&&r.startsWith("data: ")){const n=r.slice(6);try{const i=JSON.parse(n);this.handleChunk(i)}catch(i){o.warn("Failed to parse SSE data:",n,i)}}}}handleChunk(e){if(e.message_id&&!this.message.id&&(this.message.id=e.message_id),e.thread?.id&&(this.message.conversationId=e.thread.id,this.isFirstChunk&&this.message.id&&(this.isFirstChunk=!1,this.callbacks.onStart?.({conversationId:this.message.conversationId,messageId:this.message.id}))),e.content!==void 0&&(this.message.content=(this.message.content||"")+e.content,this.message.conversationId&&this.message.id&&this.callbacks.onChunk?.({content:e.content,conversationId:this.message.conversationId,messageId:this.message.id})),e.full_content!==void 0&&(this.message.content=e.full_content),e.status&&(this.message.status=e.status),e.agent&&(this.message.agent={id:e.agent.id,name:e.agent.name,displayName:e.agent.display_name}),e.knowledge_sources&&(this.sources=e.knowledge_sources.map(t=>{const s=t.knowledge_document;return{id:s.id,title:s.title,url:s.use_external_link?s.external_link_url:s.internal_link_url}})),e.type==="client_function"&&e.function_name&&this.callbacks.onClientFunction?.({invocationId:e.id||"",functionName:e.function_name,arguments:e.arguments||{},conversationId:this.message.conversationId||""}),e.tool_call_data&&e.type&&e.type!=="client_function"){const t=this.parseToolCall(e);this.callbacks.onToolCall?.(t);const s=this.toolCalls.findIndex(r=>r.id===t.id);s>=0?this.toolCalls[s]=t:this.toolCalls.push(t)}e.follow_up_replies&&(this.followUpReplies=e.follow_up_replies),this.message.status==="DONE"&&!this.answerCompleteEmitted&&this.message.id&&(this.answerCompleteEmitted=!0,this.callbacks.onAnswerComplete?.(this.buildMessage()))}parseToolCall(e){return{id:e.id||"",type:e.type||"status_message",status:e.status||"unknown",toolName:e.tool_name||"",toolDisplayName:e.tool_display_name,message:e.message,formSchema:e.form_schema,data:e.data,functionName:e.function_name,arguments:e.arguments}}buildMessage(){return{id:this.message.id||"",conversationId:this.message.conversationId||"",content:this.message.content||"",role:"assistant",status:this.message.status||"DONE",agent:this.message.agent,sources:this.sources.length>0?this.sources:void 0,toolCalls:this.toolCalls.length>0?this.toolCalls:void 0,followUpReplies:this.followUpReplies.length>0?this.followUpReplies:void 0,createdAt:new Date}}buildFinalMessage(){const e=this.buildMessage();return this.callbacks.onComplete?.(e),e}};function R(e){const t=e.message.toLowerCase();return t.includes("load failed")||t.includes("failed to fetch")||t.includes("network")||t.includes("abort")}var _=class{constructor(){this.handlers=new Map}on(e,t){this.handlers.has(e)||this.handlers.set(e,new Set),this.handlers.get(e).add(t)}off(e,t){const s=this.handlers.get(e);s&&s.delete(t)}emit(e,t){const s=this.handlers.get(e);s&&s.forEach(r=>{try{r(t)}catch(n){console.error(`Error in event handler for ${String(e)}:`,n)}})}once(e,t){const s=(r=>{this.off(e,s),t(r)});this.on(e,s)}waitFor(e,t){return new Promise((s,r)=>{let n;const i=(a=>{n&&clearTimeout(n),s(a)});this.once(e,i),t?.timeout&&(n=setTimeout(()=>{this.off(e,i),r(new Error(`waitFor("${String(e)}") timed out after ${t.timeout}ms`))},t.timeout))})}removeAllListeners(e){e?this.handlers.delete(e):this.handlers.clear()}},y='`baseUrl` is required, e.g. new AgoClient({ baseUrl: "https://YOUR-DOMAIN.api.useago.com" }). Try the live demo endpoint https://playground.api.useago.com. See docs/general/configuration.md#error-codes (config_missing_base_url).',I=/^[a-z][a-z0-9+.-]*:\/\//i,w=!1;function f(e,t="AgoClient"){if(typeof e!="object"||e===null)throw new h(`${t}: ${y}`,"config_missing_base_url");const s=e.baseUrl;if(typeof s!="string"||s.trim()==="")throw new h(`${t}: ${y}`,"config_missing_base_url");!s.startsWith("/")&&!I.test(s)&&(w||(w=!0,console.warn(`[AGO] ${t}: \`baseUrl\` has no protocol and is not a relative path (config_suspect_base_url). Requests will fail: Node throws a URL parse error, browsers fetch it as a relative path. Use "https://..." (or a "/path" for a same-origin proxy).`)))}var U=class S{constructor(t){this.warnedEmptyConversations=new Set,f(t,"AgoClient"),this.config=t,this.httpClient=new $(t),this.functionRegistry=new b,this.contextRegistry=new C,this.eventEmitter=new _,t.debug&&o.enable(),o.log("AgoClient initialized")}async sendMessage(t,s){const r=this.functionRegistry.getSchemas(),n=this.contextRegistry.getSnapshot(),i=this.config.agent||this.config.defaultAgentId,a={content:t,conversation_id:s?.conversationId,agent_id:s?.agentId||i};r.length>0&&(a.client_functions=r),n&&(a.client_context=n);let l;if(s?.files&&s.files.length>0){const c=new FormData;c.append("content",t),s.conversationId&&c.append("conversation_id",s.conversationId),(s.agentId||i)&&c.append("agent_id",s.agentId||i||""),r.length>0&&c.append("client_functions",JSON.stringify(r)),n&&c.append("client_context",JSON.stringify(n));for(const k of s.files)c.append("files",k);l=await this.httpClient.postFormData("/api/sdk/v1/messages",c)}else l=await this.httpClient.postStream("/api/sdk/v1/messages",a);return this.processSSEResponse(l)}async processSSEResponse(t){let s=!1;const r=await new E({onStart:n=>{this.eventEmitter.emit("message:start",n)},onChunk:n=>{this.eventEmitter.emit("message:chunk",n)},onToolCall:n=>{this.eventEmitter.emit("toolCall:received",n),n.type==="form"&&this.eventEmitter.emit("toolCall:form",n)},onClientFunction:async n=>{s=!0,this.eventEmitter.emit("function:invoke",n),await this.handleClientFunctionInvocation(n)},onAnswerComplete:n=>{this.eventEmitter.emit("message:answer-complete",n)},onComplete:n=>{this.eventEmitter.emit("message:complete",n)},onError:n=>{this.eventEmitter.emit("message:error",{error:n.message,code:n instanceof h?n.code:void 0})}}).processStream(t);return this.maybeFlagEmptyReply(r,s),r}maybeFlagEmptyReply(t,s){t.status==="DONE"&&(t.content??"").trim()===""&&!s&&!(t.toolCalls&&t.toolCalls.length>0)&&!(t.followUpReplies&&t.followUpReplies.length>0)&&setTimeout(()=>{if(this.eventEmitter.emit("message:empty",{conversationId:t.conversationId??"",messageId:t.id??""}),this.config.warnOnEmptyReply===!1)return;const r=t.conversationId||"(no conversation)";if(this.warnedEmptyConversations.has(r))return;this.warnedEmptyConversations.add(r);const n=this.config.agent||this.config.defaultAgentId;t.id?console.warn("[AGO] Reply completed with empty content. Possible causes: "+(n?`the agent "${n}" may not exist for this tenant, `:"an unknown `agent` slug, ")+"or the agent replied with no text. Listen for the `message:empty` event to handle this in your app; silence this warning with `warnOnEmptyReply: false`."):console.warn("[AGO] The stream completed without any message data. Possible causes: "+(n?`the agent "${n}" may not exist for this tenant, or `:"")+"`baseUrl` does not point at an AGO endpoint that returns server-sent events. Listen for the `message:empty` event to handle this in your app; silence this warning with `warnOnEmptyReply: false`.")},0)}async handleClientFunctionInvocation(t){let s,r;try{s=await this.functionRegistry.execute(t.functionName,t.arguments)}catch(n){r=n instanceof Error?n.message:"Unknown error",o.error("Client function execution failed:",n)}if(t.invocationId)try{await this.submitToolCallForm(t.invocationId,{result:s,error:r,_type:"client_function_result"})}catch(n){o.error("Failed to submit function result:",n)}this.eventEmitter.emit("function:result",{invocationId:t.invocationId,result:s,error:r})}async getConversations(){return(await this.httpClient.get("/api/sdk/v1/conversations")).items.map(t=>({id:t.id,title:t.title,lastMessageDate:new Date(t.last_message_date)}))}async getConversation(t){const s=await this.httpClient.get(`/api/sdk/v1/conversations/${t}`),r={id:s.id,title:s.title,lastMessageDate:new Date(s.last_message_date),messages:s.messages.map(n=>({id:n.id,conversationId:s.id,content:n.content,role:n.role,status:n.status,createdAt:new Date(n.created_at),toolCalls:S.mapPersistedToolCalls(n.tool_call_data)}))};return this.eventEmitter.emit("conversation:loaded",r),r}static mapPersistedToolCalls(t){if(!(!t||t.length===0))return t.map(s=>({id:String(s.id??""),type:s.type??"status_message",status:String(s.status??"unknown"),toolName:s.tool_name??"",toolDisplayName:s.tool_display_name,message:s.message,formSchema:s.form_schema,data:s.data,functionName:s.function_name,arguments:s.arguments}))}async getMessages(t){return(await this.getConversation(t)).messages||[]}async submitToolCallForm(t,s){await this.httpClient.post(`/api/sdk/v1/tool-calls/${t}/submit`,{formData:s})}async confirmToolCall(t){await this.httpClient.post(`/api/tool-calls/${t}/confirm/`)}async rejectToolCall(t){await this.httpClient.post(`/api/tool-calls/${t}/reject/`)}async getFormCollector(t){const s=await this.httpClient.get(`/api/sdk/v1/forms/${encodeURIComponent(t)}`);return{name:s.name,description:s.description,schema:s.schema,submit:s.submit??void 0}}async submitFormCollector(t,s){return this.httpClient.post("/api/sdk/v1/forms/submit",{destination:t,values:s})}async submitFeedback(t,s){await this.httpClient.post(`/api/sdk/v1/messages/${t}/feedback`,{rating:s})}registerFunction(t,s,r){typeof t=="object"?this.functionRegistry.register(t):this.functionRegistry.register(t,s,r)}register(t){if(Array.isArray(t))for(const s of t)this.functionRegistry.register(s);else this.functionRegistry.register(t)}unregisterFunction(t){return this.functionRegistry.unregister(t)}getRegisteredFunctions(){return this.functionRegistry.getSchemas()}registerNavigationFunction(t,s){const r=s.map(i=>i.name),n=s.map(i=>`- "${i.name}": ${i.description}`).join(`
5
+ `);this.registerFunction("navigateToPage",async i=>{const a=i.page,l=s.find(c=>c.name===a);return l?(t(l.path),{success:!0,navigatedTo:l.path}):{success:!1,error:`Unknown page: ${a}`}},{description:`Navigate the user to a page in the application. Available pages:
6
+ ${n}`,parameters:{type:"object",properties:{page:{type:"string",description:"The page to navigate to",enum:r}},required:["page"]}})}setContext(t,s){this.contextRegistry.set(t,s),this.notifyContextChanged()}removeContext(t){const s=this.contextRegistry.remove(t);return s&&this.notifyContextChanged(),s}addDynamicContext(t,s){this.contextRegistry.addDynamicProvider(t,s),this.notifyContextChanged()}removeDynamicContext(t){const s=this.contextRegistry.removeDynamicProvider(t);return s&&this.notifyContextChanged(),s}notifyContextChanged(){this.eventEmitter.emit("context:changed",this.contextRegistry.getSnapshot())}enableAutoPageContext(){this.addDynamicContext("browser-page",()=>{const t=typeof window<"u"?window.location.href:void 0,s=typeof document<"u"?document.title:void 0;return!t&&!s?null:{name:"Browser page",description:"Current page the user is viewing",data:{url:t,title:s}}})}getContextSnapshot(){return this.contextRegistry.getSnapshot()}on(t,s){this.eventEmitter.on(t,s)}off(t,s){this.eventEmitter.off(t,s)}once(t,s){this.eventEmitter.once(t,s)}waitFor(t,s){return this.eventEmitter.waitFor(t,s)}updateConfig(t){const s={};for(const[n,i]of Object.entries(t))i!==void 0&&(s[n]=i);const r={...this.config,...s};f(r,"AgoClient.updateConfig"),this.config=r,this.httpClient.updateConfig(s),s.debug!==void 0&&(s.debug?o.enable():o.disable())}destroy(){this.eventEmitter.removeAllListeners(),this.functionRegistry.clear(),this.contextRegistry.clear(),o.log("AgoClient destroyed")}};Object.defineProperty(exports,"AgoApiError",{enumerable:!0,get:function(){return u}});Object.defineProperty(exports,"AgoClient",{enumerable:!0,get:function(){return U}});Object.defineProperty(exports,"AgoError",{enumerable:!0,get:function(){return h}});Object.defineProperty(exports,"AgoFunctionError",{enumerable:!0,get:function(){return m}});Object.defineProperty(exports,"AgoNetworkError",{enumerable:!0,get:function(){return d}});Object.defineProperty(exports,"AgoStreamError",{enumerable:!0,get:function(){return g}});Object.defineProperty(exports,"ClientContextRegistry",{enumerable:!0,get:function(){return C}});Object.defineProperty(exports,"EventEmitter",{enumerable:!0,get:function(){return _}});Object.defineProperty(exports,"FunctionRegistry",{enumerable:!0,get:function(){return b}});Object.defineProperty(exports,"SSEHandler",{enumerable:!0,get:function(){return E}});Object.defineProperty(exports,"isStreamNetworkError",{enumerable:!0,get:function(){return R}});Object.defineProperty(exports,"logger",{enumerable:!0,get:function(){return o}});Object.defineProperty(exports,"validateConfig",{enumerable:!0,get:function(){return f}});