@useago/sdk 1.7.2 → 1.12.0

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.
Files changed (51) hide show
  1. package/CHANGELOG.md +175 -0
  2. package/README.md +96 -1
  3. package/dist/angular.cjs +1 -1
  4. package/dist/angular.d.cts +402 -5
  5. package/dist/angular.d.ts +402 -5
  6. package/dist/angular.js +11 -7
  7. package/dist/{createConversationSession-DP90nY71.js → createConversationSession-B1Qn-y0I.js} +3 -3
  8. package/dist/createConversationSession-B3v_xOU7.cjs +1 -0
  9. package/dist/createFormCollector-FvwDOqnV.cjs +1 -0
  10. package/dist/{createFormCollector-BivHRIAe.js → createFormCollector-m79zk_NA.js} +1 -1
  11. package/dist/createMockClient-BEOXI8yW.cjs +1 -0
  12. package/dist/{createMockClient-BDLW6tVc.js → createMockClient-Beao82tu.js} +31 -18
  13. package/dist/devtools.cjs +61 -12
  14. package/dist/devtools.d.cts +403 -5
  15. package/dist/devtools.d.ts +403 -5
  16. package/dist/devtools.js +225 -73
  17. package/dist/errors-BLfjXnLB.js +32 -0
  18. package/dist/errors-o1IB5fEu.cjs +1 -0
  19. package/dist/index.cjs +1 -1
  20. package/dist/index.d.cts +489 -7
  21. package/dist/index.d.ts +489 -7
  22. package/dist/index.js +124 -118
  23. package/dist/react.cjs +4 -4
  24. package/dist/react.d.cts +510 -3
  25. package/dist/react.d.ts +510 -3
  26. package/dist/react.js +1036 -752
  27. package/dist/sheetController-BThJRtOu.cjs +1 -0
  28. package/dist/sheetController-aEKXIvjk.js +343 -0
  29. package/dist/testing.cjs +1 -1
  30. package/dist/testing.d.cts +393 -3
  31. package/dist/testing.d.ts +393 -3
  32. package/dist/testing.js +1 -1
  33. package/dist/types-Ct1hlKwg.js +2408 -0
  34. package/dist/types-mcW8vB1U.cjs +10 -0
  35. package/dist/vue.cjs +1 -1
  36. package/dist/vue.d.cts +615 -3
  37. package/dist/vue.d.ts +615 -3
  38. package/dist/vue.js +202 -154
  39. package/dist/widget.cjs +6 -4
  40. package/dist/widget.d.cts +733 -5
  41. package/dist/widget.d.ts +733 -5
  42. package/dist/widget.js +3996 -786
  43. package/llms-full.txt +264 -31
  44. package/package.json +1 -1
  45. package/dist/AgoClient-7yXQ6R3O.cjs +0 -9
  46. package/dist/AgoClient-DTkBRGFE.js +0 -1965
  47. package/dist/createConversationSession-Bv6a15E0.cjs +0 -1
  48. package/dist/createFormCollector-CwfiYn0V.cjs +0 -1
  49. package/dist/createMockClient-BSNuN3ys.cjs +0 -1
  50. package/dist/sheetController-CBUVOroo.js +0 -205
  51. package/dist/sheetController-DY-sC13T.cjs +0 -1
package/llms-full.txt CHANGED
@@ -314,7 +314,11 @@ ago.registerNavigationFunction((path) => (window.location.href = path), [
314
314
 
315
315
  Page state is the mirror: let the agent change the current page's state
316
316
  (filters, sort, view mode…) and read it back via each control's `get()`. One
317
- synthesized `setPageState` function, one optional property per control.
317
+ synthesized `setPageState` function, one optional property per control. The SDK
318
+ validates type and enum, skips unchanged values, drops placeholders, and reports
319
+ each field in `applied`, `unchanged`, or `rejected`. See
320
+ [Page state shortcut](../general/functions-and-context.md#page-state-shortcut)
321
+ for `clearable`, tagged setter outcomes, and the full envelope.
318
322
 
319
323
  ```ts
320
324
  ago.registerPageStateFunction([
@@ -382,8 +386,12 @@ widget.destroy(); // removes listeners, uninstalls forms, clears the DOM
382
386
  While the agent answers, the send button becomes a Stop button that interrupts
383
387
  the turn; pass `allowStop: false` to keep a disabled spinner instead.
384
388
  Set `placement: "left"` or `"right"` for a fixed full-height side panel with a
385
- launcher button. Set `persistConversation: true` to resume the visitor's last
386
- thread across reloads. Theme it with the `theme` option or `--ago-*` CSS variables
389
+ launcher button, or `placement: "bubble"` for the hosted widget's floating
390
+ bubble (home / chat / history screens); with `loadHomeConfig: true` the bubble's
391
+ home title, subtitle and starter cards come from the AGO dashboard.
392
+ Set `persistConversation: true` to resume the visitor's last
393
+ thread across reloads. Set `feedback: true` to put thumbs under each answer,
394
+ with a "what went wrong?" panel on a thumbs-down. Theme it with the `theme` option or `--ago-*` CSS variables
387
395
  on `.ago-chat-widget`. Message text renders as GitHub-flavored markdown and is
388
396
  HTML-escaped before it touches the DOM.
389
397
 
@@ -398,6 +406,14 @@ await ago.submitToolCallForm(toolCallId, { quantity: 3 });
398
406
  await ago.confirmToolCall(toolCallId);
399
407
  await ago.rejectToolCall(toolCallId);
400
408
  await ago.submitFeedback(messageId, "positive");
409
+ // "this answer doesn't work": reasons/comment land in the feedback dashboard
410
+ await ago.submitFeedback(messageId, "negative", {
411
+ reasons: ["inaccurate"], // inaccurate | incomplete | information_not_found | technical_issue
412
+ comment: "The price is from last year.",
413
+ });
414
+ // "this conversation doesn't work": attached to its last answer
415
+ await ago.submitConversationFeedback(conversationId, "negative");
416
+ // or let the widget render the thumbs: mountChatWidget(el, { ..., feedback: true })
401
417
  ago.updateConfig({ userJwt: token }); // e.g. after login
402
418
  ago.destroy(); // clean up listeners, functions, context
403
419
  ```
@@ -571,6 +587,17 @@ each `{ id, title, url? }`):
571
587
  ))}
572
588
  ```
573
589
 
590
+ Let the user report an answer that did not work with `<ChatWidget feedback />`,
591
+ or place the row yourself with `<MessageFeedback messageId={m.id} />`. The hook
592
+ behind both is `useFeedback()`:
593
+
594
+ ```tsx
595
+ const { submitFeedback, submitConversationFeedback } = useFeedback();
596
+ // reasons: inaccurate | incomplete | information_not_found | technical_issue
597
+ await submitFeedback(m.id, "negative", { reasons: ["inaccurate"], comment: "..." });
598
+ await submitConversationFeedback(conversationId, "negative");
599
+ ```
600
+
574
601
  Finer-grained hooks: `useMessages({ conversationId? })`,
575
602
  `useConversation({ autoLoad? })`. All hooks read the client from context; pass
576
603
  `{ client }` to override. Get the raw client with `useAgoClient()` (throws
@@ -681,9 +708,15 @@ useAgoPageState(controls, {
681
708
  });
682
709
  ```
683
710
 
684
- `setPageState` then returns `{ success, applied, data }` and a read-only
685
- `readPageData` function is registered too. The SDK waits for `isLoading` to go
686
- false before reading, and truncates a long list by whole rows. Needs
711
+ `setPageState` then returns `{ success, applied, unchanged, rejected?, data }`
712
+ and a read-only `readPageData` function is registered too. The SDK validates
713
+ each field before calling `set()`: type/enum mismatches and unknown controls are
714
+ rejected, values equal to the current `get()` are skipped (`unchanged`), and
715
+ placeholders (`null`, `undefined`, `""`) are silently dropped. A `set()` that
716
+ returns `{ result: "rejected", reason }` or `{ result: "unchanged" }` feeds the
717
+ same envelope. Mark a string control `clearable: true` to let `""` clear it
718
+ instead of being dropped. The SDK waits for `isLoading` to go false before
719
+ reading, and truncates a long list by whole rows. Needs
687
720
  `clientFunctionsMode: "pause"` (the default).
688
721
 
689
722
  ## 6. Give the agent context: `useAgoContext`
@@ -739,9 +772,10 @@ Key events: `message:start`, `message:chunk` (`{ content }` per token),
739
772
  ## Exports cheat-sheet (`@useago/sdk/react`)
740
773
 
741
774
  - Provider/context: `AgoProvider`, `useAgoClient`, `useOptionalAgoClient`
742
- - Hooks: `useAgo`, `useChat`, `useMessages`, `useConversation`, `useAgoFunction`,
743
- `useAgoNavigation`, `useAgoPageState`, `useAgoContext`, `useAgoStore`, `useFormCollector`
744
- - Components: `ChatWidget`, `Message`, `ChatInput`, `Markdown`
775
+ - Hooks: `useAgo`, `useChat`, `useMessages`, `useConversation`, `useFeedback`,
776
+ `useAgoFunction`, `useAgoNavigation`, `useAgoPageState`, `useAgoContext`,
777
+ `useAgoStore`, `useFormCollector`
778
+ - Components: `ChatWidget`, `Message`, `ChatInput`, `Markdown`, `MessageFeedback`
745
779
  - Forms: `createFormCollector`
746
780
  - Testing: `createMockClient`
747
781
  - Types: `AgoConfig`, `AgoMessage`, `Conversation`, `AgoAgent`, `AgoSource`,
@@ -861,6 +895,15 @@ Finer-grained composables: `useMessages({ conversationId? })`,
861
895
  `useConversation({ autoLoad? })`. Each accepts `{ client }` to override the
862
896
  injected client.
863
897
 
898
+ Let the user report an answer that did not work with `useFeedback()`:
899
+
900
+ ```ts
901
+ const { ratings, submitFeedback, submitConversationFeedback } = useFeedback();
902
+ // reasons: inaccurate | incomplete | information_not_found | technical_issue
903
+ await submitFeedback(m.id, "negative", { reasons: ["inaccurate"], comment: "..." });
904
+ await submitConversationFeedback(conversationId, "negative");
905
+ ```
906
+
864
907
  ## 3. Let the agent call your code: `useAgoFunction`
865
908
 
866
909
  Registers on mount, unregisters on unmount.
@@ -912,7 +955,13 @@ explain the page to a colleague.
912
955
  The mirror of navigation. Let the agent change the current page's state
913
956
  (filters, sort, view mode…) and read it back. Each control becomes one optional
914
957
  property of a single synthesized `setPageState` function, and every control's
915
- `get()` value is sent as context.
958
+ `get()` value is sent as context. The SDK validates type and enum before calling
959
+ `set()`, skips values equal to the current `get()`, and drops placeholders
960
+ (`null`, `undefined`, `""`). The result reports each field in `applied`,
961
+ `unchanged`, or `rejected`. Mark a string control `clearable: true` to let `""`
962
+ clear it instead of being dropped. A `set()` that returns
963
+ `{ result: "rejected", reason }` or `{ result: "unchanged" }` feeds the same
964
+ envelope.
916
965
 
917
966
  ```vue
918
967
  <script setup lang="ts">
@@ -990,8 +1039,9 @@ const state = useAgoStore(cart);
990
1039
  ## Exports cheat-sheet (`@useago/sdk/vue`)
991
1040
 
992
1041
  - Setup: `AgoPlugin`, `useAgo`, `AGO_CLIENT_KEY`
993
- - Composables: `useChat`, `useMessages`, `useConversation`, `useAgoFunction`,
994
- `useAgoNavigation`, `useAgoPageState`, `useAgoEvents`, `useAgoStore`
1042
+ - Composables: `useChat`, `useMessages`, `useConversation`, `useFeedback`,
1043
+ `useAgoFunction`, `useAgoNavigation`, `useAgoPageState`, `useAgoEvents`,
1044
+ `useAgoStore`
995
1045
  - Types: `AgoConfig`, `AgoMessage`, `Conversation`, `AgoAgent`, `AgoSource`,
996
1046
  `ToolCallData`, `AgoPluginOptions`
997
1047
 
@@ -1155,7 +1205,8 @@ ago.registerPageStateFunction(controls, opts?) / ago.unregisterPageStateFunction
1155
1205
  ago.submitToolCallForm(toolCallId, formData)
1156
1206
  ago.confirmToolCall(toolCallId)
1157
1207
  ago.rejectToolCall(toolCallId)
1158
- ago.submitFeedback(messageId, "positive" | "negative")
1208
+ ago.submitFeedback(messageId, "positive" | "negative", { reasons?, comment? })
1209
+ ago.submitConversationFeedback(conversationId, rating, details?) // → the reported message id
1159
1210
 
1160
1211
  ago.on(event, handler) / ago.off(event, handler)
1161
1212
  ago.updateConfig(partialConfig)
@@ -1215,7 +1266,11 @@ private stopAutoContinue = this.ago.enableAutoContinueAfterNavigation();
1215
1266
  ```
1216
1267
 
1217
1268
  Register page state, the mirror of navigation (change the current page's state
1218
- and read it back):
1269
+ and read it back). The SDK validates type and enum before calling `set()`, skips
1270
+ values equal to the current `get()`, drops placeholders, and reports each field
1271
+ in `applied`, `unchanged`, or `rejected`. See
1272
+ [Page state shortcut](../general/functions-and-context.md#page-state-shortcut)
1273
+ for the full contract:
1219
1274
 
1220
1275
  ```ts
1221
1276
  this.ago.registerPageStateFunction([
@@ -1267,7 +1322,8 @@ reference, and the framework guides show the idiomatic wrappers.
1267
1322
 
1268
1323
  - [Client-side functions](#client-side-functions): the agent runs code in the browser
1269
1324
  - [Pre-built helpers](#pre-built-helpers): a catalogue of ready-made functions
1270
- - [Client context](#client-context): tell the agent what the user is doing
1325
+ - [Client context](#client-context): tell the agent what the user is doing,
1326
+ including the [JavaScript errors](#javascript-errors) the page hit
1271
1327
 
1272
1328
  ---
1273
1329
 
@@ -1458,17 +1514,77 @@ This does two things:
1458
1514
  current `get()` value, so the agent knows what to change. Controls without a
1459
1515
  `get()` are write-only and don't appear in context.
1460
1516
 
1461
- By default it's fire-and-forget: the handler applies each `set()` locally and
1462
- returns `{ success, applied }`. The new state reaches the agent through the
1463
- context on the next message. React/Vue offer `useAgoPageState(controls, opts?)`
1464
- with lifecycle cleanup; Angular exposes
1465
- `agoService.registerPageStateFunction(...)`.
1517
+ By default, the handler validates each field, applies it via `set()`, and
1518
+ returns the result as an envelope:
1519
+
1520
+ ```jsonc
1521
+ {
1522
+ "success": false,
1523
+ "applied": ["temperature"], // field names that were changed
1524
+ "unchanged": ["status"], // fields already holding that value
1525
+ "rejected": { "model": "\"gpt-5\" is not an allowed value. Allowed values: ..." }
1526
+ }
1527
+ ```
1528
+
1529
+ `applied` and `unchanged` are always present (as arrays of field names).
1530
+ `rejected` is omitted when empty. `success` is `false` when any field is
1531
+ rejected, `true` otherwise. Placeholders (`null`, `undefined`, and `""` on
1532
+ non-clearable controls) are silently dropped and appear in no bucket.
1533
+
1534
+ Before calling `set()`, the SDK validates the declared type and enum. Unknown
1535
+ control names are rejected with the list of available controls. A value that
1536
+ matches the current `get()` value is skipped without calling `set()` and
1537
+ reported as `unchanged`.
1538
+
1539
+ React/Vue offer `useAgoPageState(controls, opts?)` with lifecycle cleanup;
1540
+ Angular exposes `agoService.registerPageStateFunction(...)`.
1541
+
1542
+ #### Clearing a string field
1543
+
1544
+ By default, `""` is treated as filler and dropped. To let the agent clear a
1545
+ string field, mark it `clearable`:
1546
+
1547
+ ```ts
1548
+ {
1549
+ name: "searchQuery",
1550
+ description: "Free-text search filter",
1551
+ schema: { type: "string" },
1552
+ clearable: true,
1553
+ get: () => query,
1554
+ set: (v) => setQuery(v as string),
1555
+ }
1556
+ ```
1557
+
1558
+ The SDK adds `Pass "" to clear it.` to the control's description for the agent,
1559
+ and on enum controls adds `""` to the allowed values. `clearable` only affects
1560
+ string controls; on other types it has no effect.
1561
+
1562
+ #### Tagged setter outcomes
1563
+
1564
+ A `set()` function can return a tagged result to reject a value or signal that
1565
+ the field is already in the requested state:
1566
+
1567
+ ```ts
1568
+ set: (v) => {
1569
+ const match = availableModels.find((m) => m.name === v);
1570
+ if (!match) return { result: "rejected", reason: `No model named "${v}".` };
1571
+ if (match.id === currentModelId) return { result: "unchanged" };
1572
+ setModelId(match.id);
1573
+ },
1574
+ ```
1575
+
1576
+ `{ result: "rejected", reason }` puts the field in `rejected` with the given
1577
+ reason. `{ result: "unchanged" }` puts it in `unchanged` without calling the
1578
+ React state setter. Returning nothing (void) means the value was applied. A
1579
+ thrown or rejected promise remains a function execution error; it is not
1580
+ converted into a per-field rejection.
1466
1581
 
1467
1582
  ### Return what the page displays
1468
1583
 
1469
- `{ success, applied }` only tells the agent its own arguments were accepted. Ask
1470
- it to "find the user dupont" and it applies the filter but cannot say who it
1471
- found: the rows only reach it on the next message, as context.
1584
+ The envelope on its own tells the agent what happened to its arguments, not what
1585
+ appeared on screen. Ask it to "find the user dupont" and it applies the filter
1586
+ but cannot say who it found: the rows only reach it on the next message, as
1587
+ context.
1472
1588
 
1473
1589
  Add a `data` source and the rows come back as the result of the agent's own
1474
1590
  call, in the same turn:
@@ -1488,7 +1604,7 @@ useAgoPageState(controls, {
1488
1604
  });
1489
1605
  ```
1490
1606
 
1491
- `setPageState` now returns `{ success, applied, data }`, and a second function
1607
+ `setPageState` now returns `{ success, applied, unchanged, rejected?, data }`, and a second function
1492
1608
  `readPageData` is registered: no parameters, changes nothing, returns `{ data }`.
1493
1609
  That one answers "what's on screen?" without touching the page. Both disappear
1494
1610
  when you unregister.
@@ -1538,9 +1654,9 @@ Two things to keep in mind:
1538
1654
  the SDK keeps the first whole items that fit and adds a `truncation` field
1539
1655
  (`{ truncated, returnedItems, totalItems, hint }`) next to them. The counts
1540
1656
  are nested rather than merged into your snapshot so they can never overwrite
1541
- a field of your own called `totalItems`. `success` and `applied` always
1542
- survive intact. Return the columns the agent needs, not your whole row
1543
- objects.
1657
+ a field of your own called `totalItems`. The verdict fields (`success`,
1658
+ `applied`, `unchanged`, `rejected`) always survive intact. Return the columns
1659
+ the agent needs, not your whole row objects.
1544
1660
  - **Pause mode only.** The result travels back inside the turn only when
1545
1661
  `clientFunctionsMode` is `"pause"` (the default). In `"placeholder"` mode the
1546
1662
  turn is already over by then, so the agent will not see the data during the
@@ -1806,11 +1922,94 @@ event can't bloat the context. Read or clear the log with
1806
1922
  `client.getRecentActivity()` / `client.clearActivity()`, observe it via
1807
1923
  `client.on("activity:recorded", …)`, or use the React `useAgoActivityLog()` hook.
1808
1924
 
1925
+ ### JavaScript errors
1926
+
1927
+ "The button does nothing" is much easier to answer when the agent can see the
1928
+ exception behind it. Turn the error watcher on and the page's recent JavaScript
1929
+ errors ride along with every message:
1930
+
1931
+ ```ts
1932
+ const client = new AgoClient({
1933
+ baseUrl: "https://playground.api.useago.com",
1934
+ agent: "generic-guide",
1935
+ errorWatcher: true,
1936
+ });
1937
+ ```
1938
+
1939
+ (`<AgoProvider errorWatcher>`, the Vue plugin and `provideAgo` take the same
1940
+ option, and so does `mountChatWidget({ config: { errorWatcher: true } })`.)
1941
+
1942
+ With it on, the SDK captures:
1943
+
1944
+ - uncaught exceptions (the `window` `error` event), with file, line and stack;
1945
+ - unhandled promise rejections;
1946
+ - every `console.error` call (see the note below);
1947
+ - errors your app caught itself and hands over with `client.reportError()`.
1948
+
1949
+ They go out as the `errors:recent` context entry: at most 10 distinct errors,
1950
+ deduplicated with a `count`, each with how long ago it last fired, pruned after
1951
+ 10 minutes. Messages are cut at 500 characters and stacks at 2000, so one crash
1952
+ cannot flood the context. The entry is absent when nothing happened.
1953
+
1954
+ Report the errors you already catch, so the agent sees them too:
1955
+
1956
+ ```tsx
1957
+ // React error boundary
1958
+ componentDidCatch(error, info) {
1959
+ this.props.client.reportError(error, { componentStack: info.componentStack });
1960
+ }
1961
+
1962
+ // Vue
1963
+ app.config.errorHandler = (err, _instance, info) => client.reportError(err, { info });
1964
+
1965
+ // Angular
1966
+ @Injectable() class AgoErrorHandler implements ErrorHandler {
1967
+ constructor(private ago: AgoService) {}
1968
+ handleError(error: unknown) { this.ago.getClient().reportError(error); }
1969
+ }
1970
+ ```
1971
+
1972
+ Tune the caps or redact what leaves the page with options instead of `true`:
1973
+
1974
+ ```ts
1975
+ new AgoClient({
1976
+ baseUrl: "https://playground.api.useago.com",
1977
+ errorWatcher: {
1978
+ maxErrors: 5, // distinct errors kept (default 10)
1979
+ maxAgeMs: 120_000, // forget errors older than 2 min (default 10 min)
1980
+ maxStackChars: 1000, // default 2000
1981
+ // Return null to drop an error, or an edited copy to redact it.
1982
+ filter: (e) => (e.message.includes("token") ? null : e),
1983
+ },
1984
+ });
1985
+ ```
1986
+
1987
+ Things to know:
1988
+
1989
+ - **It is off by default.** Error messages can contain emails, ids or internal
1990
+ URLs. Enabling it is your call; use `filter` if some of them must stay home.
1991
+ - **`console.error` is wrapped.** The SDK never touches globals otherwise, but
1992
+ most caught errors (React render errors included) only surface there. The
1993
+ wrapper calls the original every time and is restored on `destroy()`.
1994
+ - **Cross-origin scripts** without a `crossorigin` attribute only yield
1995
+ `Script error.` with no details. That is the browser, not the SDK.
1996
+ - Errors also count as friction for the proactive mode: a trigger can use
1997
+ `when: { jsErrors: 1 }` to nudge right after a crash.
1998
+
1999
+ Observe or inspect it with `client.on("error:captured", …)`,
2000
+ `client.getRecentErrors()`, `client.clearErrors()`, or toggle it at runtime
2001
+ with `client.updateConfig({ errorWatcher: false })`.
2002
+
2003
+ To see it work, `examples/simple-html/error-watcher.html` is an order page
2004
+ with three planted bugs (a `TypeError`, an unhandled rejection, a swallowed
2005
+ error logged with `console.error`) and the bubble widget wired to the same
2006
+ client. Build the SDK, serve the repo, click a button, ask the agent what broke.
2007
+
1809
2008
  ### Per framework
1810
2009
 
1811
2010
  | Framework | Idiomatic API |
1812
2011
  | --- | --- |
1813
- | Core | `setContext` / `addDynamicContext` / `enableAutoPageContext` |
2012
+ | Core | `setContext` / `addDynamicContext` / `enableAutoPageContext` / `errorWatcher` |
1814
2013
  | React | `useAgoContext(entryOrFn, key?)` + `pageContext="auto"` |
1815
2014
  | Vue | core API inside `onMounted` / `onUnmounted` |
1816
2015
  | Angular | `agoService.getClient().setContext(…)` |
@@ -1872,6 +2071,7 @@ const final = await client.waitFor("message:complete", { timeout: 10_000 });
1872
2071
  | `function:awaiting-approval` | `{ invocationId, functionName, arguments, conversationId }`: a client function call is held pending user approval (pause mode + an `approvalPolicy`/`requiresApproval` match). The turn stays at `WAITING_CLIENT`; call `approveFunction(invocationId)` to run it and resume, or `rejectFunction(invocationId)` to decline. See [Approval gate](functions-and-context.md#approval-gate-ask-before-running) |
1873
2072
  | `function:result` | `{ invocationId, result, error? }` |
1874
2073
  | `activity:recorded` | `ActivityEntry`: a user- or agent-action was recorded into the activity ledger (via `recordActivity`, or auto-recorded when the agent calls a client function). See [Activity ledger](functions-and-context.md#activity-ledger) |
2074
+ | `error:captured` | `CapturedError`: the error watcher captured a JavaScript error (uncaught exception, unhandled rejection, `console.error`, or `reportError`). Fires on repeats too, with the updated `count`. Needs `errorWatcher` in the config. See [JavaScript errors](functions-and-context.md#javascript-errors) |
1875
2075
  | `form:submitted` | `{ name, values, result }`: a form collector submitted successfully; `result` is the submit response (the third-party API's answer) |
1876
2076
  | `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 |
1877
2077
  | `connection:status` | `{ connected }` |
@@ -1991,6 +2191,14 @@ client.on("toolCall:received", async (toolCall) => {
1991
2191
  `ToolCallType` is one of: `form`, `confirmation_input`, `status_message`,
1992
2192
  `progress_indicator`, `client_function`, `reasoning`, `mcp_ui_resource`.
1993
2193
 
2194
+ A `form` tool call from the agent's `ago_ticketing` tool is the ticket form:
2195
+ `toolName` is `"ago_ticketing"`, `formSchema` is empty, and the fields come from
2196
+ `getConfig().permissions[0].ticketForm` instead. The call carries the agent's
2197
+ pre-filled `ticket`, `allowedToCreateTicket`, and for embed-mode forms
2198
+ `embedHtml`. The vanilla widget renders and submits it for you (see
2199
+ [Ticket form](widget.md#ticket-form-the-form-tool-call)); a custom UI creates
2200
+ the ticket with `createTicket()` and then calls `submitToolCallForm(id, { success: true, ticket_id, ticket_url })`.
2201
+
1994
2202
  ---
1995
2203
 
1996
2204
  ## Framework equivalents
@@ -2029,10 +2237,12 @@ interface AgoConfig {
2029
2237
  permission?: string; // X-Widget-Permission header
2030
2238
  userEmail?: string; // X-User-Email header, identify the end user
2031
2239
  userJwt?: string; // Authorization: Bearer <jwt>, authenticated users
2240
+ metadata?: Record<string, unknown> | null; // Message body, for user/ticket enrichment
2032
2241
  debug?: boolean; // enable verbose console logging
2033
2242
  warnOnEmptyReply?: boolean; // default true; see "Empty replies" below
2034
2243
  clientFunctionsMode?: "placeholder" | "pause"; // default "pause"
2035
2244
  maxFunctionResultBytes?: number; // default 50000; cap on client function results
2245
+ errorWatcher?: boolean | ErrorWatcherOptions; // default off; send recent JS errors as context
2036
2246
  }
2037
2247
  ```
2038
2248
 
@@ -2044,10 +2254,12 @@ interface AgoConfig {
2044
2254
  | `permission` | — | `X-Widget-Permission` | Mirrors the widget's `window.AGO.permission`. |
2045
2255
  | `userEmail` | — | `X-User-Email` | Identifies the user to AGO. |
2046
2256
  | `userJwt` | — | `Authorization: Bearer …` | For authenticated sessions. |
2257
+ | `metadata` | — | `metadata` in the body | JSON-serializable user/ticket context. Per-message overrides replace defaults; `null` omits the field. |
2047
2258
  | `debug` | — | — | Turns on the SDK's logger. |
2048
2259
  | `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. |
2049
2260
  | `clientFunctionsMode` | — | `client_functions_mode` in the body | Default `"pause"`: the agent stops on client function call(s) and resumes 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)). Needs a backend with pause/resume support; older backends ignore the flag and fall back to placeholder behavior. Legacy `"placeholder"`: the turn continues on a placeholder and results are only visible on later turns. Per-message override via `sendMessage(..., { clientFunctionsMode })`. |
2050
2261
  | `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). |
2262
+ | `errorWatcher` | — | `errors:recent` in `client_context` | Off by default. `true` (or options) captures the page's JavaScript errors (uncaught exceptions, unhandled rejections, `console.error`, `reportError`) and sends the recent ones with every message. See [JavaScript errors](functions-and-context.md#javascript-errors). |
2051
2263
 
2052
2264
  ---
2053
2265
 
@@ -2109,7 +2321,7 @@ provideAgo({ baseUrl: "https://acme.useago.com", agent: "support" });
2109
2321
  order:
2110
2322
 
2111
2323
  1. **`window.AGO`** (the widget config object): `basepath`, `widgetId`,
2112
- `agent`/`defaultAgent`, `permission`, `email`, `jwt`.
2324
+ `agent`/`defaultAgent`, `permission`, `email`, `jwt`, `metadata`.
2113
2325
  2. **`<meta>` tags**: `ago-base-url`, `ago-widget-id`, `ago-agent`
2114
2326
  (or `ago-agent-id`), `ago-permission`, `ago-user-email`.
2115
2327
  3. **`data-ago-*` attributes** on `<body>` or a `<script>` tag:
@@ -2143,13 +2355,32 @@ client.updateConfig({ userJwt: token, userEmail: "jane@acme.com" });
2143
2355
 
2144
2356
  Changing `permission` to a falsy value removes the header. In React, the
2145
2357
  `useAgo` hook re-applies `agent`, `permission`, `userEmail`, `userJwt`,
2146
- `debug` and `warnOnEmptyReply` automatically when those props change (changing
2147
- `baseUrl`/`widgetId` recreates the client).
2358
+ `metadata`, `debug` and `warnOnEmptyReply` automatically when those props
2359
+ change (changing `baseUrl`/`widgetId` recreates the client).
2148
2360
 
2149
2361
  `updateConfig` validates the merged result: omitting a key (or passing it as
2150
2362
  `undefined`) keeps the current value; an explicit empty or non-string `baseUrl`
2151
2363
  throws `config_missing_base_url` and leaves the client unchanged.
2152
2364
 
2365
+ ### User and ticket metadata
2366
+
2367
+ Pass JSON-serializable data in `config.metadata` to include it in every new
2368
+ message request (JSON and multipart uploads). `sendMessage(text, { metadata })`
2369
+ replaces those defaults for that message; it does not merge objects. `null`
2370
+ omits metadata. `updateConfig({ metadata: null })` stops sending defaults; it
2371
+ does not erase data already stored by the server.
2372
+
2373
+ ```ts
2374
+ client.updateConfig({ metadata: { brevo: { clientid: 11970100, planCategory: "Free" } } });
2375
+ await client.sendMessage("I need help");
2376
+ ```
2377
+
2378
+ The AGO API stores this data as widget metadata on the user. Configure metadata
2379
+ field mappings in AGO to include selected fields in tickets. This does not
2380
+ populate a separately hosted Zendesk form. Use `setContext` as well when the
2381
+ agent needs these values in its prompt. Metadata supplied by the browser is
2382
+ untrusted context, not an authentication credential or permission grant.
2383
+
2153
2384
  ---
2154
2385
 
2155
2386
  ## Errors
@@ -2196,6 +2427,8 @@ may be reworded in any release.
2196
2427
  | `stream_error` | `AgoStreamError` | the stream failed mid-flight | Usually a dropped connection; retry the message |
2197
2428
  | `function_invalid_registration` | `AgoError` | `registerFunction` called without a handler or schema | Use `registerFunction({ name, parameters, handler })` |
2198
2429
  | `function_error` | `AgoFunctionError` | a registered client function threw | Inspect `functionName` and `originalError` |
2430
+ | `feedback_no_message` | `AgoError` | `submitConversationFeedback` on a conversation with no answer to attach the report to | Send a message first, or pass `details.lastMessageId` |
2431
+ | `missing_client` | `AgoError` | a React hook was used without a client | Pass `options.client` or wrap the app in `<AgoProvider>` |
2199
2432
 
2200
2433
  Server-supplied errors (`AgoApiError` with a structured body) keep the
2201
2434
  backend's own `code` and, when provided, link their docs in the message
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@useago/sdk",
3
- "version": "1.7.2",
3
+ "version": "1.12.0",
4
4
  "description": "Create In-APP AI agents that answers question and performs actions. Works with React, Vue, Angular and vanilla JS/TS.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -1,9 +0,0 @@
1
- var ie=10,$=1e3;function ne(e){if(typeof e!="object"||e===null)return!1;const t=Object.getPrototypeOf(e);return t===Object.prototype||t===null}function E(e,t){if(typeof e=="string"){if(e.length<=1e3)return e;const s=e.length-$;return e.slice(0,$)+`…[truncated ${s} chars]`}if(Array.isArray(e)){if(t.has(e))return"…[circular]";t.add(e);const s=e.slice(0,50).map(i=>E(i,t));return e.length>50&&s.push(`…[+${e.length-50} more]`),t.delete(e),s}if(ne(e)){if(t.has(e))return"…[circular]";t.add(e);const s={};for(const[i,n]of Object.entries(e))s[i]=E(n,t);return t.delete(e),s}return e}function re(e){return E(e,new WeakSet)}var K=class{constructor(e=10){this.entries=[];const t=Math.floor(e);this.maxEntries=Number.isFinite(t)?Math.max(1,t):10}add(e){const t={ts:e.ts??Date.now(),actor:e.actor??"user",name:e.name,summary:e.summary,...e.data!==void 0?{data:re(e.data)}:{}};this.entries.push(t);const s=this.entries.length-this.maxEntries;return s>0&&this.entries.splice(0,s),t}getRecent(){return[...this.entries]}get size(){return this.entries.length}clear(){this.entries=[]}},g=class extends Error{constructor(e,t,s){super(e),this.code=t,this.statusCode=s,this.name="AgoError"}},y=class H extends g{constructor(t,s,i,n,r,c){super(t,s,i),this.type=n,this.param=r,this.docUrl=c,this.name="AgoApiError"}static fromResponse(t,s){const i=t.error;return new H(i.doc_url?`${i.message} See ${i.doc_url}`:i.message,i.code,s,i.type,i.param,i.doc_url)}},w=class extends g{constructor(e,t){super(e,"network_error"),this.originalError=t,this.name="AgoNetworkError"}},_=class extends g{constructor(e,t="stream_error"){super(e,t),this.name="AgoStreamError"}},I=class extends g{constructor(e,t,s){super(e,"function_error"),this.functionName=t,this.originalError=s,this.name="AgoFunctionError"}},oe=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)}},l=new oe,N="ago_widget_id";function R(e){return typeof DOMException<"u"&&e instanceof DOMException&&e.name==="AbortError"||e instanceof Error&&e.name==="AbortError"}function ae(){if(typeof localStorage<"u"){const t=localStorage.getItem(N);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(N,e)}catch{}return e}var ce=class{constructor(e){this.baseUrl=e.baseUrl.replace(/\/$/,""),this.headers={"X-User-Anon-Id":e.widgetId||ae()},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,s){return this.request("POST",e,t,s)}async postStream(e,t,s){const i=`${this.baseUrl}${e}`;l.debug("POST (stream)",i,t);try{const n=await fetch(i,{method:"POST",headers:{...this.headers,"Content-Type":"application/json"},body:t?JSON.stringify(t):void 0,signal:s?.signal});return n.ok||await this.handleErrorResponse(n),n}catch(n){throw n instanceof y||R(n)?n:new w(`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 postFormData(e,t,s){const i=`${this.baseUrl}${e}`;l.debug("POST (formData)",i);const n={...this.headers};try{const r=await fetch(i,{method:"POST",headers:n,body:t,signal:s?.signal});return r.ok||await this.handleErrorResponse(r),r}catch(r){throw r instanceof y||R(r)?r:new w(`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 request(e,t,s,i){const n=`${this.baseUrl}${t}`;l.debug(e,n,s);try{const r=await fetch(n,{method:e,headers:{...this.headers,"Content-Type":"application/json"},body:s?JSON.stringify(s):void 0,...i?.keepalive?{keepalive:!0}:{}});return r.ok||await this.handleErrorResponse(r),r.status===204?void 0:r.json()}catch(r){throw r instanceof y?r:new w(`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 handleErrorResponse(e){let t;try{t=await e.json()}catch{}if(t?.error)throw y.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 y(`HTTP ${e.status}: ${e.statusText}.${s}`,"http_error",e.status,"api_error")}},le=5e4,ue=2e4,he=2e3,z=class{constructor(e){this.functions=new Map,this.defaultMaxResultBytes=e?.maxResultBytes??le}setDefaultMaxResultBytes(e){this.defaultMaxResultBytes=e}getDefaultMaxResultBytes(){return this.defaultMaxResultBytes}register(e,t,s){if(typeof e=="object"){const{name:o,handler:a,description:h,parameters:u,maxResultBytes:d,requiresApproval:f}=e;return this.register(o,a,{description:h,parameters:u,maxResultBytes:d,requiresApproval:f})}const i=e;if(!t||!s)throw new g(`registerFunction("${i}"): 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(i)&&l.warn(`Function "${i}" is being overwritten`);const{maxResultBytes:n,requiresApproval:r,...c}=s;this.functions.set(i,{schema:{...c,name:i},handler:t,maxResultBytes:n,requiresApproval:r}),l.log(`Registered function: ${i}`)}unregister(e){const t=this.functions.delete(e);return t&&l.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 I(`Function "${e}" is not registered`,e);l.log(`Executing function: ${e}`,t);try{const i=await s.handler(t);return l.log(`Function ${e} completed:`,i),this.guardResultSize(e,i,s.maxResultBytes)}catch(i){throw l.error(`Function ${e} failed:`,i),new I(`Function "${e}" execution failed: ${i instanceof Error?i.message:"Unknown error"}`,e,i instanceof Error?i:void 0)}}guardResultSize(e,t,s){let i;try{i=JSON.stringify(t)}catch{return t}if(i===void 0)return t;const n=new TextEncoder().encode(i).length,r=s??this.defaultMaxResultBytes;return n>r?(l.warn(`Function "${e}" result is ${n} bytes, over the ${r}-byte limit; a truncated preview was sent to the agent instead. Return fewer fields, paginate, or raise maxResultBytes for this function.`),{truncated:!0,originalBytes:n,maxBytes:r,preview:i.slice(0,he),hint:`The result of "${e}" exceeded the ${r}-byte limit and was truncated. Request less data (use limit or filter parameters if available), or tell the user the result is too large to process.`}):(n>ue&&l.warn(`Function "${e}" returned a large result (${Math.round(n/1024)} KB). Large results consume LLM context: return fewer fields, paginate, or push the data to the UI and return a summary.`),t)}clear(){this.functions.clear(),l.log("Cleared all registered functions")}get size(){return this.functions.size}},de=50,ge=2;var fe=2e3,pe=new TextEncoder;function m(e){return pe.encode(e).length}function v(e){try{return JSON.stringify(e)}catch{return}}var k=Symbol("ago.pageData.timeout");function me(e,t){return t<=0?Promise.resolve(k):new Promise((s,i)=>{const n=setTimeout(()=>s(k),t);e.then(r=>{clearTimeout(n),s(r)},r=>{clearTimeout(n),i(r)})})}async function ye(e,t){const s=Date.now()+t;let i=0;for(;Date.now()<s&&i<ge;)i=e().isLoading?.()?0:i+1,await new Promise(o=>setTimeout(o,de));const n=e().get();if(!M(n))return n;const r=await me(Promise.resolve(n),s-Date.now());if(r!==k)return r;l.warn("Page data did not resolve within the settle timeout; returning what the page has now. Raise settleTimeoutMs if the work legitimately takes longer.");const c=e().get();if(!M(c))return c;Promise.resolve(c).catch(()=>{})}function M(e){return typeof e=="object"&&e!==null&&typeof e.then=="function"}function ve(e){if(typeof e!="object"||e===null||Array.isArray(e))return;const t=e;let s;for(const i of Object.keys(t)){const n=t[i];Array.isArray(n)&&(s===void 0||n.length>t[s].length)&&(s=i)}return s}function we(e,t){let s=2,i=0;for(const n of e){const r=m(v(n)??"null")+(i>0?1:0);if(s+r>t)break;s+=r,i++}return i}function Ce(e,t){if(t<=0)return"";const s=r=>m(JSON.stringify(r))-2;let i=e.slice(0,t),n=s(i);for(;n>t&&i.length>0;)i=i.slice(0,Math.max(0,Math.floor(i.length*t/n)-1)),n=s(i);return i}function T(e){return`The page data returned by "${e}" was too large to send in full. Only the first items are included: ask for fewer rows or narrow the filters to see the rest.`}function Se(e,t,s,i){const n={truncated:!0,originalBytes:s,maxBytes:i,preview:"",hint:T(e)},r=m(v(n)??"{}");return{...n,preview:Ce(t,Math.min(fe,i-r))}}function be(e,t,s,i,n){const r=s===void 0?{}:{...t},c=s??"items",o=m(v({...r,[c]:[],truncation:{truncated:!0,returnedItems:i.length,totalItems:i.length,hint:T(e)}})??"{}")-2;if(o>=n)return;const a=i.slice(0,we(i,n-o));return{...r,[c]:a,truncation:{truncated:!0,returnedItems:a.length,totalItems:i.length,hint:T(e)}}}function U(e,t,s){const i=v(t);if(i===void 0)return t;const n=m(i);if(n<=s)return t;l.warn(`Page data for "${e}" is ${n} bytes, over the ${s}-byte limit; it was truncated to the first items that fit. Return fewer fields per item, paginate, or raise maxResultBytes on the data source.`);const r=ve(t),c=Array.isArray(t)?t:r!==void 0?t[r]:void 0;if(c){const o=be(e,t,r,c,s),a=m(v(o)??"");if(o!==void 0&&a<=s)return o}return Se(e,i,n,s)}var Ae=250,Ee=3,_e=1e3,Ie=24,Re=10,ke=50,Te=["mousemove","mousedown","keydown","scroll","touchstart"],J=class{constructor(e={}){this.started=!1,this.teardowns=[],this.route="",this.routeEnteredAt=0,this.lastActivityAt=0,this.lastHeavyActivityAt=0,this.recentRoutes=[],this.routeBounces=0,this.rageClicks=0,this.fieldErrors={},this.recentClicks=[],this.signals=[],this.listeners=new Set,this.getPageState=e.getPageState,this.activityThrottleMs=e.activityThrottleMs??Ae,this.rageClickCount=e.rageClickCount??Ee,this.rageClickWindowMs=e.rageClickWindowMs??_e,this.rageClickRadiusPx=e.rageClickRadiusPx??Ie,this.maxRecentRoutes=e.maxRecentRoutes??Re,this.maxSignals=e.maxSignals??ke}start(){if(this.started||typeof window>"u"||typeof document>"u")return;this.started=!0;const e=Date.now();this.route=this.currentPathname(),this.routeEnteredAt=e,this.lastActivityAt=e,this.recentRoutes=[this.route];const t=(i,n,r)=>{i.addEventListener(n,r,{passive:!0}),this.teardowns.push(()=>i.removeEventListener(n,r))},s=()=>this.recordActivity();for(const i of Te)t(window,i,s);t(document,"visibilitychange",()=>{document.visibilityState==="visible"&&this.recordActivity()}),t(window,"popstate",()=>this.checkRoute()),t(window,"hashchange",()=>this.checkRoute()),t(window,"click",i=>this.onClick(i)),l.log("SignalCollector started")}destroy(){for(const e of this.teardowns)e();this.teardowns=[],this.listeners.clear(),this.recentClicks=[],this.started=!1,l.log("SignalCollector destroyed")}signalFieldError(e){this.checkRoute();const t=(this.fieldErrors[e]??0)+1;this.fieldErrors[e]=t,this.pushSignal({type:"field_error",route:this.route,at:Date.now(),count:t,meta:{field:e}})}subscribe(e){return this.listeners.add(e),()=>{this.listeners.delete(e)}}getSignals(){return[...this.signals]}getSnapshot(e){this.checkRoute();const t=Date.now();return{route:this.route,dwellMs:this.started?t-this.routeEnteredAt:0,idleMs:this.started?t-this.lastActivityAt:0,rageClicks:this.rageClicks,routeBounces:this.routeBounces,fieldErrors:{...this.fieldErrors},recentRoutes:[...this.recentRoutes],pageState:e?.pageState===!1?null:this.readPageState()}}currentPathname(){return typeof window<"u"&&window.location?window.location.pathname:""}readPageState(){if(!this.getPageState)return null;try{return this.getPageState()??null}catch(e){return l.warn("SignalCollector getPageState threw:",e),null}}recordActivity(){const e=Date.now();this.lastActivityAt=e,e-this.lastHeavyActivityAt>=this.activityThrottleMs&&(this.lastHeavyActivityAt=e,this.checkRoute())}checkRoute(){if(!this.started)return;const e=this.currentPathname();if(e===this.route)return;const t=Date.now(),s=this.route;this.route=e,this.routeEnteredAt=t,this.rageClicks=0,this.fieldErrors={},this.recentClicks=[],this.recentRoutes.push(e),this.recentRoutes.length>this.maxRecentRoutes&&this.recentRoutes.splice(0,this.recentRoutes.length-this.maxRecentRoutes),this.pushSignal({type:"route_change",route:e,at:t,meta:{from:s}});const i=this.recentRoutes,n=i.length;n>=4&&i[n-1]===i[n-3]&&i[n-2]===i[n-4]&&i[n-1]!==i[n-2]&&(this.routeBounces+=1,this.pushSignal({type:"route_bounce",route:e,at:t,count:this.routeBounces,meta:{between:`${i[n-2]}↔${i[n-1]}`}}))}onClick(e){this.recordActivity();const t=Date.now(),s=this.pageStateKey(),i={at:t,x:e.clientX,y:e.clientY,stateKey:s};this.recentClicks=this.recentClicks.filter(o=>t-o.at<=this.rageClickWindowMs);const n=this.recentClicks[0],r=!n||Math.abs(i.x-n.x)<=this.rageClickRadiusPx&&Math.abs(i.y-n.y)<=this.rageClickRadiusPx,c=!n||n.stateKey===s;if(!r||!c){this.recentClicks=[i];return}this.recentClicks.push(i),this.recentClicks.length>=this.rageClickCount&&(this.rageClicks+=1,this.recentClicks=[],this.pushSignal({type:"rage_click",route:this.route,at:t,count:this.rageClicks}))}pageStateKey(){const e=this.readPageState();if(!e)return"";try{return JSON.stringify(e)}catch{return""}}pushSignal(e){this.signals.push(e),this.signals.length>this.maxSignals&&this.signals.splice(0,this.signals.length-this.maxSignals);for(const t of this.listeners)try{t(e)}catch(s){l.error("Signal listener threw:",s)}}};function X(e,t){const s=t?t.storage??(typeof localStorage<"u"?localStorage:void 0):void 0;let i=e;if(t&&s)try{const o=s.getItem(t.key);o!=null&&(i=JSON.parse(o))}catch{}const n=new Set,r=()=>n.forEach(o=>o(i));function c(){if(t&&s)try{s.setItem(t.key,JSON.stringify(i))}catch{}}return{get:()=>i,set:o=>{i=o,c(),r()},subscribe:o=>(n.add(o),()=>{n.delete(o)})}}var xe=6e4,Pe=1440*60*1e3,Fe="ago_proactive";function b(e){const t=new Date(e),s=String(t.getMonth()+1).padStart(2,"0"),i=String(t.getDate()).padStart(2,"0");return`${t.getFullYear()}-${s}-${i}`}var Oe=class{constructor(e={}){this.killSwitchEnabled=!1,this.engaged=!1,this.turnInFlight=!1,this.slotOccupied=!1,this.sessionCount=0,this.perSessionCap=e.frequencyCap?.perSession,this.perDayCap=e.frequencyCap?.perDay,this.store=X({optOut:!1,lastFiredAt:{},dismissedAt:{},day:{date:b(Date.now()),count:0}},{key:e.storageKey??Fe,storage:e.storage})}setKillSwitch(e){this.killSwitchEnabled=e}optOut(){this.store.set({...this.store.get(),optOut:!0})}optIn(){this.store.set({...this.store.get(),optOut:!1})}isOptedOut(){return this.store.get().optOut}setEngaged(e){this.engaged=e}setTurnInFlight(e){this.turnInFlight=e}isSlotOccupied(){return this.slotOccupied}occupySlot(){this.slotOccupied=!0}releaseSlot(){this.slotOccupied=!1}canFire(e,t=Date.now()){if(!this.killSwitchEnabled)return this.deny(e,"kill-switch (proactive disabled server-side)");const s=this.store.get();if(s.optOut)return this.deny(e,"user opted out");if(this.engaged)return this.deny(e,"user engaged (widget open)");if(this.turnInFlight)return this.deny(e,"message turn in flight");if(this.slotOccupied)return this.deny(e,"nudge slot occupied");const i=s.lastFiredAt[e.id],n=e.cooldownMs??xe;if(i!==void 0&&t-i<n)return this.deny(e,"cooldown");const r=s.dismissedAt[e.id],c=e.resurfaceAfterMs??Pe;return r!==void 0&&t-r<c?this.deny(e,"dismissed recently"):this.perSessionCap!==void 0&&this.sessionCount>=this.perSessionCap?this.deny(e,"perSession cap"):this.perDayCap!==void 0&&this.dayCount(t)>=this.perDayCap?this.deny(e,"perDay cap"):!0}recordFired(e,t=Date.now()){const s=this.store.get();this.store.set({...s,lastFiredAt:{...s.lastFiredAt,[e]:t}})}recordNudgeShown(e=Date.now()){this.sessionCount+=1;const t=this.store.get(),s=b(e),i=t.day.date===s?{date:s,count:t.day.count+1}:{date:s,count:1};this.store.set({...t,day:i})}recordDismissed(e,t=Date.now()){const s=this.store.get();this.store.set({...s,dismissedAt:{...s.dismissedAt,[e]:t}})}dayCount(e){const t=this.store.get();return t.day.date===b(e)?t.day.count:0}deny(e,t){return l.debug(`NudgeGovernor: trigger "${e.id}" blocked — ${t}`),!1}};function j(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const t=Math.floor(Math.random()*16);return(e==="x"?t:t&3|8).toString(16)})}function De(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function Y(e,t){if(e==="*"||e===t)return!0;const s=e.split("/"),i=s.map((n,r)=>{const c=r===s.length-1;return n==="*"?c?"[^/]+(?:/[^/]+)*":"[^/]+":n.startsWith(":")?"[^/]+":De(n)});return new RegExp("^"+i.join("/")+"/?$").test(t)}function D(e,t){return e===t?!0:e===null||t===null?!1:Array.isArray(e)?!Array.isArray(t)||t.length!==e.length?!1:e.every((s,i)=>x(s,t[i])):typeof e=="object"?typeof t!="object"||Array.isArray(t)?!1:Object.entries(e).every(([s,i])=>D(i,t[s])):!1}function x(e,t){if(e===t)return!0;if(e===null||t===null||typeof e!=typeof t)return!1;if(Array.isArray(e))return!Array.isArray(t)||e.length!==t.length?!1:e.every((s,i)=>x(s,t[i]));if(typeof e=="object"){const s=Object.keys(e),i=Object.keys(t);return s.length!==i.length?!1:s.every(n=>x(e[n],t[n]))}return!1}function V(e,t){if(e.route!==void 0&&!Y(e.route,t.route)||e.idleMs!==void 0&&t.idleMs<e.idleMs||e.dwellMs!==void 0&&t.dwellMs<e.dwellMs||e.rageClicks!==void 0&&t.rageClicks<e.rageClicks||e.routeBounces!==void 0&&t.routeBounces<e.routeBounces)return!1;if(e.fieldErrors!==void 0){for(const[s,i]of Object.entries(e.fieldErrors))if((t.fieldErrors[s]??0)<i)return!1}return!(e.pageState!==void 0&&!D(e.pageState,t.pageState??{}))}var $e=1e3,Ne="/api/sdk/v1/config",Me="/api/sdk/v1/proactive/evaluate",Ue=class{constructor(e){this.tickTimer=null,this.teardowns=[],this.started=!1,this.activeNudge=null,this.lastConversationId=null,this.shownIds=new Set,this.resolvedIds=new Set,this.lastEvaluationAt=0,this.client=e.client,this.collector=e.collector,this.governor=e.governor,this.tracker=e.tracker,this.options=e.options,this.tickMs=e.options.tickMs??$e}start(){if(this.started)return;this.started=!0,this.options.enabledOverride!==void 0?this.governor.setKillSwitch(this.options.enabledOverride):this.loadKillSwitch();const e=n=>{n.conversationId&&(this.lastConversationId=n.conversationId),this.governor.setTurnInFlight(!0)},t=()=>this.governor.setTurnInFlight(!1),s=()=>this.governor.setTurnInFlight(!1),i=n=>{n?.id&&(this.lastConversationId=n.id)};this.client.on("message:start",e),this.client.on("message:complete",t),this.client.on("message:error",s),this.client.on("conversation:loaded",i),this.teardowns.push(()=>{this.client.off("message:start",e),this.client.off("message:complete",t),this.client.off("message:error",s),this.client.off("conversation:loaded",i)}),this.tickTimer=setInterval(()=>this.evaluate(),this.tickMs),this.teardowns.push(this.collector.subscribe(()=>{Date.now()-this.lastEvaluationAt>=this.tickMs&&this.evaluate()}))}stop(){this.tickTimer!==null&&(clearInterval(this.tickTimer),this.tickTimer=null);for(const e of this.teardowns)e();this.teardowns=[],this.started=!1}getActiveNudge(){return this.activeNudge}async loadKillSwitch(){try{const e=await this.client.getHttp().get(Ne);this.governor.setKillSwitch(e?.proactive?.enabled===!0)}catch(e){l.debug("Proactive config fetch failed; staying disabled:",e),this.governor.setKillSwitch(!1)}}evaluate(){if(this.lastEvaluationAt=Date.now(),typeof document<"u"&&document.visibilityState==="hidden"||this.governor.isSlotOccupied())return;const e=this.collector.getSnapshot();for(const t of this.options.triggers)if(V(t.when,e)&&this.governor.canFire(t)){this.fire(t);return}}fire(e){if(this.governor.recordFired(e.id),this.governor.occupySlot(),e.intervene==="agent"){this.evaluateWithAgent(e);return}this.setReady({id:j(),triggerId:e.id,source:"static",message:e.intervene.message,action:e.intervene.action,goal:e.goal,kickoff:e.kickoff,createdAt:new Date})}async evaluateWithAgent(e){const t=j(),s={client_nudge_id:t,trigger_id:e.id,goal:e.goal??"",agent:this.client.getConfiguredAgent(),signals:this.collector.getSnapshot(),client_context:this.client.getContextSnapshot()??{entries:{}},client_functions:this.client.getRegisteredFunctions()};let i;try{i=await this.client.getHttp().post(Me,s)}catch(n){l.debug("Proactive evaluate failed (treated as no-intervene):",n),this.governor.releaseSlot();return}if(!i?.intervene||!i.message){this.governor.releaseSlot();return}this.setReady({id:t,triggerId:e.id,source:"agent",message:i.message,action:je(i.action),goal:e.goal,kickoff:e.kickoff,createdAt:new Date,serverNudgeId:i.nudge_id})}setReady(e){this.activeNudge=e,this.client.emitProactiveEvent("nudge:ready",e)}shown(e){this.shownIds.has(e.id)||(this.shownIds.add(e.id),this.governor.recordNudgeShown(),this.tracker.track({clientNudgeId:e.id,triggerId:e.triggerId,source:e.source,type:"shown",message:e.message,occurredAt:Date.now()}),this.client.emitProactiveEvent("nudge:shown",{nudge:e}))}dismiss(e){this.resolvedIds.has(e.id)||(this.resolvedIds.add(e.id),this.governor.recordDismissed(e.triggerId),this.tracker.track({clientNudgeId:e.id,triggerId:e.triggerId,source:e.source,type:"dismissed",occurredAt:Date.now()}),this.clearActive(e),this.client.emitProactiveEvent("nudge:dismissed",{nudge:e}))}async accept(e){this.resolvedIds.has(e.id)||(this.resolvedIds.add(e.id),this.tracker.track({clientNudgeId:e.id,triggerId:e.triggerId,source:e.source,type:"accepted",occurredAt:Date.now()}),this.clearActive(e),this.client.emitProactiveEvent("nudge:accepted",{nudge:e}),(e.source==="agent"||e.goal)&&(this.lastConversationId?this.client.sendMessage(Le(e),{hidden:!0,conversationId:this.lastConversationId}).catch(t=>l.debug("Nudge kickoff message failed:",t)):e.kickoff?this.client.sendMessage(e.kickoff).catch(t=>l.debug("Nudge kickoff message failed:",t)):l.debug("Nudge accepted with no open conversation and no trigger.kickoff; skipping the chat pre-seed (a hidden message cannot open a conversation).")),e.action&&await this.runAction(e.action))}markConverted(e){this.tracker.track({clientNudgeId:e.id,triggerId:e.triggerId,source:e.source,type:"converted",occurredAt:Date.now()})}clearActive(e){this.activeNudge?.id===e.id&&(this.activeNudge=null),this.governor.releaseSlot()}async runAction(e){try{"functionName"in e?await this.client.executeClientFunction(e.functionName,e.arguments??{}):await this.client.executeClientFunction("setPageState",e.setPageState)}catch(t){l.warn("Nudge action failed:",t)}}};function je(e){if(e)return{label:e.label,functionName:e.function_name,arguments:e.arguments}}function Le(e){const t=e.goal?` Goal: ${e.goal}.`:"";return`The user accepted a proactive nudge that said: "${e.message}".${t} Recent friction signals are in the client context (friction_signals). Start helping with this now: briefly explain what you can do and take the first concrete step.`}var Be=5e3,qe="/api/sdk/v1/proactive/events",Ge=class{constructor(e){if(this.queue=[],this.flushTimer=null,this.teardowns=[],this.destroyed=!1,this.post=e.post,this.agent=e.agent,this.flushIntervalMs=e.flushIntervalMs??Be,typeof document<"u"){const t=()=>{document.visibilityState==="hidden"&&this.flush({keepalive:!0})};document.addEventListener("visibilitychange",t),this.teardowns.push(()=>document.removeEventListener("visibilitychange",t))}if(typeof window<"u"){const t=()=>this.flush({keepalive:!0});window.addEventListener("pagehide",t),this.teardowns.push(()=>window.removeEventListener("pagehide",t))}}track(e){this.destroyed||(this.queue.push(e),this.flushTimer===null&&(this.flushTimer=setTimeout(()=>this.flush(),this.flushIntervalMs)))}flush(e){if(this.flushTimer!==null&&(clearTimeout(this.flushTimer),this.flushTimer=null),this.queue.length===0)return;const t=this.queue;this.queue=[];const s={events:t.map(i=>({client_nudge_id:i.clientNudgeId,trigger_id:i.triggerId,source:i.source,type:i.type,...i.message!==void 0?{message:i.message}:{},...this.agent?{agent:this.agent}:{},occurred_at:new Date(i.occurredAt).toISOString()}))};this.post(qe,s,e).catch(i=>{l.debug("Proactive event batch failed:",i)})}destroy(){if(!this.destroyed){this.flush({keepalive:!0}),this.destroyed=!0;for(const e of this.teardowns)e();this.teardowns=[]}}},L="friction_signals";function P(e,t){e.proactive&&(l.warn("createAgoProactive: a proactive controller is already attached; replacing it."),e.proactive.destroy());const s=new J({...t.signals,getPageState:()=>{const o=e.getContextSnapshot();if(!o)return null;const a={};for(const[h,u]of Object.entries(o.entries))h.startsWith("page-state:")&&u.data&&Object.assign(a,u.data);return Object.keys(a).length>0?a:null}});s.start(),e.addDynamicContext(L,()=>{const{pageState:o,...a}=s.getSnapshot({pageState:!1});return{name:"Friction signals",description:"Behavioral friction observed on the current page: dwell/idle times, rage clicks, route bounces, repeated field errors (names and counters only).",data:a}});const i=new Oe({frequencyCap:t.frequencyCap,storage:t.storage,storageKey:t.storageKey}),n=new Ge({post:(o,a,h)=>e.getHttp().post(o,a,h),agent:e.getConfiguredAgent()}),r=new Ue({client:e,collector:s,governor:i,tracker:n,options:t});r.start();const c={optOut:()=>i.optOut(),optIn:()=>i.optIn(),isOptedOut:()=>i.isOptedOut(),setEngaged:o=>i.setEngaged(o),signalFieldError:o=>s.signalFieldError(o),getSignalsSnapshot:()=>s.getSnapshot(),getActiveNudge:()=>r.getActiveNudge(),shown:o=>r.shown(o),accept:o=>r.accept(o),dismiss:o=>r.dismiss(o),markConverted:o=>r.markConverted(o),destroy:()=>{r.stop(),n.destroy(),s.destroy(),e.removeDynamicContext(L),e.proactive===c&&(e.proactive=null)}};return e.proactive=c,c}var Q=class{constructor(){this.entries=new Map,this.dynamicProviders=new Map}set(e,t){this.entries.set(e,t),l.log(`ClientContext set: ${e}`)}remove(e){const t=this.entries.delete(e);return t&&l.log(`ClientContext removed: ${e}`),t}addDynamicProvider(e,t){this.dynamicProviders.set(e,t),l.log(`DynamicContext provider added: ${e}`)}removeDynamicProvider(e){const t=this.dynamicProviders.delete(e);return t&&l.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 i=s();i&&(e[t]=i)}catch(i){l.error(`DynamicContext provider "${t}" threw:`,i)}return Object.keys(e).length===0?null:{entries:e}}clear(){this.entries.clear(),this.dynamicProviders.clear(),l.log("ClientContext cleared")}},We="page-state:";function Ke(e){const t={};for(const[s,i]of Object.entries(e))s.startsWith(We)&&i.data&&Object.assign(t,i.data);return t}function He(e){const t=e["current-page"]?.data;return t?.page?t.page:t?.url?t.url:e["browser-page"]?.data?.url}function ze(e,t){return e===t?!0:JSON.stringify(e)===JSON.stringify(t)}function Je(e,t){const s=[];for(const i of Object.keys(t))i in e&&(ze(e[i],t[i])||s.push({field:i,from:e[i],to:t[i]}));return s}function Xe(e,t){const s=Ke(t),i=He(t);return{changes:e&&e.page===i?Je(e.state,s):[],baseline:{page:i,state:s}}}function F(e){return e===null||typeof e!="object"?JSON.stringify(e)??"null":Array.isArray(e)?`[${e.map(F).join(",")}]`:`{${Object.keys(e).sort().map(t=>`${JSON.stringify(t)}:${F(e[t])}`).join(",")}}`}var B="",Z=class{constructor(e,t={}){this.callbacks=e,this.options=t,this.buffer="",this.message={},this.toolCalls=[],this.sources=[],this.followUpReplies=[],this.isFirstChunk=!0,this.answerCompleteEmitted=!1,this.waitingClientEmitted=!1,this.firedClientFunctions=new Map}async processStream(e){if(!e.body)throw new _("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:i,value:n}=await t.read();if(i)break;const r=s.decode(n,{stream:!0});this.buffer+=r,this.processBuffer()}return this.buffer.trim()&&this.processBuffer(),this.buildFinalMessage()}catch(i){if(this.options.signal?.aborted)return this.message.status="CANCELED",this.buildFinalMessage();const n=i instanceof Error?i:new _("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 i of s)if(!i.startsWith(": ")&&i.startsWith("data: ")){const n=i.slice(6);try{const r=JSON.parse(n);this.handleChunk(r)}catch(r){l.warn("Failed to parse SSE data:",n,r)}}}}handleChunk(e){if(this.callbacks.onRawChunk?.(e),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.title&&this.message.conversationId&&this.callbacks.onTitle?.({conversationId:this.message.conversationId,title:e.title}),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){const t=`${e.function_name}::${F(e.arguments??{})}`,s=e.id||"",i=this.firedClientFunctions.get(t);let n=!1;i?s&&!i.has(s)&&(i.has(B)?(i.delete(B),i.add(s)):(i.add(s),n=!0)):(this.firedClientFunctions.set(t,new Set([s])),n=!0),n&&this.callbacks.onClientFunction?.({invocationId:s,functionName:e.function_name,arguments:e.arguments||{},conversationId:this.message.conversationId||""})}if(e.tool_call_data&&e.type&&e.type!=="client_function"){const t=this.parseToolCall(e);this.callbacks.onToolCall?.(t);const s=this.toolCalls.findIndex(i=>i.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())),this.message.status==="WAITING_CLIENT"&&!this.waitingClientEmitted&&this.message.id&&(this.waitingClientEmitted=!0,this.callbacks.onWaitingClient?.({conversationId:this.message.conversationId||"",messageId:this.message.id,waitingToolCallIds:e.waiting_tool_call_ids??[]}))}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 e.status!=="WAITING_CLIENT"&&this.callbacks.onComplete?.(e),e}};function Ye(e){const t=e.message.toLowerCase();return t.includes("load failed")||t.includes("failed to fetch")||t.includes("network")||t.includes("abort")}var Ve=new Set(["image/jpeg","image/png","image/gif","image/webp","image/bmp","image/tiff"]);function ee(e){if(!e)return;const t=typeof window<"u"&&window.location?window.location.href:"https://ago.local/";try{const s=new URL(e,t);if(s.protocol==="http:"||s.protocol==="https:"||s.protocol==="blob:")return e}catch{}}function Qe(e){return e.isSafeImage===!0&&typeof e.contentType=="string"&&e.contentType.startsWith("image/")&&ee(e.url)!==void 0}function Ze(e){if(e==null||e<0)return"";if(e<1024)return`${e} B`;const t=e/1024;return t<1024?`${t.toFixed(1)} KB`:`${(t/1024).toFixed(1)} MB`}function et(e){return{id:String(e.id??""),name:typeof e.name=="string"&&e.name?e.name:"file",contentType:typeof e.content_type=="string"?e.content_type:void 0,fileSize:typeof e.file_size=="number"?e.file_size:void 0,url:typeof e.file_url=="string"?e.file_url:void 0,isSafeImage:e.is_safe_image===!0}}function tt(e){return e.map((t,s)=>({id:`local-${s}-${t.name}`,name:t.name,contentType:t.type||void 0,fileSize:t.size,url:typeof URL<"u"&&URL.createObjectURL?URL.createObjectURL(t):void 0,isSafeImage:Ve.has(t.type)}))}var te=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(i=>{try{i(t)}catch(n){console.error(`Error in event handler for ${String(e)}:`,n)}})}once(e,t){const s=(i=>{this.off(e,s),t(i)});this.on(e,s)}waitFor(e,t){return new Promise((s,i)=>{let n;const r=(c=>{n&&clearTimeout(n),s(c)});this.once(e,r),t?.timeout&&(n=setTimeout(()=>{this.off(e,r),i(new Error(`waitFor("${String(e)}") timed out after ${t.timeout}ms`))},t.timeout))})}removeAllListeners(e){e?this.handlers.delete(e):this.handlers.clear()}},q='`baseUrl` is required, e.g. new AgoClient({ baseUrl: "https://YOUR-DOMAIN.api.useago.com" }). Try the public demo account https://playground.api.useago.com (agent generic-guide). See docs/general/configuration.md#error-codes (config_missing_base_url).',st=/^[a-z][a-z0-9+.-]*:\/\//i,G=!1;function O(e,t="AgoClient"){if(typeof e!="object"||e===null)throw new g(`${t}: ${q}`,"config_missing_base_url");const s=e.baseUrl;if(typeof s!="string"||s.trim()==="")throw new g(`${t}: ${q}`,"config_missing_base_url");!s.startsWith("/")&&!st.test(s)&&(G||(G=!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).`)))}function it(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function A(e){return e.split("/").filter(t=>t.startsWith(":")).map(t=>t.slice(1))}function nt(e,t){const s=[];return{path:e.split("/").map(i=>{if(!i.startsWith(":"))return i;const n=i.slice(1),r=t[n];return r==null||r===""?(s.push(n),i):encodeURIComponent(String(r))}).join("/"),missing:s}}function rt(e,t){const s=t.find(n=>n.path===e);if(s)return s;const i=t.find(n=>{if(!n.path.includes(":"))return!1;const r="^"+n.path.split("/").map(c=>c.startsWith(":")?"[^/]+":it(c)).join("/")+"/?$";return new RegExp(r).test(e)});return i||t.filter(n=>n.path!=="/"&&e.startsWith(n.path)).sort((n,r)=>r.path.length-n.path.length)[0]}var ot="activity:recent",at="state:delta";function W(e){return e==="setPageState"?"readPageData":`read${e.charAt(0).toUpperCase()}${e.slice(1)}Data`}var ct=class se{constructor(t){this.lastSentPageState=null,this.warnedEmptyConversations=new Set,this.warnedPageDataPlaceholder=!1,this.pageDataCompanions=new Set,this.pendingResumes=new Map,this.pendingApprovals=new Map,this.resumeGate=null,this.activeTurn=null,this.pausedTurn=null,this.stoppedTurnIds=new Set,this.proactive=null,O(t,"AgoClient"),this.config=t,this.activityLedger=new K(t.maxActivityEntries),this.httpClient=new ce(t),this.functionRegistry=new z({maxResultBytes:t.maxFunctionResultBytes}),this.contextRegistry=new Q,this.eventEmitter=new te,this.registerActivityContext(),t.debug&&l.enable(),t.proactive&&P(this,t.proactive),l.log("AgoClient initialized")}async sendMessage(t,s){const i=this.functionRegistry.getSchemas(),n=this.buildSendContext(),r=this.config.agent||this.config.defaultAgentId,c=this.resolveClientFunctionsMode(s),o={content:t,conversation_id:s?.conversationId,agent_id:s?.agentId||r};i.length>0&&(o.client_functions=i,c==="pause"&&(o.client_functions_mode=c)),n&&(o.client_context=n),s?.hidden&&(o.hidden=!0);const a=this.beginTurn();let h;if(s?.files&&s.files.length>0){const u=new FormData;u.append("content",t),s.conversationId&&u.append("conversation_id",s.conversationId),(s.agentId||r)&&u.append("agent_id",s.agentId||r||""),i.length>0&&(u.append("client_functions",JSON.stringify(i)),c==="pause"&&u.append("client_functions_mode",c)),n&&u.append("client_context",JSON.stringify(n)),s.hidden&&u.append("hidden","true");for(const d of s.files)u.append("files",d);h=await this.sendRequest(a,()=>this.httpClient.postFormData("/api/sdk/v1/messages",u,{signal:a.controller.signal}))}else h=await this.sendRequest(a,()=>this.httpClient.postStream("/api/sdk/v1/messages",o,{signal:a.controller.signal}));return h?this.processSSEResponse(h,a):this.buildStoppedMessage(a)}async sendRequest(t,s){try{return await s()}catch(i){if(this.endTurn(t),R(i))return null;throw i}}async processSSEResponse(t,s){let i=!1;const n=new Z({onRawChunk:r=>{s&&(r.message_id&&!s.messageId&&(s.messageId=r.message_id),r.thread?.id&&(s.conversationId=r.thread.id)),this.eventEmitter.emit("stream:message",r)},onStart:r=>{this.eventEmitter.emit("message:start",r)},onChunk:r=>{this.eventEmitter.emit("message:chunk",r)},onToolCall:r=>{this.eventEmitter.emit("toolCall:received",r),r.type==="form"&&this.eventEmitter.emit("toolCall:form",r)},onClientFunction:async r=>{if(i=!0,this.eventEmitter.emit("function:invoke",r),this.recordAgentAction(r),r.invocationId&&this.requiresApproval(r)){this.pendingApprovals.set(r.invocationId,r),this.eventEmitter.emit("function:awaiting-approval",r);return}await this.handleClientFunctionInvocation(r)},onTitle:r=>{this.eventEmitter.emit("conversation:title",r)},onAnswerComplete:r=>{this.eventEmitter.emit("message:answer-complete",r)},onWaitingClient:r=>{this.pausedTurn={messageId:r.messageId,conversationId:r.conversationId},this.eventEmitter.emit("message:waiting-client",r),this.markResumeSignal(r.messageId,r.conversationId,"waitingConfirmed")},onComplete:r=>{s?.stopRequested&&!r.id||this.eventEmitter.emit("message:complete",r)},onError:r=>{this.eventEmitter.emit("message:error",{error:r.message,code:r instanceof g?r.code:void 0})}},{signal:s?.controller.signal});try{const r=await n.processStream(t);return s?.stopRequested&&(this.postStopForTurn(s,s.messageId??r.id),this.eventEmitter.emit("message:stopped",{conversationId:s.conversationId??r.conversationId??"",messageId:s.messageId??r.id??""})),r.status!=="WAITING_CLIENT"&&this.maybeFlagEmptyReply(r,i),r}finally{s&&this.endTurn(s)}}async stop(){const t=this.activeTurn;if(t){t.stopRequested=!0;const n=t.messageId;return t.controller.abort(),n?this.postStopForTurn(t,n):null}const s=this.pausedTurn;if(!s)return null;this.pausedTurn=null,this.pendingResumes.delete(s.messageId),this.stoppedTurnIds.add(s.messageId);let i=null;try{i=await this.stopMessage(s.messageId)}catch(n){l.warn("Failed to stop the paused turn:",n)}return this.eventEmitter.emit("message:stopped",{conversationId:s.conversationId,messageId:s.messageId}),i}async stopMessage(t){const s=await this.httpClient.post(`/api/sdk/v1/messages/${t}/stop`);return{status:s.status,messageStatus:s.message_status}}isGenerating(){return this.activeTurn!==null||this.pausedTurn!==null}beginTurn(){this.pausedTurn=null;const t={controller:new AbortController,messageId:null,conversationId:null,stopRequested:!1,stopPosted:!1};return this.activeTurn=t,t}endTurn(t){this.activeTurn===t&&(this.activeTurn=null)}async postStopForTurn(t,s){if(!s||t.stopPosted)return null;t.stopPosted=!0;try{return await this.stopMessage(s)}catch(i){return l.warn("Failed to stop the turn server-side:",i),null}}buildStoppedMessage(t){const s={id:t.messageId??"",conversationId:t.conversationId??"",content:"",role:"assistant",status:"CANCELED",createdAt:new Date};return s.id&&this.eventEmitter.emit("message:complete",s),this.eventEmitter.emit("message:stopped",{conversationId:s.conversationId,messageId:s.id}),s}resolveClientFunctionsMode(t){return t?.clientFunctionsMode??this.config.clientFunctionsMode??"pause"}registerResumeGate(t){return this.resumeGate=t,()=>{this.resumeGate===t&&(this.resumeGate=null)}}async continueMessage(t){const s=this.functionRegistry.getSchemas(),i=this.buildSendContext(),n={};s.length>0&&(n.client_functions=s),i&&(n.client_context=i);const r=this.beginTurn();r.messageId=t;const c=await this.sendRequest(r,()=>this.httpClient.postStream(`/api/sdk/v1/messages/${t}/continue`,n,{signal:r.controller.signal}));return c?this.processSSEResponse(c,r):this.buildStoppedMessage(r)}markResumeSignal(t,s,i){if(!t)return;const n=this.pendingResumes.get(t)??{conversationId:s,waitingConfirmed:!1,resultsReady:!1,started:!1};n[i]=!0,s&&(n.conversationId=s),this.pendingResumes.set(t,n),!(!n.waitingConfirmed||!n.resultsReady||n.started)&&(n.started=!0,this.runAutoResume(t,n.conversationId))}async runAutoResume(t,s){try{this.resumeGate&&await this.resumeGate({conversationId:s,messageId:t})}catch(i){l.warn("Resume gate threw; resuming anyway:",i)}if(this.pendingResumes.delete(t),!this.stoppedTurnIds.delete(t))try{await this.continueMessage(t)}catch(i){l.warn("Auto-resume of paused turn failed:",i)}}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 i=t.conversationId||"(no conversation)";if(this.warnedEmptyConversations.has(i))return;this.warnedEmptyConversations.add(i);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,i;try{s=await this.functionRegistry.execute(t.functionName,t.arguments)}catch(n){i=n instanceof Error?n.message:"Unknown error",l.error("Client function execution failed:",n)}if(t.invocationId)try{const n=await this.submitToolCallForm(t.invocationId,{result:s,error:i,_type:"client_function_result"});n?.resume?.ready&&this.markResumeSignal(n.resume.message_id,t.conversationId,"resultsReady")}catch(n){l.error("Failed to submit function result:",n)}this.eventEmitter.emit("function:result",{invocationId:t.invocationId,result:s,error:i})}requiresApproval(t){const s=this.config.approvalPolicy;if(s)try{if(s(t))return!0}catch(i){l.warn("approvalPolicy threw; not gating this call:",i)}return this.functionRegistry.get(t.functionName)?.requiresApproval===!0}async approveFunction(t){const s=this.pendingApprovals.get(t);s&&(this.pendingApprovals.delete(t),await this.handleClientFunctionInvocation(s))}async rejectFunction(t){const s=this.pendingApprovals.get(t);if(!s)return;this.pendingApprovals.delete(t);const i={approved:!1,reason:"user_rejected"};try{const n=await this.submitToolCallForm(t,{result:i,_type:"client_function_result"});n?.resume?.ready&&this.markResumeSignal(n.resume.message_id,s.conversationId,"resultsReady")}catch(n){l.error("Failed to submit function rejection:",n)}this.eventEmitter.emit("function:result",{invocationId:t,result:i})}async getConversations(t){const s=new URLSearchParams;t?.page!==void 0&&s.set("page",String(t.page)),t?.pageSize!==void 0&&s.set("page_size",String(t.pageSize));const i=s.toString(),n=await this.httpClient.get(`/api/sdk/v1/conversations${i?`?${i}`:""}`),r=(Array.isArray(n.data)?n.data:[]).map(c=>({id:String(c.id),title:c.title??"",lastMessageDate:new Date(c.last_message_at??c.created_at??0)}));return{data:r,hasMore:n.has_more??!1,total:n.total??r.length}}async getConversation(t){const s=await this.httpClient.get(`/api/sdk/v1/conversations/${t}`),i={id:s.id,title:s.title,lastMessageDate:new Date(s.last_message_at??s.created_at??0),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),hidden:n.hidden,toolCalls:se.mapPersistedToolCalls(n.tool_call_data),attachments:n.attachments&&n.attachments.length>0?n.attachments.map(et):void 0,followUpReplies:n.follow_up_replies&&n.follow_up_replies.length>0?n.follow_up_replies:void 0}))};return this.eventEmitter.emit("conversation:loaded",i),i}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 resumePendingClientFunctions(t){const s=[...t.messages??[]].reverse().find(o=>o.role==="assistant"&&o.status==="WAITING_CLIENT");if(!s)return null;const i=(s.toolCalls??[]).filter(o=>o.type==="client_function"&&o.status==="waiting_input"&&o.functionName),n=new Set(this.functionRegistry.getSchemas().map(o=>o.name)),r=i.filter(o=>!n.has(o.functionName));if(r.length>0)return l.warn("Cannot resume paused turn: function(s) not registered on this page:",r.map(o=>o.functionName)),null;let c=i.length===0;for(const o of i){const a={invocationId:o.id,functionName:o.functionName,arguments:o.arguments??{},conversationId:t.id};if(this.requiresApproval(a)){this.pendingApprovals.set(o.id,a),this.markResumeSignal(s.id,t.id,"waitingConfirmed"),this.eventEmitter.emit("function:awaiting-approval",a),c=!1;continue}this.eventEmitter.emit("function:invoke",a);let h,u;try{h=await this.functionRegistry.execute(o.functionName,o.arguments??{})}catch(d){u=d instanceof Error?d.message:"Unknown error",l.error("Client function execution failed:",d)}this.eventEmitter.emit("function:result",{invocationId:o.id,result:h,error:u}),c=(await this.submitToolCallForm(o.id,{result:h,error:u,_type:"client_function_result"}))?.resume?.ready??c}return c?this.continueMessage(s.id):null}async submitToolCallForm(t,s){return 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",{name:t,values:s})}async submitFeedback(t,s){await this.httpClient.post(`/api/sdk/v1/messages/${t}/feedback`,{rating:s})}registerFunction(t,s,i){typeof t=="object"?this.functionRegistry.register(t):this.functionRegistry.register(t,s,i)}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()}async executeClientFunction(t,s){return this.functionRegistry.execute(t,s)}registerNavigationFunction(t,s){const i=s.map(o=>o.name),n=s.map(o=>{const a=A(o.path),h=a.length>0?` (requires ${a.map(u=>`"${u}"`).join(", ")})`:"";return`- "${o.name}"${h}: ${o.description}`}).join(`
5
- `),r=new Map;for(const o of s)for(const a of A(o.path))r.set(a,[...r.get(a)??[],o.name]);const c={page:{type:"string",description:"The page to navigate to",enum:i}};for(const[o,a]of r){if(o==="page"){l.error('registerNavigationFunction: a ":page" placeholder collides with the "page" argument and is ignored. Rename the placeholder.');continue}c[o]={type:"string",description:`Value for ":${o}" in the page path. Required when page is ${a.map(h=>`"${h}"`).join(" or ")}.`}}this.registerFunction("navigateToPage",async o=>{const a=o.page,h=s.find(p=>p.name===a);if(!h)return{success:!1,error:`Unknown page: ${a}`};let u={};if(typeof o.params=="string")try{u=JSON.parse(o.params)}catch{}else o.params&&typeof o.params=="object"&&(u=o.params);const d={};for(const p of A(h.path))d[p]=o[p]??u[p];const{path:f,missing:C}=nt(h.path,d);if(C.length>0){const p=C.map(S=>`"${S}": "..."`).join(", ");return{success:!1,error:`Page "${a}" needs ${C.map(S=>`"${S}"`).join(", ")}. Retry with { "page": "${a}", ${p} }.`}}return t(f),{success:!0,navigatedTo:f}},{description:`Navigate the user to a page in the application. Available pages:
6
- ${n}`,parameters:{type:"object",properties:c,required:["page"]}}),this.addDynamicContext("current-page",()=>{if(typeof window>"u"||!window.location)return null;const o=window.location.href,a=typeof document<"u"?document.title:void 0,h=rt(window.location.pathname,s),u={url:o};return h&&(u.page=h.name),a&&(u.title=a),{name:"Current page",description:"The page the user is currently viewing.",data:u}})}unregisterNavigationFunction(){this.unregisterFunction("navigateToPage"),this.removeDynamicContext("current-page")}registerPageStateFunction(t,s){const i=s?.functionName??"setPageState",n=new Map(t.map(a=>[a.name,a])),r=s?.data,c=t.map(a=>`- "${a.name}": ${a.description}`).join(`
7
- `);this.registerFunction(i,async a=>{const h={};for(const[u,d]of Object.entries(a)){if(d===void 0)continue;const f=n.get(u);f&&(await f.set(d),h[u]=d)}return r?{success:!0,applied:h,data:U(i,await this.readPageData(i,r,s),this.pageDataBudget(r)-this.envelopeBytes({success:!0,applied:h}))}:{success:!0,applied:h}},{description:`Change the state of the current page. Set ONLY the controls the user explicitly asked for; leave the others unset. Available controls:
8
- ${c}`+(r?`
9
- Returns the resulting page data once it has loaded: ${r.description}`:""),parameters:{type:"object",properties:Object.fromEntries(t.map(a=>[a.name,{...a.schema,description:a.description}])),required:[]},requiresApproval:s?.requiresApproval,maxResultBytes:r?.maxResultBytes});const o=W(i);r?(this.warnIfPlaceholderMode(i),this.registerFunction(o,async()=>({data:U(i,await this.readPageData(i,r,s),this.pageDataBudget(r)-this.envelopeBytes({}))}),{description:`Read what the current page is displaying, without changing anything: ${r.description}`,parameters:{type:"object",properties:{}},maxResultBytes:r.maxResultBytes}),this.pageDataCompanions.add(o)):this.pageDataCompanions.delete(o)&&this.unregisterFunction(o),this.addDynamicContext(`page-state:${i}`,()=>{const a=t.filter(u=>typeof u.get=="function");if(a.length===0)return null;const h={};for(const u of a)h[u.name]=u.get();return{name:"Page state",description:"Current, editable state of the page the user is viewing.",data:h}})}unregisterPageStateFunction(t){const s=t??"setPageState";this.unregisterFunction(s);const i=W(s);this.pageDataCompanions.delete(i)&&this.unregisterFunction(i),this.removeDynamicContext(`page-state:${s}`)}pageDataBudget(t){return t.maxResultBytes??this.functionRegistry.getDefaultMaxResultBytes()}envelopeBytes(t){return m(JSON.stringify({...t,data:null}))-4}readPageData(t,s,i){return ye(()=>s,i?.settleTimeoutMs??1e4).catch(n=>(l.warn(`Reading page data for "${t}" failed:`,n),{error:"page_data_unavailable",message:n instanceof Error?n.message:String(n)}))}warnIfPlaceholderMode(t){this.warnedPageDataPlaceholder||this.config.clientFunctionsMode==="placeholder"&&(this.warnedPageDataPlaceholder=!0,l.warn(`"${t}" declares a data source, but clientFunctionsMode is "placeholder": the agent will not see that data during the turn it calls the function. Switch to "pause" (the default) for the data to come back as the result of its own call.`))}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())}emitFormEvent(t,s){this.eventEmitter.emit(t,s)}emitProactiveEvent(t,s){this.eventEmitter.emit(t,s)}getHttp(){return this.httpClient}getConfiguredAgent(){return this.config.agent||this.config.defaultAgentId}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()}buildSendContext(){const t=this.contextRegistry.getSnapshot(),s=t?.entries??{},{changes:i,baseline:n}=Xe(this.lastSentPageState,s);return this.lastSentPageState=n,i.length===0?t:{entries:{...s,[at]:{name:"State changes",description:"Fields in the page state that changed since your last message, as field: <old> -> <new>.",data:{changes:i}}}}}recordActivity(t){this.addActivity(t)}getRecentActivity(){return this.activityLedger.getRecent()}clearActivity(){this.activityLedger.clear()}addActivity(t){const s=this.activityLedger.add(t);this.eventEmitter.emit("activity:recorded",s)}recordAgentAction(t){const s=t.functionName,i=t.arguments;let n,r;if(s==="navigateToPage"){const c=i?.page;n="agent.navigate",r=c?`Agent navigated to "${String(c)}"`:"Agent navigated"}else s==="setPageState"?(n="agent.page_state",r="Agent changed the page state"):(n=`agent.${s}`,r=`Agent ran "${s}"`);this.addActivity({actor:"agent",name:n,summary:r,...i&&Object.keys(i).length>0?{data:{arguments:i}}:{}})}registerActivityContext(){this.contextRegistry.addDynamicProvider(ot,()=>{const t=this.activityLedger.getRecent();return t.length===0?null:{name:"Recent activity",description:"Recent actions by the user and the agent in the app, oldest first. Use this to understand what just happened before this message.",data:{events:t}}})}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,r]of Object.entries(t))r!==void 0&&(s[n]=r);const i={...this.config,...s};O(i,"AgoClient.updateConfig"),this.config=i,this.httpClient.updateConfig(s),s.debug!==void 0&&(s.debug?l.enable():l.disable()),s.maxFunctionResultBytes!==void 0&&this.functionRegistry.setDefaultMaxResultBytes(s.maxFunctionResultBytes)}destroy(){this.proactive?.destroy(),this.proactive=null,this.activeTurn?.controller.abort(),this.activeTurn=null,this.pausedTurn=null,this.stoppedTurnIds.clear(),this.eventEmitter.removeAllListeners(),this.functionRegistry.clear(),this.contextRegistry.clear(),this.pendingResumes.clear(),this.pendingApprovals.clear(),this.resumeGate=null,l.log("AgoClient destroyed")}reviveAfterDestroy(){this.registerActivityContext(),this.config.proactive&&!this.proactive&&P(this,this.config.proactive)}};Object.defineProperty(exports,"ActivityLedger",{enumerable:!0,get:function(){return K}});Object.defineProperty(exports,"AgoApiError",{enumerable:!0,get:function(){return y}});Object.defineProperty(exports,"AgoClient",{enumerable:!0,get:function(){return ct}});Object.defineProperty(exports,"AgoError",{enumerable:!0,get:function(){return g}});Object.defineProperty(exports,"AgoFunctionError",{enumerable:!0,get:function(){return I}});Object.defineProperty(exports,"AgoNetworkError",{enumerable:!0,get:function(){return w}});Object.defineProperty(exports,"AgoStreamError",{enumerable:!0,get:function(){return _}});Object.defineProperty(exports,"ClientContextRegistry",{enumerable:!0,get:function(){return Q}});Object.defineProperty(exports,"DEFAULT_MAX_ACTIVITY_ENTRIES",{enumerable:!0,get:function(){return ie}});Object.defineProperty(exports,"EventEmitter",{enumerable:!0,get:function(){return te}});Object.defineProperty(exports,"FunctionRegistry",{enumerable:!0,get:function(){return z}});Object.defineProperty(exports,"SSEHandler",{enumerable:!0,get:function(){return Z}});Object.defineProperty(exports,"SignalCollector",{enumerable:!0,get:function(){return J}});Object.defineProperty(exports,"attachmentsFromFiles",{enumerable:!0,get:function(){return tt}});Object.defineProperty(exports,"canInlineImage",{enumerable:!0,get:function(){return Qe}});Object.defineProperty(exports,"createAgoProactive",{enumerable:!0,get:function(){return P}});Object.defineProperty(exports,"createStore",{enumerable:!0,get:function(){return X}});Object.defineProperty(exports,"formatFileSize",{enumerable:!0,get:function(){return Ze}});Object.defineProperty(exports,"isStreamNetworkError",{enumerable:!0,get:function(){return Ye}});Object.defineProperty(exports,"logger",{enumerable:!0,get:function(){return l}});Object.defineProperty(exports,"matchesRoutePattern",{enumerable:!0,get:function(){return Y}});Object.defineProperty(exports,"matchesWhen",{enumerable:!0,get:function(){return V}});Object.defineProperty(exports,"partialDeepMatch",{enumerable:!0,get:function(){return D}});Object.defineProperty(exports,"safeAttachmentUrl",{enumerable:!0,get:function(){return ee}});Object.defineProperty(exports,"validateConfig",{enumerable:!0,get:function(){return O}});