@useago/sdk 1.0.1 → 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/dist/index.d.ts CHANGED
@@ -27,12 +27,26 @@ export declare class AgoClient {
27
27
  private contextRegistry;
28
28
  private eventEmitter;
29
29
  private config;
30
+ /** Conversations already warned about an empty reply (one warning each). */
31
+ private warnedEmptyConversations;
30
32
  constructor(config: AgoConfig);
31
33
  /**
32
34
  * Send a message and receive a streaming response
33
35
  */
34
36
  sendMessage(content: string, options?: SendMessageOptions): Promise<AgoMessage>;
35
37
  private processSSEResponse;
38
+ /**
39
+ * Detect a reply that completed as DONE with no content and no activity —
40
+ * today's backend answers an unknown `agent` slug with an empty 200, which
41
+ * otherwise fails silently. Emits `message:empty` (always) and warns on the
42
+ * console once per conversation (unless `warnOnEmptyReply: false`).
43
+ *
44
+ * Deferred with setTimeout(0) so both land AFTER `sendMessage` resolves:
45
+ * `await client.sendMessage(...)` followed by `client.waitFor("message:empty")`
46
+ * must not race. TEMPORARY heuristic: remove once the backend returns an
47
+ * explicit error for unknown agents (see TODOS.md, backend issue).
48
+ */
49
+ private maybeFlagEmptyReply;
36
50
  private handleClientFunctionInvocation;
37
51
  /**
38
52
  * Get list of conversations
@@ -205,6 +219,11 @@ export declare class AgoClient {
205
219
  }): Promise<AgoClientEvents[K]>;
206
220
  /**
207
221
  * Update client configuration
222
+ *
223
+ * Validates the MERGED config before committing: omitting a key (or passing
224
+ * it as `undefined`, common with spread-built partials) keeps the current
225
+ * value; an explicit empty/non-string `baseUrl` throws `config_missing_base_url`
226
+ * and leaves the client unchanged (no half-updated state).
208
227
  */
209
228
  updateConfig(config: Partial<AgoConfig>): void;
210
229
  /**
@@ -240,8 +259,23 @@ export declare interface AgoClientEvents {
240
259
  */
241
260
  "message:answer-complete": AgoMessage;
242
261
  "message:complete": AgoMessage;
262
+ /**
263
+ * A reply finished as `DONE` with empty content and no tool calls, client
264
+ * functions, or follow-up replies — usually an unknown `agent` slug (the
265
+ * backend currently answers those with an empty 200). Fires after
266
+ * `message:complete` AND after `sendMessage` resolves, so subscribing right
267
+ * after `await sendMessage(...)` still catches it. `messageId` and
268
+ * `conversationId` are empty strings when the stream completed without any
269
+ * message data at all (e.g. a proxy stripped the SSE stream).
270
+ */
271
+ "message:empty": {
272
+ conversationId: string;
273
+ messageId: string;
274
+ };
243
275
  "message:error": {
244
276
  error: string;
277
+ /** Stable error code (see configuration.md#error-codes) when the failure was an AgoError. */
278
+ code?: string;
245
279
  conversationId?: string;
246
280
  messageId?: string;
247
281
  };
@@ -270,7 +304,7 @@ export declare interface AgoClientEvents {
270
304
  export declare interface AgoConfig {
271
305
  /** API base URL (e.g., https://YOUR-DOMAIN.useago.com) */
272
306
  baseUrl: string;
273
- /** Widget ID (X-Widget-Id header). Auto-generated if not provided. */
307
+ /** Per-visitor anonymous ID (X-User-Anon-Id header). Auto-generated if not provided. */
274
308
  widgetId?: string;
275
309
  /** Default agent (id or slug) for new conversations. Shorthand for `defaultAgentId`. */
276
310
  agent?: string;
@@ -284,10 +318,23 @@ export declare interface AgoConfig {
284
318
  userJwt?: string;
285
319
  /** Enable debug logging */
286
320
  debug?: boolean;
321
+ /**
322
+ * Warn on the console when a reply completes with empty content and no tool
323
+ * calls (the usual cause is an unknown `agent` slug, which the backend
324
+ * currently answers with an empty 200). Default `true`. The `message:empty`
325
+ * event fires regardless of this setting.
326
+ */
327
+ warnOnEmptyReply?: boolean;
287
328
  }
288
329
 
289
330
  /**
290
331
  * Base error class for AGO SDK
332
+ *
333
+ * Error convention: every throw carries a stable `code` (snake_case), a
334
+ * one-line fix hint in the message, and a doc URL/anchor when one exists.
335
+ * `code` is the compatibility surface — match on it, never on message text,
336
+ * which may be reworded in any release. The code registry lives in
337
+ * docs/general/configuration.md#error-codes.
291
338
  */
292
339
  export declare class AgoError extends Error {
293
340
  code: string;
@@ -343,9 +390,14 @@ export declare interface AgoSource {
343
390
 
344
391
  /**
345
392
  * SSE stream error
393
+ *
394
+ * `code` distinguishes failure classes without message parsing:
395
+ * - `stream_no_body`: the response had no streamable body (usually a
396
+ * misconfigured endpoint or a proxy stripping the SSE stream)
397
+ * - `stream_error` (default): the stream failed mid-flight
346
398
  */
347
399
  export declare class AgoStreamError extends AgoError {
348
- constructor(message: string);
400
+ constructor(message: string, code?: string);
349
401
  }
350
402
 
351
403
  /**
@@ -623,8 +675,8 @@ export declare const copyToClipboard: ClientFunctionDefinition;
623
675
  export declare function createAgo(overrides?: Partial<AgoConfig>): AgoClient;
624
676
 
625
677
  /**
626
- * Identify a returning visitor with a single, stable widget id — the same model as
627
- * the frontend's `getOrGenerateWidgetId` — and resume their last active thread.
678
+ * Identify a returning visitor with a single, stable anon id (one id, generated
679
+ * once and reused forever) and resume their last active thread.
628
680
  *
629
681
  * There is no per-agent conversation id: the widget id is the identity, and the last
630
682
  * active thread (its id + the time of its last message) is cached locally so resuming
@@ -1361,6 +1413,29 @@ export declare type ToolCallType = "form" | "confirmation_input" | "status_messa
1361
1413
  */
1362
1414
  export declare const trackEvent: ClientFunctionDefinition;
1363
1415
 
1416
+ /**
1417
+ * Validate an AgoConfig at the public API boundary.
1418
+ *
1419
+ * Rules:
1420
+ * - nullish / non-object config, or a missing/empty/non-string `baseUrl`,
1421
+ * throws `AgoError` with the stable code `config_missing_base_url`.
1422
+ * - Values starting with `/` are allowed (same-origin proxy paths like
1423
+ * `/ago-proxy` are a supported embed pattern).
1424
+ * - Values with any `scheme://` are allowed.
1425
+ * - Anything else (no scheme, no leading slash, e.g. "ago.api.useago.com")
1426
+ * warns once per process — it usually means a forgotten protocol. It does
1427
+ * not throw: the request fails at send time with a runtime-specific error
1428
+ * (Node: URL parse error; browser: relative fetch), and a hard throw here
1429
+ * could break embeds we cannot foresee.
1430
+ *
1431
+ * The warning never echoes the configured value: misconfigured baseUrls
1432
+ * sometimes carry credentials (e.g. `https://user:token@host`).
1433
+ *
1434
+ * @param context Prefix for the error message, naming the API the developer
1435
+ * actually called (e.g. "AgoClient", "createAgo()").
1436
+ */
1437
+ export declare function validateConfig(config: unknown, context?: string): asserts config is AgoConfig;
1438
+
1364
1439
  /**
1365
1440
  * Attach a handler to a pre-built function definition.
1366
1441
  *
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- import { a as w, c as h, d as A, f as y, i as C, l as E, n as S, o as b, r as v, s as k, t as d, u as F } from "./AgoClient-CZYNL25R.js";
2
- import { i as T, n as I, r as U, t as G } from "./createFormCollector-Bk0DRhUf.js";
3
- import { t as q } from "./createConversationSession-Bmqj2rSS.js";
4
- import { t as L } from "./createMockClient-DwdxNwEn.js";
1
+ import { a as h, c as A, d as y, f as C, i as E, l as b, n as S, o as v, p as k, r as F, s as M, t as d, u as c } from "./AgoClient-bQozE6-A.js";
2
+ import { i as I, n as U, r as G, t as O } from "./createFormCollector-D-TpSd9x.js";
3
+ import { t as x } from "./createConversationSession-BbK3ou1k.js";
4
+ import { t as N } from "./createMockClient-DwdxNwEn.js";
5
5
  import "./testing.js";
6
6
  import { a as H, c as J, d as R, f as j, i as B, l as P, m as $, o as z, p as K, r as Q, s as V, t as W, u as X } from "./functions-C1-5vadY.js";
7
7
  function Y(e) {
@@ -10,26 +10,26 @@ function Y(e) {
10
10
  function Z(e, o) {
11
11
  return e.on("message:complete", o), () => e.off("message:complete", o);
12
12
  }
13
- function _(e, o) {
13
+ function ee(e, o) {
14
14
  return e.on("message:chunk", o), () => e.off("message:chunk", o);
15
15
  }
16
- function ee(e, o) {
16
+ function oe(e, o) {
17
17
  return e.on("message:start", o), () => e.off("message:start", o);
18
18
  }
19
- function oe(e, o) {
19
+ function te(e, o) {
20
20
  return e.on("message:error", o), () => e.off("message:error", o);
21
21
  }
22
- function te(e, o) {
22
+ function ae(e, o) {
23
23
  return e.on("toolCall:received", o), () => e.off("toolCall:received", o);
24
24
  }
25
25
  function re(e, o) {
26
26
  return e.on("function:invoke", o), () => e.off("function:invoke", o);
27
27
  }
28
- async function* ae(e, o, g) {
29
- const a = [];
30
- let r = null, n = !1;
28
+ async function* ne(e, o, g) {
29
+ const r = [];
30
+ let a = null, n = !1;
31
31
  const u = (s) => {
32
- a.push(s), r?.();
32
+ r.push(s), a?.();
33
33
  }, f = (s) => u({
34
34
  type: "start",
35
35
  data: s
@@ -40,98 +40,98 @@ async function* ae(e, o, g) {
40
40
  u({
41
41
  type: "complete",
42
42
  data: s
43
- }), n = !0, r?.();
44
- }, m = (s) => {
43
+ }), n = !0, a?.();
44
+ }, l = (s) => {
45
45
  u({
46
46
  type: "error",
47
47
  data: s
48
- }), n = !0, r?.();
49
- }, l = (s) => u({
48
+ }), n = !0, a?.();
49
+ }, m = (s) => u({
50
50
  type: "toolCall",
51
51
  data: s
52
52
  });
53
- e.on("message:start", f), e.on("message:chunk", t), e.on("message:complete", i), e.on("message:error", m), e.on("toolCall:received", l), e.sendMessage(o, g).catch(() => {
54
- n = !0, r?.();
53
+ e.on("message:start", f), e.on("message:chunk", t), e.on("message:complete", i), e.on("message:error", l), e.on("toolCall:received", m), e.sendMessage(o, g).catch(() => {
54
+ n = !0, a?.();
55
55
  });
56
56
  try {
57
- for (; !n || a.length > 0; ) a.length > 0 ? yield a.shift() : (await new Promise((s) => {
58
- r = s;
59
- }), r = null);
57
+ for (; !n || r.length > 0; ) r.length > 0 ? yield r.shift() : (await new Promise((s) => {
58
+ a = s;
59
+ }), a = null);
60
60
  } finally {
61
- e.off("message:start", f), e.off("message:chunk", t), e.off("message:complete", i), e.off("message:error", m), e.off("toolCall:received", l);
61
+ e.off("message:start", f), e.off("message:chunk", t), e.off("message:complete", i), e.off("message:error", l), e.off("toolCall:received", m);
62
62
  }
63
63
  }
64
- function c(e) {
65
- let o, g, a, r, n, u;
64
+ function p(e) {
65
+ let o, g, r, a, n, u;
66
66
  if (typeof window < "u" && window.AGO) {
67
67
  const t = window.AGO;
68
- o = t.basepath, g = t.widgetId, a = t.agent || t.defaultAgent, r = t.permission, n = t.email, u = t.jwt;
68
+ o = t.basepath, g = t.widgetId, r = t.agent || t.defaultAgent, a = t.permission, n = t.email, u = t.jwt;
69
69
  }
70
70
  if (typeof document < "u") {
71
71
  const t = (i) => document.querySelector(`meta[name="${i}"]`)?.getAttribute("content") || void 0;
72
- o = o || t("ago-base-url"), g = g || t("ago-widget-id"), a = a || t("ago-agent") || t("ago-agent-id"), r = r || t("ago-permission"), n = n || t("ago-user-email");
72
+ o = o || t("ago-base-url"), g = g || t("ago-widget-id"), r = r || t("ago-agent") || t("ago-agent-id"), a = a || t("ago-permission"), n = n || t("ago-user-email");
73
73
  }
74
74
  if (typeof document < "u") {
75
75
  const t = [document.body, ...Array.from(document.querySelectorAll("script[data-ago-base-url]"))].filter(Boolean);
76
76
  for (const i of t)
77
- i && (o = o || i.getAttribute("data-ago-base-url") || void 0, g = g || i.getAttribute("data-ago-widget-id") || void 0, a = a || i.getAttribute("data-ago-agent") || i.getAttribute("data-ago-agent-id") || void 0, r = r || i.getAttribute("data-ago-permission") || void 0, n = n || i.getAttribute("data-ago-user-email") || void 0);
77
+ i && (o = o || i.getAttribute("data-ago-base-url") || void 0, g = g || i.getAttribute("data-ago-widget-id") || void 0, r = r || i.getAttribute("data-ago-agent") || i.getAttribute("data-ago-agent-id") || void 0, a = a || i.getAttribute("data-ago-permission") || void 0, n = n || i.getAttribute("data-ago-user-email") || void 0);
78
78
  }
79
- o = e?.baseUrl || o, g = e?.widgetId || g, a = e?.agent || e?.defaultAgentId || a, r = e?.permission || r, n = e?.userEmail || n, u = e?.userJwt || u;
79
+ o = e?.baseUrl || o, g = e?.widgetId || g, r = e?.agent || e?.defaultAgentId || r, a = e?.permission || a, n = e?.userEmail || n, u = e?.userJwt || u;
80
80
  const f = e?.debug;
81
81
  return o ? {
82
82
  baseUrl: o,
83
83
  widgetId: g,
84
- agent: a,
85
- permission: r,
84
+ agent: r,
85
+ permission: a,
86
86
  userEmail: n,
87
87
  userJwt: u,
88
88
  debug: f
89
89
  } : null;
90
90
  }
91
- function ne(e) {
92
- const o = c(e);
93
- if (!o) throw new Error('createAgo(): could not detect AGO configuration. Set window.AGO, add <meta name="ago-base-url">, or pass { baseUrl } explicitly.');
91
+ function se(e) {
92
+ const o = p(e);
93
+ if (!o) throw new c('createAgo(): could not detect AGO configuration. Set window.AGO, add <meta name="ago-base-url">, or pass { baseUrl } explicitly.', "config_missing_base_url");
94
94
  return new d(o);
95
95
  }
96
- function se(e, o) {
96
+ function ie(e, o) {
97
97
  return {
98
98
  ...e,
99
99
  handler: o
100
100
  };
101
101
  }
102
102
  export {
103
- h as AgoApiError,
103
+ b as AgoApiError,
104
104
  d as AgoClient,
105
- E as AgoError,
106
- F as AgoFunctionError,
107
- A as AgoNetworkError,
108
- y as AgoStreamError,
109
- w as ClientContextRegistry,
110
- S as EventEmitter,
111
- b as FunctionRegistry,
112
- v as SSEHandler,
113
- c as autoDetectConfig,
105
+ c as AgoError,
106
+ y as AgoFunctionError,
107
+ C as AgoNetworkError,
108
+ k as AgoStreamError,
109
+ v as ClientContextRegistry,
110
+ F as EventEmitter,
111
+ M as FunctionRegistry,
112
+ E as SSEHandler,
113
+ p as autoDetectConfig,
114
114
  W as copyToClipboard,
115
- ne as createAgo,
116
- q as createConversationSession,
117
- G as createFormCollector,
118
- ae as createMessageStream,
119
- L as createMockClient,
120
- T as createStore,
115
+ se as createAgo,
116
+ x as createConversationSession,
117
+ O as createFormCollector,
118
+ ne as createMessageStream,
119
+ N as createMockClient,
120
+ I as createStore,
121
121
  Y as defineFunction,
122
- I as deriveFormStatus,
122
+ U as deriveFormStatus,
123
123
  Q as getLocalStorage,
124
124
  B as getUserLocation,
125
125
  H as highlightElement,
126
- C as isStreamNetworkError,
127
- U as loadFormCollector,
128
- k as logger,
126
+ h as isStreamNetworkError,
127
+ G as loadFormCollector,
128
+ A as logger,
129
129
  re as onFunctionInvoke,
130
130
  Z as onMessage,
131
- _ as onMessageChunk,
132
- oe as onMessageError,
133
- ee as onMessageStart,
134
- te as onToolCall,
131
+ ee as onMessageChunk,
132
+ te as onMessageError,
133
+ oe as onMessageStart,
134
+ ae as onToolCall,
135
135
  z as openUrl,
136
136
  V as scrollToElement,
137
137
  J as setLocalStorage,
@@ -141,5 +141,6 @@ export {
141
141
  j as showToast,
142
142
  K as submitForm,
143
143
  $ as trackEvent,
144
- se as withHandler
144
+ S as validateConfig,
145
+ ie as withHandler
145
146
  };