@useago/sdk 1.4.4 → 1.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/CHANGELOG.md +63 -1
  2. package/dist/AgoClient-5VGybmvz.cjs +8 -0
  3. package/dist/AgoClient-DXrH2AMi.js +964 -0
  4. package/dist/angular.cjs +1 -1
  5. package/dist/angular.d.cts +124 -5
  6. package/dist/angular.d.ts +124 -5
  7. package/dist/angular.js +5 -1
  8. package/dist/autoContinue-CC8K9mBU.js +63 -0
  9. package/dist/autoContinue-CP9pJR-V.cjs +1 -0
  10. package/dist/{createConversationSession-CUkNp6mZ.cjs → createConversationSession-0WTqcCLK.cjs} +1 -1
  11. package/dist/{createConversationSession-QIBN0gLx.js → createConversationSession-DPrrr70o.js} +1 -1
  12. package/dist/createFormCollector-1GKKwQV_.cjs +1 -0
  13. package/dist/{createFormCollector-F3YJfsbg.js → createFormCollector-DYh90kiG.js} +97 -91
  14. package/dist/devtools.d.cts +82 -5
  15. package/dist/devtools.d.ts +82 -5
  16. package/dist/functions-CPs7omgM.cjs +1 -0
  17. package/dist/helpers.cjs +1 -1
  18. package/dist/helpers.d.cts +5 -0
  19. package/dist/helpers.d.ts +5 -0
  20. package/dist/helpers.js +1 -1
  21. package/dist/index.cjs +1 -1
  22. package/dist/index.d.cts +147 -7
  23. package/dist/index.d.ts +147 -7
  24. package/dist/index.js +45 -43
  25. package/dist/react.cjs +3 -3
  26. package/dist/react.d.cts +191 -21
  27. package/dist/react.d.ts +191 -21
  28. package/dist/react.js +666 -529
  29. package/dist/testing.cjs +1 -1
  30. package/dist/testing.d.cts +82 -5
  31. package/dist/testing.d.ts +82 -5
  32. package/dist/testing.js +1 -1
  33. package/dist/vue.cjs +1 -1
  34. package/dist/vue.d.cts +82 -5
  35. package/dist/vue.d.ts +82 -5
  36. package/dist/vue.js +1 -1
  37. package/dist/widget.cjs +4 -4
  38. package/dist/widget.d.cts +82 -5
  39. package/dist/widget.d.ts +82 -5
  40. package/dist/widget.js +437 -423
  41. package/llms-full.txt +100 -6
  42. package/package.json +1 -1
  43. package/dist/AgoClient-CLmtYTJx.cjs +0 -8
  44. package/dist/AgoClient-Ozz2b1dQ.js +0 -912
  45. package/dist/createFormCollector-DHT1ObOb.cjs +0 -1
  46. package/dist/functions-Djnq-JAo.cjs +0 -1
  47. package/dist/rolldown-runtime-16Nf8qvH.cjs +0 -1
  48. /package/dist/{createMockClient-BSr-UV8R.cjs → createMockClient-C-wrjb2C.cjs} +0 -0
  49. /package/dist/{createMockClient-bAZ9KBHC.js → createMockClient-we-XoPom.js} +0 -0
  50. /package/dist/{functions-CApuQZ2b.js → functions-tzyOZNp6.js} +0 -0
@@ -1,72 +1,72 @@
1
- import { f as $ } from "./AgoClient-Ozz2b1dQ.js";
2
- function A(s, e) {
1
+ import { f as w } from "./AgoClient-DXrH2AMi.js";
2
+ function J(n, e) {
3
3
  const a = e ? e.storage ?? (typeof localStorage < "u" ? localStorage : void 0) : void 0;
4
- let n = s;
4
+ let s = n;
5
5
  if (e && a) try {
6
6
  const m = a.getItem(e.key);
7
- m != null && (n = JSON.parse(m));
7
+ m != null && (s = JSON.parse(m));
8
8
  } catch {
9
9
  }
10
- const o = /* @__PURE__ */ new Set(), c = () => o.forEach((m) => m(n));
10
+ const o = /* @__PURE__ */ new Set(), c = () => o.forEach((m) => m(s));
11
11
  function b() {
12
12
  if (e && a) try {
13
- a.setItem(e.key, JSON.stringify(n));
13
+ a.setItem(e.key, JSON.stringify(s));
14
14
  } catch {
15
15
  }
16
16
  }
17
17
  return {
18
- get: () => n,
18
+ get: () => s,
19
19
  set: (m) => {
20
- n = m, b(), c();
20
+ s = m, b(), c();
21
21
  },
22
22
  subscribe: (m) => (o.add(m), () => {
23
23
  o.delete(m);
24
24
  })
25
25
  };
26
26
  }
27
- var P = /^[a-zA-Z0-9_-]{1,40}$/, W = (s) => s == null || s === "";
28
- function v(s, e) {
29
- if ("anyOf" in s) return s.anyOf.some((c) => v(c, e));
30
- if ("allOf" in s) return s.allOf.every((c) => v(c, e));
31
- const a = e[s.property];
32
- if (W(a)) return !1;
33
- if (Array.isArray(s.value)) return s.value.some((c) => String(a) === String(c));
34
- if (!s.op) return String(a) === String(s.value);
35
- const n = Number(a), o = Number(s.value);
36
- if (Number.isNaN(n) || Number.isNaN(o)) return !1;
37
- switch (s.op) {
27
+ var j = /^[a-zA-Z0-9_-]{1,40}$/, I = (n) => n == null || n === "";
28
+ function N(n, e) {
29
+ if ("anyOf" in n) return n.anyOf.some((c) => N(c, e));
30
+ if ("allOf" in n) return n.allOf.every((c) => N(c, e));
31
+ const a = e[n.property];
32
+ if (I(a)) return !1;
33
+ if (Array.isArray(n.value)) return n.value.some((c) => String(a) === String(c));
34
+ if (!n.op) return String(a) === String(n.value);
35
+ const s = Number(a), o = Number(n.value);
36
+ if (Number.isNaN(s) || Number.isNaN(o)) return !1;
37
+ switch (n.op) {
38
38
  case ">=":
39
- return n >= o;
39
+ return s >= o;
40
40
  case "<=":
41
- return n <= o;
41
+ return s <= o;
42
42
  case ">":
43
- return n > o;
43
+ return s > o;
44
44
  case "<":
45
- return n < o;
45
+ return s < o;
46
46
  default:
47
- return $.warn(`Unknown requiredWhen op "${s.op}"; treating as not required.`), !1;
47
+ return w.warn(`Unknown requiredWhen op "${n.op}"; treating as not required.`), !1;
48
48
  }
49
49
  }
50
- function y(s, e) {
51
- const a = s.properties ?? {}, n = /* @__PURE__ */ new Set();
52
- for (const c of s.required ?? []) {
50
+ function p(n, e) {
51
+ const a = n.properties ?? {}, s = /* @__PURE__ */ new Set();
52
+ for (const c of n.required ?? []) {
53
53
  const b = a[c]?.requiredWhen;
54
- (!b || v(b, e)) && n.add(c);
54
+ (!b || N(b, e)) && s.add(c);
55
55
  }
56
- for (const [c, b] of Object.entries(a)) b.requiredWhen && v(b.requiredWhen, e) && n.add(c);
57
- const o = [...n].filter((c) => W(e[c]));
56
+ for (const [c, b] of Object.entries(a)) b.requiredWhen && N(b.requiredWhen, e) && s.add(c);
57
+ const o = [...s].filter((c) => I(e[c]));
58
58
  return {
59
59
  missing: o,
60
60
  complete: o.length === 0
61
61
  };
62
62
  }
63
- function J(s) {
63
+ function R(n) {
64
64
  const e = {};
65
- for (const [a, n] of Object.entries(s.properties ?? {})) e[a] = {
66
- type: n.type,
67
- ...n.description != null && { description: n.description },
68
- ...n.enum && { enum: n.enum },
69
- ...n.default !== void 0 && { default: n.default }
65
+ for (const [a, s] of Object.entries(n.properties ?? {})) e[a] = {
66
+ type: s.type,
67
+ ...s.description != null && { description: s.description },
68
+ ...s.enum && { enum: s.enum },
69
+ ...s.default !== void 0 && { default: s.default }
70
70
  };
71
71
  return {
72
72
  type: "object",
@@ -74,15 +74,15 @@ function J(s) {
74
74
  required: []
75
75
  };
76
76
  }
77
- function R(s) {
78
- const { name: e, description: a, schema: n } = s;
79
- if (!P.test(e)) throw new Error(`Invalid form collector name "${e}": must match ${P} (it becomes the update_/submit_ function names).`);
80
- const o = typeof s.submit == "string" ? s.submit ? {
77
+ function B(n) {
78
+ const { name: e, description: a, schema: s } = n;
79
+ if (!j.test(e)) throw new Error(`Invalid form collector name "${e}": must match ${j} (it becomes the update_/submit_ function names).`);
80
+ const o = typeof n.submit == "string" ? n.submit ? {
81
81
  via: "client",
82
- url: s.submit
83
- } : !1 : s.submit ?? !1;
84
- s.autoSubmit === !0 && !o && $.warn(`FormCollector "${e}": autoSubmit is ignored because no submit target is configured.`);
85
- const c = (s.autoSubmit ?? !0) && !!o, b = n.properties ?? {}, m = (t) => {
82
+ url: n.submit
83
+ } : !1 : n.submit ?? !1;
84
+ n.autoSubmit === !0 && !o && w.warn(`FormCollector "${e}": autoSubmit is ignored because no submit target is configured.`);
85
+ const c = (n.autoSubmit ?? !0) && !!o, b = s.properties ?? {}, m = (t) => {
86
86
  const r = {};
87
87
  for (const [u, i] of Object.entries(t)) {
88
88
  const l = b[u];
@@ -94,10 +94,10 @@ function R(s) {
94
94
  } else l.type === "boolean" ? r[u] = typeof i == "boolean" ? i : i === "true" ? !0 : i === "false" ? !1 : !!i : r[u] = i;
95
95
  }
96
96
  return r;
97
- }, S = s.initialValues ?? {}, f = A({
98
- values: S,
97
+ }, $ = n.initialValues ?? {}, f = J({
98
+ values: $,
99
99
  submitted: !1
100
- }), C = (t) => {
100
+ }), k = (t) => {
101
101
  const r = f.get(), u = {
102
102
  ...r,
103
103
  values: {
@@ -108,16 +108,16 @@ function R(s) {
108
108
  return f.set(u), u;
109
109
  };
110
110
  let h = null;
111
- const k = (t, r) => {
111
+ const F = (t, r) => {
112
112
  h?.emitFormEvent("form:error", {
113
113
  name: e,
114
114
  values: t,
115
115
  error: r
116
116
  });
117
117
  };
118
- let p = null;
119
- const j = async () => {
120
- const { values: t } = f.get(), { complete: r, missing: u } = y(n, t);
118
+ let y = null;
119
+ const V = async () => {
120
+ const { values: t } = f.get(), { complete: r, missing: u } = p(s, t);
121
121
  if (!r) return {
122
122
  ok: !1,
123
123
  missing: u
@@ -138,10 +138,10 @@ function R(s) {
138
138
  body: JSON.stringify(i)
139
139
  });
140
140
  if (!d.ok) {
141
- const x = `Submit failed: HTTP ${d.status}`;
142
- return k(i, x), {
141
+ const W = `Submit failed: HTTP ${d.status}`;
142
+ return F(i, W), {
143
143
  ok: !1,
144
- error: x
144
+ error: W
145
145
  };
146
146
  }
147
147
  l = await d.json().catch(() => {
@@ -168,27 +168,27 @@ function R(s) {
168
168
  };
169
169
  } catch (l) {
170
170
  const d = l instanceof Error ? l.message : "Submit failed";
171
- return $.error(`FormCollector "${e}" submit failed:`, l), k(i, d), {
171
+ return w.error(`FormCollector "${e}" submit failed:`, l), F(i, d), {
172
172
  ok: !1,
173
173
  error: d
174
174
  };
175
175
  }
176
- }, N = () => p || (p = (async () => {
176
+ }, C = () => y || (y = (async () => {
177
177
  try {
178
- return await j();
178
+ return await V();
179
179
  } finally {
180
- p = null;
180
+ y = null;
181
181
  }
182
- })(), p), F = async () => {
182
+ })(), y), q = async () => {
183
183
  if (!c || !o) return null;
184
184
  const { values: t, submitted: r } = f.get();
185
- return r || !y(n, t).complete ? null : N();
186
- }, q = {
185
+ return r || !p(s, t).complete ? null : C();
186
+ }, O = {
187
187
  name: `update_${e}`,
188
188
  description: `Fill or update fields of the "${e}" form. ${a} Pass any subset of fields; call it repeatedly to fill the form as the user provides information. Returns the current values and which required fields are still missing.`,
189
- parameters: J(n),
189
+ parameters: R(s),
190
190
  handler: async (t) => {
191
- const r = C(m(t)), { missing: u } = y(n, r.values), i = await F();
191
+ const r = k(m(t)), { missing: u } = p(s, r.values), i = await q();
192
192
  return i ? i.ok ? {
193
193
  ok: !0,
194
194
  values: r.values,
@@ -206,7 +206,7 @@ function R(s) {
206
206
  missing: u
207
207
  };
208
208
  }
209
- }, g = [q];
209
+ }, g = [O];
210
210
  o && !c && g.push({
211
211
  name: `submit_${e}`,
212
212
  description: `Submit the completed "${e}" form. Only call this once every required field is collected and the user has confirmed. If fields are missing it returns them instead of submitting.`,
@@ -214,30 +214,34 @@ function R(s) {
214
214
  type: "object",
215
215
  properties: {}
216
216
  },
217
- handler: () => N()
217
+ handler: () => C()
218
218
  });
219
- const I = "client__", O = o ? `submit_${e}` : null, V = (t) => {
219
+ const A = "client__", x = o ? `submit_${e}` : null, D = (t) => {
220
220
  const r = t.functionName ?? t.toolName;
221
221
  if (r)
222
- return r.startsWith(I) ? r.slice(8) : r;
222
+ return r.startsWith(A) ? r.slice(8) : r;
223
223
  }, _ = (t) => {
224
- let r = { ...S }, u = !1;
224
+ let r = { ...$ }, u = !1;
225
225
  for (const i of t) {
226
- const l = V(i);
227
- l === q.name ? i.arguments && (r = {
226
+ const l = D(i);
227
+ l === O.name ? i.arguments && (r = {
228
228
  ...r,
229
229
  ...m(i.arguments)
230
- }) : O && l === O && i.status?.toLowerCase() === "done" && (u = !0);
230
+ }) : x && l === x && i.status?.toLowerCase() === "done" && (u = !0);
231
231
  }
232
232
  f.set({
233
233
  values: r,
234
234
  submitted: u
235
235
  });
236
- }, w = `form:${e}`, E = () => {
237
- const { values: t, submitted: r } = f.get(), { missing: u, complete: i } = y(n, t), l = o ? c ? " The form is submitted automatically as soon as every required field is filled, so there is no submit function to call." : ` When nothing is missing and the user confirms, call submit_${e}.` : "", d = r ? `The "${e}" form has been submitted.` : `You are currently collecting information from the user to fill the "${e}" form.`;
236
+ }, v = `form:${e}`, S = `${v}:definition`, E = () => ({
237
+ name: `Form definition: ${e}`,
238
+ description: `${a} Some fields are conditionally required; the requiredWhen field in the schema tells you when each becomes required. The form schema is the following: ${JSON.stringify(s)}.`,
239
+ stable: !0
240
+ }), T = () => {
241
+ const { values: t, submitted: r } = f.get(), { missing: u, complete: i } = p(s, t), l = o ? c ? " The form is submitted automatically as soon as every required field is filled, so there is no submit function to call." : ` When nothing is missing and the user confirms, call submit_${e}.` : "", d = r ? `The "${e}" form has been submitted.` : `You are currently collecting information from the user to fill the "${e}" form.`;
238
242
  return {
239
243
  name: `Form: ${e}`,
240
- description: `${d} ${a} Some fields are conditionally required; the requiredWhen field in the schema tells you when each becomes required. The form schema is the following: ${JSON.stringify(n)}. Data collected so far: ${JSON.stringify(t)}. Call update_${e} with any fields the user provides; ask only for what is still missing.${l}`,
244
+ description: `${d} The form's schema and field requirements are in the "${S}" client context. Call update_${e} with any fields the user provides; ask only for what is still missing.${l}`,
241
245
  data: {
242
246
  values: t,
243
247
  missing: u,
@@ -245,44 +249,46 @@ function R(s) {
245
249
  submitted: r
246
250
  }
247
251
  };
248
- }, T = (t) => {
252
+ }, P = (t) => {
249
253
  _((t.messages ?? []).flatMap((r) => r.toolCalls ?? []));
250
254
  };
251
255
  return {
252
256
  name: e,
253
257
  store: f,
254
258
  functions: g,
255
- contextKey: w,
256
- contextProvider: E,
259
+ contextKey: v,
260
+ contextProvider: T,
261
+ definitionContextKey: S,
262
+ definitionContextProvider: E,
257
263
  install: (t) => {
258
264
  h = t;
259
265
  for (const u of g) t.register(u);
260
- t.addDynamicContext(w, E);
266
+ t.addDynamicContext(S, E), t.addDynamicContext(v, T);
261
267
  const r = f.subscribe(() => t.notifyContextChanged());
262
- return t.on("conversation:loaded", T), () => {
268
+ return t.on("conversation:loaded", P), () => {
263
269
  r();
264
270
  for (const u of g) t.unregisterFunction(u.name);
265
- t.removeDynamicContext(w), t.off("conversation:loaded", T), h === t && (h = null);
271
+ t.removeDynamicContext(v), t.removeDynamicContext(S), t.off("conversation:loaded", P), h === t && (h = null);
266
272
  };
267
273
  },
268
274
  hydrate: _,
269
- submit: N,
275
+ submit: C,
270
276
  getValues: () => f.get().values,
271
277
  setValues: (t) => {
272
- C(m(t)), F();
278
+ k(m(t)), q();
273
279
  },
274
280
  reset: () => {
275
281
  f.set({
276
- values: S,
282
+ values: $,
277
283
  submitted: !1
278
284
  });
279
285
  },
280
- getStatus: () => y(n, f.get().values)
286
+ getStatus: () => p(s, f.get().values)
281
287
  };
282
288
  }
283
- async function L(s, e) {
284
- const a = await s.getFormCollector(e.name);
285
- return R({
289
+ async function K(n, e) {
290
+ const a = await n.getFormCollector(e.name);
291
+ return B({
286
292
  name: a.name,
287
293
  description: e.description ?? a.description,
288
294
  schema: e.schema ?? a.schema,
@@ -292,8 +298,8 @@ async function L(s, e) {
292
298
  });
293
299
  }
294
300
  export {
295
- A as i,
296
- y as n,
297
- L as r,
298
- R as t
301
+ J as i,
302
+ p as n,
303
+ K as r,
304
+ B as t
299
305
  };
@@ -41,6 +41,12 @@ declare class AgoClient {
41
41
  private warnedEmptyConversations;
42
42
  /** Paused turns awaiting auto-resume, keyed by assistant message id. */
43
43
  private pendingResumes;
44
+ /**
45
+ * Client function invocations held pending user approval, keyed by
46
+ * invocationId (the tool-call id). Populated when {@link requiresApproval}
47
+ * matches; drained by {@link approveFunction} / {@link rejectFunction}.
48
+ */
49
+ private pendingApprovals;
44
50
  /** App-provided gate deciding WHEN a paused turn resumes (see {@link registerResumeGate}). */
45
51
  private resumeGate;
46
52
  constructor(config: AgoConfig);
@@ -91,6 +97,24 @@ declare class AgoClient {
91
97
  */
92
98
  private maybeFlagEmptyReply;
93
99
  private handleClientFunctionInvocation;
100
+ /**
101
+ * Whether a client function invocation must wait for explicit user approval.
102
+ * True when the {@link AgoConfig.approvalPolicy} returns true OR the function
103
+ * was registered with `requiresApproval: true` — the two OR together.
104
+ */
105
+ private requiresApproval;
106
+ /**
107
+ * Approve a client function call held by the approval gate: run it, submit its
108
+ * result, and let the paused turn resume. No-op if the invocation is unknown
109
+ * (already handled, or never gated).
110
+ */
111
+ approveFunction(invocationId: string): Promise<void>;
112
+ /**
113
+ * Reject a client function call held by the approval gate: submit a rejection
114
+ * result so the agent sees the user declined, and let the paused turn resume
115
+ * (the agent decides what to do next). No-op if the invocation is unknown.
116
+ */
117
+ rejectFunction(invocationId: string): Promise<void>;
94
118
  /**
95
119
  * Get list of conversations
96
120
  */
@@ -120,8 +144,10 @@ declare class AgoClient {
120
144
  *
121
145
  * Opt-in on purpose — re-running functions on reload can be surprising (e.g. a
122
146
  * navigation function would navigate again), so the app decides. Returns the
123
- * resumed turn's final message, or `null` when there was nothing to resume or
124
- * some waiting function is not registered on this page.
147
+ * resumed turn's final message, or `null` when there was nothing to resume,
148
+ * some waiting function is not registered on this page, or a waiting call is
149
+ * gated by the approval policy (it is held for `approveFunction` /
150
+ * `rejectFunction` and the turn stays paused, exactly as on the live stream).
125
151
  */
126
152
  resumePendingClientFunctions(conversation: Conversation): Promise<AgoMessage | null>;
127
153
  /**
@@ -416,6 +442,15 @@ declare interface AgoClientEvents {
416
442
  "toolCall:received": ToolCallData;
417
443
  "toolCall:form": ToolCallData;
418
444
  "function:invoke": ClientFunctionInvocation;
445
+ /**
446
+ * A client function call is held pending the user's approval (pause mode +
447
+ * an {@link AgoConfig.approvalPolicy}/`requiresApproval` match). The turn stays
448
+ * at `WAITING_CLIENT`; call `approveFunction(invocationId)` to run it and
449
+ * resume, or `rejectFunction(invocationId)` to decline. Fires after
450
+ * `function:invoke` (which still fires for every call) and instead of the
451
+ * call running immediately.
452
+ */
453
+ "function:awaiting-approval": ClientFunctionInvocation;
419
454
  "function:result": {
420
455
  invocationId: string;
421
456
  result: unknown;
@@ -476,14 +511,28 @@ declare interface AgoConfig {
476
511
  warnOnEmptyReply?: boolean;
477
512
  /**
478
513
  * How the agent loop treats client function calls (requires backend support):
479
- * - `"placeholder"` (default): the turn continues immediately on a placeholder
480
- * result; the real result is only visible to the agent on later turns.
481
- * - `"pause"`: the turn stops on client function call(s) with status
514
+ * - `"pause"` (default): the turn stops on client function call(s) with status
482
515
  * `WAITING_CLIENT`; once every result is submitted, the SDK resumes the SAME
483
516
  * turn via `POST /messages/{id}/continue`, so the agent sees the real results.
517
+ * - `"placeholder"` (legacy): the turn continues immediately on a placeholder
518
+ * result; the real result is only visible to the agent on later turns.
484
519
  * Older backends ignore the flag, so `"pause"` degrades to legacy behavior.
485
520
  */
486
521
  clientFunctionsMode?: ClientFunctionsMode;
522
+ /**
523
+ * Gate client function calls behind explicit user approval. Return `true` for
524
+ * an invocation the user must approve/reject before it runs; return `false`
525
+ * (or omit the policy) to let it run immediately as before.
526
+ *
527
+ * Only effective in pause mode (`clientFunctionsMode: "pause"`) — a gated call
528
+ * holds at `WAITING_CLIENT`, the SDK emits `function:awaiting-approval`, and it
529
+ * runs (and the turn resumes) only after `approveFunction(invocationId)`; a
530
+ * `rejectFunction(invocationId)` submits a rejection so the agent sees the
531
+ * decline. In placeholder mode there is no pause to hold, so the policy is a
532
+ * no-op and the call runs normally. A per-function `requiresApproval: true`
533
+ * also gates a call; the policy and the flag OR together.
534
+ */
535
+ approvalPolicy?: (invocation: ClientFunctionInvocation) => boolean;
487
536
  /**
488
537
  * Max serialized size (bytes) of a client function result before the SDK
489
538
  * replaces it with a flagged, truncated preview. Everything a handler
@@ -528,6 +577,11 @@ declare interface AgoMessage {
528
577
  declare interface AgoPageStateOptions {
529
578
  /** Name of the synthesized client function. Defaults to "setPageState". */
530
579
  functionName?: string;
580
+ /**
581
+ * Require explicit user approval before this state change runs. See
582
+ * {@link AgoConfig.approvalPolicy} — pause mode only. Defaults to `false`.
583
+ */
584
+ requiresApproval?: boolean;
531
585
  }
532
586
 
533
587
  /**
@@ -589,6 +643,11 @@ declare interface ClientFunctionDefinition {
589
643
  * `Infinity` disables the guard for this function.
590
644
  */
591
645
  maxResultBytes?: number;
646
+ /**
647
+ * Require explicit user approval before this function runs. See
648
+ * {@link AgoConfig.approvalPolicy} — pause mode only. ORs with the policy.
649
+ */
650
+ requiresApproval?: boolean;
592
651
  }
593
652
 
594
653
  /**
@@ -615,6 +674,11 @@ declare type ClientFunctionRegisterOptions = Omit<ClientFunctionSchema, "name">
615
674
  * disables the guard for this function.
616
675
  */
617
676
  maxResultBytes?: number;
677
+ /**
678
+ * Require explicit user approval before this function runs. See
679
+ * {@link AgoConfig.approvalPolicy} — pause mode only. ORs with the policy.
680
+ */
681
+ requiresApproval?: boolean;
618
682
  };
619
683
 
620
684
  /**
@@ -655,6 +719,19 @@ declare interface ContextEntry {
655
719
  description?: string;
656
720
  /** Arbitrary structured data the AI should know about */
657
721
  data?: Record<string, unknown>;
722
+ /**
723
+ * Marks the entry as constant for the whole conversation. The backend then
724
+ * pins it right after the system prompt, where LLM providers cache it across
725
+ * turns, instead of re-processing it next to every new user message.
726
+ *
727
+ * Only set this when the entry's content is byte-identical on every message
728
+ * of a conversation (a schema, a capability list, static instructions). A
729
+ * stable entry that changes between messages invalidates the provider's
730
+ * cache for the entire conversation history, which costs more than leaving
731
+ * the flag off. Default: `false` (re-sent near the newest user message).
732
+ * Backends that don't know the flag ignore it.
733
+ */
734
+ stable?: boolean;
658
735
  }
659
736
 
660
737
  /**
@@ -41,6 +41,12 @@ declare class AgoClient {
41
41
  private warnedEmptyConversations;
42
42
  /** Paused turns awaiting auto-resume, keyed by assistant message id. */
43
43
  private pendingResumes;
44
+ /**
45
+ * Client function invocations held pending user approval, keyed by
46
+ * invocationId (the tool-call id). Populated when {@link requiresApproval}
47
+ * matches; drained by {@link approveFunction} / {@link rejectFunction}.
48
+ */
49
+ private pendingApprovals;
44
50
  /** App-provided gate deciding WHEN a paused turn resumes (see {@link registerResumeGate}). */
45
51
  private resumeGate;
46
52
  constructor(config: AgoConfig);
@@ -91,6 +97,24 @@ declare class AgoClient {
91
97
  */
92
98
  private maybeFlagEmptyReply;
93
99
  private handleClientFunctionInvocation;
100
+ /**
101
+ * Whether a client function invocation must wait for explicit user approval.
102
+ * True when the {@link AgoConfig.approvalPolicy} returns true OR the function
103
+ * was registered with `requiresApproval: true` — the two OR together.
104
+ */
105
+ private requiresApproval;
106
+ /**
107
+ * Approve a client function call held by the approval gate: run it, submit its
108
+ * result, and let the paused turn resume. No-op if the invocation is unknown
109
+ * (already handled, or never gated).
110
+ */
111
+ approveFunction(invocationId: string): Promise<void>;
112
+ /**
113
+ * Reject a client function call held by the approval gate: submit a rejection
114
+ * result so the agent sees the user declined, and let the paused turn resume
115
+ * (the agent decides what to do next). No-op if the invocation is unknown.
116
+ */
117
+ rejectFunction(invocationId: string): Promise<void>;
94
118
  /**
95
119
  * Get list of conversations
96
120
  */
@@ -120,8 +144,10 @@ declare class AgoClient {
120
144
  *
121
145
  * Opt-in on purpose — re-running functions on reload can be surprising (e.g. a
122
146
  * navigation function would navigate again), so the app decides. Returns the
123
- * resumed turn's final message, or `null` when there was nothing to resume or
124
- * some waiting function is not registered on this page.
147
+ * resumed turn's final message, or `null` when there was nothing to resume,
148
+ * some waiting function is not registered on this page, or a waiting call is
149
+ * gated by the approval policy (it is held for `approveFunction` /
150
+ * `rejectFunction` and the turn stays paused, exactly as on the live stream).
125
151
  */
126
152
  resumePendingClientFunctions(conversation: Conversation): Promise<AgoMessage | null>;
127
153
  /**
@@ -416,6 +442,15 @@ declare interface AgoClientEvents {
416
442
  "toolCall:received": ToolCallData;
417
443
  "toolCall:form": ToolCallData;
418
444
  "function:invoke": ClientFunctionInvocation;
445
+ /**
446
+ * A client function call is held pending the user's approval (pause mode +
447
+ * an {@link AgoConfig.approvalPolicy}/`requiresApproval` match). The turn stays
448
+ * at `WAITING_CLIENT`; call `approveFunction(invocationId)` to run it and
449
+ * resume, or `rejectFunction(invocationId)` to decline. Fires after
450
+ * `function:invoke` (which still fires for every call) and instead of the
451
+ * call running immediately.
452
+ */
453
+ "function:awaiting-approval": ClientFunctionInvocation;
419
454
  "function:result": {
420
455
  invocationId: string;
421
456
  result: unknown;
@@ -476,14 +511,28 @@ declare interface AgoConfig {
476
511
  warnOnEmptyReply?: boolean;
477
512
  /**
478
513
  * How the agent loop treats client function calls (requires backend support):
479
- * - `"placeholder"` (default): the turn continues immediately on a placeholder
480
- * result; the real result is only visible to the agent on later turns.
481
- * - `"pause"`: the turn stops on client function call(s) with status
514
+ * - `"pause"` (default): the turn stops on client function call(s) with status
482
515
  * `WAITING_CLIENT`; once every result is submitted, the SDK resumes the SAME
483
516
  * turn via `POST /messages/{id}/continue`, so the agent sees the real results.
517
+ * - `"placeholder"` (legacy): the turn continues immediately on a placeholder
518
+ * result; the real result is only visible to the agent on later turns.
484
519
  * Older backends ignore the flag, so `"pause"` degrades to legacy behavior.
485
520
  */
486
521
  clientFunctionsMode?: ClientFunctionsMode;
522
+ /**
523
+ * Gate client function calls behind explicit user approval. Return `true` for
524
+ * an invocation the user must approve/reject before it runs; return `false`
525
+ * (or omit the policy) to let it run immediately as before.
526
+ *
527
+ * Only effective in pause mode (`clientFunctionsMode: "pause"`) — a gated call
528
+ * holds at `WAITING_CLIENT`, the SDK emits `function:awaiting-approval`, and it
529
+ * runs (and the turn resumes) only after `approveFunction(invocationId)`; a
530
+ * `rejectFunction(invocationId)` submits a rejection so the agent sees the
531
+ * decline. In placeholder mode there is no pause to hold, so the policy is a
532
+ * no-op and the call runs normally. A per-function `requiresApproval: true`
533
+ * also gates a call; the policy and the flag OR together.
534
+ */
535
+ approvalPolicy?: (invocation: ClientFunctionInvocation) => boolean;
487
536
  /**
488
537
  * Max serialized size (bytes) of a client function result before the SDK
489
538
  * replaces it with a flagged, truncated preview. Everything a handler
@@ -528,6 +577,11 @@ declare interface AgoMessage {
528
577
  declare interface AgoPageStateOptions {
529
578
  /** Name of the synthesized client function. Defaults to "setPageState". */
530
579
  functionName?: string;
580
+ /**
581
+ * Require explicit user approval before this state change runs. See
582
+ * {@link AgoConfig.approvalPolicy} — pause mode only. Defaults to `false`.
583
+ */
584
+ requiresApproval?: boolean;
531
585
  }
532
586
 
533
587
  /**
@@ -589,6 +643,11 @@ declare interface ClientFunctionDefinition {
589
643
  * `Infinity` disables the guard for this function.
590
644
  */
591
645
  maxResultBytes?: number;
646
+ /**
647
+ * Require explicit user approval before this function runs. See
648
+ * {@link AgoConfig.approvalPolicy} — pause mode only. ORs with the policy.
649
+ */
650
+ requiresApproval?: boolean;
592
651
  }
593
652
 
594
653
  /**
@@ -615,6 +674,11 @@ declare type ClientFunctionRegisterOptions = Omit<ClientFunctionSchema, "name">
615
674
  * disables the guard for this function.
616
675
  */
617
676
  maxResultBytes?: number;
677
+ /**
678
+ * Require explicit user approval before this function runs. See
679
+ * {@link AgoConfig.approvalPolicy} — pause mode only. ORs with the policy.
680
+ */
681
+ requiresApproval?: boolean;
618
682
  };
619
683
 
620
684
  /**
@@ -655,6 +719,19 @@ declare interface ContextEntry {
655
719
  description?: string;
656
720
  /** Arbitrary structured data the AI should know about */
657
721
  data?: Record<string, unknown>;
722
+ /**
723
+ * Marks the entry as constant for the whole conversation. The backend then
724
+ * pins it right after the system prompt, where LLM providers cache it across
725
+ * turns, instead of re-processing it next to every new user message.
726
+ *
727
+ * Only set this when the entry's content is byte-identical on every message
728
+ * of a conversation (a schema, a capability list, static instructions). A
729
+ * stable entry that changes between messages invalidates the provider's
730
+ * cache for the entire conversation history, which costs more than leaving
731
+ * the flag off. Default: `false` (re-sent near the newest user message).
732
+ * Backends that don't know the flag ignore it.
733
+ */
734
+ stable?: boolean;
658
735
  }
659
736
 
660
737
  /**
@@ -0,0 +1 @@
1
+ const v=require("./react.cjs");var w=v.__exportAll({copyToClipboard:()=>c,getLocalStorage:()=>f,getUserLocation:()=>p,highlightElement:()=>h,openUrl:()=>a,scrollToElement:()=>d,setLocalStorage:()=>m,setTheme:()=>l,showConfirmDialog:()=>u,showNotification:()=>s,showToast:()=>i,submitForm:()=>y,trackEvent:()=>g}),i={name:"showToast",description:"Show a toast notification to the user. Use this to confirm actions or display brief messages.",parameters:{type:"object",properties:{message:{type:"string",description:"The message to display"},type:{type:"string",description:"Toast type",enum:["success","error","warning","info"]},duration:{type:"number",description:"Duration in milliseconds (default 3000)"}},required:["message"]},handler:async()=>({shown:!1,error:"No toast handler configured"})},s={name:"showNotification",description:"Show a browser notification to the user. Useful for important alerts even when the tab is not focused.",parameters:{type:"object",properties:{title:{type:"string",description:"Notification title"},body:{type:"string",description:"Notification body text"}},required:["title"]},handler:async e=>typeof Notification>"u"?{shown:!1,error:"Notifications not supported"}:Notification.permission==="denied"?{shown:!1,error:"Notification permission denied"}:(Notification.permission!=="granted"&&await Notification.requestPermission(),Notification.permission==="granted"?(new Notification(e.title,{body:e.body}),{shown:!0}):{shown:!1,error:"Permission not granted"})},a={name:"openUrl",description:"Open a URL in a new browser tab.",parameters:{type:"object",properties:{url:{type:"string",description:"The URL to open"}},required:["url"]},handler:async e=>(window.open(e.url,"_blank","noopener,noreferrer"),{opened:!0})},c={name:"copyToClipboard",description:"Copy text to the user's clipboard.",parameters:{type:"object",properties:{text:{type:"string",description:"The text to copy"}},required:["text"]},handler:async e=>(await navigator.clipboard.writeText(e.text),{copied:!0})},l={name:"setTheme",description:"Change the application's color theme. Sets a data-theme attribute on the document element.",parameters:{type:"object",properties:{theme:{type:"string",description:"Theme to apply",enum:["light","dark","system"]}},required:["theme"]},handler:async e=>{const t=e.theme;return document.documentElement.setAttribute("data-theme",t),t==="dark"?document.documentElement.classList.add("dark"):document.documentElement.classList.remove("dark"),{theme:t,applied:!0}}},u={name:"showConfirmDialog",description:"Show a confirmation dialog to the user and return their choice (true/false).",parameters:{type:"object",properties:{message:{type:"string",description:"The confirmation message to display"}},required:["message"]},handler:async e=>({confirmed:window.confirm(e.message)})},p={name:"getUserLocation",description:"Get the user's current geographic location (requires permission).",parameters:{type:"object",properties:{}},handler:async()=>navigator.geolocation?new Promise(e=>{navigator.geolocation.getCurrentPosition(t=>e({latitude:t.coords.latitude,longitude:t.coords.longitude,accuracy:t.coords.accuracy}),t=>e({error:t.message}))}):{error:"Geolocation not supported"}},d={name:"scrollToElement",description:"Scroll the page to a specific element identified by CSS selector.",parameters:{type:"object",properties:{selector:{type:"string",description:"CSS selector of the element to scroll to (e.g. '#section-pricing')"},behavior:{type:"string",description:"Scroll behavior",enum:["smooth","instant"]}},required:["selector"]},handler:async e=>{const t=document.querySelector(e.selector);return t?(t.scrollIntoView({behavior:e.behavior||"smooth"}),{scrolled:!0}):{scrolled:!1,error:`Element not found: ${e.selector}`}}},m={name:"setLocalStorage",description:"Store a value in the browser's localStorage.",parameters:{type:"object",properties:{key:{type:"string",description:"Storage key"},value:{type:"string",description:"Value to store"}},required:["key","value"]},handler:async e=>(localStorage.setItem(e.key,e.value),{stored:!0})},f={name:"getLocalStorage",description:"Retrieve a value from the browser's localStorage.",parameters:{type:"object",properties:{key:{type:"string",description:"Storage key"}},required:["key"]},handler:async e=>{const t=localStorage.getItem(e.key);return{key:e.key,value:t,found:t!==null}}},h={name:"highlightElement",description:"Highlight a DOM element to draw the user's attention (e.g. for onboarding).",parameters:{type:"object",properties:{selector:{type:"string",description:"CSS selector of the element to highlight"},color:{type:"string",description:"Highlight border color (default: #3b82f6)"}},required:["selector"]},handler:async e=>{const t=document.querySelector(e.selector);if(!t)return{highlighted:!1,error:`Element not found: ${e.selector}`};const r=t.style.outline,o=e.color||"#3b82f6";return t.style.outline=`3px solid ${o}`,t.style.outlineOffset="2px",t.scrollIntoView({behavior:"smooth",block:"center"}),setTimeout(()=>{t.style.outline=r,t.style.outlineOffset=""},3e3),{highlighted:!0}}},y={name:"submitForm",description:"Fill and submit an HTML form on the page. Pass field values as key-value pairs.",parameters:{type:"object",properties:{selector:{type:"string",description:"CSS selector of the form element"},values:{type:"string",description:"JSON string of field name → value pairs"}},required:["selector"]},handler:async e=>{const t=document.querySelector(e.selector);if(!t)return{submitted:!1,error:`Form not found: ${e.selector}`};if(e.values){const r=JSON.parse(e.values);for(const[o,b]of Object.entries(r)){const n=t.elements.namedItem(o);n&&(n.value=b)}}return t.requestSubmit(),{submitted:!0}}},g={name:"trackEvent",description:"Track a custom analytics event. Override the handler to wire up your analytics provider.",parameters:{type:"object",properties:{event:{type:"string",description:"Event name"},properties:{type:"string",description:"JSON string of event properties"}},required:["event"]},handler:async e=>{const t=e.properties?JSON.parse(e.properties):{};return console.log(`[AGO Analytics] ${e.event}`,t),{tracked:!0,event:e.event}}};Object.defineProperty(exports,"copyToClipboard",{enumerable:!0,get:function(){return c}});Object.defineProperty(exports,"functions_exports",{enumerable:!0,get:function(){return w}});Object.defineProperty(exports,"getLocalStorage",{enumerable:!0,get:function(){return f}});Object.defineProperty(exports,"getUserLocation",{enumerable:!0,get:function(){return p}});Object.defineProperty(exports,"highlightElement",{enumerable:!0,get:function(){return h}});Object.defineProperty(exports,"openUrl",{enumerable:!0,get:function(){return a}});Object.defineProperty(exports,"scrollToElement",{enumerable:!0,get:function(){return d}});Object.defineProperty(exports,"setLocalStorage",{enumerable:!0,get:function(){return m}});Object.defineProperty(exports,"setTheme",{enumerable:!0,get:function(){return l}});Object.defineProperty(exports,"showConfirmDialog",{enumerable:!0,get:function(){return u}});Object.defineProperty(exports,"showNotification",{enumerable:!0,get:function(){return s}});Object.defineProperty(exports,"showToast",{enumerable:!0,get:function(){return i}});Object.defineProperty(exports,"submitForm",{enumerable:!0,get:function(){return y}});Object.defineProperty(exports,"trackEvent",{enumerable:!0,get:function(){return g}});