@useago/sdk 0.4.4 → 0.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/widget.d.ts CHANGED
@@ -516,11 +516,14 @@ export declare interface CreateFormCollectorOptions<V = Record<string, unknown>>
516
516
  declare type DynamicContextProvider = () => ContextEntry | null | undefined;
517
517
 
518
518
  /**
519
- * JSON-schema shape of a form's fields — identical to a client function's
520
- * `parameters`, so one schema feeds the store, the update function, and the
521
- * dynamic context with no second type.
519
+ * Shape of a form's fields. Mirrors a client function's `parameters` (so the same
520
+ * 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.
522
521
  */
523
- declare type FormCollectorSchema = ClientFunctionSchema["parameters"];
522
+ declare interface FormCollectorSchema {
523
+ type: "object";
524
+ properties: Record<string, FormFieldSchema>;
525
+ required?: string[];
526
+ }
524
527
 
525
528
  declare interface FormField {
526
529
  type: "string" | "number" | "boolean" | "array";
@@ -530,6 +533,43 @@ declare interface FormField {
530
533
  default?: unknown;
531
534
  }
532
535
 
536
+ /**
537
+ * A field's conditional-requiredness rule. The field becomes required exactly
538
+ * when the controlling `property` matches `value` against the answers collected
539
+ * so far:
540
+ * - a single `value`, no `op` — equality (compared as strings);
541
+ * - a single `value` with `op` — numeric comparison (both sides coerced to numbers);
542
+ * - an array `value`, no `op` — "one of": matches when the property equals any
543
+ * listed value.
544
+ *
545
+ * SDK-only — it is stripped from the schema before it reaches the LLM tool
546
+ */
547
+ declare interface FormFieldCondition {
548
+ /** The controlling field whose value the rule tests. */
549
+ property: string;
550
+ /** Comparison operator; omitted means equality. Ignored when `value` is an array. */
551
+ op?: ">=" | "<=" | ">" | "<";
552
+ /**
553
+ * The value(s) to test the controlling field against. A single value is an
554
+ * equality (or, with `op`, a numeric comparison); an array is an "is one of"
555
+ * membership test, e.g. `value: ["1", "2", "3"]`.
556
+ */
557
+ value: string | number | Array<string | number>;
558
+ }
559
+
560
+ /** A single field's schema: the wire-legal keys plus the SDK-only `requiredWhen`. */
561
+ declare interface FormFieldSchema {
562
+ type: string;
563
+ description?: string;
564
+ enum?: string[];
565
+ default?: unknown;
566
+ /**
567
+ * Makes the field required only while this condition holds. Drives the dynamic
568
+ * `missing` list; stripped before the LLM tool — see {@link toWireParameters}.
569
+ */
570
+ requiredWhen?: FormFieldCondition;
571
+ }
572
+
533
573
  /**
534
574
  * Form schema for tool calls requiring user input
535
575
  */
package/dist/widget.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { t as ge } from "./AgoClient-rvTz2thR.js";
2
- import { t as ue } from "./createFormCollector-D4-yZh1i.js";
3
- import { t as xe } from "./createConversationSession-XkiOGAUT.js";
2
+ import { t as ue } from "./createFormCollector-Cujr7hW4.js";
3
+ import { t as xe } from "./createConversationSession-DbuVzvoX.js";
4
4
  var oe = 'var(--ago-font, "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif)', Z = {
5
5
  font: "--ago-font",
6
6
  radius: "--ago-radius",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@useago/sdk",
3
- "version": "0.4.4",
3
+ "version": "0.4.5",
4
4
  "description": "JavaScript SDK for AGO Chat integration",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -1,207 +0,0 @@
1
- import { s as E } from "./AgoClient-rvTz2thR.js";
2
- function T(i, r) {
3
- const f = r ? r.storage ?? (typeof localStorage < "u" ? localStorage : void 0) : void 0;
4
- let a = i;
5
- if (r && f) try {
6
- const u = f.getItem(r.key);
7
- u != null && (a = JSON.parse(u));
8
- } catch {
9
- }
10
- const l = /* @__PURE__ */ new Set(), g = () => l.forEach((u) => u(a));
11
- function d() {
12
- if (r && f) try {
13
- f.setItem(r.key, JSON.stringify(a));
14
- } catch {
15
- }
16
- }
17
- return {
18
- get: () => a,
19
- set: (u) => {
20
- a = u, d(), g();
21
- },
22
- subscribe: (u) => (l.add(u), () => {
23
- l.delete(u);
24
- })
25
- };
26
- }
27
- var k = /^[a-zA-Z0-9_-]{1,40}$/, I = (i) => i == null || i === "";
28
- function y(i, r) {
29
- const f = (i.required ?? []).filter((a) => I(r[a]));
30
- return {
31
- missing: f,
32
- complete: f.length === 0
33
- };
34
- }
35
- function q(i) {
36
- const { name: r, description: f, schema: a } = i;
37
- if (!k.test(r)) throw new Error(`Invalid form collector name "${r}": must match ${k} (it becomes the update_/submit_ function names).`);
38
- const l = typeof i.submit == "string" ? i.submit ? {
39
- via: "client",
40
- url: i.submit
41
- } : !1 : i.submit ?? !1, g = a.properties ?? {}, d = (e) => {
42
- const t = {};
43
- for (const [o, s] of Object.entries(e)) {
44
- const n = g[o];
45
- if (n)
46
- if (s == null) t[o] = s;
47
- else if (n.type === "number" || n.type === "integer") {
48
- const m = typeof s == "number" ? s : Number(s);
49
- t[o] = Number.isNaN(m) ? s : m;
50
- } else n.type === "boolean" ? t[o] = typeof s == "boolean" ? s : s === "true" ? !0 : s === "false" ? !1 : !!s : t[o] = s;
51
- }
52
- return t;
53
- }, u = i.initialValues ?? {}, c = T({
54
- values: u,
55
- submitted: !1
56
- }), v = (e) => {
57
- const t = c.get(), o = {
58
- ...t,
59
- values: {
60
- ...t.values,
61
- ...e
62
- }
63
- };
64
- return c.set(o), o;
65
- };
66
- let b = null;
67
- const C = async () => {
68
- const { values: e } = c.get(), { complete: t, missing: o } = y(a, e);
69
- if (!t) return {
70
- ok: !1,
71
- missing: o
72
- };
73
- if (!l) return {
74
- ok: !1,
75
- error: "No submit target configured for this form collector."
76
- };
77
- const s = e;
78
- try {
79
- let n;
80
- if (l.via === "client") if ("handler" in l) n = await l.handler(s);
81
- else {
82
- const m = await fetch(l.url, {
83
- method: "POST",
84
- headers: { "Content-Type": "application/json" },
85
- credentials: "include",
86
- body: JSON.stringify(s)
87
- });
88
- if (!m.ok) return {
89
- ok: !1,
90
- error: `Submit failed: HTTP ${m.status}`
91
- };
92
- n = await m.json().catch(() => {
93
- });
94
- }
95
- else {
96
- if (!b) return {
97
- ok: !1,
98
- error: "Backend submit requires install(client) (or useFormCollector) first."
99
- };
100
- n = await b.submitFormCollector(l.destination, s);
101
- }
102
- return c.set({
103
- ...c.get(),
104
- submitted: !0
105
- }), {
106
- ok: !0,
107
- result: n
108
- };
109
- } catch (n) {
110
- const m = n instanceof Error ? n.message : "Submit failed";
111
- return E.error(`FormCollector "${r}" submit failed:`, n), {
112
- ok: !1,
113
- error: m
114
- };
115
- }
116
- }, N = {
117
- name: `update_${r}`,
118
- description: `Fill or update fields of the "${r}" form. ${f} 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.`,
119
- parameters: {
120
- ...a,
121
- required: []
122
- },
123
- handler: (e) => {
124
- const t = v(d(e)), { missing: o } = y(a, t.values);
125
- return {
126
- ok: !0,
127
- values: t.values,
128
- missing: o
129
- };
130
- }
131
- }, h = [N];
132
- l && h.push({
133
- name: `submit_${r}`,
134
- description: `Submit the completed "${r}" 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.`,
135
- parameters: {
136
- type: "object",
137
- properties: {}
138
- },
139
- handler: () => C()
140
- });
141
- const _ = "client__", $ = l ? `submit_${r}` : null, x = (e) => {
142
- const t = e.functionName ?? e.toolName;
143
- if (t)
144
- return t.startsWith(_) ? t.slice(8) : t;
145
- }, w = (e) => {
146
- let t = { ...u }, o = !1;
147
- for (const s of e) {
148
- const n = x(s);
149
- n === N.name ? s.arguments && (t = {
150
- ...t,
151
- ...d(s.arguments)
152
- }) : $ && n === $ && s.status?.toLowerCase() === "done" && (o = !0);
153
- }
154
- c.set({
155
- values: t,
156
- submitted: o
157
- });
158
- }, p = `form:${r}`, F = () => {
159
- const { values: e, submitted: t } = c.get(), { missing: o } = y(a, e), s = l ? ` When nothing is missing and the user confirms, call submit_${r}.` : "";
160
- return {
161
- name: `Form: ${r}`,
162
- description: `Form being filled conversationally. ${f} Call update_${r} with any fields the user provides; ask only for what is still missing.${s}`,
163
- data: {
164
- values: e,
165
- missing: o,
166
- submitted: t
167
- }
168
- };
169
- }, S = (e) => {
170
- w((e.messages ?? []).flatMap((t) => t.toolCalls ?? []));
171
- };
172
- return {
173
- name: r,
174
- store: c,
175
- functions: h,
176
- contextKey: p,
177
- contextProvider: F,
178
- install: (e) => {
179
- b = e;
180
- for (const o of h) e.register(o);
181
- e.addDynamicContext(p, F);
182
- const t = c.subscribe(() => e.notifyContextChanged());
183
- return e.on("conversation:loaded", S), () => {
184
- t();
185
- for (const o of h) e.unregisterFunction(o.name);
186
- e.removeDynamicContext(p), e.off("conversation:loaded", S), b === e && (b = null);
187
- };
188
- },
189
- hydrate: w,
190
- submit: C,
191
- getValues: () => c.get().values,
192
- setValues: (e) => {
193
- v(d(e));
194
- },
195
- reset: () => {
196
- c.set({
197
- values: u,
198
- submitted: !1
199
- });
200
- }
201
- };
202
- }
203
- export {
204
- y as n,
205
- T as r,
206
- q as t
207
- };
@@ -1 +0,0 @@
1
- const E=require("./AgoClient-D4EL6nva.cjs");function k(i,s){const f=s?s.storage??(typeof localStorage<"u"?localStorage:void 0):void 0;let a=i;if(s&&f)try{const u=f.getItem(s.key);u!=null&&(a=JSON.parse(u))}catch{}const l=new Set,y=()=>l.forEach(u=>u(a));function d(){if(s&&f)try{f.setItem(s.key,JSON.stringify(a))}catch{}}return{get:()=>a,set:u=>{a=u,d(),y()},subscribe:u=>(l.add(u),()=>{l.delete(u)})}}var _=/^[a-zA-Z0-9_-]{1,40}$/,T=i=>i==null||i==="";function h(i,s){const f=(i.required??[]).filter(a=>T(s[a]));return{missing:f,complete:f.length===0}}function j(i){const{name:s,description:f,schema:a}=i;if(!_.test(s))throw new Error(`Invalid form collector name "${s}": must match ${_} (it becomes the update_/submit_ function names).`);const l=typeof i.submit=="string"?i.submit?{via:"client",url:i.submit}:!1:i.submit??!1,y=a.properties??{},d=e=>{const t={};for(const[n,r]of Object.entries(e)){const o=y[n];if(o)if(r==null)t[n]=r;else if(o.type==="number"||o.type==="integer"){const m=typeof r=="number"?r:Number(r);t[n]=Number.isNaN(m)?r:m}else o.type==="boolean"?t[n]=typeof r=="boolean"?r:r==="true"?!0:r==="false"?!1:!!r:t[n]=r}return t},u=i.initialValues??{},c=k({values:u,submitted:!1}),v=e=>{const t=c.get(),n={...t,values:{...t.values,...e}};return c.set(n),n};let b=null;const C=async()=>{const{values:e}=c.get(),{complete:t,missing:n}=h(a,e);if(!t)return{ok:!1,missing:n};if(!l)return{ok:!1,error:"No submit target configured for this form collector."};const r=e;try{let o;if(l.via==="client")if("handler"in l)o=await l.handler(r);else{const m=await fetch(l.url,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify(r)});if(!m.ok)return{ok:!1,error:`Submit failed: HTTP ${m.status}`};o=await m.json().catch(()=>{})}else{if(!b)return{ok:!1,error:"Backend submit requires install(client) (or useFormCollector) first."};o=await b.submitFormCollector(l.destination,r)}return c.set({...c.get(),submitted:!0}),{ok:!0,result:o}}catch(o){const m=o instanceof Error?o.message:"Submit failed";return E.logger.error(`FormCollector "${s}" submit failed:`,o),{ok:!1,error:m}}},N={name:`update_${s}`,description:`Fill or update fields of the "${s}" form. ${f} 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:{...a,required:[]},handler:e=>{const t=v(d(e)),{missing:n}=h(a,t.values);return{ok:!0,values:t.values,missing:n}}},g=[N];l&&g.push({name:`submit_${s}`,description:`Submit the completed "${s}" 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:()=>C()});const O="client__",$=l?`submit_${s}`:null,P=e=>{const t=e.functionName??e.toolName;if(t)return t.startsWith(O)?t.slice(8):t},F=e=>{let t={...u},n=!1;for(const r of e){const o=P(r);o===N.name?r.arguments&&(t={...t,...d(r.arguments)}):$&&o===$&&r.status?.toLowerCase()==="done"&&(n=!0)}c.set({values:t,submitted:n})},p=`form:${s}`,S=()=>{const{values:e,submitted:t}=c.get(),{missing:n}=h(a,e),r=l?` When nothing is missing and the user confirms, call submit_${s}.`:"";return{name:`Form: ${s}`,description:`Form being filled conversationally. ${f} Call update_${s} with any fields the user provides; ask only for what is still missing.${r}`,data:{values:e,missing:n,submitted:t}}},w=e=>{F((e.messages??[]).flatMap(t=>t.toolCalls??[]))};return{name:s,store:c,functions:g,contextKey:p,contextProvider:S,install:e=>{b=e;for(const n of g)e.register(n);e.addDynamicContext(p,S);const t=c.subscribe(()=>e.notifyContextChanged());return e.on("conversation:loaded",w),()=>{t();for(const n of g)e.unregisterFunction(n.name);e.removeDynamicContext(p),e.off("conversation:loaded",w),b===e&&(b=null)}},hydrate:F,submit:C,getValues:()=>c.get().values,setValues:e=>{v(d(e))},reset:()=>{c.set({values:u,submitted:!1})}}}Object.defineProperty(exports,"createFormCollector",{enumerable:!0,get:function(){return j}});Object.defineProperty(exports,"createStore",{enumerable:!0,get:function(){return k}});Object.defineProperty(exports,"deriveFormStatus",{enumerable:!0,get:function(){return h}});