@useago/sdk 1.5.5 → 1.7.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 (42) hide show
  1. package/CHANGELOG.md +230 -0
  2. package/README.md +59 -0
  3. package/dist/AgoClient-BHegN73y.cjs +9 -0
  4. package/dist/{AgoClient-BI5esqbd.js → AgoClient-PWPaekcg.js} +657 -387
  5. package/dist/angular.cjs +1 -1
  6. package/dist/angular.d.cts +230 -5
  7. package/dist/angular.d.ts +230 -5
  8. package/dist/angular.js +10 -1
  9. package/dist/{createConversationSession-0zVkMdYj.cjs → createConversationSession-BbUUukV0.cjs} +1 -1
  10. package/dist/{createConversationSession-BlYTpxwz.js → createConversationSession-BkruoOVm.js} +1 -1
  11. package/dist/{createFormCollector-DiCe_8ik.cjs → createFormCollector-C1ixsjV0.cjs} +1 -1
  12. package/dist/{createFormCollector-Bgs94NyR.js → createFormCollector-CvVFNkp6.js} +1 -1
  13. package/dist/{createMockClient-DcuNlufr.js → createMockClient-BDLW6tVc.js} +5 -2
  14. package/dist/createMockClient-BSNuN3ys.cjs +1 -0
  15. package/dist/devtools.d.cts +220 -5
  16. package/dist/devtools.d.ts +220 -5
  17. package/dist/index.cjs +1 -1
  18. package/dist/index.d.cts +238 -6
  19. package/dist/index.d.ts +238 -6
  20. package/dist/index.js +111 -109
  21. package/dist/react.cjs +40 -3
  22. package/dist/react.d.cts +419 -7
  23. package/dist/react.d.ts +419 -7
  24. package/dist/react.js +1195 -743
  25. package/dist/sheetController-CBUVOroo.js +205 -0
  26. package/dist/sheetController-DY-sC13T.cjs +1 -0
  27. package/dist/testing.cjs +1 -1
  28. package/dist/testing.d.cts +220 -5
  29. package/dist/testing.d.ts +220 -5
  30. package/dist/testing.js +1 -1
  31. package/dist/vue.cjs +1 -1
  32. package/dist/vue.d.cts +235 -5
  33. package/dist/vue.d.ts +235 -5
  34. package/dist/vue.js +121 -100
  35. package/dist/widget.cjs +4 -4
  36. package/dist/widget.d.cts +328 -5
  37. package/dist/widget.d.ts +328 -5
  38. package/dist/widget.js +721 -629
  39. package/llms-full.txt +202 -14
  40. package/package.json +1 -1
  41. package/dist/AgoClient-CQh8YovA.cjs +0 -8
  42. package/dist/createMockClient-Dw-cyISN.cjs +0 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,236 @@ All notable changes to `@useago/sdk` are documented here.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.7.0] - 2026-08-07
9
+
10
+ ### Added
11
+
12
+ - `registerPageStateFunction` / `useAgoPageState` accept a `data` source, so the
13
+ agent gets back **what the page displays** as the result of its own
14
+ `setPageState` call instead of only as context on the next message. An agent
15
+ that filters a list can now say what it found.
16
+
17
+ ```ts
18
+ useAgoPageState(controls, {
19
+ data: {
20
+ description: "The users matching the current filters.",
21
+ get: () => users ?? [],
22
+ isLoading: () => isFetching, // isFetching, not isLoading
23
+ },
24
+ });
25
+ ```
26
+
27
+ `setPageState` then returns `{ success, applied, data }`. Declaring `data`
28
+ also registers a read-only `readPageData` companion (no parameters, changes
29
+ nothing, returns `{ data }`) for "what's on screen?" questions; it is removed
30
+ by `unregisterPageStateFunction`.
31
+ - The SDK waits for the work a control change triggered before reading the
32
+ snapshot. Two ways to tell it when that work is done:
33
+ - **`get()` returns a promise** (preferred): awaiting it *is* the completion
34
+ signal, so the SDK is exact. `get: () => queryClient.ensureQueryData({...})`
35
+ shares the single in-flight request with the UI.
36
+ - **`isLoading` is declared**: the SDK polls it every 50 ms and treats two
37
+ consecutive idle readings as settled. A heuristic, and a flag raised more
38
+ than ~100 ms after the change (a debounced fetch) is missed, so the agent
39
+ reads the previous rows.
40
+
41
+ Both are capped by `settleTimeoutMs` (10 s by default); a promise that never
42
+ settles no longer holds the turn open, and a rejected `get()` is reported to
43
+ the agent as a failed read instead of an empty page.
44
+ - Oversized snapshots are truncated **by item**: the first whole items that fit
45
+ the budget (`data.maxResultBytes`, defaulting to `maxFunctionResultBytes`) plus
46
+ a `truncation` field carrying `{ truncated, returnedItems, totalItems, hint }`.
47
+ The counts are nested so they cannot overwrite a same-named field of your own
48
+ snapshot. `success` and `applied` are never dropped, so a filter that worked is
49
+ never reported as an unusable blob. When the snapshot's other fields overflow
50
+ on their own, it falls back to a size-bounded preview rather than returning
51
+ something over the ceiling.
52
+ - A `readPageData` companion is only removed by the SDK if the SDK registered it,
53
+ so a host app that already has a function by that name keeps it, and
54
+ re-registering a page-state function without `data` drops the stale companion
55
+ instead of letting it answer with the previous page's rows.
56
+ - `AgoPageDataSource` is exported from the root, `/react`, `/vue` and `/angular`
57
+ entries.
58
+ - `<ChatWidget>` now says **what the agent is doing** while it works, instead of
59
+ three dots. While a client function is running it shows a status row naming it
60
+ ("Updating the page", "Looking up prices"), which matters most during a
61
+ pause/resume loop: the per-message dots disappear as soon as the first token
62
+ lands, so a tool pause after some text had been written showed nothing at all.
63
+
64
+ ```tsx
65
+ <ChatWidget functionLabels={{ lookupFlavorPrices: "Je regarde les prix" }} />
66
+ ```
67
+
68
+ The row shows the latest step of the current turn and **keeps it until
69
+ something replaces it** (another call, a reasoning step) or the turn ends.
70
+ Clearing it when the call returned made a fast function flash for a few frames
71
+ and read as nothing at all. The agent's `reasoning` steps are included by
72
+ default (`showReasoning={false}` to leave them out), so the row does not go
73
+ blank between two calls.
74
+
75
+ `functionLabels` takes a map, or a function `(name, args) => string` for full
76
+ control. Unmapped names fall back to a prettified function name, so the row
77
+ works with no configuration. Pass `false` to keep the plain indicator. The row
78
+ is a `role="status"` live region and is driven by the existing
79
+ `useAgoActivity` hook, so it covers server-side tool calls and approval holds
80
+ too. It is scoped to the assistant message being written, so a new turn never
81
+ opens showing the previous one's last step.
82
+
83
+ The label itself is animated: a slow sheen keeps travelling across the text,
84
+ so a step that sits for several seconds (a fetch, a settle wait) reads as
85
+ still working rather than stuck, and each new step fades in as its own node so
86
+ one visibly gives way to the next instead of the characters swapping in place.
87
+ Colours go through `--ago-activity-color` / `--ago-activity-sheen` /
88
+ `--ago-activity-bg`; the sheen is behind `@supports (background-clip: text)`
89
+ and stops under `prefers-reduced-motion`.
90
+
91
+ The label carries no animated dots, and the answer bubble above it drops its
92
+ own while the row is up: the text already reads as progress, and the two
93
+ together were just noise. `<Message>` takes a `showStreamingDots` prop for
94
+ the same reason in a custom UI (`false` removes the empty bubble entirely
95
+ rather than leaving a blank box). It renders simple inline Markdown
96
+ (`**bold**`, `*italic*`, `` `code` ``) so a backend reasoning step keeps its
97
+ emphasis; block Markdown is deliberately not supported there, since a
98
+ paragraph or a table would break a one-line row.
99
+
100
+ Omit `data` and the result stays exactly `{ success, applied }`, byte for byte.
101
+ The snapshot stays out of the `page-state:` context entry, so it travels only
102
+ when the agent asks for it. The intra-turn round trip requires
103
+ `clientFunctionsMode: "pause"` (the default); the SDK warns once if `data` is
104
+ declared in `"placeholder"` mode.
105
+
106
+ ### Fixed
107
+
108
+ - Client functions executed on the resume path (after a reload, via
109
+ `resumePendingClientFunctions`) now emit `function:invoke` and
110
+ `function:result` like the live stream does. A UI driven by those events used
111
+ to sit on a spinner for those calls.
112
+ - An agent calling the same client function twice in one turn no longer strands
113
+ the turn. The per-stream dedupe (which exists because the backend emits one
114
+ call under two SSE shapes) keyed on function name + arguments alone, so a
115
+ second call with identical arguments was swallowed: its result was never
116
+ submitted, the backend never saw every result, and the paused turn stayed in
117
+ `WAITING_CLIENT` forever. It now uses the invocation id to tell a repeat call
118
+ apart from a repeat *emission* of the same call. Zero-argument functions (such
119
+ as the new `readPageData`) hit this every time.
120
+ - Vue's `useAgoPageState` no longer drops `requiresApproval` (and now forwards
121
+ every option, matching React and Angular).
122
+
123
+ ## [1.6.2] - 2026-08-06
124
+
125
+ ### Fixed
126
+
127
+ - Side-placement panels (`placement: "left" | "right"`) now get the mobile
128
+ treatment the inline card already had. On a small viewport the open panel
129
+ tracks the visible viewport, so the on-screen keyboard no longer covers the
130
+ composer, and it is exposed as a real dialog (`role="dialog"`, `aria-modal`,
131
+ Tab trap, Escape to close). Opening it moves focus into the panel rather than
132
+ the text field, so the keyboard no longer opens unprompted.
133
+ - A desktop side panel is explicitly **not** modal: it no longer traps Tab or
134
+ locks the page behind it, so the host page stays usable beside it.
135
+ - The background scroll lock now restores the host page's own inline `<body>` /
136
+ `<html>` styles instead of deleting them. A host that had already pinned its
137
+ own body (for its own modal) is left exactly as it was found.
138
+ - The scroll lock is tracked per owner instead of by a counter, so overlapping
139
+ open / close / destroy paths can no longer release a lock they don't hold,
140
+ and `destroy()` is idempotent.
141
+ - `destroy()` during an opening animation no longer re-pins the page after
142
+ teardown, which could leave `<body>` fixed with no owner left to release it.
143
+ - `destroy()` now drops its keydown and `visualViewport` listeners for every
144
+ placement; side-placement widgets previously leaked them.
145
+ - A phone in landscape (short viewport, e.g. 844x390) now counts as mobile, so
146
+ it keeps the full-screen sheet and the keyboard handling instead of falling
147
+ back to the desktop layout mid-conversation.
148
+ - A streamed answer no longer rebuilds the whole thread on every token. Screen
149
+ readers announced the entire conversation per chunk, and the rebuild collapsed
150
+ the pane's scroll height, which is what made a "follow the bottom" check latch
151
+ off after the first token.
152
+ - The message pane now follows new content only while you are already at the
153
+ bottom. Scroll up to re-read something and the stream stops yanking you back;
154
+ a "jump to latest" button returns you. Sending always re-attaches.
155
+ - On a compact viewport the composer is no longer re-focused after every answer,
156
+ which was re-opening the on-screen keyboard over the reply you were reading.
157
+ - A failed send no longer destroys the message: the text (and any attachments)
158
+ go back into the composer instead of being lost.
159
+ - Touch targets are at least 44px across both the vanilla widget and the React
160
+ components: send, stop, attach, remove-attachment, suggested replies, and both
161
+ close buttons.
162
+
163
+ ### Added
164
+
165
+ - Bottom-sheet presentation for compact viewports, opt-in via the new `sheet`
166
+ option on the React `<ChatWidget>`. It rests as a `peek` bar pinned to the
167
+ bottom edge (named header, two-line preview of the last reply, composer) and
168
+ expands to a `full` screen with `role="dialog"`, a background scroll lock and
169
+ Escape to collapse. `peek` is deliberately never modal: the page behind it
170
+ stays scrollable and keyboard-reachable.
171
+ - `createSheetController` (`@useago/sdk/widget`): the framework-agnostic state
172
+ machine behind it. It returns a **props bag** rather than touching the DOM, so
173
+ the React binding, the vanilla widget and any Vue or Angular binding drive the
174
+ same implementation.
175
+ - `useSheet` (`@useago/sdk/react`), which reads that controller through
176
+ `useSyncExternalStore` with a non-compact server snapshot, so `matchMedia`
177
+ never runs during render and hydration matches.
178
+ - `<ChatWidget>` now forwards a ref exposing `expand()`, `collapse()`,
179
+ `toggle()` and the current `state`.
180
+ - `sheet.bottomOffset` leaves room for a host page that already owns the bottom
181
+ edge (a bottom nav, a sticky checkout bar). `sheet: false` (the default) keeps
182
+ the widget out of the host's fixed layers entirely.
183
+ - `thinkingLabel` on `<ChatWidget>`: what the collapsed sheet says next to the
184
+ animated dots while the agent is working and has produced no text yet.
185
+ - `--ago-sheet-height`: the collapsed sheet measures itself and publishes its
186
+ footprint as a custom property on the host document. A fixed bar covers the end
187
+ of the page, so hosts need to reserve that space, and a constant in their CSS
188
+ would drift from the real height (which depends on their own composer styling).
189
+ Use it as `padding-bottom: var(--ago-sheet-height, 0px)`.
190
+
191
+ ### Changed
192
+
193
+ - The streaming dots now paint in `currentColor` instead of a fixed grey, so
194
+ they stay legible on a dark surface as well as a light one.
195
+ - React `<ChatInput>`'s `onSend` may now return a boolean (or a promise of one).
196
+ Returning `false` tells the composer the send failed so it can restore the
197
+ draft. Existing handlers that return nothing keep working unchanged.
198
+
199
+ ## [1.6.1] - 2026-08-05
200
+
201
+ ### Added
202
+
203
+ - The React `<ChatInput>` textarea now exposes an
204
+ `ago-chat-input__field` class for stable custom styling.
205
+
206
+ ### Fixed
207
+
208
+ - React `<ChatWidget>` auto-scroll now stays inside the message pane instead of
209
+ moving ancestor containers or the whole page, and preserves the reader's
210
+ position while they review earlier messages.
211
+
212
+ ## [1.6.0] - 2026-08-04
213
+
214
+ ### Added
215
+
216
+ - Stop generation. `client.stop()` interrupts the turn being generated: it
217
+ closes the stream and calls the new `POST /api/sdk/v1/messages/{id}/stop`, so
218
+ the agent really stops instead of finishing in the background. The text
219
+ produced so far is kept and the message ends as `CANCELED`; the in-flight
220
+ `sendMessage` resolves with it rather than rejecting.
221
+ - `message:stopped` event, fired once the stopped turn has unwound.
222
+ - `client.stopMessage(messageId)` to stop a turn by id (e.g. one still running
223
+ after a page reload), and `client.isGenerating()` to know whether there is
224
+ anything to stop.
225
+ - `stop()` on the React `useMessages` / `useChat` hooks, the Vue `useMessages` /
226
+ `useChat` composables, the Angular `AgoService`, and the vanilla widget handle.
227
+ - The React `<ChatWidget>` and the vanilla widget now show a Stop button in
228
+ place of the send button while the agent answers. Opt out with
229
+ `allowStop: false`.
230
+
231
+ ## [1.5.6] - 2026-07-29
232
+
233
+ ### Fixed
234
+
235
+ - `getConversation` now maps each message's `follow_up_replies`, so follow-up
236
+ pills survive a reload instead of disappearing on a reopened thread.
237
+
8
238
  ## [1.5.5] - 2026-07-28
9
239
 
10
240
  ### Added
package/README.md CHANGED
@@ -145,6 +145,30 @@ so the agent changes only what the user asked for. The current value from each
145
145
  `get()` is sent with every message, so the agent knows the state before it
146
146
  changes it. Vue and Angular have the same helper.
147
147
 
148
+ Add a `data` source and the agent also gets back **what the page now shows**, as
149
+ the result of its own call, so it can answer "did you find dupont?" in the same
150
+ turn:
151
+
152
+ ```tsx
153
+ const { data: invoices, isFetching } = useQuery({
154
+ queryKey: ["invoices", status, sort],
155
+ queryFn: fetchInvoices,
156
+ });
157
+
158
+ useAgoPageState(controls, {
159
+ data: {
160
+ description: "The invoices matching the current filters.",
161
+ get: () => invoices ?? [],
162
+ isLoading: () => isFetching, // isFetching, not isLoading
163
+ },
164
+ });
165
+ ```
166
+
167
+ The SDK waits for `isLoading` to go false before reading, so the agent never
168
+ receives the previous search's rows, and it truncates a long list by whole rows
169
+ rather than sending a huge payload. Details in
170
+ [functions and context](docs/general/functions-and-context.md#return-what-the-page-displays).
171
+
148
172
  ## Turn a JavaScript function into an agent tool
149
173
 
150
174
  Keep your application logic as a regular JavaScript function, then describe its
@@ -198,6 +222,41 @@ Whenever the agent calls the tool, the SDK runs your handler in the browser
198
222
  with the generated arguments and sends its return value back to the agent. Keep
199
223
  the return value small and structured so the agent can use it reliably.
200
224
 
225
+ ## Let the user stop a long answer
226
+
227
+ `<ChatWidget>` already does this: while the agent answers, its send button turns
228
+ into a **Stop** button (pass `allowStop={false}` to opt out). In a custom UI, wire
229
+ the `stop` returned by `useChat` / `useMessages`:
230
+
231
+ ```tsx
232
+ import { useChat } from "@useago/sdk/react";
233
+
234
+ function Chat() {
235
+ const { messages, sendMessage, stop, isLoading } = useChat();
236
+
237
+ return (
238
+ <div>
239
+ {messages.map((m) => (
240
+ <p key={m.id}><b>{m.role}:</b> {m.content}</p>
241
+ ))}
242
+ {isLoading ? (
243
+ <button onClick={() => void stop()}>Stop</button>
244
+ ) : (
245
+ <button onClick={() => sendMessage("Write me a long report")}>Send</button>
246
+ )}
247
+ </div>
248
+ );
249
+ }
250
+ ```
251
+
252
+ Stopping closes the stream **and** tells the backend to stop generating. Closing
253
+ the stream alone would not stop the agent: it would keep running and the full
254
+ answer would reappear on the next load. The text already produced stays on
255
+ screen, the message ends as `CANCELED`, and the pending `sendMessage` resolves
256
+ with that partial message instead of rejecting. Vue and Angular have the same
257
+ `stop`, and the vanilla `mountChatWidget` handle gained `stop()`. Details in
258
+ [Stop the answer](docs/general/core.md#stop-the-answer).
259
+
201
260
  ## Show the source docs the agent retrieved
202
261
 
203
262
  Each assistant message carries the knowledge sources it used in `m.sources`
@@ -0,0 +1,9 @@
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 live demo endpoint https://playground.api.useago.com. 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}]))},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}});