@useago/sdk 1.0.8 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,9 +3,20 @@
3
3
  [![npm version](https://img.shields.io/npm/v/@useago/sdk.svg)](https://www.npmjs.com/package/@useago/sdk)
4
4
  [![types](https://img.shields.io/npm/types/@useago/sdk.svg)](https://www.npmjs.com/package/@useago/sdk)
5
5
 
6
- **The agent layer for your frontend stack.** AGO's agents plug into React, Vue,
7
- Angular or plain TypeScript like any other dependency: send a message, stream
8
- the reply, and let the agent call **your** code and navigate **your** UI.
6
+ **The customer-facing agent platform for your product.**
7
+
8
+ AGO embeds AI agents directly into your frontend stack: React, Vue, Angular or
9
+ plain TypeScript. Send messages, stream responses, expose frontend actions, and
10
+ let agents guide users, trigger workflows, or operate **your** UI.
11
+
12
+ Internal agent platforms help employees work faster. Customer-facing agents are
13
+ a different problem. They have to be reliable, safe, measurable, brand-aligned,
14
+ and integrated into the product experience.
15
+
16
+ AGO provides the operating layer behind the SDK: business and user context, tool
17
+ orchestration, evaluations, quality monitoring, security controls, human
18
+ fallback, and continuous improvement. Your team ships product-native agents that
19
+ resolve customer needs, without rebuilding the full customer-facing agent platform.
9
20
 
10
21
  In two minutes you'll have a working chat on your page and an agent that can
11
22
  navigate your app. The quickstart below uses **React**; every other stack gets
@@ -572,7 +572,7 @@ declare interface FormCollectorDefinition<V = Record<string, unknown>> {
572
572
 
573
573
  /**
574
574
  * Shape of a form's fields. Mirrors a client function's `parameters` (so the same
575
- * object feeds the store, the update function, and the dynamic context). Additionally allows the SDK-only per-field `requiredWhen`, which {@link toWireParameters} removes before the schema reaches the LLM.
575
+ * object feeds the store, the update function, and the dynamic context). Additionally allows the SDK-only per-field `requiredWhen`, which {@link toWireParameters} removes from the LLM tool's `parameters` (the full schema still reaches the agent via dynamic context).
576
576
  */
577
577
  declare interface FormCollectorSchema {
578
578
  type: "object";
@@ -589,17 +589,32 @@ declare interface FormField {
589
589
  }
590
590
 
591
591
  /**
592
- * A field's conditional-requiredness rule. The field becomes required exactly
593
- * when the controlling `property` matches `value` against the answers collected
594
- * so far:
592
+ * A field's conditional-requiredness rule: either a single {@link FormFieldLeafCondition}
593
+ * or a boolean combination of rules. `anyOf` is OR (true when any nested rule
594
+ * holds); `allOf` is AND (true when every nested rule holds). Both nest
595
+ * arbitrarily, so you can mix them, e.g.
596
+ * `{ anyOf: [{ property: "a", op: ">=", value: 1 }, { property: "b", value: "1" }] }`.
597
+ * An empty `anyOf` is never required; an empty `allOf` is always required.
598
+ *
599
+ * Stripped from the LLM tool's `parameters` (see {@link toWireParameters}), but the
600
+ * full schema, `requiredWhen` included, still reaches the agent via the dynamic
601
+ * context so it knows when each field becomes required.
602
+ */
603
+ declare type FormFieldCondition = FormFieldLeafCondition | {
604
+ anyOf: FormFieldCondition[];
605
+ } | {
606
+ allOf: FormFieldCondition[];
607
+ };
608
+
609
+ /**
610
+ * A single leaf comparison. The field it guards becomes required exactly when the
611
+ * controlling `property` matches `value` against the answers collected so far:
595
612
  * - a single `value`, no `op` — equality (compared as strings);
596
613
  * - a single `value` with `op` — numeric comparison (both sides coerced to numbers);
597
614
  * - an array `value`, no `op` — "one of": matches when the property equals any
598
615
  * listed value.
599
- *
600
- * SDK-only — it is stripped from the schema before it reaches the LLM tool
601
616
  */
602
- declare interface FormFieldCondition {
617
+ declare interface FormFieldLeafCondition {
603
618
  /** The controlling field whose value the rule tests. */
604
619
  property: string;
605
620
  /** Comparison operator; omitted means equality. Ignored when `value` is an array. */
@@ -620,7 +635,8 @@ declare interface FormFieldSchema {
620
635
  default?: unknown;
621
636
  /**
622
637
  * Makes the field required only while this condition holds. Drives the dynamic
623
- * `missing` list; stripped before the LLM tool — see {@link toWireParameters}.
638
+ * `missing` list; stripped from the LLM tool's `parameters` (still sent to the
639
+ * agent via dynamic context). See {@link toWireParameters}.
624
640
  */
625
641
  requiredWhen?: FormFieldCondition;
626
642
  }
package/dist/angular.d.ts CHANGED
@@ -572,7 +572,7 @@ declare interface FormCollectorDefinition<V = Record<string, unknown>> {
572
572
 
573
573
  /**
574
574
  * Shape of a form's fields. Mirrors a client function's `parameters` (so the same
575
- * object feeds the store, the update function, and the dynamic context). Additionally allows the SDK-only per-field `requiredWhen`, which {@link toWireParameters} removes before the schema reaches the LLM.
575
+ * object feeds the store, the update function, and the dynamic context). Additionally allows the SDK-only per-field `requiredWhen`, which {@link toWireParameters} removes from the LLM tool's `parameters` (the full schema still reaches the agent via dynamic context).
576
576
  */
577
577
  declare interface FormCollectorSchema {
578
578
  type: "object";
@@ -589,17 +589,32 @@ declare interface FormField {
589
589
  }
590
590
 
591
591
  /**
592
- * A field's conditional-requiredness rule. The field becomes required exactly
593
- * when the controlling `property` matches `value` against the answers collected
594
- * so far:
592
+ * A field's conditional-requiredness rule: either a single {@link FormFieldLeafCondition}
593
+ * or a boolean combination of rules. `anyOf` is OR (true when any nested rule
594
+ * holds); `allOf` is AND (true when every nested rule holds). Both nest
595
+ * arbitrarily, so you can mix them, e.g.
596
+ * `{ anyOf: [{ property: "a", op: ">=", value: 1 }, { property: "b", value: "1" }] }`.
597
+ * An empty `anyOf` is never required; an empty `allOf` is always required.
598
+ *
599
+ * Stripped from the LLM tool's `parameters` (see {@link toWireParameters}), but the
600
+ * full schema, `requiredWhen` included, still reaches the agent via the dynamic
601
+ * context so it knows when each field becomes required.
602
+ */
603
+ declare type FormFieldCondition = FormFieldLeafCondition | {
604
+ anyOf: FormFieldCondition[];
605
+ } | {
606
+ allOf: FormFieldCondition[];
607
+ };
608
+
609
+ /**
610
+ * A single leaf comparison. The field it guards becomes required exactly when the
611
+ * controlling `property` matches `value` against the answers collected so far:
595
612
  * - a single `value`, no `op` — equality (compared as strings);
596
613
  * - a single `value` with `op` — numeric comparison (both sides coerced to numbers);
597
614
  * - an array `value`, no `op` — "one of": matches when the property equals any
598
615
  * listed value.
599
- *
600
- * SDK-only — it is stripped from the schema before it reaches the LLM tool
601
616
  */
602
- declare interface FormFieldCondition {
617
+ declare interface FormFieldLeafCondition {
603
618
  /** The controlling field whose value the rule tests. */
604
619
  property: string;
605
620
  /** Comparison operator; omitted means equality. Ignored when `value` is an array. */
@@ -620,7 +635,8 @@ declare interface FormFieldSchema {
620
635
  default?: unknown;
621
636
  /**
622
637
  * Makes the field required only while this condition holds. Drives the dynamic
623
- * `missing` list; stripped before the LLM tool — see {@link toWireParameters}.
638
+ * `missing` list; stripped from the LLM tool's `parameters` (still sent to the
639
+ * agent via dynamic context). See {@link toWireParameters}.
624
640
  */
625
641
  requiredWhen?: FormFieldCondition;
626
642
  }
@@ -1 +1 @@
1
- const d=require("./createFormCollector-BHHEW6uX.cjs");var f="ago_widget_id",g="ago_last_thread",y=7200*1e3;function v(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const r=Math.floor(Math.random()*16);return(e==="x"?r:r&3|8).toString(16)})}function _(e={}){const r=e.key??f,i=e.threadKey??g,u=e.ttlMs??y,l=e.storage??(typeof localStorage<"u"?localStorage:void 0);let n=e.widgetId??"";if(!n&&l)try{n=l.getItem(r)??""}catch{}if(n||(n=v()),l)try{l.setItem(r,n)}catch{}const o=d.createStore({value:null,lastMessageAt:null},{key:i,storage:l}),c=o.get();c.value!==null&&s(c.lastMessageAt)&&o.set({value:null,lastMessageAt:null});function s(t){return t===null?!0:Number.isFinite(u)?Date.now()-t>u:!1}return{widgetId:n,key:r,getLastActiveThread(){const{value:t,lastMessageAt:a}=o.get();return t===null||s(a)?null:t},setActiveThread(t,a){const x=a==null?null:a instanceof Date?a.getTime():a;o.set({value:t,lastMessageAt:x})},clear(){o.set({value:null,lastMessageAt:null})}}}Object.defineProperty(exports,"createConversationSession",{enumerable:!0,get:function(){return _}});
1
+ const d=require("./createFormCollector-BI3DXaew.cjs");var f="ago_widget_id",g="ago_last_thread",y=7200*1e3;function v(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const r=Math.floor(Math.random()*16);return(e==="x"?r:r&3|8).toString(16)})}function _(e={}){const r=e.key??f,i=e.threadKey??g,u=e.ttlMs??y,l=e.storage??(typeof localStorage<"u"?localStorage:void 0);let n=e.widgetId??"";if(!n&&l)try{n=l.getItem(r)??""}catch{}if(n||(n=v()),l)try{l.setItem(r,n)}catch{}const o=d.createStore({value:null,lastMessageAt:null},{key:i,storage:l}),c=o.get();c.value!==null&&s(c.lastMessageAt)&&o.set({value:null,lastMessageAt:null});function s(t){return t===null?!0:Number.isFinite(u)?Date.now()-t>u:!1}return{widgetId:n,key:r,getLastActiveThread(){const{value:t,lastMessageAt:a}=o.get();return t===null||s(a)?null:t},setActiveThread(t,a){const x=a==null?null:a instanceof Date?a.getTime():a;o.set({value:t,lastMessageAt:x})},clear(){o.set({value:null,lastMessageAt:null})}}}Object.defineProperty(exports,"createConversationSession",{enumerable:!0,get:function(){return _}});
@@ -1,4 +1,4 @@
1
- import { i as d } from "./createFormCollector-QyojCH_-.js";
1
+ import { i as d } from "./createFormCollector-CFt1_QSL.js";
2
2
  var f = "ago_widget_id", g = "ago_last_thread", y = 7200 * 1e3;
3
3
  function v() {
4
4
  return typeof crypto < "u" && typeof crypto.randomUUID == "function" ? crypto.randomUUID() : "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (e) => {
@@ -0,0 +1 @@
1
+ const C=require("./AgoClient-B3-WGVne.cjs");function W(n,e){const a=e?e.storage??(typeof localStorage<"u"?localStorage:void 0):void 0;let s=n;if(e&&a)try{const m=a.getItem(e.key);m!=null&&(s=JSON.parse(m))}catch{}const i=new Set,c=()=>i.forEach(m=>m(s));function b(){if(e&&a)try{a.setItem(e.key,JSON.stringify(s))}catch{}}return{get:()=>s,set:m=>{s=m,b(),c()},subscribe:m=>(i.add(m),()=>{i.delete(m)})}}var T=/^[a-zA-Z0-9_-]{1,40}$/,x=n=>n==null||n==="";function v(n,e){if("anyOf"in n)return n.anyOf.some(c=>v(c,e));if("allOf"in n)return n.allOf.every(c=>v(c,e));const a=e[n.property];if(x(a))return!1;if(Array.isArray(n.value))return n.value.some(c=>String(a)===String(c));if(!n.op)return String(a)===String(n.value);const s=Number(a),i=Number(n.value);if(Number.isNaN(s)||Number.isNaN(i))return!1;switch(n.op){case">=":return s>=i;case"<=":return s<=i;case">":return s>i;case"<":return s<i;default:return C.logger.warn(`Unknown requiredWhen op "${n.op}"; treating as not required.`),!1}}function h(n,e){const a=n.properties??{},s=new Set;for(const c of n.required??[]){const b=a[c]?.requiredWhen;(!b||v(b,e))&&s.add(c)}for(const[c,b]of Object.entries(a))b.requiredWhen&&v(b.requiredWhen,e)&&s.add(c);const i=[...s].filter(c=>x(e[c]));return{missing:i,complete:i.length===0}}function R(n){const e={};for(const[a,s]of Object.entries(n.properties??{}))e[a]={type:s.type,...s.description!=null&&{description:s.description},...s.enum&&{enum:s.enum},...s.default!==void 0&&{default:s.default}};return{type:"object",properties:e,required:[]}}function A(n){const{name:e,description:a,schema:s}=n;if(!T.test(e))throw new Error(`Invalid form collector name "${e}": must match ${T} (it becomes the update_/submit_ function names).`);const i=typeof n.submit=="string"?n.submit?{via:"client",url:n.submit}:!1:n.submit??!1;n.autoSubmit===!0&&!i&&C.logger.warn(`FormCollector "${e}": autoSubmit is ignored because no submit target is configured.`);const c=(n.autoSubmit??!0)&&!!i,b=s.properties??{},m=t=>{const r={};for(const[u,o]of Object.entries(t)){const l=b[u];if(l)if(o==null)r[u]=o;else if(l.type==="number"||l.type==="integer"){const d=typeof o=="number"?o:Number(o);r[u]=Number.isNaN(d)?o:d}else l.type==="boolean"?r[u]=typeof o=="boolean"?o:o==="true"?!0:o==="false"?!1:!!o:r[u]=o}return r},S=n.initialValues??{},f=W({values:S,submitted:!1}),$=t=>{const r=f.get(),u={...r,values:{...r.values,...t}};return f.set(u),u};let g=null;const F=(t,r)=>{g?.emitFormEvent("form:error",{name:e,values:t,error:r})};let y=null;const I=async()=>{const{values:t}=f.get(),{complete:r,missing:u}=h(s,t);if(!r)return{ok:!1,missing:u};if(!i)return{ok:!1,error:"No submit target configured for this form collector."};const o=t;try{let l;if(i.via==="client")if("handler"in i)l=await i.handler(o);else{const d=await fetch(i.url,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify(o)});if(!d.ok){const j=`Submit failed: HTTP ${d.status}`;return F(o,j),{ok:!1,error:j}}l=await d.json().catch(()=>{})}else{if(!g)return{ok:!1,error:"Backend submit requires install(client) (or useFormCollector) first."};l=await g.submitFormCollector(e,o)}return f.set({...f.get(),submitted:!0,submitResult:l}),g?.emitFormEvent("form:submitted",{name:e,values:o,result:l}),{ok:!0,result:l}}catch(l){const d=l instanceof Error?l.message:"Submit failed";return C.logger.error(`FormCollector "${e}" submit failed:`,l),F(o,d),{ok:!1,error:d}}},N=()=>y||(y=(async()=>{try{return await I()}finally{y=null}})(),y),k=async()=>{if(!c||!i)return null;const{values:t,submitted:r}=f.get();return r||!h(s,t).complete?null:N()},O={name:`update_${e}`,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.`,parameters:R(s),handler:async t=>{const r=$(m(t)),{missing:u}=h(s,r.values),o=await k();return o?o.ok?{ok:!0,values:r.values,missing:u,submitted:!0}:{ok:!0,values:r.values,missing:u,submitted:!1,submitError:o.error}:{ok:!0,values:r.values,missing:u}}},p=[O];i&&!c&&p.push({name:`submit_${e}`,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.`,parameters:{type:"object",properties:{}},handler:()=>N()});const V="client__",q=i?`submit_${e}`:null,J=t=>{const r=t.functionName??t.toolName;if(r)return r.startsWith(V)?r.slice(8):r},_=t=>{let r={...S},u=!1;for(const o of t){const l=J(o);l===O.name?o.arguments&&(r={...r,...m(o.arguments)}):q&&l===q&&o.status?.toLowerCase()==="done"&&(u=!0)}f.set({values:r,submitted:u})},w=`form:${e}`,E=()=>{const{values:t,submitted:r}=f.get(),{missing:u,complete:o}=h(s,t),l=i?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.`;return{name:`Form: ${e}`,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(s)}. Data collected so far: ${JSON.stringify(t)}. Call update_${e} with any fields the user provides; ask only for what is still missing.${l}`,data:{values:t,missing:u,complete:o,submitted:r}}},P=t=>{_((t.messages??[]).flatMap(r=>r.toolCalls??[]))};return{name:e,store:f,functions:p,contextKey:w,contextProvider:E,install:t=>{g=t;for(const u of p)t.register(u);t.addDynamicContext(w,E);const r=f.subscribe(()=>t.notifyContextChanged());return t.on("conversation:loaded",P),()=>{r();for(const u of p)t.unregisterFunction(u.name);t.removeDynamicContext(w),t.off("conversation:loaded",P),g===t&&(g=null)}},hydrate:_,submit:N,getValues:()=>f.get().values,setValues:t=>{$(m(t)),k()},reset:()=>{f.set({values:S,submitted:!1})},getStatus:()=>h(s,f.get().values)}}async function B(n,e){const a=await n.getFormCollector(e.name);return A({name:a.name,description:e.description??a.description,schema:e.schema??a.schema,submit:e.submit??a.submit,initialValues:e.initialValues,autoSubmit:e.autoSubmit??a.autoSubmit})}Object.defineProperty(exports,"createFormCollector",{enumerable:!0,get:function(){return A}});Object.defineProperty(exports,"createStore",{enumerable:!0,get:function(){return W}});Object.defineProperty(exports,"deriveFormStatus",{enumerable:!0,get:function(){return h}});Object.defineProperty(exports,"loadFormCollector",{enumerable:!0,get:function(){return B}});
@@ -1,70 +1,72 @@
1
- import { c as w } from "./AgoClient-QcTtrN7u.js";
2
- function A(o, e) {
1
+ import { c as $ } from "./AgoClient-QcTtrN7u.js";
2
+ function A(s, e) {
3
3
  const a = e ? e.storage ?? (typeof localStorage < "u" ? localStorage : void 0) : void 0;
4
- let s = o;
4
+ let n = s;
5
5
  if (e && a) try {
6
6
  const m = a.getItem(e.key);
7
- m != null && (s = JSON.parse(m));
7
+ m != null && (n = JSON.parse(m));
8
8
  } catch {
9
9
  }
10
- const i = /* @__PURE__ */ new Set(), c = () => i.forEach((m) => m(s));
10
+ const o = /* @__PURE__ */ new Set(), c = () => o.forEach((m) => m(n));
11
11
  function b() {
12
12
  if (e && a) try {
13
- a.setItem(e.key, JSON.stringify(s));
13
+ a.setItem(e.key, JSON.stringify(n));
14
14
  } catch {
15
15
  }
16
16
  }
17
17
  return {
18
- get: () => s,
18
+ get: () => n,
19
19
  set: (m) => {
20
- s = m, b(), c();
20
+ n = m, b(), c();
21
21
  },
22
- subscribe: (m) => (i.add(m), () => {
23
- i.delete(m);
22
+ subscribe: (m) => (o.add(m), () => {
23
+ o.delete(m);
24
24
  })
25
25
  };
26
26
  }
27
- var x = /^[a-zA-Z0-9_-]{1,40}$/, W = (o) => o == null || o === "";
28
- function P(o, e) {
29
- const a = e[o.property];
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];
30
32
  if (W(a)) return !1;
31
- if (Array.isArray(o.value)) return o.value.some((c) => String(a) === String(c));
32
- if (!o.op) return String(a) === String(o.value);
33
- const s = Number(a), i = Number(o.value);
34
- if (Number.isNaN(s) || Number.isNaN(i)) return !1;
35
- switch (o.op) {
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) {
36
38
  case ">=":
37
- return s >= i;
39
+ return n >= o;
38
40
  case "<=":
39
- return s <= i;
41
+ return n <= o;
40
42
  case ">":
41
- return s > i;
43
+ return n > o;
42
44
  case "<":
43
- return s < i;
45
+ return n < o;
44
46
  default:
45
- return w.warn(`Unknown requiredWhen op "${o.op}"; treating as not required.`), !1;
47
+ return $.warn(`Unknown requiredWhen op "${s.op}"; treating as not required.`), !1;
46
48
  }
47
49
  }
48
- function g(o, e) {
49
- const a = o.properties ?? {}, s = /* @__PURE__ */ new Set();
50
- for (const c of o.required ?? []) {
50
+ function y(s, e) {
51
+ const a = s.properties ?? {}, n = /* @__PURE__ */ new Set();
52
+ for (const c of s.required ?? []) {
51
53
  const b = a[c]?.requiredWhen;
52
- (!b || P(b, e)) && s.add(c);
54
+ (!b || v(b, e)) && n.add(c);
53
55
  }
54
- for (const [c, b] of Object.entries(a)) b.requiredWhen && P(b.requiredWhen, e) && s.add(c);
55
- const i = [...s].filter((c) => W(e[c]));
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
58
  return {
57
- missing: i,
58
- complete: i.length === 0
59
+ missing: o,
60
+ complete: o.length === 0
59
61
  };
60
62
  }
61
- function J(o) {
63
+ function J(s) {
62
64
  const e = {};
63
- for (const [a, s] of Object.entries(o.properties ?? {})) e[a] = {
64
- type: s.type,
65
- ...s.description != null && { description: s.description },
66
- ...s.enum && { enum: s.enum },
67
- ...s.default !== void 0 && { default: s.default }
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 }
68
70
  };
69
71
  return {
70
72
  type: "object",
@@ -72,30 +74,30 @@ function J(o) {
72
74
  required: []
73
75
  };
74
76
  }
75
- function R(o) {
76
- const { name: e, description: a, schema: s } = o;
77
- if (!x.test(e)) throw new Error(`Invalid form collector name "${e}": must match ${x} (it becomes the update_/submit_ function names).`);
78
- const i = typeof o.submit == "string" ? o.submit ? {
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 ? {
79
81
  via: "client",
80
- url: o.submit
81
- } : !1 : o.submit ?? !1;
82
- o.autoSubmit === !0 && !i && w.warn(`FormCollector "${e}": autoSubmit is ignored because no submit target is configured.`);
83
- const c = (o.autoSubmit ?? !0) && !!i, b = s.properties ?? {}, m = (t) => {
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) => {
84
86
  const r = {};
85
- for (const [u, n] of Object.entries(t)) {
87
+ for (const [u, i] of Object.entries(t)) {
86
88
  const l = b[u];
87
89
  if (l)
88
- if (n == null) r[u] = n;
90
+ if (i == null) r[u] = i;
89
91
  else if (l.type === "number" || l.type === "integer") {
90
- const d = typeof n == "number" ? n : Number(n);
91
- r[u] = Number.isNaN(d) ? n : d;
92
- } else l.type === "boolean" ? r[u] = typeof n == "boolean" ? n : n === "true" ? !0 : n === "false" ? !1 : !!n : r[u] = n;
92
+ const d = typeof i == "number" ? i : Number(i);
93
+ r[u] = Number.isNaN(d) ? i : d;
94
+ } else l.type === "boolean" ? r[u] = typeof i == "boolean" ? i : i === "true" ? !0 : i === "false" ? !1 : !!i : r[u] = i;
93
95
  }
94
96
  return r;
95
- }, v = o.initialValues ?? {}, f = A({
96
- values: v,
97
+ }, S = s.initialValues ?? {}, f = A({
98
+ values: S,
97
99
  submitted: !1
98
- }), $ = (t) => {
100
+ }), C = (t) => {
99
101
  const r = f.get(), u = {
100
102
  ...r,
101
103
  values: {
@@ -106,7 +108,7 @@ function R(o) {
106
108
  return f.set(u), u;
107
109
  };
108
110
  let h = null;
109
- const C = (t, r) => {
111
+ const k = (t, r) => {
110
112
  h?.emitFormEvent("form:error", {
111
113
  name: e,
112
114
  values: t,
@@ -115,31 +117,31 @@ function R(o) {
115
117
  };
116
118
  let p = null;
117
119
  const j = async () => {
118
- const { values: t } = f.get(), { complete: r, missing: u } = g(s, t);
120
+ const { values: t } = f.get(), { complete: r, missing: u } = y(n, t);
119
121
  if (!r) return {
120
122
  ok: !1,
121
123
  missing: u
122
124
  };
123
- if (!i) return {
125
+ if (!o) return {
124
126
  ok: !1,
125
127
  error: "No submit target configured for this form collector."
126
128
  };
127
- const n = t;
129
+ const i = t;
128
130
  try {
129
131
  let l;
130
- if (i.via === "client") if ("handler" in i) l = await i.handler(n);
132
+ if (o.via === "client") if ("handler" in o) l = await o.handler(i);
131
133
  else {
132
- const d = await fetch(i.url, {
134
+ const d = await fetch(o.url, {
133
135
  method: "POST",
134
136
  headers: { "Content-Type": "application/json" },
135
137
  credentials: "include",
136
- body: JSON.stringify(n)
138
+ body: JSON.stringify(i)
137
139
  });
138
140
  if (!d.ok) {
139
- const O = `Submit failed: HTTP ${d.status}`;
140
- return C(n, O), {
141
+ const x = `Submit failed: HTTP ${d.status}`;
142
+ return k(i, x), {
141
143
  ok: !1,
142
- error: O
144
+ error: x
143
145
  };
144
146
  }
145
147
  l = await d.json().catch(() => {
@@ -150,7 +152,7 @@ function R(o) {
150
152
  ok: !1,
151
153
  error: "Backend submit requires install(client) (or useFormCollector) first."
152
154
  };
153
- l = await h.submitFormCollector(e, n);
155
+ l = await h.submitFormCollector(e, i);
154
156
  }
155
157
  return f.set({
156
158
  ...f.get(),
@@ -158,7 +160,7 @@ function R(o) {
158
160
  submitResult: l
159
161
  }), h?.emitFormEvent("form:submitted", {
160
162
  name: e,
161
- values: n,
163
+ values: i,
162
164
  result: l
163
165
  }), {
164
166
  ok: !0,
@@ -166,28 +168,28 @@ function R(o) {
166
168
  };
167
169
  } catch (l) {
168
170
  const d = l instanceof Error ? l.message : "Submit failed";
169
- return w.error(`FormCollector "${e}" submit failed:`, l), C(n, d), {
171
+ return $.error(`FormCollector "${e}" submit failed:`, l), k(i, d), {
170
172
  ok: !1,
171
173
  error: d
172
174
  };
173
175
  }
174
- }, S = () => p || (p = (async () => {
176
+ }, N = () => p || (p = (async () => {
175
177
  try {
176
178
  return await j();
177
179
  } finally {
178
180
  p = null;
179
181
  }
180
- })(), p), k = async () => {
181
- if (!c || !i) return null;
182
+ })(), p), F = async () => {
183
+ if (!c || !o) return null;
182
184
  const { values: t, submitted: r } = f.get();
183
- return r || !g(s, t).complete ? null : S();
184
- }, F = {
185
+ return r || !y(n, t).complete ? null : N();
186
+ }, q = {
185
187
  name: `update_${e}`,
186
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.`,
187
- parameters: J(s),
189
+ parameters: J(n),
188
190
  handler: async (t) => {
189
- const r = $(m(t)), { missing: u } = g(s, r.values), n = await k();
190
- return n ? n.ok ? {
191
+ const r = C(m(t)), { missing: u } = y(n, r.values), i = await F();
192
+ return i ? i.ok ? {
191
193
  ok: !0,
192
194
  values: r.values,
193
195
  missing: u,
@@ -197,49 +199,49 @@ function R(o) {
197
199
  values: r.values,
198
200
  missing: u,
199
201
  submitted: !1,
200
- submitError: n.error
202
+ submitError: i.error
201
203
  } : {
202
204
  ok: !0,
203
205
  values: r.values,
204
206
  missing: u
205
207
  };
206
208
  }
207
- }, y = [F];
208
- i && !c && y.push({
209
+ }, g = [q];
210
+ o && !c && g.push({
209
211
  name: `submit_${e}`,
210
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.`,
211
213
  parameters: {
212
214
  type: "object",
213
215
  properties: {}
214
216
  },
215
- handler: () => S()
217
+ handler: () => N()
216
218
  });
217
- const I = "client__", q = i ? `submit_${e}` : null, V = (t) => {
219
+ const I = "client__", O = o ? `submit_${e}` : null, V = (t) => {
218
220
  const r = t.functionName ?? t.toolName;
219
221
  if (r)
220
222
  return r.startsWith(I) ? r.slice(8) : r;
221
223
  }, _ = (t) => {
222
- let r = { ...v }, u = !1;
223
- for (const n of t) {
224
- const l = V(n);
225
- l === F.name ? n.arguments && (r = {
224
+ let r = { ...S }, u = !1;
225
+ for (const i of t) {
226
+ const l = V(i);
227
+ l === q.name ? i.arguments && (r = {
226
228
  ...r,
227
- ...m(n.arguments)
228
- }) : q && l === q && n.status?.toLowerCase() === "done" && (u = !0);
229
+ ...m(i.arguments)
230
+ }) : O && l === O && i.status?.toLowerCase() === "done" && (u = !0);
229
231
  }
230
232
  f.set({
231
233
  values: r,
232
234
  submitted: u
233
235
  });
234
- }, N = `form:${e}`, E = () => {
235
- const { values: t, submitted: r } = f.get(), { missing: u, complete: n } = g(s, t), l = i ? 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
+ }, 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
238
  return {
237
239
  name: `Form: ${e}`,
238
- description: `${d} ${a} There are conditionnals questions/values to collected, you can find this information with the requiredWhen field in the form. The form schema is the following: ${JSON.stringify(s)}. Data collected so far: ${JSON.stringify(t)}. Call update_${e} with any fields the user provides; ask only for what is still missing.${l}`,
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}`,
239
241
  data: {
240
242
  values: t,
241
243
  missing: u,
242
- complete: n,
244
+ complete: i,
243
245
  submitted: r
244
246
  }
245
247
  };
@@ -249,37 +251,37 @@ function R(o) {
249
251
  return {
250
252
  name: e,
251
253
  store: f,
252
- functions: y,
253
- contextKey: N,
254
+ functions: g,
255
+ contextKey: w,
254
256
  contextProvider: E,
255
257
  install: (t) => {
256
258
  h = t;
257
- for (const u of y) t.register(u);
258
- t.addDynamicContext(N, E);
259
+ for (const u of g) t.register(u);
260
+ t.addDynamicContext(w, E);
259
261
  const r = f.subscribe(() => t.notifyContextChanged());
260
262
  return t.on("conversation:loaded", T), () => {
261
263
  r();
262
- for (const u of y) t.unregisterFunction(u.name);
263
- t.removeDynamicContext(N), t.off("conversation:loaded", T), h === t && (h = null);
264
+ for (const u of g) t.unregisterFunction(u.name);
265
+ t.removeDynamicContext(w), t.off("conversation:loaded", T), h === t && (h = null);
264
266
  };
265
267
  },
266
268
  hydrate: _,
267
- submit: S,
269
+ submit: N,
268
270
  getValues: () => f.get().values,
269
271
  setValues: (t) => {
270
- $(m(t)), k();
272
+ C(m(t)), F();
271
273
  },
272
274
  reset: () => {
273
275
  f.set({
274
- values: v,
276
+ values: S,
275
277
  submitted: !1
276
278
  });
277
279
  },
278
- getStatus: () => g(s, f.get().values)
280
+ getStatus: () => y(n, f.get().values)
279
281
  };
280
282
  }
281
- async function L(o, e) {
282
- const a = await o.getFormCollector(e.name);
283
+ async function L(s, e) {
284
+ const a = await s.getFormCollector(e.name);
283
285
  return R({
284
286
  name: a.name,
285
287
  description: e.description ?? a.description,
@@ -291,7 +293,7 @@ async function L(o, e) {
291
293
  }
292
294
  export {
293
295
  A as i,
294
- g as n,
296
+ y as n,
295
297
  L as r,
296
298
  R as t
297
299
  };
package/dist/devtools.cjs CHANGED
@@ -1,26 +1,26 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});var v,x,p,h=()=>({});function s(){v&&(v.textContent=JSON.stringify(h(),null,2))}function m(e,t,d){if(!e)return;const n=document.createElement("div");n.className=`dev-log-line dev-log-${d}`,n.textContent=`${new Date().toLocaleTimeString()} ${t}`,e.appendChild(n),e.scrollTop=e.scrollHeight}function c(e,t){m(x,e,t)}function y(e){let t;return e.type?t=e.type:e.content!==void 0?t="content":e.full_content!==void 0?t="full_content":e.status?t=`status:${e.status}`:t="event",`${t} ${JSON.stringify(e)}`}var S="ago_dev_panel_collapsed",E="ago_dev_events_collapsed";function g(e,t,d,n){e.classList.toggle("collapsed",t);const o=e.querySelector(".dev-toggle");o&&(o.textContent=t?"▢":"—",o.title=t?`Expand ${n}`:`Collapse ${n}`,o.setAttribute("aria-expanded",String(!t)));try{localStorage.setItem(d,t?"1":"0")}catch{}}function u(e,t,d){e.querySelector(".dev-toggle")?.addEventListener("click",o=>{o.stopPropagation(),g(e,!e.classList.contains("collapsed"),t,d)}),e.addEventListener("click",()=>{e.classList.contains("collapsed")&&g(e,!1,t,d)});let n=!1;try{n=localStorage.getItem(t)==="1"}catch{}g(e,n,t,d)}function C(e){const{client:t,target:d}=e;h=()=>{const a=t.getContextSnapshot()?.entries??{};return Object.fromEntries(Object.entries(a).map(([l,{name:r,data:b}])=>[l,{name:r,data:b}]))},w();const n=typeof d=="string"?document.querySelector(d):d instanceof Element?d:document.body,o=document.createElement("aside");o.id="ago-dev-panel",o.className="ago-dev-card",(n??document.body).appendChild(o),o.innerHTML=`
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function E(e,t,d){if(!e)return;const a=document.createElement("div");a.className=`dev-log-line dev-log-${d}`,a.textContent=`${new Date().toLocaleTimeString()} ${t}`,e.appendChild(a),e.scrollTop=e.scrollHeight}function k(e){let t;return e.type?t=e.type:e.content!==void 0?t="content":e.full_content!==void 0?t="full_content":e.status?t=`status:${e.status}`:t="event",`${t} ${JSON.stringify(e)}`}var T="ago_dev_panel_collapsed",N="ago_dev_events_collapsed";function u(e,t,d,a){e.classList.toggle("collapsed",t);const l=e.querySelector(".dev-toggle");l&&(l.textContent=t?"▢":"—",l.title=t?`Expand ${a}`:`Collapse ${a}`,l.setAttribute("aria-expanded",String(!t)));try{localStorage.setItem(d,t?"1":"0")}catch{}}function $(e,t,d){e.querySelector(".dev-toggle")?.addEventListener("click",l=>{l.stopPropagation(),u(e,!e.classList.contains("collapsed"),t,d)}),e.addEventListener("click",()=>{e.classList.contains("collapsed")&&u(e,!1,t,d)});let a=!1;try{a=localStorage.getItem(t)==="1"}catch{}u(e,a,t,d)}function _(e){const{client:t,target:d,label:a,side:l="right"}=e,f=document.querySelectorAll("#ago-dev-panel").length,x=f===0?"":`-${f+1}`,h=document.querySelectorAll(`#ago-dev-panel[data-ago-dev-side="${l}"]`).length,r=h>0?`${16+h*376}px`:"";let g=null,m=null,b=null;const L=()=>{const o=t.getContextSnapshot()?.entries??{};return Object.fromEntries(Object.entries(o).map(([n,{name:p,data:O}])=>[n,{name:p,data:O}]))},c=()=>{g&&(g.textContent=JSON.stringify(L(),null,2))},v=(o,n)=>{E(m,o,n)};q();const y=typeof d=="string"?document.querySelector(d):d instanceof Element?d:document.body,S=o=>{o.dataset.agoDevSide=l,l==="left"?(o.style.right="auto",o.style.left=r||"16px"):r&&(o.style.right=r)},s=document.createElement("aside");s.id="ago-dev-panel",s.className="ago-dev-card",S(s),(y??document.body).appendChild(s);const w=(t.getRegisteredFunctions?.()??[]).map(o=>o.name).join(", ")||"—";s.innerHTML=`
2
2
  <div class="dev-head">
3
3
  <span class="dev-badge">DEV</span>
4
- <span class="dev-title">DEV TOOLS · client-side function state — not for production</span>
4
+ <span class="dev-title">${a?`DEV TOOLS · ${a}`:"DEV TOOLS · client-side function state — not for production"}</span>
5
5
  <button type="button" class="dev-toggle" aria-label="Toggle dev tools">—</button>
6
6
  </div>
7
7
  <div class="dev-body">
8
- <div class="dev-fns">Registered functions: <code>${(t.getRegisteredFunctions?.()??[]).map(a=>a.name).join(", ")||"—"}</code></div>
8
+ <div class="dev-fns">Registered functions: <code>${w}</code></div>
9
9
  <div class="dev-section-label">JSON object (built by the agent)</div>
10
10
  <pre class="dev-state" id="ago-dev-state"></pre>
11
11
  <div class="dev-section-label">Function calls</div>
12
12
  <div class="dev-log" id="ago-dev-log"></div>
13
13
  </div>
14
- `,v=o.querySelector("#ago-dev-state"),x=o.querySelector("#ago-dev-log"),u(o,S,"dev tools");const i=document.createElement("aside");i.id="ago-dev-events",i.className="ago-dev-card",(n??document.body).appendChild(i),i.innerHTML=`
14
+ `,g=s.querySelector(".dev-state"),m=s.querySelector(".dev-log"),$(s,T+x,"dev tools");const i=document.createElement("aside");i.id="ago-dev-events",i.className="ago-dev-card",S(i),(y??document.body).appendChild(i),i.innerHTML=`
15
15
  <div class="dev-head">
16
16
  <span class="dev-badge">SSE</span>
17
- <span class="dev-title">SSE EVENT LOG · raw stream messages</span>
17
+ <span class="dev-title">${a?`SSE EVENT LOG · ${a}`:"SSE EVENT LOG · raw stream messages"}</span>
18
18
  <button type="button" class="dev-toggle" aria-label="Toggle SSE event log">—</button>
19
19
  </div>
20
20
  <div class="dev-body">
21
21
  <div class="dev-log" id="ago-dev-event-log"></div>
22
22
  </div>
23
- `,p=i.querySelector("#ago-dev-event-log"),u(i,E,"SSE event log"),t.on("stream:message",a=>{m(p,y(a),"event")}),t.on("function:invoke",({functionName:a,arguments:l})=>{c(`→ ${a}(${JSON.stringify(l??{})})`,"invoke")}),t.on("function:result",({result:a,error:l})=>{c(l?`✗ error: ${l}`:`← ${JSON.stringify(a)}`,l?"error":"result"),s()}),t.on("context:changed",s),t.on("conversation:loaded",a=>{const l=(a.messages??[]).flatMap(r=>r.toolCalls??[]);c(`⟳ hydrated "${a.title||a.id}" — replayed ${l.length} tool call${l.length===1?"":"s"}`,"hydrate"),s()}),s()}var f="ago-dev-panel-styles";function w(){if(document.getElementById(f))return;const e=document.createElement("style");e.id=f,e.textContent=L,document.head.appendChild(e)}var L=`
23
+ `,b=i.querySelector(".dev-log"),$(i,N+x,"SSE event log"),t.on("stream:message",o=>{E(b,k(o),"event")}),t.on("function:invoke",({functionName:o,arguments:n})=>{v(`→ ${o}(${JSON.stringify(n??{})})`,"invoke")}),t.on("function:result",({result:o,error:n})=>{v(n?`✗ error: ${n}`:`← ${JSON.stringify(o)}`,n?"error":"result"),c()}),t.on("context:changed",c),t.on("conversation:loaded",o=>{const n=(o.messages??[]).flatMap(p=>p.toolCalls??[]);v(`⟳ hydrated "${o.title||o.id}" — replayed ${n.length} tool call${n.length===1?"":"s"}`,"hydrate"),c()}),c()}var C="ago-dev-panel-styles";function q(){if(document.getElementById(C))return;const e=document.createElement("style");e.id=C,e.textContent=z,document.head.appendChild(e)}var z=`
24
24
  .ago-dev-card {
25
25
  position: fixed;
26
26
  right: 16px;
@@ -120,4 +120,4 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});var v,x,p,h=(
120
120
  .ago-dev-card .dev-log-error { color: #fca5a5; }
121
121
  .ago-dev-card .dev-log-hydrate { color: #d8b4fe; }
122
122
  .ago-dev-card .dev-log-event { color: #7c8a99; }
123
- `;exports.initDevPanel=C;
123
+ `;exports.initDevPanel=_;