@useago/sdk 1.0.8 → 1.0.9
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 +14 -3
- package/dist/angular.d.cts +24 -8
- package/dist/angular.d.ts +24 -8
- package/dist/{createConversationSession-BwYNMZWq.cjs → createConversationSession-C0diFTUk.cjs} +1 -1
- package/dist/{createConversationSession-DrCMdwkV.js → createConversationSession-CR-71aLR.js} +1 -1
- package/dist/createFormCollector-BI3DXaew.cjs +1 -0
- package/dist/{createFormCollector-QyojCH_-.js → createFormCollector-CFt1_QSL.js} +104 -102
- package/dist/devtools.d.cts +24 -8
- package/dist/devtools.d.ts +24 -8
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +25 -9
- package/dist/index.d.ts +25 -9
- package/dist/index.js +2 -2
- package/dist/react.cjs +1 -1
- package/dist/react.d.cts +25 -9
- package/dist/react.d.ts +25 -9
- package/dist/react.js +1 -1
- package/dist/testing.d.cts +24 -8
- package/dist/testing.d.ts +24 -8
- package/dist/vue.d.cts +24 -8
- package/dist/vue.d.ts +24 -8
- package/dist/widget.cjs +4 -4
- package/dist/widget.d.cts +36 -8
- package/dist/widget.d.ts +36 -8
- package/dist/widget.js +308 -305
- package/package.json +1 -1
- package/dist/createFormCollector-BHHEW6uX.cjs +0 -1
package/README.md
CHANGED
|
@@ -3,9 +3,20 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@useago/sdk)
|
|
4
4
|
[](https://www.npmjs.com/package/@useago/sdk)
|
|
5
5
|
|
|
6
|
-
**The agent
|
|
7
|
-
|
|
8
|
-
|
|
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
|
package/dist/angular.d.cts
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
|
|
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
|
|
593
|
-
*
|
|
594
|
-
*
|
|
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
|
|
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
|
|
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
|
|
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
|
|
593
|
-
*
|
|
594
|
-
*
|
|
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
|
|
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
|
|
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/{createConversationSession-BwYNMZWq.cjs → createConversationSession-C0diFTUk.cjs}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
const d=require("./createFormCollector-
|
|
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 _}});
|
package/dist/{createConversationSession-DrCMdwkV.js → createConversationSession-CR-71aLR.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as d } from "./createFormCollector-
|
|
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
|
|
2
|
-
function A(
|
|
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
|
|
4
|
+
let n = s;
|
|
5
5
|
if (e && a) try {
|
|
6
6
|
const m = a.getItem(e.key);
|
|
7
|
-
m != null && (
|
|
7
|
+
m != null && (n = JSON.parse(m));
|
|
8
8
|
} catch {
|
|
9
9
|
}
|
|
10
|
-
const
|
|
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(
|
|
13
|
+
a.setItem(e.key, JSON.stringify(n));
|
|
14
14
|
} catch {
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
return {
|
|
18
|
-
get: () =>
|
|
18
|
+
get: () => n,
|
|
19
19
|
set: (m) => {
|
|
20
|
-
|
|
20
|
+
n = m, b(), c();
|
|
21
21
|
},
|
|
22
|
-
subscribe: (m) => (
|
|
23
|
-
|
|
22
|
+
subscribe: (m) => (o.add(m), () => {
|
|
23
|
+
o.delete(m);
|
|
24
24
|
})
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
|
-
var
|
|
28
|
-
function
|
|
29
|
-
|
|
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(
|
|
32
|
-
if (!
|
|
33
|
-
const
|
|
34
|
-
if (Number.isNaN(
|
|
35
|
-
switch (
|
|
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
|
|
39
|
+
return n >= o;
|
|
38
40
|
case "<=":
|
|
39
|
-
return
|
|
41
|
+
return n <= o;
|
|
40
42
|
case ">":
|
|
41
|
-
return
|
|
43
|
+
return n > o;
|
|
42
44
|
case "<":
|
|
43
|
-
return
|
|
45
|
+
return n < o;
|
|
44
46
|
default:
|
|
45
|
-
return
|
|
47
|
+
return $.warn(`Unknown requiredWhen op "${s.op}"; treating as not required.`), !1;
|
|
46
48
|
}
|
|
47
49
|
}
|
|
48
|
-
function
|
|
49
|
-
const a =
|
|
50
|
-
for (const c of
|
|
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 ||
|
|
54
|
+
(!b || v(b, e)) && n.add(c);
|
|
53
55
|
}
|
|
54
|
-
for (const [c, b] of Object.entries(a)) b.requiredWhen &&
|
|
55
|
-
const
|
|
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:
|
|
58
|
-
complete:
|
|
59
|
+
missing: o,
|
|
60
|
+
complete: o.length === 0
|
|
59
61
|
};
|
|
60
62
|
}
|
|
61
|
-
function J(
|
|
63
|
+
function J(s) {
|
|
62
64
|
const e = {};
|
|
63
|
-
for (const [a,
|
|
64
|
-
type:
|
|
65
|
-
...
|
|
66
|
-
...
|
|
67
|
-
...
|
|
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(
|
|
76
|
-
const { name: e, description: a, schema:
|
|
77
|
-
if (!
|
|
78
|
-
const
|
|
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:
|
|
81
|
-
} : !1 :
|
|
82
|
-
|
|
83
|
-
const c = (
|
|
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,
|
|
87
|
+
for (const [u, i] of Object.entries(t)) {
|
|
86
88
|
const l = b[u];
|
|
87
89
|
if (l)
|
|
88
|
-
if (
|
|
90
|
+
if (i == null) r[u] = i;
|
|
89
91
|
else if (l.type === "number" || l.type === "integer") {
|
|
90
|
-
const d = typeof
|
|
91
|
-
r[u] = Number.isNaN(d) ?
|
|
92
|
-
} else l.type === "boolean" ? r[u] = typeof
|
|
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
|
-
},
|
|
96
|
-
values:
|
|
97
|
+
}, S = s.initialValues ?? {}, f = A({
|
|
98
|
+
values: S,
|
|
97
99
|
submitted: !1
|
|
98
|
-
}),
|
|
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
|
|
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 } =
|
|
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 (!
|
|
125
|
+
if (!o) return {
|
|
124
126
|
ok: !1,
|
|
125
127
|
error: "No submit target configured for this form collector."
|
|
126
128
|
};
|
|
127
|
-
const
|
|
129
|
+
const i = t;
|
|
128
130
|
try {
|
|
129
131
|
let l;
|
|
130
|
-
if (
|
|
132
|
+
if (o.via === "client") if ("handler" in o) l = await o.handler(i);
|
|
131
133
|
else {
|
|
132
|
-
const d = await fetch(
|
|
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(
|
|
138
|
+
body: JSON.stringify(i)
|
|
137
139
|
});
|
|
138
140
|
if (!d.ok) {
|
|
139
|
-
const
|
|
140
|
-
return
|
|
141
|
+
const x = `Submit failed: HTTP ${d.status}`;
|
|
142
|
+
return k(i, x), {
|
|
141
143
|
ok: !1,
|
|
142
|
-
error:
|
|
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,
|
|
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:
|
|
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
|
|
171
|
+
return $.error(`FormCollector "${e}" submit failed:`, l), k(i, d), {
|
|
170
172
|
ok: !1,
|
|
171
173
|
error: d
|
|
172
174
|
};
|
|
173
175
|
}
|
|
174
|
-
},
|
|
176
|
+
}, N = () => p || (p = (async () => {
|
|
175
177
|
try {
|
|
176
178
|
return await j();
|
|
177
179
|
} finally {
|
|
178
180
|
p = null;
|
|
179
181
|
}
|
|
180
|
-
})(), p),
|
|
181
|
-
if (!c || !
|
|
182
|
+
})(), p), F = async () => {
|
|
183
|
+
if (!c || !o) return null;
|
|
182
184
|
const { values: t, submitted: r } = f.get();
|
|
183
|
-
return r || !
|
|
184
|
-
},
|
|
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(
|
|
189
|
+
parameters: J(n),
|
|
188
190
|
handler: async (t) => {
|
|
189
|
-
const r =
|
|
190
|
-
return
|
|
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:
|
|
202
|
+
submitError: i.error
|
|
201
203
|
} : {
|
|
202
204
|
ok: !0,
|
|
203
205
|
values: r.values,
|
|
204
206
|
missing: u
|
|
205
207
|
};
|
|
206
208
|
}
|
|
207
|
-
},
|
|
208
|
-
|
|
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: () =>
|
|
217
|
+
handler: () => N()
|
|
216
218
|
});
|
|
217
|
-
const I = "client__",
|
|
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 = { ...
|
|
223
|
-
for (const
|
|
224
|
-
const l = V(
|
|
225
|
-
l ===
|
|
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(
|
|
228
|
-
}) :
|
|
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
|
-
},
|
|
235
|
-
const { values: t, submitted: r } = f.get(), { missing: u, complete:
|
|
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}
|
|
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:
|
|
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:
|
|
253
|
-
contextKey:
|
|
254
|
+
functions: g,
|
|
255
|
+
contextKey: w,
|
|
254
256
|
contextProvider: E,
|
|
255
257
|
install: (t) => {
|
|
256
258
|
h = t;
|
|
257
|
-
for (const u of
|
|
258
|
-
t.addDynamicContext(
|
|
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
|
|
263
|
-
t.removeDynamicContext(
|
|
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:
|
|
269
|
+
submit: N,
|
|
268
270
|
getValues: () => f.get().values,
|
|
269
271
|
setValues: (t) => {
|
|
270
|
-
|
|
272
|
+
C(m(t)), F();
|
|
271
273
|
},
|
|
272
274
|
reset: () => {
|
|
273
275
|
f.set({
|
|
274
|
-
values:
|
|
276
|
+
values: S,
|
|
275
277
|
submitted: !1
|
|
276
278
|
});
|
|
277
279
|
},
|
|
278
|
-
getStatus: () =>
|
|
280
|
+
getStatus: () => y(n, f.get().values)
|
|
279
281
|
};
|
|
280
282
|
}
|
|
281
|
-
async function L(
|
|
282
|
-
const a = await
|
|
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
|
-
|
|
296
|
+
y as n,
|
|
295
297
|
L as r,
|
|
296
298
|
R as t
|
|
297
299
|
};
|
package/dist/devtools.d.cts
CHANGED
|
@@ -495,7 +495,7 @@ declare interface FormCollectorDefinition<V = Record<string, unknown>> {
|
|
|
495
495
|
|
|
496
496
|
/**
|
|
497
497
|
* Shape of a form's fields. Mirrors a client function's `parameters` (so the same
|
|
498
|
-
* object feeds the store, the update function, and the dynamic context). Additionally allows the SDK-only per-field `requiredWhen`, which {@link toWireParameters} removes
|
|
498
|
+
* 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).
|
|
499
499
|
*/
|
|
500
500
|
declare interface FormCollectorSchema {
|
|
501
501
|
type: "object";
|
|
@@ -512,17 +512,32 @@ declare interface FormField {
|
|
|
512
512
|
}
|
|
513
513
|
|
|
514
514
|
/**
|
|
515
|
-
* A field's conditional-requiredness rule
|
|
516
|
-
*
|
|
517
|
-
*
|
|
515
|
+
* A field's conditional-requiredness rule: either a single {@link FormFieldLeafCondition}
|
|
516
|
+
* or a boolean combination of rules. `anyOf` is OR (true when any nested rule
|
|
517
|
+
* holds); `allOf` is AND (true when every nested rule holds). Both nest
|
|
518
|
+
* arbitrarily, so you can mix them, e.g.
|
|
519
|
+
* `{ anyOf: [{ property: "a", op: ">=", value: 1 }, { property: "b", value: "1" }] }`.
|
|
520
|
+
* An empty `anyOf` is never required; an empty `allOf` is always required.
|
|
521
|
+
*
|
|
522
|
+
* Stripped from the LLM tool's `parameters` (see {@link toWireParameters}), but the
|
|
523
|
+
* full schema, `requiredWhen` included, still reaches the agent via the dynamic
|
|
524
|
+
* context so it knows when each field becomes required.
|
|
525
|
+
*/
|
|
526
|
+
declare type FormFieldCondition = FormFieldLeafCondition | {
|
|
527
|
+
anyOf: FormFieldCondition[];
|
|
528
|
+
} | {
|
|
529
|
+
allOf: FormFieldCondition[];
|
|
530
|
+
};
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* A single leaf comparison. The field it guards becomes required exactly when the
|
|
534
|
+
* controlling `property` matches `value` against the answers collected so far:
|
|
518
535
|
* - a single `value`, no `op` — equality (compared as strings);
|
|
519
536
|
* - a single `value` with `op` — numeric comparison (both sides coerced to numbers);
|
|
520
537
|
* - an array `value`, no `op` — "one of": matches when the property equals any
|
|
521
538
|
* listed value.
|
|
522
|
-
*
|
|
523
|
-
* SDK-only — it is stripped from the schema before it reaches the LLM tool
|
|
524
539
|
*/
|
|
525
|
-
declare interface
|
|
540
|
+
declare interface FormFieldLeafCondition {
|
|
526
541
|
/** The controlling field whose value the rule tests. */
|
|
527
542
|
property: string;
|
|
528
543
|
/** Comparison operator; omitted means equality. Ignored when `value` is an array. */
|
|
@@ -543,7 +558,8 @@ declare interface FormFieldSchema {
|
|
|
543
558
|
default?: unknown;
|
|
544
559
|
/**
|
|
545
560
|
* Makes the field required only while this condition holds. Drives the dynamic
|
|
546
|
-
* `missing` list; stripped
|
|
561
|
+
* `missing` list; stripped from the LLM tool's `parameters` (still sent to the
|
|
562
|
+
* agent via dynamic context). See {@link toWireParameters}.
|
|
547
563
|
*/
|
|
548
564
|
requiredWhen?: FormFieldCondition;
|
|
549
565
|
}
|