@useago/sdk 1.0.2 → 1.0.4

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
@@ -46,6 +46,11 @@ Start your dev server. A chat panel renders on the page; type a message and
46
46
  watch the reply stream in. That's a complete integration. (`generic-guide` is a
47
47
  public demo agent; swap `baseUrl` and `agent` for your own once you have a tenant.)
48
48
 
49
+ `ChatWidget` is the high-level component to get started in minutes. Everything it
50
+ does is also exposed through hooks and APIs (`useChat`, `useMessages`, the raw
51
+ client, events), so when you outgrow the default panel you can drop down and build
52
+ the exact experience you want. The example below renders one piece of that.
53
+
49
54
  ## Let the agent drive your existing routes
50
55
 
51
56
  This is the part existing apps care about. You already have a router and pages;
@@ -76,6 +81,49 @@ guards, auth and layouts keep working exactly as they do today.
76
81
  `react-router-dom` here is *your* router, not an SDK dependency:
77
82
  `useAgoNavigation` accepts any `(path: string) => void` function.
78
83
 
84
+ ## Show the source docs the agent retrieved
85
+
86
+ Each assistant message carries the knowledge sources it used in `m.sources`
87
+ (each one is `{ id, title, url? }`). Build your own chat with `useChat` and
88
+ render them as links to show the URL of every retrieved doc:
89
+
90
+ ```tsx
91
+ import { useChat } from "@useago/sdk/react";
92
+
93
+ function Chat() {
94
+ const { messages, sendMessage, isLoading } = useChat();
95
+
96
+ return (
97
+ <div>
98
+ {messages.map((m) => (
99
+ <div key={m.id}>
100
+ <p><b>{m.role}:</b> {m.content}</p>
101
+
102
+ {m.sources?.length ? (
103
+ <ul>
104
+ {m.sources.map((s) => (
105
+ <li key={s.id}>
106
+ {s.url ? (
107
+ <a href={s.url} target="_blank" rel="noreferrer">
108
+ {s.title || s.url}
109
+ </a>
110
+ ) : (
111
+ s.title
112
+ )}
113
+ </li>
114
+ ))}
115
+ </ul>
116
+ ) : null}
117
+ </div>
118
+ ))}
119
+ <button onClick={() => sendMessage("Hello!")} disabled={isLoading}>
120
+ Send
121
+ </button>
122
+ </div>
123
+ );
124
+ }
125
+ ```
126
+
79
127
  **Next: the [Getting started guide](docs/general/getting-started.md)** has both
80
128
  examples assembled into a running app, plus everything else the SDK can do, in
81
129
  about 5 minutes.
@@ -0,0 +1,6 @@
1
+ var h=class extends Error{constructor(e,t,s){super(e),this.code=t,this.statusCode=s,this.name="AgoError"}},u=class b extends h{constructor(t,s,n,r,i,a){super(t,s,n),this.type=r,this.param=i,this.docUrl=a,this.name="AgoApiError"}static fromResponse(t,s){const n=t.error;return new b(n.doc_url?`${n.message} See ${n.doc_url}`:n.message,n.code,s,n.type,n.param,n.doc_url)}},d=class extends h{constructor(e,t){super(e,"network_error"),this.originalError=t,this.name="AgoNetworkError"}},g=class extends h{constructor(e,t="stream_error"){super(e,t),this.name="AgoStreamError"}},m=class extends h{constructor(e,t,s){super(e,"function_error"),this.functionName=t,this.originalError=s,this.name="AgoFunctionError"}},$=class{constructor(e=!1){this.enabled=e}enable(){this.enabled=!0}disable(){this.enabled=!1}log(...e){this.enabled&&console.log("[AGO SDK]",...e)}warn(...e){this.enabled&&console.warn("[AGO SDK]",...e)}error(...e){console.error("[AGO SDK]",...e)}debug(...e){this.enabled&&console.debug("[AGO SDK]",...e)}},o=new $,y="ago_widget_id";function x(){if(typeof localStorage<"u"){const t=localStorage.getItem(y);if(t)return t}const e=typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():`ago-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,12)}`;if(typeof localStorage<"u")try{localStorage.setItem(y,e)}catch{}return e}var R=class{constructor(e){this.baseUrl=e.baseUrl.replace(/\/$/,""),this.headers={"X-User-Anon-Id":e.widgetId||x()},e.userEmail&&(this.headers["X-User-Email"]=e.userEmail),e.userJwt&&(this.headers.Authorization=`Bearer ${e.userJwt}`),e.permission&&(this.headers["X-Widget-Permission"]=e.permission)}updateConfig(e){e.baseUrl&&(this.baseUrl=e.baseUrl.replace(/\/$/,"")),e.widgetId&&(this.headers["X-User-Anon-Id"]=e.widgetId),e.userEmail&&(this.headers["X-User-Email"]=e.userEmail),e.userJwt&&(this.headers.Authorization=`Bearer ${e.userJwt}`),e.permission!==void 0&&(e.permission?this.headers["X-Widget-Permission"]=e.permission:delete this.headers["X-Widget-Permission"])}async get(e){return this.request("GET",e)}async post(e,t){return this.request("POST",e,t)}async postStream(e,t){const s=`${this.baseUrl}${e}`;o.debug("POST (stream)",s,t);try{const n=await fetch(s,{method:"POST",headers:{...this.headers,"Content-Type":"application/json"},body:t?JSON.stringify(t):void 0});return n.ok||await this.handleErrorResponse(n),n}catch(n){throw n instanceof u?n:new d(`Network error: ${n instanceof Error?n.message:"Unknown error"}. Check that \`baseUrl\` is reachable and includes the protocol (https://).`,n instanceof Error?n:void 0)}}async postFormData(e,t){const s=`${this.baseUrl}${e}`;o.debug("POST (formData)",s);const n={...this.headers};try{const r=await fetch(s,{method:"POST",headers:n,body:t});return r.ok||await this.handleErrorResponse(r),r}catch(r){throw r instanceof u?r:new d(`Network error: ${r instanceof Error?r.message:"Unknown error"}. Check that \`baseUrl\` is reachable and includes the protocol (https://).`,r instanceof Error?r:void 0)}}async request(e,t,s){const n=`${this.baseUrl}${t}`;o.debug(e,n,s);try{const r=await fetch(n,{method:e,headers:{...this.headers,"Content-Type":"application/json"},body:s?JSON.stringify(s):void 0});return r.ok||await this.handleErrorResponse(r),r.status===204?void 0:r.json()}catch(r){throw r instanceof u?r:new d(`Network error: ${r instanceof Error?r.message:"Unknown error"}. Check that \`baseUrl\` is reachable and includes the protocol (https://).`,r instanceof Error?r:void 0)}}async handleErrorResponse(e){let t;try{t=await e.json()}catch{}if(t?.error)throw u.fromResponse(t,e.status);let s="";throw e.status===401||e.status===403?s=" Check `userJwt` (and that it has not expired).":e.status===404&&(s=" Check that `baseUrl` points at your AGO API root."),new u(`HTTP ${e.status}: ${e.statusText}.${s}`,"http_error",e.status,"api_error")}},C=class{constructor(){this.functions=new Map}register(e,t,s){if(typeof e=="object"){const{name:r,handler:i,description:a,parameters:l}=e;return this.register(r,i,{description:a,parameters:l})}const n=e;if(!t||!s)throw new h(`registerFunction("${n}"): both a handler and a schema are required. Pass them as arguments or use the single-object form: registerFunction({ name, parameters, handler }).`,"function_invalid_registration");this.functions.has(n)&&o.warn(`Function "${n}" is being overwritten`),this.functions.set(n,{schema:{...s,name:n},handler:t}),o.log(`Registered function: ${n}`)}unregister(e){const t=this.functions.delete(e);return t&&o.log(`Unregistered function: ${e}`),t}get(e){return this.functions.get(e)}has(e){return this.functions.has(e)}getSchemas(){return Array.from(this.functions.values()).map(e=>e.schema)}async execute(e,t){const s=this.functions.get(e);if(!s)throw new m(`Function "${e}" is not registered`,e);o.log(`Executing function: ${e}`,t);try{const n=await s.handler(t);return o.log(`Function ${e} completed:`,n),n}catch(n){throw o.error(`Function ${e} failed:`,n),new m(`Function "${e}" execution failed: ${n instanceof Error?n.message:"Unknown error"}`,e,n instanceof Error?n:void 0)}}clear(){this.functions.clear(),o.log("Cleared all registered functions")}get size(){return this.functions.size}},E=class{constructor(){this.entries=new Map,this.dynamicProviders=new Map}set(e,t){this.entries.set(e,t),o.log(`ClientContext set: ${e}`)}remove(e){const t=this.entries.delete(e);return t&&o.log(`ClientContext removed: ${e}`),t}addDynamicProvider(e,t){this.dynamicProviders.set(e,t),o.log(`DynamicContext provider added: ${e}`)}removeDynamicProvider(e){const t=this.dynamicProviders.delete(e);return t&&o.log(`DynamicContext provider removed: ${e}`),t}getSnapshot(){if(this.entries.size===0&&this.dynamicProviders.size===0)return null;const e={};for(const[t,s]of this.entries)e[t]=s;for(const[t,s]of this.dynamicProviders)try{const n=s();n&&(e[t]=n)}catch(n){o.error(`DynamicContext provider "${t}" threw:`,n)}return Object.keys(e).length===0?null:{entries:e}}clear(){this.entries.clear(),this.dynamicProviders.clear(),o.log("ClientContext cleared")}};function f(e){return e===null||typeof e!="object"?JSON.stringify(e)??"null":Array.isArray(e)?`[${e.map(f).join(",")}]`:`{${Object.keys(e).sort().map(t=>`${JSON.stringify(t)}:${f(e[t])}`).join(",")}}`}var _=class{constructor(e){this.callbacks=e,this.buffer="",this.message={},this.toolCalls=[],this.sources=[],this.followUpReplies=[],this.isFirstChunk=!0,this.answerCompleteEmitted=!1,this.firedClientFunctions=new Set}async processStream(e){if(!e.body)throw new g("Response has no body. The endpoint did not return a stream: check that `baseUrl` points at an AGO API and nothing (proxy, mock) strips the SSE body.","stream_no_body");const t=e.body.getReader(),s=new TextDecoder;try{for(;;){const{done:n,value:r}=await t.read();if(n)break;const i=s.decode(r,{stream:!0});this.buffer+=i,this.processBuffer()}return this.buffer.trim()&&this.processBuffer(),this.buildFinalMessage()}catch(n){const r=n instanceof Error?n:new g("Stream processing failed mid-flight. The connection may have dropped; retry the message.");throw this.callbacks.onError?.(r),r}finally{t.releaseLock()}}processBuffer(){const e=this.buffer.split(`
2
+
3
+ `);this.buffer=e.pop()||"";for(const t of e){if(!t.trim())continue;const s=t.split(`
4
+ `);for(const n of s)if(!n.startsWith(": ")&&n.startsWith("data: ")){const r=n.slice(6);try{const i=JSON.parse(r);this.handleChunk(i)}catch(i){o.warn("Failed to parse SSE data:",r,i)}}}}handleChunk(e){if(e.message_id&&!this.message.id&&(this.message.id=e.message_id),e.thread?.id&&(this.message.conversationId=e.thread.id,this.isFirstChunk&&this.message.id&&(this.isFirstChunk=!1,this.callbacks.onStart?.({conversationId:this.message.conversationId,messageId:this.message.id}))),e.content!==void 0&&(this.message.content=(this.message.content||"")+e.content,this.message.conversationId&&this.message.id&&this.callbacks.onChunk?.({content:e.content,conversationId:this.message.conversationId,messageId:this.message.id})),e.full_content!==void 0&&(this.message.content=e.full_content),e.status&&(this.message.status=e.status),e.agent&&(this.message.agent={id:e.agent.id,name:e.agent.name,displayName:e.agent.display_name}),e.knowledge_sources&&(this.sources=e.knowledge_sources.map(t=>{const s=t.knowledge_document;return{id:s.id,title:s.title,url:s.use_external_link?s.external_link_url:s.internal_link_url}})),e.type==="client_function"&&e.function_name){const t=`${e.function_name}::${f(e.arguments??{})}`;this.firedClientFunctions.has(t)||(this.firedClientFunctions.add(t),this.callbacks.onClientFunction?.({invocationId:e.id||"",functionName:e.function_name,arguments:e.arguments||{},conversationId:this.message.conversationId||""}))}if(e.tool_call_data&&e.type&&e.type!=="client_function"){const t=this.parseToolCall(e);this.callbacks.onToolCall?.(t);const s=this.toolCalls.findIndex(n=>n.id===t.id);s>=0?this.toolCalls[s]=t:this.toolCalls.push(t)}e.follow_up_replies&&(this.followUpReplies=e.follow_up_replies),this.message.status==="DONE"&&!this.answerCompleteEmitted&&this.message.id&&(this.answerCompleteEmitted=!0,this.callbacks.onAnswerComplete?.(this.buildMessage()))}parseToolCall(e){return{id:e.id||"",type:e.type||"status_message",status:e.status||"unknown",toolName:e.tool_name||"",toolDisplayName:e.tool_display_name,message:e.message,formSchema:e.form_schema,data:e.data,functionName:e.function_name,arguments:e.arguments}}buildMessage(){return{id:this.message.id||"",conversationId:this.message.conversationId||"",content:this.message.content||"",role:"assistant",status:this.message.status||"DONE",agent:this.message.agent,sources:this.sources.length>0?this.sources:void 0,toolCalls:this.toolCalls.length>0?this.toolCalls:void 0,followUpReplies:this.followUpReplies.length>0?this.followUpReplies:void 0,createdAt:new Date}}buildFinalMessage(){const e=this.buildMessage();return this.callbacks.onComplete?.(e),e}};function I(e){const t=e.message.toLowerCase();return t.includes("load failed")||t.includes("failed to fetch")||t.includes("network")||t.includes("abort")}var S=class{constructor(){this.handlers=new Map}on(e,t){this.handlers.has(e)||this.handlers.set(e,new Set),this.handlers.get(e).add(t)}off(e,t){const s=this.handlers.get(e);s&&s.delete(t)}emit(e,t){const s=this.handlers.get(e);s&&s.forEach(n=>{try{n(t)}catch(r){console.error(`Error in event handler for ${String(e)}:`,r)}})}once(e,t){const s=(n=>{this.off(e,s),t(n)});this.on(e,s)}waitFor(e,t){return new Promise((s,n)=>{let r;const i=(a=>{r&&clearTimeout(r),s(a)});this.once(e,i),t?.timeout&&(r=setTimeout(()=>{this.off(e,i),n(new Error(`waitFor("${String(e)}") timed out after ${t.timeout}ms`))},t.timeout))})}removeAllListeners(e){e?this.handlers.delete(e):this.handlers.clear()}},w='`baseUrl` is required, e.g. new AgoClient({ baseUrl: "https://YOUR-DOMAIN.api.useago.com" }). Try the live demo endpoint https://playground.api.useago.com. See docs/general/configuration.md#error-codes (config_missing_base_url).',U=/^[a-z][a-z0-9+.-]*:\/\//i,v=!1;function p(e,t="AgoClient"){if(typeof e!="object"||e===null)throw new h(`${t}: ${w}`,"config_missing_base_url");const s=e.baseUrl;if(typeof s!="string"||s.trim()==="")throw new h(`${t}: ${w}`,"config_missing_base_url");!s.startsWith("/")&&!U.test(s)&&(v||(v=!0,console.warn(`[AGO] ${t}: \`baseUrl\` has no protocol and is not a relative path (config_suspect_base_url). Requests will fail: Node throws a URL parse error, browsers fetch it as a relative path. Use "https://..." (or a "/path" for a same-origin proxy).`)))}var F=class k{constructor(t){this.warnedEmptyConversations=new Set,p(t,"AgoClient"),this.config=t,this.httpClient=new R(t),this.functionRegistry=new C,this.contextRegistry=new E,this.eventEmitter=new S,t.debug&&o.enable(),o.log("AgoClient initialized")}async sendMessage(t,s){const n=this.functionRegistry.getSchemas(),r=this.contextRegistry.getSnapshot(),i=this.config.agent||this.config.defaultAgentId,a={content:t,conversation_id:s?.conversationId,agent_id:s?.agentId||i};n.length>0&&(a.client_functions=n),r&&(a.client_context=r);let l;if(s?.files&&s.files.length>0){const c=new FormData;c.append("content",t),s.conversationId&&c.append("conversation_id",s.conversationId),(s.agentId||i)&&c.append("agent_id",s.agentId||i||""),n.length>0&&c.append("client_functions",JSON.stringify(n)),r&&c.append("client_context",JSON.stringify(r));for(const A of s.files)c.append("files",A);l=await this.httpClient.postFormData("/api/sdk/v1/messages",c)}else l=await this.httpClient.postStream("/api/sdk/v1/messages",a);return this.processSSEResponse(l)}async processSSEResponse(t){let s=!1;const n=await new _({onStart:r=>{this.eventEmitter.emit("message:start",r)},onChunk:r=>{this.eventEmitter.emit("message:chunk",r)},onToolCall:r=>{this.eventEmitter.emit("toolCall:received",r),r.type==="form"&&this.eventEmitter.emit("toolCall:form",r)},onClientFunction:async r=>{s=!0,this.eventEmitter.emit("function:invoke",r),await this.handleClientFunctionInvocation(r)},onAnswerComplete:r=>{this.eventEmitter.emit("message:answer-complete",r)},onComplete:r=>{this.eventEmitter.emit("message:complete",r)},onError:r=>{this.eventEmitter.emit("message:error",{error:r.message,code:r instanceof h?r.code:void 0})}}).processStream(t);return this.maybeFlagEmptyReply(n,s),n}maybeFlagEmptyReply(t,s){t.status==="DONE"&&(t.content??"").trim()===""&&!s&&!(t.toolCalls&&t.toolCalls.length>0)&&!(t.followUpReplies&&t.followUpReplies.length>0)&&setTimeout(()=>{if(this.eventEmitter.emit("message:empty",{conversationId:t.conversationId??"",messageId:t.id??""}),this.config.warnOnEmptyReply===!1)return;const n=t.conversationId||"(no conversation)";if(this.warnedEmptyConversations.has(n))return;this.warnedEmptyConversations.add(n);const r=this.config.agent||this.config.defaultAgentId;t.id?console.warn("[AGO] Reply completed with empty content. Possible causes: "+(r?`the agent "${r}" may not exist for this tenant, `:"an unknown `agent` slug, ")+"or the agent replied with no text. Listen for the `message:empty` event to handle this in your app; silence this warning with `warnOnEmptyReply: false`."):console.warn("[AGO] The stream completed without any message data. Possible causes: "+(r?`the agent "${r}" may not exist for this tenant, or `:"")+"`baseUrl` does not point at an AGO endpoint that returns server-sent events. Listen for the `message:empty` event to handle this in your app; silence this warning with `warnOnEmptyReply: false`.")},0)}async handleClientFunctionInvocation(t){let s,n;try{s=await this.functionRegistry.execute(t.functionName,t.arguments)}catch(r){n=r instanceof Error?r.message:"Unknown error",o.error("Client function execution failed:",r)}if(t.invocationId)try{await this.submitToolCallForm(t.invocationId,{result:s,error:n,_type:"client_function_result"})}catch(r){o.error("Failed to submit function result:",r)}this.eventEmitter.emit("function:result",{invocationId:t.invocationId,result:s,error:n})}async getConversations(){return(await this.httpClient.get("/api/sdk/v1/conversations")).items.map(t=>({id:t.id,title:t.title,lastMessageDate:new Date(t.last_message_date)}))}async getConversation(t){const s=await this.httpClient.get(`/api/sdk/v1/conversations/${t}`),n={id:s.id,title:s.title,lastMessageDate:new Date(s.last_message_date),messages:s.messages.map(r=>({id:r.id,conversationId:s.id,content:r.content,role:r.role,status:r.status,createdAt:new Date(r.created_at),toolCalls:k.mapPersistedToolCalls(r.tool_call_data)}))};return this.eventEmitter.emit("conversation:loaded",n),n}static mapPersistedToolCalls(t){if(!(!t||t.length===0))return t.map(s=>({id:String(s.id??""),type:s.type??"status_message",status:String(s.status??"unknown"),toolName:s.tool_name??"",toolDisplayName:s.tool_display_name,message:s.message,formSchema:s.form_schema,data:s.data,functionName:s.function_name,arguments:s.arguments}))}async getMessages(t){return(await this.getConversation(t)).messages||[]}async submitToolCallForm(t,s){await this.httpClient.post(`/api/sdk/v1/tool-calls/${t}/submit`,{formData:s})}async confirmToolCall(t){await this.httpClient.post(`/api/tool-calls/${t}/confirm/`)}async rejectToolCall(t){await this.httpClient.post(`/api/tool-calls/${t}/reject/`)}async getFormCollector(t){const s=await this.httpClient.get(`/api/sdk/v1/forms/${encodeURIComponent(t)}`);return{name:s.name,description:s.description,schema:s.schema,submit:s.submit??void 0}}async submitFormCollector(t,s){return this.httpClient.post("/api/sdk/v1/forms/submit",{name:t,values:s})}async submitFeedback(t,s){await this.httpClient.post(`/api/sdk/v1/messages/${t}/feedback`,{rating:s})}registerFunction(t,s,n){typeof t=="object"?this.functionRegistry.register(t):this.functionRegistry.register(t,s,n)}register(t){if(Array.isArray(t))for(const s of t)this.functionRegistry.register(s);else this.functionRegistry.register(t)}unregisterFunction(t){return this.functionRegistry.unregister(t)}getRegisteredFunctions(){return this.functionRegistry.getSchemas()}registerNavigationFunction(t,s){const n=s.map(i=>i.name),r=s.map(i=>`- "${i.name}": ${i.description}`).join(`
5
+ `);this.registerFunction("navigateToPage",async i=>{const a=i.page,l=s.find(c=>c.name===a);return l?(t(l.path),{success:!0,navigatedTo:l.path}):{success:!1,error:`Unknown page: ${a}`}},{description:`Navigate the user to a page in the application. Available pages:
6
+ ${r}`,parameters:{type:"object",properties:{page:{type:"string",description:"The page to navigate to",enum:n}},required:["page"]}})}setContext(t,s){this.contextRegistry.set(t,s),this.notifyContextChanged()}removeContext(t){const s=this.contextRegistry.remove(t);return s&&this.notifyContextChanged(),s}addDynamicContext(t,s){this.contextRegistry.addDynamicProvider(t,s),this.notifyContextChanged()}removeDynamicContext(t){const s=this.contextRegistry.removeDynamicProvider(t);return s&&this.notifyContextChanged(),s}notifyContextChanged(){this.eventEmitter.emit("context:changed",this.contextRegistry.getSnapshot())}enableAutoPageContext(){this.addDynamicContext("browser-page",()=>{const t=typeof window<"u"?window.location.href:void 0,s=typeof document<"u"?document.title:void 0;return!t&&!s?null:{name:"Browser page",description:"Current page the user is viewing",data:{url:t,title:s}}})}getContextSnapshot(){return this.contextRegistry.getSnapshot()}on(t,s){this.eventEmitter.on(t,s)}off(t,s){this.eventEmitter.off(t,s)}once(t,s){this.eventEmitter.once(t,s)}waitFor(t,s){return this.eventEmitter.waitFor(t,s)}updateConfig(t){const s={};for(const[r,i]of Object.entries(t))i!==void 0&&(s[r]=i);const n={...this.config,...s};p(n,"AgoClient.updateConfig"),this.config=n,this.httpClient.updateConfig(s),s.debug!==void 0&&(s.debug?o.enable():o.disable())}destroy(){this.eventEmitter.removeAllListeners(),this.functionRegistry.clear(),this.contextRegistry.clear(),o.log("AgoClient destroyed")}};Object.defineProperty(exports,"AgoApiError",{enumerable:!0,get:function(){return u}});Object.defineProperty(exports,"AgoClient",{enumerable:!0,get:function(){return F}});Object.defineProperty(exports,"AgoError",{enumerable:!0,get:function(){return h}});Object.defineProperty(exports,"AgoFunctionError",{enumerable:!0,get:function(){return m}});Object.defineProperty(exports,"AgoNetworkError",{enumerable:!0,get:function(){return d}});Object.defineProperty(exports,"AgoStreamError",{enumerable:!0,get:function(){return g}});Object.defineProperty(exports,"ClientContextRegistry",{enumerable:!0,get:function(){return E}});Object.defineProperty(exports,"EventEmitter",{enumerable:!0,get:function(){return S}});Object.defineProperty(exports,"FunctionRegistry",{enumerable:!0,get:function(){return C}});Object.defineProperty(exports,"SSEHandler",{enumerable:!0,get:function(){return _}});Object.defineProperty(exports,"isStreamNetworkError",{enumerable:!0,get:function(){return I}});Object.defineProperty(exports,"logger",{enumerable:!0,get:function(){return o}});Object.defineProperty(exports,"validateConfig",{enumerable:!0,get:function(){return p}});
@@ -2,27 +2,27 @@ var h = class extends Error {
2
2
  constructor(e, t, s) {
3
3
  super(e), this.code = t, this.statusCode = s, this.name = "AgoError";
4
4
  }
5
- }, d = class v extends h {
5
+ }, d = class C extends h {
6
6
  constructor(t, s, n, i, r, a) {
7
7
  super(t, s, n), this.type = i, this.param = r, this.docUrl = a, this.name = "AgoApiError";
8
8
  }
9
9
  static fromResponse(t, s) {
10
10
  const n = t.error;
11
- return new v(n.doc_url ? `${n.message} See ${n.doc_url}` : n.message, n.code, s, n.type, n.param, n.doc_url);
11
+ return new C(n.doc_url ? `${n.message} See ${n.doc_url}` : n.message, n.code, s, n.type, n.param, n.doc_url);
12
12
  }
13
13
  }, u = class extends h {
14
14
  constructor(e, t) {
15
15
  super(e, "network_error"), this.originalError = t, this.name = "AgoNetworkError";
16
16
  }
17
- }, m = class extends h {
17
+ }, g = class extends h {
18
18
  constructor(e, t = "stream_error") {
19
19
  super(e, t), this.name = "AgoStreamError";
20
20
  }
21
- }, g = class extends h {
21
+ }, f = class extends h {
22
22
  constructor(e, t, s) {
23
23
  super(e, "function_error"), this.functionName = t, this.originalError = s, this.name = "AgoFunctionError";
24
24
  }
25
- }, E = class {
25
+ }, _ = class {
26
26
  constructor(e = !1) {
27
27
  this.enabled = e;
28
28
  }
@@ -44,22 +44,22 @@ var h = class extends Error {
44
44
  debug(...e) {
45
45
  this.enabled && console.debug("[AGO SDK]", ...e);
46
46
  }
47
- }, o = new E(), f = "ago_widget_id";
48
- function _() {
47
+ }, o = new _(), p = "ago_widget_id";
48
+ function S() {
49
49
  if (typeof localStorage < "u") {
50
- const t = localStorage.getItem(f);
50
+ const t = localStorage.getItem(p);
51
51
  if (t) return t;
52
52
  }
53
53
  const e = typeof crypto < "u" && typeof crypto.randomUUID == "function" ? crypto.randomUUID() : `ago-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 12)}`;
54
54
  if (typeof localStorage < "u") try {
55
- localStorage.setItem(f, e);
55
+ localStorage.setItem(p, e);
56
56
  } catch {
57
57
  }
58
58
  return e;
59
59
  }
60
- var S = class {
60
+ var k = class {
61
61
  constructor(e) {
62
- this.baseUrl = e.baseUrl.replace(/\/$/, ""), this.headers = { "X-User-Anon-Id": e.widgetId || _() }, e.userEmail && (this.headers["X-User-Email"] = e.userEmail), e.userJwt && (this.headers.Authorization = `Bearer ${e.userJwt}`), e.permission && (this.headers["X-Widget-Permission"] = e.permission);
62
+ this.baseUrl = e.baseUrl.replace(/\/$/, ""), this.headers = { "X-User-Anon-Id": e.widgetId || S() }, e.userEmail && (this.headers["X-User-Email"] = e.userEmail), e.userJwt && (this.headers.Authorization = `Bearer ${e.userJwt}`), e.permission && (this.headers["X-Widget-Permission"] = e.permission);
63
63
  }
64
64
  updateConfig(e) {
65
65
  e.baseUrl && (this.baseUrl = e.baseUrl.replace(/\/$/, "")), e.widgetId && (this.headers["X-User-Anon-Id"] = e.widgetId), e.userEmail && (this.headers["X-User-Email"] = e.userEmail), e.userJwt && (this.headers.Authorization = `Bearer ${e.userJwt}`), e.permission !== void 0 && (e.permission ? this.headers["X-Widget-Permission"] = e.permission : delete this.headers["X-Widget-Permission"]);
@@ -129,7 +129,7 @@ var S = class {
129
129
  let s = "";
130
130
  throw e.status === 401 || e.status === 403 ? s = " Check `userJwt` (and that it has not expired)." : e.status === 404 && (s = " Check that `baseUrl` points at your AGO API root."), new d(`HTTP ${e.status}: ${e.statusText}.${s}`, "http_error", e.status, "api_error");
131
131
  }
132
- }, k = class {
132
+ }, $ = class {
133
133
  constructor() {
134
134
  this.functions = /* @__PURE__ */ new Map();
135
135
  }
@@ -166,13 +166,13 @@ var S = class {
166
166
  }
167
167
  async execute(e, t) {
168
168
  const s = this.functions.get(e);
169
- if (!s) throw new g(`Function "${e}" is not registered`, e);
169
+ if (!s) throw new f(`Function "${e}" is not registered`, e);
170
170
  o.log(`Executing function: ${e}`, t);
171
171
  try {
172
172
  const n = await s.handler(t);
173
173
  return o.log(`Function ${e} completed:`, n), n;
174
174
  } catch (n) {
175
- throw o.error(`Function ${e} failed:`, n), new g(`Function "${e}" execution failed: ${n instanceof Error ? n.message : "Unknown error"}`, e, n instanceof Error ? n : void 0);
175
+ throw o.error(`Function ${e} failed:`, n), new f(`Function "${e}" execution failed: ${n instanceof Error ? n.message : "Unknown error"}`, e, n instanceof Error ? n : void 0);
176
176
  }
177
177
  }
178
178
  clear() {
@@ -214,12 +214,16 @@ var S = class {
214
214
  clear() {
215
215
  this.entries.clear(), this.dynamicProviders.clear(), o.log("ClientContext cleared");
216
216
  }
217
- }, x = class {
217
+ };
218
+ function m(e) {
219
+ return e === null || typeof e != "object" ? JSON.stringify(e) ?? "null" : Array.isArray(e) ? `[${e.map(m).join(",")}]` : `{${Object.keys(e).sort().map((t) => `${JSON.stringify(t)}:${m(e[t])}`).join(",")}}`;
220
+ }
221
+ var x = class {
218
222
  constructor(e) {
219
- this.callbacks = e, this.buffer = "", this.message = {}, this.toolCalls = [], this.sources = [], this.followUpReplies = [], this.isFirstChunk = !0, this.answerCompleteEmitted = !1;
223
+ this.callbacks = e, this.buffer = "", this.message = {}, this.toolCalls = [], this.sources = [], this.followUpReplies = [], this.isFirstChunk = !0, this.answerCompleteEmitted = !1, this.firedClientFunctions = /* @__PURE__ */ new Set();
220
224
  }
221
225
  async processStream(e) {
222
- if (!e.body) throw new m("Response has no body. The endpoint did not return a stream: check that `baseUrl` points at an AGO API and nothing (proxy, mock) strips the SSE body.", "stream_no_body");
226
+ if (!e.body) throw new g("Response has no body. The endpoint did not return a stream: check that `baseUrl` points at an AGO API and nothing (proxy, mock) strips the SSE body.", "stream_no_body");
223
227
  const t = e.body.getReader(), s = new TextDecoder();
224
228
  try {
225
229
  for (; ; ) {
@@ -230,7 +234,7 @@ var S = class {
230
234
  }
231
235
  return this.buffer.trim() && this.processBuffer(), this.buildFinalMessage();
232
236
  } catch (n) {
233
- const i = n instanceof Error ? n : new m("Stream processing failed mid-flight. The connection may have dropped; retry the message.");
237
+ const i = n instanceof Error ? n : new g("Stream processing failed mid-flight. The connection may have dropped; retry the message.");
234
238
  throw this.callbacks.onError?.(i), i;
235
239
  } finally {
236
240
  t.releaseLock();
@@ -276,12 +280,16 @@ var S = class {
276
280
  title: s.title,
277
281
  url: s.use_external_link ? s.external_link_url : s.internal_link_url
278
282
  };
279
- })), e.type === "client_function" && e.function_name && this.callbacks.onClientFunction?.({
280
- invocationId: e.id || "",
281
- functionName: e.function_name,
282
- arguments: e.arguments || {},
283
- conversationId: this.message.conversationId || ""
284
- }), e.tool_call_data && e.type && e.type !== "client_function") {
283
+ })), e.type === "client_function" && e.function_name) {
284
+ const t = `${e.function_name}::${m(e.arguments ?? {})}`;
285
+ this.firedClientFunctions.has(t) || (this.firedClientFunctions.add(t), this.callbacks.onClientFunction?.({
286
+ invocationId: e.id || "",
287
+ functionName: e.function_name,
288
+ arguments: e.arguments || {},
289
+ conversationId: this.message.conversationId || ""
290
+ }));
291
+ }
292
+ if (e.tool_call_data && e.type && e.type !== "client_function") {
285
293
  const t = this.parseToolCall(e);
286
294
  this.callbacks.onToolCall?.(t);
287
295
  const s = this.toolCalls.findIndex((n) => n.id === t.id);
@@ -322,11 +330,11 @@ var S = class {
322
330
  return this.callbacks.onComplete?.(e), e;
323
331
  }
324
332
  };
325
- function R(e) {
333
+ function U(e) {
326
334
  const t = e.message.toLowerCase();
327
335
  return t.includes("load failed") || t.includes("failed to fetch") || t.includes("network") || t.includes("abort");
328
336
  }
329
- var $ = class {
337
+ var I = class {
330
338
  constructor() {
331
339
  this.handlers = /* @__PURE__ */ new Map();
332
340
  }
@@ -367,16 +375,16 @@ var $ = class {
367
375
  removeAllListeners(e) {
368
376
  e ? this.handlers.delete(e) : this.handlers.clear();
369
377
  }
370
- }, p = '`baseUrl` is required, e.g. new AgoClient({ baseUrl: "https://YOUR-DOMAIN.api.useago.com" }). Try the live demo endpoint https://playground.api.useago.com. See docs/general/configuration.md#error-codes (config_missing_base_url).', I = /^[a-z][a-z0-9+.-]*:\/\//i, y = !1;
371
- function w(e, t = "AgoClient") {
372
- if (typeof e != "object" || e === null) throw new h(`${t}: ${p}`, "config_missing_base_url");
378
+ }, y = '`baseUrl` is required, e.g. new AgoClient({ baseUrl: "https://YOUR-DOMAIN.api.useago.com" }). Try the live demo endpoint https://playground.api.useago.com. See docs/general/configuration.md#error-codes (config_missing_base_url).', R = /^[a-z][a-z0-9+.-]*:\/\//i, w = !1;
379
+ function v(e, t = "AgoClient") {
380
+ if (typeof e != "object" || e === null) throw new h(`${t}: ${y}`, "config_missing_base_url");
373
381
  const s = e.baseUrl;
374
- if (typeof s != "string" || s.trim() === "") throw new h(`${t}: ${p}`, "config_missing_base_url");
375
- !s.startsWith("/") && !I.test(s) && (y || (y = !0, console.warn(`[AGO] ${t}: \`baseUrl\` has no protocol and is not a relative path (config_suspect_base_url). Requests will fail: Node throws a URL parse error, browsers fetch it as a relative path. Use "https://..." (or a "/path" for a same-origin proxy).`)));
382
+ if (typeof s != "string" || s.trim() === "") throw new h(`${t}: ${y}`, "config_missing_base_url");
383
+ !s.startsWith("/") && !R.test(s) && (w || (w = !0, console.warn(`[AGO] ${t}: \`baseUrl\` has no protocol and is not a relative path (config_suspect_base_url). Requests will fail: Node throws a URL parse error, browsers fetch it as a relative path. Use "https://..." (or a "/path" for a same-origin proxy).`)));
376
384
  }
377
- var U = class C {
385
+ var F = class b {
378
386
  constructor(t) {
379
- this.warnedEmptyConversations = /* @__PURE__ */ new Set(), w(t, "AgoClient"), this.config = t, this.httpClient = new S(t), this.functionRegistry = new k(), this.contextRegistry = new A(), this.eventEmitter = new $(), t.debug && o.enable(), o.log("AgoClient initialized");
387
+ this.warnedEmptyConversations = /* @__PURE__ */ new Set(), v(t, "AgoClient"), this.config = t, this.httpClient = new k(t), this.functionRegistry = new $(), this.contextRegistry = new A(), this.eventEmitter = new I(), t.debug && o.enable(), o.log("AgoClient initialized");
380
388
  }
381
389
  async sendMessage(t, s) {
382
390
  const n = this.functionRegistry.getSchemas(), i = this.contextRegistry.getSnapshot(), r = this.config.agent || this.config.defaultAgentId, a = {
@@ -389,7 +397,7 @@ var U = class C {
389
397
  if (s?.files && s.files.length > 0) {
390
398
  const c = new FormData();
391
399
  c.append("content", t), s.conversationId && c.append("conversation_id", s.conversationId), (s.agentId || r) && c.append("agent_id", s.agentId || r || ""), n.length > 0 && c.append("client_functions", JSON.stringify(n)), i && c.append("client_context", JSON.stringify(i));
392
- for (const b of s.files) c.append("files", b);
400
+ for (const E of s.files) c.append("files", E);
393
401
  l = await this.httpClient.postFormData("/api/sdk/v1/messages", c);
394
402
  } else l = await this.httpClient.postStream("/api/sdk/v1/messages", a);
395
403
  return this.processSSEResponse(l);
@@ -478,7 +486,7 @@ var U = class C {
478
486
  role: i.role,
479
487
  status: i.status,
480
488
  createdAt: new Date(i.created_at),
481
- toolCalls: C.mapPersistedToolCalls(i.tool_call_data)
489
+ toolCalls: b.mapPersistedToolCalls(i.tool_call_data)
482
490
  }))
483
491
  };
484
492
  return this.eventEmitter.emit("conversation:loaded", n), n;
@@ -521,7 +529,7 @@ var U = class C {
521
529
  }
522
530
  async submitFormCollector(t, s) {
523
531
  return this.httpClient.post("/api/sdk/v1/forms/submit", {
524
- destination: t,
532
+ name: t,
525
533
  values: s
526
534
  });
527
535
  }
@@ -619,24 +627,24 @@ ${i}`,
619
627
  ...this.config,
620
628
  ...s
621
629
  };
622
- w(n, "AgoClient.updateConfig"), this.config = n, this.httpClient.updateConfig(s), s.debug !== void 0 && (s.debug ? o.enable() : o.disable());
630
+ v(n, "AgoClient.updateConfig"), this.config = n, this.httpClient.updateConfig(s), s.debug !== void 0 && (s.debug ? o.enable() : o.disable());
623
631
  }
624
632
  destroy() {
625
633
  this.eventEmitter.removeAllListeners(), this.functionRegistry.clear(), this.contextRegistry.clear(), o.log("AgoClient destroyed");
626
634
  }
627
635
  };
628
636
  export {
629
- R as a,
637
+ U as a,
630
638
  o as c,
631
- g as d,
639
+ f as d,
632
640
  u as f,
633
641
  x as i,
634
642
  d as l,
635
- w as n,
643
+ v as n,
636
644
  A as o,
637
- m as p,
638
- $ as r,
639
- k as s,
640
- U as t,
645
+ g as p,
646
+ I as r,
647
+ $ as s,
648
+ F as t,
641
649
  h as u
642
650
  };
package/dist/angular.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("./AgoClient-_fig8FoN.cjs");var n=class{constructor(e){this.client=new o.AgoClient(e),this.messages$=this.fromEvent("message:complete"),this.chunks$=this.fromEvent("message:chunk"),this.errors$=this.fromEvent("message:error"),this.messageStart$=this.fromEvent("message:start")}fromEvent(e){const t=this.client;return{subscribe(i){const s=r=>{i.next?.(r)};return t.on(e,s),{unsubscribe:()=>t.off(e,s)}}}}sendMessage(e,t){return this.client.sendMessage(e,t)}getConversations(){return this.client.getConversations()}getConversation(e){return this.client.getConversation(e)}getMessages(e){return this.client.getMessages(e)}submitToolCallForm(e,t){return this.client.submitToolCallForm(e,t)}confirmToolCall(e){return this.client.confirmToolCall(e)}rejectToolCall(e){return this.client.rejectToolCall(e)}submitFeedback(e,t){return this.client.submitFeedback(e,t)}registerFunction(e,t,i){typeof e=="object"?this.client.registerFunction(e):this.client.registerFunction(e,t,i)}unregisterFunction(e){return this.client.unregisterFunction(e)}registerNavigationFunction(e,t){this.client.registerNavigationFunction(e,t)}on(e,t){this.client.on(e,t)}off(e,t){this.client.off(e,t)}updateConfig(e){this.client.updateConfig(e)}getClient(){return this.client}destroy(){this.client.destroy()}};function c(e){return{provide:n,useValue:new n(e)}}exports.AgoService=n;exports.provideAgo=c;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("./AgoClient-1jgDndKs.cjs");var n=class{constructor(e){this.client=new o.AgoClient(e),this.messages$=this.fromEvent("message:complete"),this.chunks$=this.fromEvent("message:chunk"),this.errors$=this.fromEvent("message:error"),this.messageStart$=this.fromEvent("message:start")}fromEvent(e){const t=this.client;return{subscribe(i){const s=r=>{i.next?.(r)};return t.on(e,s),{unsubscribe:()=>t.off(e,s)}}}}sendMessage(e,t){return this.client.sendMessage(e,t)}getConversations(){return this.client.getConversations()}getConversation(e){return this.client.getConversation(e)}getMessages(e){return this.client.getMessages(e)}submitToolCallForm(e,t){return this.client.submitToolCallForm(e,t)}confirmToolCall(e){return this.client.confirmToolCall(e)}rejectToolCall(e){return this.client.rejectToolCall(e)}submitFeedback(e,t){return this.client.submitFeedback(e,t)}registerFunction(e,t,i){typeof e=="object"?this.client.registerFunction(e):this.client.registerFunction(e,t,i)}unregisterFunction(e){return this.client.unregisterFunction(e)}registerNavigationFunction(e,t){this.client.registerNavigationFunction(e,t)}on(e,t){this.client.on(e,t)}off(e,t){this.client.off(e,t)}updateConfig(e){this.client.updateConfig(e)}getClient(){return this.client}destroy(){this.client.destroy()}};function c(e){return{provide:n,useValue:new n(e)}}exports.AgoService=n;exports.provideAgo=c;
@@ -78,10 +78,11 @@ declare class AgoClient {
78
78
  * Relay a completed form collector's values to a server-configured destination.
79
79
  *
80
80
  * Used by `createFormCollector` / `useFormCollector` in `{ via: "backend" }` mode:
81
- * the browser never holds the destination URL or its secret — the backend resolves
82
- * `destination` (a named, opted-in tool) and forwards the values from the server.
81
+ * the browser never holds the destination name, URL, or its secret — the backend
82
+ * resolves the destination from the named form's stored definition and forwards
83
+ * the values from the server.
83
84
  */
84
- submitFormCollector(destination: string, values: Record<string, unknown>): Promise<unknown>;
85
+ submitFormCollector(name: string, values: Record<string, unknown>): Promise<unknown>;
85
86
  /**
86
87
  * Submit feedback for a message
87
88
  */
@@ -656,8 +657,9 @@ declare interface SendMessageOptions {
656
657
  * POSTs the values to that URL.
657
658
  * - `{ via: "client", url }` — same, written out.
658
659
  * - `{ via: "client", handler }` — the browser runs your own submit logic.
659
- * - `{ via: "backend", destination }` — the browser relays the values to the backend,
660
- * which forwards them to the server-configured destination (URL + secret stay server-side).
660
+ * - `{ via: "backend" }` — the browser relays the values to the backend, which resolves
661
+ * the destination from the form's backend-stored definition (name, URL and secret all
662
+ * stay server-side). `destination` is deprecated and ignored.
661
663
  * - `false` — collect only, no submit function.
662
664
  */
663
665
  declare type SubmitConfig<V = Record<string, unknown>> = string | {
@@ -668,7 +670,7 @@ declare type SubmitConfig<V = Record<string, unknown>> = string | {
668
670
  handler: (values: V) => Promise<unknown> | unknown;
669
671
  } | {
670
672
  via: "backend";
671
- destination: string;
673
+ destination?: string;
672
674
  } | false;
673
675
 
674
676
  /**
package/dist/angular.d.ts CHANGED
@@ -78,10 +78,11 @@ declare class AgoClient {
78
78
  * Relay a completed form collector's values to a server-configured destination.
79
79
  *
80
80
  * Used by `createFormCollector` / `useFormCollector` in `{ via: "backend" }` mode:
81
- * the browser never holds the destination URL or its secret — the backend resolves
82
- * `destination` (a named, opted-in tool) and forwards the values from the server.
81
+ * the browser never holds the destination name, URL, or its secret — the backend
82
+ * resolves the destination from the named form's stored definition and forwards
83
+ * the values from the server.
83
84
  */
84
- submitFormCollector(destination: string, values: Record<string, unknown>): Promise<unknown>;
85
+ submitFormCollector(name: string, values: Record<string, unknown>): Promise<unknown>;
85
86
  /**
86
87
  * Submit feedback for a message
87
88
  */
@@ -656,8 +657,9 @@ declare interface SendMessageOptions {
656
657
  * POSTs the values to that URL.
657
658
  * - `{ via: "client", url }` — same, written out.
658
659
  * - `{ via: "client", handler }` — the browser runs your own submit logic.
659
- * - `{ via: "backend", destination }` — the browser relays the values to the backend,
660
- * which forwards them to the server-configured destination (URL + secret stay server-side).
660
+ * - `{ via: "backend" }` — the browser relays the values to the backend, which resolves
661
+ * the destination from the form's backend-stored definition (name, URL and secret all
662
+ * stay server-side). `destination` is deprecated and ignored.
661
663
  * - `false` — collect only, no submit function.
662
664
  */
663
665
  declare type SubmitConfig<V = Record<string, unknown>> = string | {
@@ -668,7 +670,7 @@ declare type SubmitConfig<V = Record<string, unknown>> = string | {
668
670
  handler: (values: V) => Promise<unknown> | unknown;
669
671
  } | {
670
672
  via: "backend";
671
- destination: string;
673
+ destination?: string;
672
674
  } | false;
673
675
 
674
676
  /**
package/dist/angular.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as o } from "./AgoClient-bQozE6-A.js";
1
+ import { t as o } from "./AgoClient-j1sk1hK5.js";
2
2
  var n = class {
3
3
  constructor(t) {
4
4
  this.client = new o(t), this.messages$ = this.fromEvent("message:complete"), this.chunks$ = this.fromEvent("message:chunk"), this.errors$ = this.fromEvent("message:error"), this.messageStart$ = this.fromEvent("message:start");
@@ -1 +1 @@
1
- const d=require("./createFormCollector-CdQjGAl_.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-B8V9YSGe.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-D-TpSd9x.js";
1
+ import { i as d } from "./createFormCollector-BPKzKDeX.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 O=require("./AgoClient-1jgDndKs.cjs");function _(o,e){const i=e?e.storage??(typeof localStorage<"u"?localStorage:void 0):void 0;let t=o;if(e&&i)try{const c=i.getItem(e.key);c!=null&&(t=JSON.parse(c))}catch{}const a=new Set,f=()=>a.forEach(c=>c(t));function d(){if(e&&i)try{i.setItem(e.key,JSON.stringify(t))}catch{}}return{get:()=>t,set:c=>{t=c,d(),f()},subscribe:c=>(a.add(c),()=>{a.delete(c)})}}var k=/^[a-zA-Z0-9_-]{1,40}$/,P=o=>o==null||o==="";function q(o,e){const i=e[o.property];if(P(i))return!1;if(Array.isArray(o.value))return o.value.some(f=>String(i)===String(f));if(!o.op)return String(i)===String(o.value);const t=Number(i),a=Number(o.value);if(Number.isNaN(t)||Number.isNaN(a))return!1;switch(o.op){case">=":return t>=a;case"<=":return t<=a;case">":return t>a;case"<":return t<a;default:return O.logger.warn(`Unknown requiredWhen op "${o.op}"; treating as not required.`),!1}}function p(o,e){const i=o.properties??{},t=new Set;for(const f of o.required??[]){const d=i[f]?.requiredWhen;(!d||q(d,e))&&t.add(f)}for(const[f,d]of Object.entries(i))d.requiredWhen&&q(d.requiredWhen,e)&&t.add(f);const a=[...t].filter(f=>P(e[f]));return{missing:a,complete:a.length===0}}function W(o){const e={};for(const[i,t]of Object.entries(o.properties??{}))e[i]={type:t.type,...t.description!=null&&{description:t.description},...t.enum&&{enum:t.enum},...t.default!==void 0&&{default:t.default}};return{type:"object",properties:e,required:[]}}function j(o){const{name:e,description:i,schema:t}=o;if(!k.test(e))throw new Error(`Invalid form collector name "${e}": must match ${k} (it becomes the update_/submit_ function names).`);const a=typeof o.submit=="string"?o.submit?{via:"client",url:o.submit}:!1:o.submit??!1,f=t.properties??{},d=r=>{const n={};for(const[u,s]of Object.entries(r)){const l=f[u];if(l)if(s==null)n[u]=s;else if(l.type==="number"||l.type==="integer"){const b=typeof s=="number"?s:Number(s);n[u]=Number.isNaN(b)?s:b}else l.type==="boolean"?n[u]=typeof s=="boolean"?s:s==="true"?!0:s==="false"?!1:!!s:n[u]=s}return n},c=o.initialValues??{},m=_({values:c,submitted:!1}),v=r=>{const n=m.get(),u={...n,values:{...n.values,...r}};return m.set(u),u};let h=null;const N=async()=>{const{values:r}=m.get(),{complete:n,missing:u}=p(t,r);if(!n)return{ok:!1,missing:u};if(!a)return{ok:!1,error:"No submit target configured for this form collector."};const s=r;try{let l;if(a.via==="client")if("handler"in a)l=await a.handler(s);else{const b=await fetch(a.url,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify(s)});if(!b.ok)return{ok:!1,error:`Submit failed: HTTP ${b.status}`};l=await b.json().catch(()=>{})}else{if(!h)return{ok:!1,error:"Backend submit requires install(client) (or useFormCollector) first."};l=await h.submitFormCollector(e,s)}return m.set({...m.get(),submitted:!0}),{ok:!0,result:l}}catch(l){const b=l instanceof Error?l.message:"Submit failed";return O.logger.error(`FormCollector "${e}" submit failed:`,l),{ok:!1,error:b}}},S={name:`update_${e}`,description:`Fill or update fields of the "${e}" form. ${i} 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:W(t),handler:r=>{const n=v(d(r)),{missing:u}=p(t,n.values);return{ok:!0,values:n.values,missing:u}}},g=[S];a&&g.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 T="client__",C=a?`submit_${e}`:null,E=r=>{const n=r.functionName??r.toolName;if(n)return n.startsWith(T)?n.slice(8):n},$=r=>{let n={...c},u=!1;for(const s of r){const l=E(s);l===S.name?s.arguments&&(n={...n,...d(s.arguments)}):C&&l===C&&s.status?.toLowerCase()==="done"&&(u=!0)}m.set({values:n,submitted:u})},y=`form:${e}`,w=()=>{const{values:r,submitted:n}=m.get(),{missing:u,complete:s}=p(t,r),l=a?` When nothing is missing and the user confirms, call submit_${e}.`:"",b=n?`The "${e}" form has been submitted.`:`You are currently collecting information from the user to fill the "${e}" form.`;return{name:`Form: ${e}`,description:`${b} ${i} 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(t)}. Data collected so far: ${JSON.stringify(r)}. Call update_${e} with any fields the user provides; ask only for what is still missing.${l}`,data:{values:r,missing:u,complete:s,submitted:n}}},F=r=>{$((r.messages??[]).flatMap(n=>n.toolCalls??[]))};return{name:e,store:m,functions:g,contextKey:y,contextProvider:w,install:r=>{h=r;for(const u of g)r.register(u);r.addDynamicContext(y,w);const n=m.subscribe(()=>r.notifyContextChanged());return r.on("conversation:loaded",F),()=>{n();for(const u of g)r.unregisterFunction(u.name);r.removeDynamicContext(y),r.off("conversation:loaded",F),h===r&&(h=null)}},hydrate:$,submit:N,getValues:()=>m.get().values,setValues:r=>{v(d(r))},reset:()=>{m.set({values:c,submitted:!1})},getStatus:()=>p(t,m.get().values)}}async function x(o,e){const i=await o.getFormCollector(e.name);return j({name:i.name,description:e.description??i.description,schema:e.schema??i.schema,submit:e.submit??i.submit,initialValues:e.initialValues})}Object.defineProperty(exports,"createFormCollector",{enumerable:!0,get:function(){return j}});Object.defineProperty(exports,"createStore",{enumerable:!0,get:function(){return _}});Object.defineProperty(exports,"deriveFormStatus",{enumerable:!0,get:function(){return p}});Object.defineProperty(exports,"loadFormCollector",{enumerable:!0,get:function(){return x}});
@@ -1,4 +1,4 @@
1
- import { c as _ } from "./AgoClient-bQozE6-A.js";
1
+ import { c as _ } from "./AgoClient-j1sk1hK5.js";
2
2
  function E(n, e) {
3
3
  const i = e ? e.storage ?? (typeof localStorage < "u" ? localStorage : void 0) : void 0;
4
4
  let t = n;
@@ -137,7 +137,7 @@ function W(n) {
137
137
  ok: !1,
138
138
  error: "Backend submit requires install(client) (or useFormCollector) first."
139
139
  };
140
- l = await b.submitFormCollector(a.destination, o);
140
+ l = await b.submitFormCollector(e, o);
141
141
  }
142
142
  return m.set({
143
143
  ...m.get(),
@@ -78,10 +78,11 @@ declare class AgoClient {
78
78
  * Relay a completed form collector's values to a server-configured destination.
79
79
  *
80
80
  * Used by `createFormCollector` / `useFormCollector` in `{ via: "backend" }` mode:
81
- * the browser never holds the destination URL or its secret — the backend resolves
82
- * `destination` (a named, opted-in tool) and forwards the values from the server.
81
+ * the browser never holds the destination name, URL, or its secret — the backend
82
+ * resolves the destination from the named form's stored definition and forwards
83
+ * the values from the server.
83
84
  */
84
- submitFormCollector(destination: string, values: Record<string, unknown>): Promise<unknown>;
85
+ submitFormCollector(name: string, values: Record<string, unknown>): Promise<unknown>;
85
86
  /**
86
87
  * Submit feedback for a message
87
88
  */
@@ -547,8 +548,9 @@ declare interface SendMessageOptions {
547
548
  * POSTs the values to that URL.
548
549
  * - `{ via: "client", url }` — same, written out.
549
550
  * - `{ via: "client", handler }` — the browser runs your own submit logic.
550
- * - `{ via: "backend", destination }` — the browser relays the values to the backend,
551
- * which forwards them to the server-configured destination (URL + secret stay server-side).
551
+ * - `{ via: "backend" }` — the browser relays the values to the backend, which resolves
552
+ * the destination from the form's backend-stored definition (name, URL and secret all
553
+ * stay server-side). `destination` is deprecated and ignored.
552
554
  * - `false` — collect only, no submit function.
553
555
  */
554
556
  declare type SubmitConfig<V = Record<string, unknown>> = string | {
@@ -559,7 +561,7 @@ declare type SubmitConfig<V = Record<string, unknown>> = string | {
559
561
  handler: (values: V) => Promise<unknown> | unknown;
560
562
  } | {
561
563
  via: "backend";
562
- destination: string;
564
+ destination?: string;
563
565
  } | false;
564
566
 
565
567
  /**
@@ -78,10 +78,11 @@ declare class AgoClient {
78
78
  * Relay a completed form collector's values to a server-configured destination.
79
79
  *
80
80
  * Used by `createFormCollector` / `useFormCollector` in `{ via: "backend" }` mode:
81
- * the browser never holds the destination URL or its secret — the backend resolves
82
- * `destination` (a named, opted-in tool) and forwards the values from the server.
81
+ * the browser never holds the destination name, URL, or its secret — the backend
82
+ * resolves the destination from the named form's stored definition and forwards
83
+ * the values from the server.
83
84
  */
84
- submitFormCollector(destination: string, values: Record<string, unknown>): Promise<unknown>;
85
+ submitFormCollector(name: string, values: Record<string, unknown>): Promise<unknown>;
85
86
  /**
86
87
  * Submit feedback for a message
87
88
  */
@@ -547,8 +548,9 @@ declare interface SendMessageOptions {
547
548
  * POSTs the values to that URL.
548
549
  * - `{ via: "client", url }` — same, written out.
549
550
  * - `{ via: "client", handler }` — the browser runs your own submit logic.
550
- * - `{ via: "backend", destination }` — the browser relays the values to the backend,
551
- * which forwards them to the server-configured destination (URL + secret stay server-side).
551
+ * - `{ via: "backend" }` — the browser relays the values to the backend, which resolves
552
+ * the destination from the form's backend-stored definition (name, URL and secret all
553
+ * stay server-side). `destination` is deprecated and ignored.
552
554
  * - `false` — collect only, no submit function.
553
555
  */
554
556
  declare type SubmitConfig<V = Record<string, unknown>> = string | {
@@ -559,7 +561,7 @@ declare type SubmitConfig<V = Record<string, unknown>> = string | {
559
561
  handler: (values: V) => Promise<unknown> | unknown;
560
562
  } | {
561
563
  via: "backend";
562
- destination: string;
564
+ destination?: string;
563
565
  } | false;
564
566
 
565
567
  /**
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const n=require("./AgoClient-_fig8FoN.cjs"),m=require("./createFormCollector-CdQjGAl_.cjs"),p=require("./createConversationSession-DJUFgOXy.cjs"),w=require("./createMockClient-zHQ0cjUH.cjs");require("./testing.cjs");const g=require("./functions-DsmzgHjC.cjs");function h(e){return e}function E(e,o){return e.on("message:complete",o),()=>e.off("message:complete",o)}function S(e,o){return e.on("message:chunk",o),()=>e.off("message:chunk",o)}function y(e,o){return e.on("message:start",o),()=>e.off("message:start",o)}function v(e,o){return e.on("message:error",o),()=>e.off("message:error",o)}function b(e,o){return e.on("toolCall:received",o),()=>e.off("toolCall:received",o)}function F(e,o){return e.on("function:invoke",o),()=>e.off("function:invoke",o)}async function*M(e,o,l){const a=[];let r=null,s=!1;const c=i=>{a.push(i),r?.()},f=i=>c({type:"start",data:i}),t=i=>c({type:"chunk",data:i}),u=i=>{c({type:"complete",data:i}),s=!0,r?.()},d=i=>{c({type:"error",data:i}),s=!0,r?.()},C=i=>c({type:"toolCall",data:i});e.on("message:start",f),e.on("message:chunk",t),e.on("message:complete",u),e.on("message:error",d),e.on("toolCall:received",C),e.sendMessage(o,l).catch(()=>{s=!0,r?.()});try{for(;!s||a.length>0;)a.length>0?yield a.shift():(await new Promise(i=>{r=i}),r=null)}finally{e.off("message:start",f),e.off("message:chunk",t),e.off("message:complete",u),e.off("message:error",d),e.off("toolCall:received",C)}}function A(e){let o,l,a,r,s,c;if(typeof window<"u"&&window.AGO){const t=window.AGO;o=t.basepath,l=t.widgetId,a=t.agent||t.defaultAgent,r=t.permission,s=t.email,c=t.jwt}if(typeof document<"u"){const t=u=>document.querySelector(`meta[name="${u}"]`)?.getAttribute("content")||void 0;o=o||t("ago-base-url"),l=l||t("ago-widget-id"),a=a||t("ago-agent")||t("ago-agent-id"),r=r||t("ago-permission"),s=s||t("ago-user-email")}if(typeof document<"u"){const t=[document.body,...Array.from(document.querySelectorAll("script[data-ago-base-url]"))].filter(Boolean);for(const u of t)u&&(o=o||u.getAttribute("data-ago-base-url")||void 0,l=l||u.getAttribute("data-ago-widget-id")||void 0,a=a||u.getAttribute("data-ago-agent")||u.getAttribute("data-ago-agent-id")||void 0,r=r||u.getAttribute("data-ago-permission")||void 0,s=s||u.getAttribute("data-ago-user-email")||void 0)}o=e?.baseUrl||o,l=e?.widgetId||l,a=e?.agent||e?.defaultAgentId||a,r=e?.permission||r,s=e?.userEmail||s,c=e?.userJwt||c;const f=e?.debug;return o?{baseUrl:o,widgetId:l,agent:a,permission:r,userEmail:s,userJwt:c,debug:f}:null}function k(e){const o=A(e);if(!o)throw new n.AgoError('createAgo(): could not detect AGO configuration. Set window.AGO, add <meta name="ago-base-url">, or pass { baseUrl } explicitly.',"config_missing_base_url");return new n.AgoClient(o)}function q(e,o){return{...e,handler:o}}exports.AgoApiError=n.AgoApiError;exports.AgoClient=n.AgoClient;exports.AgoError=n.AgoError;exports.AgoFunctionError=n.AgoFunctionError;exports.AgoNetworkError=n.AgoNetworkError;exports.AgoStreamError=n.AgoStreamError;exports.ClientContextRegistry=n.ClientContextRegistry;exports.EventEmitter=n.EventEmitter;exports.FunctionRegistry=n.FunctionRegistry;exports.SSEHandler=n.SSEHandler;exports.autoDetectConfig=A;exports.copyToClipboard=g.copyToClipboard;exports.createAgo=k;exports.createConversationSession=p.createConversationSession;exports.createFormCollector=m.createFormCollector;exports.createMessageStream=M;exports.createMockClient=w.createMockClient;exports.createStore=m.createStore;exports.defineFunction=h;exports.deriveFormStatus=m.deriveFormStatus;exports.getLocalStorage=g.getLocalStorage;exports.getUserLocation=g.getUserLocation;exports.highlightElement=g.highlightElement;exports.isStreamNetworkError=n.isStreamNetworkError;exports.loadFormCollector=m.loadFormCollector;exports.logger=n.logger;exports.onFunctionInvoke=F;exports.onMessage=E;exports.onMessageChunk=S;exports.onMessageError=v;exports.onMessageStart=y;exports.onToolCall=b;exports.openUrl=g.openUrl;exports.scrollToElement=g.scrollToElement;exports.setLocalStorage=g.setLocalStorage;exports.setTheme=g.setTheme;exports.showConfirmDialog=g.showConfirmDialog;exports.showNotification=g.showNotification;exports.showToast=g.showToast;exports.submitForm=g.submitForm;exports.trackEvent=g.trackEvent;exports.validateConfig=n.validateConfig;exports.withHandler=q;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const n=require("./AgoClient-1jgDndKs.cjs"),m=require("./createFormCollector-B8V9YSGe.cjs"),p=require("./createConversationSession-BYNhYbkY.cjs"),w=require("./createMockClient-zHQ0cjUH.cjs");require("./testing.cjs");const g=require("./functions-DsmzgHjC.cjs");function h(e){return e}function E(e,o){return e.on("message:complete",o),()=>e.off("message:complete",o)}function S(e,o){return e.on("message:chunk",o),()=>e.off("message:chunk",o)}function y(e,o){return e.on("message:start",o),()=>e.off("message:start",o)}function v(e,o){return e.on("message:error",o),()=>e.off("message:error",o)}function b(e,o){return e.on("toolCall:received",o),()=>e.off("toolCall:received",o)}function F(e,o){return e.on("function:invoke",o),()=>e.off("function:invoke",o)}async function*M(e,o,l){const a=[];let r=null,s=!1;const c=i=>{a.push(i),r?.()},f=i=>c({type:"start",data:i}),t=i=>c({type:"chunk",data:i}),u=i=>{c({type:"complete",data:i}),s=!0,r?.()},d=i=>{c({type:"error",data:i}),s=!0,r?.()},C=i=>c({type:"toolCall",data:i});e.on("message:start",f),e.on("message:chunk",t),e.on("message:complete",u),e.on("message:error",d),e.on("toolCall:received",C),e.sendMessage(o,l).catch(()=>{s=!0,r?.()});try{for(;!s||a.length>0;)a.length>0?yield a.shift():(await new Promise(i=>{r=i}),r=null)}finally{e.off("message:start",f),e.off("message:chunk",t),e.off("message:complete",u),e.off("message:error",d),e.off("toolCall:received",C)}}function A(e){let o,l,a,r,s,c;if(typeof window<"u"&&window.AGO){const t=window.AGO;o=t.basepath,l=t.widgetId,a=t.agent||t.defaultAgent,r=t.permission,s=t.email,c=t.jwt}if(typeof document<"u"){const t=u=>document.querySelector(`meta[name="${u}"]`)?.getAttribute("content")||void 0;o=o||t("ago-base-url"),l=l||t("ago-widget-id"),a=a||t("ago-agent")||t("ago-agent-id"),r=r||t("ago-permission"),s=s||t("ago-user-email")}if(typeof document<"u"){const t=[document.body,...Array.from(document.querySelectorAll("script[data-ago-base-url]"))].filter(Boolean);for(const u of t)u&&(o=o||u.getAttribute("data-ago-base-url")||void 0,l=l||u.getAttribute("data-ago-widget-id")||void 0,a=a||u.getAttribute("data-ago-agent")||u.getAttribute("data-ago-agent-id")||void 0,r=r||u.getAttribute("data-ago-permission")||void 0,s=s||u.getAttribute("data-ago-user-email")||void 0)}o=e?.baseUrl||o,l=e?.widgetId||l,a=e?.agent||e?.defaultAgentId||a,r=e?.permission||r,s=e?.userEmail||s,c=e?.userJwt||c;const f=e?.debug;return o?{baseUrl:o,widgetId:l,agent:a,permission:r,userEmail:s,userJwt:c,debug:f}:null}function k(e){const o=A(e);if(!o)throw new n.AgoError('createAgo(): could not detect AGO configuration. Set window.AGO, add <meta name="ago-base-url">, or pass { baseUrl } explicitly.',"config_missing_base_url");return new n.AgoClient(o)}function q(e,o){return{...e,handler:o}}exports.AgoApiError=n.AgoApiError;exports.AgoClient=n.AgoClient;exports.AgoError=n.AgoError;exports.AgoFunctionError=n.AgoFunctionError;exports.AgoNetworkError=n.AgoNetworkError;exports.AgoStreamError=n.AgoStreamError;exports.ClientContextRegistry=n.ClientContextRegistry;exports.EventEmitter=n.EventEmitter;exports.FunctionRegistry=n.FunctionRegistry;exports.SSEHandler=n.SSEHandler;exports.autoDetectConfig=A;exports.copyToClipboard=g.copyToClipboard;exports.createAgo=k;exports.createConversationSession=p.createConversationSession;exports.createFormCollector=m.createFormCollector;exports.createMessageStream=M;exports.createMockClient=w.createMockClient;exports.createStore=m.createStore;exports.defineFunction=h;exports.deriveFormStatus=m.deriveFormStatus;exports.getLocalStorage=g.getLocalStorage;exports.getUserLocation=g.getUserLocation;exports.highlightElement=g.highlightElement;exports.isStreamNetworkError=n.isStreamNetworkError;exports.loadFormCollector=m.loadFormCollector;exports.logger=n.logger;exports.onFunctionInvoke=F;exports.onMessage=E;exports.onMessageChunk=S;exports.onMessageError=v;exports.onMessageStart=y;exports.onToolCall=b;exports.openUrl=g.openUrl;exports.scrollToElement=g.scrollToElement;exports.setLocalStorage=g.setLocalStorage;exports.setTheme=g.setTheme;exports.showConfirmDialog=g.showConfirmDialog;exports.showNotification=g.showNotification;exports.showToast=g.showToast;exports.submitForm=g.submitForm;exports.trackEvent=g.trackEvent;exports.validateConfig=n.validateConfig;exports.withHandler=q;