@useago/sdk 1.4.4 → 1.5.0
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/CHANGELOG.md +15 -1
- package/dist/{AgoClient-Ozz2b1dQ.js → AgoClient-q39FIjSC.js} +1 -1
- package/dist/{AgoClient-CLmtYTJx.cjs → AgoClient-wcPRaIMs.cjs} +1 -1
- package/dist/angular.cjs +1 -1
- package/dist/angular.d.cts +45 -3
- package/dist/angular.d.ts +45 -3
- package/dist/angular.js +5 -1
- package/dist/autoContinue-CcCTva-D.cjs +1 -0
- package/dist/autoContinue-D3Mmaaca.js +63 -0
- package/dist/{createConversationSession-CUkNp6mZ.cjs → createConversationSession-BBYfQLLS.cjs} +1 -1
- package/dist/{createConversationSession-QIBN0gLx.js → createConversationSession-DCSWShaW.js} +1 -1
- package/dist/{createFormCollector-DHT1ObOb.cjs → createFormCollector-eimwwWEi.cjs} +1 -1
- package/dist/{createFormCollector-F3YJfsbg.js → createFormCollector-sB6rMccD.js} +1 -1
- package/dist/devtools.d.cts +3 -3
- package/dist/devtools.d.ts +3 -3
- package/dist/helpers.cjs +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +52 -3
- package/dist/index.d.ts +52 -3
- package/dist/index.js +45 -43
- package/dist/react.cjs +3 -3
- package/dist/react.d.cts +5 -17
- package/dist/react.d.ts +5 -17
- package/dist/react.js +396 -454
- package/dist/testing.cjs +1 -1
- package/dist/testing.d.cts +3 -3
- package/dist/testing.d.ts +3 -3
- package/dist/testing.js +1 -1
- package/dist/vue.cjs +1 -1
- package/dist/vue.d.cts +3 -3
- package/dist/vue.d.ts +3 -3
- package/dist/vue.js +1 -1
- package/dist/widget.cjs +1 -1
- package/dist/widget.d.cts +3 -3
- package/dist/widget.d.ts +3 -3
- package/dist/widget.js +3 -3
- package/llms-full.txt +26 -4
- package/package.json +1 -1
- /package/dist/{createMockClient-bAZ9KBHC.js → createMockClient-BUc9PSbo.js} +0 -0
- /package/dist/{createMockClient-BSr-UV8R.cjs → createMockClient-CjdCqixj.cjs} +0 -0
- /package/dist/{functions-Djnq-JAo.cjs → functions-IGSjjvF4.cjs} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,10 +5,18 @@ All notable changes to `@useago/sdk` are documented here.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [
|
|
8
|
+
## [1.5.0] - 2026-07-07
|
|
9
9
|
|
|
10
10
|
### Added
|
|
11
11
|
|
|
12
|
+
- `attachAutoContinueAfterNavigation(client, options?)`: the auto-continue
|
|
13
|
+
behavior ("go to page B and change X" in one user message) is now a
|
|
14
|
+
framework-agnostic core export, returning a detach function. The React hook
|
|
15
|
+
keeps its API and delegates to it.
|
|
16
|
+
- Angular: `AgoService.enableAutoContinueAfterNavigation(options?)` enables
|
|
17
|
+
auto-continue after navigation and returns the disable function (call it in
|
|
18
|
+
`ngOnDestroy`). `AgoAutoContinueOptions` is exported from
|
|
19
|
+
`@useago/sdk/angular`.
|
|
12
20
|
- `AgoConfig.warnOnEmptyReply` (default `true`) and a `message:empty` event:
|
|
13
21
|
a reply that completes `DONE` with no content, tool calls, or follow-ups
|
|
14
22
|
(usually an unknown `agent` slug) now warns on the console once per
|
|
@@ -25,6 +33,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
25
33
|
|
|
26
34
|
### Changed
|
|
27
35
|
|
|
36
|
+
- `clientFunctionsMode` now defaults to `"pause"`: with client functions
|
|
37
|
+
registered, the turn stops on the client function call(s) and resumes the
|
|
38
|
+
SAME turn once the results are submitted, so the agent sees the real
|
|
39
|
+
results. Backends without pause/resume support ignore the flag and keep the
|
|
40
|
+
old placeholder behavior. Set `clientFunctionsMode: "placeholder"` (config
|
|
41
|
+
or per message) to opt back into the legacy mode.
|
|
28
42
|
- `new AgoClient({})` (or missing/empty `baseUrl`) now throws an actionable
|
|
29
43
|
`AgoError` (`config_missing_base_url`) instead of a raw `TypeError`.
|
|
30
44
|
Same-origin `/path` baseUrls remain supported; a baseUrl with no protocol
|
|
@@ -540,7 +540,7 @@ var V = class A {
|
|
|
540
540
|
return i.status !== "WAITING_CLIENT" && this.maybeFlagEmptyReply(i, s), i;
|
|
541
541
|
}
|
|
542
542
|
resolveClientFunctionsMode(t) {
|
|
543
|
-
return t?.clientFunctionsMode ?? this.config.clientFunctionsMode ?? "
|
|
543
|
+
return t?.clientFunctionsMode ?? this.config.clientFunctionsMode ?? "pause";
|
|
544
544
|
}
|
|
545
545
|
registerResumeGate(t) {
|
|
546
546
|
return this.resumeGate = t, () => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
var d=class extends Error{constructor(e,t,n){super(e),this.code=t,this.statusCode=n,this.name="AgoError"}},f=class A extends d{constructor(t,n,i,s,o,a){super(t,n,i),this.type=s,this.param=o,this.docUrl=a,this.name="AgoApiError"}static fromResponse(t,n){const i=t.error;return new A(i.doc_url?`${i.message} See ${i.doc_url}`:i.message,i.code,n,i.type,i.param,i.doc_url)}},g=class extends d{constructor(e,t){super(e,"network_error"),this.originalError=t,this.name="AgoNetworkError"}},v=class extends d{constructor(e,t="stream_error"){super(e,t),this.name="AgoStreamError"}},b=class extends d{constructor(e,t,n){super(e,"function_error"),this.functionName=t,this.originalError=n,this.name="AgoFunctionError"}},P=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)}},l=new P,S="ago_widget_id";function O(){if(typeof localStorage<"u"){const t=localStorage.getItem(S);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(S,e)}catch{}return e}var j=class{constructor(e){this.baseUrl=e.baseUrl.replace(/\/$/,""),this.headers={"X-User-Anon-Id":e.widgetId||O()},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 n=`${this.baseUrl}${e}`;l.debug("POST (stream)",n,t);try{const i=await fetch(n,{method:"POST",headers:{...this.headers,"Content-Type":"application/json"},body:t?JSON.stringify(t):void 0});return i.ok||await this.handleErrorResponse(i),i}catch(i){throw i instanceof f?i:new g(`Network error: ${i instanceof Error?i.message:"Unknown error"}. Check that \`baseUrl\` is reachable and includes the protocol (https://).`,i instanceof Error?i:void 0)}}async postFormData(e,t){const n=`${this.baseUrl}${e}`;l.debug("POST (formData)",n);const i={...this.headers};try{const s=await fetch(n,{method:"POST",headers:i,body:t});return s.ok||await this.handleErrorResponse(s),s}catch(s){throw s instanceof f?s:new g(`Network error: ${s instanceof Error?s.message:"Unknown error"}. Check that \`baseUrl\` is reachable and includes the protocol (https://).`,s instanceof Error?s:void 0)}}async request(e,t,n){const i=`${this.baseUrl}${t}`;l.debug(e,i,n);try{const s=await fetch(i,{method:e,headers:{...this.headers,"Content-Type":"application/json"},body:n?JSON.stringify(n):void 0});return s.ok||await this.handleErrorResponse(s),s.status===204?void 0:s.json()}catch(s){throw s instanceof f?s:new g(`Network error: ${s instanceof Error?s.message:"Unknown error"}. Check that \`baseUrl\` is reachable and includes the protocol (https://).`,s instanceof Error?s:void 0)}}async handleErrorResponse(e){let t;try{t=await e.json()}catch{}if(t?.error)throw f.fromResponse(t,e.status);let n="";throw e.status===401||e.status===403?n=" Check `userJwt` (and that it has not expired).":e.status===404&&(n=" Check that `baseUrl` points at your AGO API root."),new f(`HTTP ${e.status}: ${e.statusText}.${n}`,"http_error",e.status,"api_error")}},D=5e4,M=2e4,B=2e3,k=class{constructor(e){this.functions=new Map,this.defaultMaxResultBytes=e?.maxResultBytes??D}setDefaultMaxResultBytes(e){this.defaultMaxResultBytes=e}register(e,t,n){if(typeof e=="object"){const{name:a,handler:r,description:c,parameters:u,maxResultBytes:h}=e;return this.register(a,r,{description:c,parameters:u,maxResultBytes:h})}const i=e;if(!t||!n)throw new d(`registerFunction("${i}"): 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(i)&&l.warn(`Function "${i}" is being overwritten`);const{maxResultBytes:s,...o}=n;this.functions.set(i,{schema:{...o,name:i},handler:t,maxResultBytes:s}),l.log(`Registered function: ${i}`)}unregister(e){const t=this.functions.delete(e);return t&&l.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 n=this.functions.get(e);if(!n)throw new b(`Function "${e}" is not registered`,e);l.log(`Executing function: ${e}`,t);try{const i=await n.handler(t);return l.log(`Function ${e} completed:`,i),this.guardResultSize(e,i,n.maxResultBytes)}catch(i){throw l.error(`Function ${e} failed:`,i),new b(`Function "${e}" execution failed: ${i instanceof Error?i.message:"Unknown error"}`,e,i instanceof Error?i:void 0)}}guardResultSize(e,t,n){let i;try{i=JSON.stringify(t)}catch{return t}if(i===void 0)return t;const s=new TextEncoder().encode(i).length,o=n??this.defaultMaxResultBytes;return s>o?(l.warn(`Function "${e}" result is ${s} bytes, over the ${o}-byte limit; a truncated preview was sent to the agent instead. Return fewer fields, paginate, or raise maxResultBytes for this function.`),{truncated:!0,originalBytes:s,maxBytes:o,preview:i.slice(0,B),hint:`The result of "${e}" exceeded the ${o}-byte limit and was truncated. Request less data (use limit or filter parameters if available), or tell the user the result is too large to process.`}):(s>M&&l.warn(`Function "${e}" returned a large result (${Math.round(s/1024)} KB). Large results consume LLM context: return fewer fields, paginate, or push the data to the UI and return a summary.`),t)}clear(){this.functions.clear(),l.log("Cleared all registered functions")}get size(){return this.functions.size}},F=class{constructor(){this.entries=new Map,this.dynamicProviders=new Map}set(e,t){this.entries.set(e,t),l.log(`ClientContext set: ${e}`)}remove(e){const t=this.entries.delete(e);return t&&l.log(`ClientContext removed: ${e}`),t}addDynamicProvider(e,t){this.dynamicProviders.set(e,t),l.log(`DynamicContext provider added: ${e}`)}removeDynamicProvider(e){const t=this.dynamicProviders.delete(e);return t&&l.log(`DynamicContext provider removed: ${e}`),t}getSnapshot(){if(this.entries.size===0&&this.dynamicProviders.size===0)return null;const e={};for(const[t,n]of this.entries)e[t]=n;for(const[t,n]of this.dynamicProviders)try{const i=n();i&&(e[t]=i)}catch(i){l.error(`DynamicContext provider "${t}" threw:`,i)}return Object.keys(e).length===0?null:{entries:e}}clear(){this.entries.clear(),this.dynamicProviders.clear(),l.log("ClientContext cleared")}};function C(e){return e===null||typeof e!="object"?JSON.stringify(e)??"null":Array.isArray(e)?`[${e.map(C).join(",")}]`:`{${Object.keys(e).sort().map(t=>`${JSON.stringify(t)}:${C(e[t])}`).join(",")}}`}var I=class{constructor(e){this.callbacks=e,this.buffer="",this.message={},this.toolCalls=[],this.sources=[],this.followUpReplies=[],this.isFirstChunk=!0,this.answerCompleteEmitted=!1,this.waitingClientEmitted=!1,this.firedClientFunctions=new Set}async processStream(e){if(!e.body)throw new v("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(),n=new TextDecoder;try{for(;;){const{done:i,value:s}=await t.read();if(i)break;const o=n.decode(s,{stream:!0});this.buffer+=o,this.processBuffer()}return this.buffer.trim()&&this.processBuffer(),this.buildFinalMessage()}catch(i){const s=i instanceof Error?i:new v("Stream processing failed mid-flight. The connection may have dropped; retry the message.");throw this.callbacks.onError?.(s),s}finally{t.releaseLock()}}processBuffer(){const e=this.buffer.split(`
|
|
2
2
|
|
|
3
3
|
`);this.buffer=e.pop()||"";for(const t of e){if(!t.trim())continue;const n=t.split(`
|
|
4
|
-
`);for(const i of n)if(!i.startsWith(": ")&&i.startsWith("data: ")){const s=i.slice(6);try{const o=JSON.parse(s);this.handleChunk(o)}catch(o){l.warn("Failed to parse SSE data:",s,o)}}}}handleChunk(e){if(this.callbacks.onRawChunk?.(e),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 n=t.knowledge_document;return{id:n.id,title:n.title,url:n.use_external_link?n.external_link_url:n.internal_link_url}})),e.type==="client_function"&&e.function_name){const t=`${e.function_name}::${C(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 n=this.toolCalls.findIndex(i=>i.id===t.id);n>=0?this.toolCalls[n]=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())),this.message.status==="WAITING_CLIENT"&&!this.waitingClientEmitted&&this.message.id&&(this.waitingClientEmitted=!0,this.callbacks.onWaitingClient?.({conversationId:this.message.conversationId||"",messageId:this.message.id,waitingToolCallIds:e.waiting_tool_call_ids??[]}))}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 e.status!=="WAITING_CLIENT"&&this.callbacks.onComplete?.(e),e}};function L(e){const t=e.message.toLowerCase();return t.includes("load failed")||t.includes("failed to fetch")||t.includes("network")||t.includes("abort")}var G=new Set(["image/jpeg","image/png","image/gif","image/webp","image/bmp","image/tiff"]);function U(e){if(!e)return;const t=typeof window<"u"&&window.location?window.location.href:"https://ago.local/";try{const n=new URL(e,t);if(n.protocol==="http:"||n.protocol==="https:"||n.protocol==="blob:")return e}catch{}}function W(e){return e.isSafeImage===!0&&typeof e.contentType=="string"&&e.contentType.startsWith("image/")&&U(e.url)!==void 0}function z(e){if(e==null||e<0)return"";if(e<1024)return`${e} B`;const t=e/1024;return t<1024?`${t.toFixed(1)} KB`:`${(t/1024).toFixed(1)} MB`}function J(e){return{id:String(e.id??""),name:typeof e.name=="string"&&e.name?e.name:"file",contentType:typeof e.content_type=="string"?e.content_type:void 0,fileSize:typeof e.file_size=="number"?e.file_size:void 0,url:typeof e.file_url=="string"?e.file_url:void 0,isSafeImage:e.is_safe_image===!0}}function q(e){return e.map((t,n)=>({id:`local-${n}-${t.name}`,name:t.name,contentType:t.type||void 0,fileSize:t.size,url:typeof URL<"u"&&URL.createObjectURL?URL.createObjectURL(t):void 0,isSafeImage:G.has(t.type)}))}var N=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 n=this.handlers.get(e);n&&n.delete(t)}emit(e,t){const n=this.handlers.get(e);n&&n.forEach(i=>{try{i(t)}catch(s){console.error(`Error in event handler for ${String(e)}:`,s)}})}once(e,t){const n=(i=>{this.off(e,n),t(i)});this.on(e,n)}waitFor(e,t){return new Promise((n,i)=>{let s;const o=(a=>{s&&clearTimeout(s),n(a)});this.once(e,o),t?.timeout&&(s=setTimeout(()=>{this.off(e,o),i(new Error(`waitFor("${String(e)}") timed out after ${t.timeout}ms`))},t.timeout))})}removeAllListeners(e){e?this.handlers.delete(e):this.handlers.clear()}},$='`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).',H=/^[a-z][a-z0-9+.-]*:\/\//i,x=!1;function E(e,t="AgoClient"){if(typeof e!="object"||e===null)throw new d(`${t}: ${$}`,"config_missing_base_url");const n=e.baseUrl;if(typeof n!="string"||n.trim()==="")throw new d(`${t}: ${$}`,"config_missing_base_url");!n.startsWith("/")&&!H.test(n)&&(x||(x=!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).`)))}function K(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function w(e){return e.split("/").filter(t=>t.startsWith(":")).map(t=>t.slice(1))}function X(e,t){const n=[];return{path:e.split("/").map(i=>{if(!i.startsWith(":"))return i;const s=i.slice(1),o=t[s];return o==null||o===""?(n.push(s),i):encodeURIComponent(String(o))}).join("/"),missing:n}}function Y(e,t){const n=t.find(s=>s.path===e);if(n)return n;const i=t.find(s=>{if(!s.path.includes(":"))return!1;const o="^"+s.path.split("/").map(a=>a.startsWith(":")?"[^/]+":K(a)).join("/")+"/?$";return new RegExp(o).test(e)});return i||t.filter(s=>s.path!=="/"&&e.startsWith(s.path)).sort((s,o)=>o.path.length-s.path.length)[0]}var V=class T{constructor(t){this.warnedEmptyConversations=new Set,this.pendingResumes=new Map,this.resumeGate=null,E(t,"AgoClient"),this.config=t,this.httpClient=new j(t),this.functionRegistry=new k({maxResultBytes:t.maxFunctionResultBytes}),this.contextRegistry=new F,this.eventEmitter=new N,t.debug&&l.enable(),l.log("AgoClient initialized")}async sendMessage(t,n){const i=this.functionRegistry.getSchemas(),s=this.contextRegistry.getSnapshot(),o=this.config.agent||this.config.defaultAgentId,a=this.resolveClientFunctionsMode(n),r={content:t,conversation_id:n?.conversationId,agent_id:n?.agentId||o};i.length>0&&(r.client_functions=i,a==="pause"&&(r.client_functions_mode=a)),s&&(r.client_context=s),n?.hidden&&(r.hidden=!0);let c;if(n?.files&&n.files.length>0){const u=new FormData;u.append("content",t),n.conversationId&&u.append("conversation_id",n.conversationId),(n.agentId||o)&&u.append("agent_id",n.agentId||o||""),i.length>0&&(u.append("client_functions",JSON.stringify(i)),a==="pause"&&u.append("client_functions_mode",a)),s&&u.append("client_context",JSON.stringify(s)),n.hidden&&u.append("hidden","true");for(const h of n.files)u.append("files",h);c=await this.httpClient.postFormData("/api/sdk/v1/messages",u)}else c=await this.httpClient.postStream("/api/sdk/v1/messages",r);return this.processSSEResponse(c)}async processSSEResponse(t){let n=!1;const i=await new I({onRawChunk:s=>{this.eventEmitter.emit("stream:message",s)},onStart:s=>{this.eventEmitter.emit("message:start",s)},onChunk:s=>{this.eventEmitter.emit("message:chunk",s)},onToolCall:s=>{this.eventEmitter.emit("toolCall:received",s),s.type==="form"&&this.eventEmitter.emit("toolCall:form",s)},onClientFunction:async s=>{n=!0,this.eventEmitter.emit("function:invoke",s),await this.handleClientFunctionInvocation(s)},onAnswerComplete:s=>{this.eventEmitter.emit("message:answer-complete",s)},onWaitingClient:s=>{this.eventEmitter.emit("message:waiting-client",s),this.markResumeSignal(s.messageId,s.conversationId,"waitingConfirmed")},onComplete:s=>{this.eventEmitter.emit("message:complete",s)},onError:s=>{this.eventEmitter.emit("message:error",{error:s.message,code:s instanceof d?s.code:void 0})}}).processStream(t);return i.status!=="WAITING_CLIENT"&&this.maybeFlagEmptyReply(i,n),i}resolveClientFunctionsMode(t){return t?.clientFunctionsMode??this.config.clientFunctionsMode??"placeholder"}registerResumeGate(t){return this.resumeGate=t,()=>{this.resumeGate===t&&(this.resumeGate=null)}}async continueMessage(t){const n=this.functionRegistry.getSchemas(),i=this.contextRegistry.getSnapshot(),s={};n.length>0&&(s.client_functions=n),i&&(s.client_context=i);const o=await this.httpClient.postStream(`/api/sdk/v1/messages/${t}/continue`,s);return this.processSSEResponse(o)}markResumeSignal(t,n,i){if(!t)return;const s=this.pendingResumes.get(t)??{conversationId:n,waitingConfirmed:!1,resultsReady:!1,started:!1};s[i]=!0,n&&(s.conversationId=n),this.pendingResumes.set(t,s),!(!s.waitingConfirmed||!s.resultsReady||s.started)&&(s.started=!0,this.runAutoResume(t,s.conversationId))}async runAutoResume(t,n){try{this.resumeGate&&await this.resumeGate({conversationId:n,messageId:t})}catch(i){l.warn("Resume gate threw; resuming anyway:",i)}this.pendingResumes.delete(t);try{await this.continueMessage(t)}catch(i){l.warn("Auto-resume of paused turn failed:",i)}}maybeFlagEmptyReply(t,n){t.status==="DONE"&&(t.content??"").trim()===""&&!n&&!(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 i=t.conversationId||"(no conversation)";if(this.warnedEmptyConversations.has(i))return;this.warnedEmptyConversations.add(i);const s=this.config.agent||this.config.defaultAgentId;t.id?console.warn("[AGO] Reply completed with empty content. Possible causes: "+(s?`the agent "${s}" 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: "+(s?`the agent "${s}" 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 n,i;try{n=await this.functionRegistry.execute(t.functionName,t.arguments)}catch(s){i=s instanceof Error?s.message:"Unknown error",l.error("Client function execution failed:",s)}if(t.invocationId)try{const s=await this.submitToolCallForm(t.invocationId,{result:n,error:i,_type:"client_function_result"});s?.resume?.ready&&this.markResumeSignal(s.resume.message_id,t.conversationId,"resultsReady")}catch(s){l.error("Failed to submit function result:",s)}this.eventEmitter.emit("function:result",{invocationId:t.invocationId,result:n,error:i})}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 n=await this.httpClient.get(`/api/sdk/v1/conversations/${t}`),i={id:n.id,title:n.title,lastMessageDate:new Date(n.last_message_date),messages:n.messages.map(s=>({id:s.id,conversationId:n.id,content:s.content,role:s.role,status:s.status,createdAt:new Date(s.created_at),hidden:s.hidden,toolCalls:T.mapPersistedToolCalls(s.tool_call_data),attachments:s.attachments&&s.attachments.length>0?s.attachments.map(J):void 0}))};return this.eventEmitter.emit("conversation:loaded",i),i}static mapPersistedToolCalls(t){if(!(!t||t.length===0))return t.map(n=>({id:String(n.id??""),type:n.type??"status_message",status:String(n.status??"unknown"),toolName:n.tool_name??"",toolDisplayName:n.tool_display_name,message:n.message,formSchema:n.form_schema,data:n.data,functionName:n.function_name,arguments:n.arguments}))}async getMessages(t){return(await this.getConversation(t)).messages||[]}async resumePendingClientFunctions(t){const n=[...t.messages??[]].reverse().find(r=>r.role==="assistant"&&r.status==="WAITING_CLIENT");if(!n)return null;const i=(n.toolCalls??[]).filter(r=>r.type==="client_function"&&r.status==="waiting_input"&&r.functionName),s=new Set(this.functionRegistry.getSchemas().map(r=>r.name)),o=i.filter(r=>!s.has(r.functionName));if(o.length>0)return l.warn("Cannot resume paused turn: function(s) not registered on this page:",o.map(r=>r.functionName)),null;let a=i.length===0;for(const r of i){let c,u;try{c=await this.functionRegistry.execute(r.functionName,r.arguments??{})}catch(h){u=h instanceof Error?h.message:"Unknown error",l.error("Client function execution failed:",h)}a=(await this.submitToolCallForm(r.id,{result:c,error:u,_type:"client_function_result"}))?.resume?.ready??a}return a?this.continueMessage(n.id):null}async submitToolCallForm(t,n){return this.httpClient.post(`/api/sdk/v1/tool-calls/${t}/submit`,{formData:n})}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 n=await this.httpClient.get(`/api/sdk/v1/forms/${encodeURIComponent(t)}`);return{name:n.name,description:n.description,schema:n.schema,submit:n.submit??void 0}}async submitFormCollector(t,n){return this.httpClient.post("/api/sdk/v1/forms/submit",{name:t,values:n})}async submitFeedback(t,n){await this.httpClient.post(`/api/sdk/v1/messages/${t}/feedback`,{rating:n})}registerFunction(t,n,i){typeof t=="object"?this.functionRegistry.register(t):this.functionRegistry.register(t,n,i)}register(t){if(Array.isArray(t))for(const n of t)this.functionRegistry.register(n);else this.functionRegistry.register(t)}unregisterFunction(t){return this.functionRegistry.unregister(t)}getRegisteredFunctions(){return this.functionRegistry.getSchemas()}registerNavigationFunction(t,n){const i=n.map(r=>r.name),s=n.map(r=>{const c=w(r.path),u=c.length>0?` (requires ${c.map(h=>`"${h}"`).join(", ")})`:"";return`- "${r.name}"${u}: ${r.description}`}).join(`
|
|
4
|
+
`);for(const i of n)if(!i.startsWith(": ")&&i.startsWith("data: ")){const s=i.slice(6);try{const o=JSON.parse(s);this.handleChunk(o)}catch(o){l.warn("Failed to parse SSE data:",s,o)}}}}handleChunk(e){if(this.callbacks.onRawChunk?.(e),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 n=t.knowledge_document;return{id:n.id,title:n.title,url:n.use_external_link?n.external_link_url:n.internal_link_url}})),e.type==="client_function"&&e.function_name){const t=`${e.function_name}::${C(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 n=this.toolCalls.findIndex(i=>i.id===t.id);n>=0?this.toolCalls[n]=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())),this.message.status==="WAITING_CLIENT"&&!this.waitingClientEmitted&&this.message.id&&(this.waitingClientEmitted=!0,this.callbacks.onWaitingClient?.({conversationId:this.message.conversationId||"",messageId:this.message.id,waitingToolCallIds:e.waiting_tool_call_ids??[]}))}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 e.status!=="WAITING_CLIENT"&&this.callbacks.onComplete?.(e),e}};function L(e){const t=e.message.toLowerCase();return t.includes("load failed")||t.includes("failed to fetch")||t.includes("network")||t.includes("abort")}var G=new Set(["image/jpeg","image/png","image/gif","image/webp","image/bmp","image/tiff"]);function U(e){if(!e)return;const t=typeof window<"u"&&window.location?window.location.href:"https://ago.local/";try{const n=new URL(e,t);if(n.protocol==="http:"||n.protocol==="https:"||n.protocol==="blob:")return e}catch{}}function W(e){return e.isSafeImage===!0&&typeof e.contentType=="string"&&e.contentType.startsWith("image/")&&U(e.url)!==void 0}function z(e){if(e==null||e<0)return"";if(e<1024)return`${e} B`;const t=e/1024;return t<1024?`${t.toFixed(1)} KB`:`${(t/1024).toFixed(1)} MB`}function J(e){return{id:String(e.id??""),name:typeof e.name=="string"&&e.name?e.name:"file",contentType:typeof e.content_type=="string"?e.content_type:void 0,fileSize:typeof e.file_size=="number"?e.file_size:void 0,url:typeof e.file_url=="string"?e.file_url:void 0,isSafeImage:e.is_safe_image===!0}}function q(e){return e.map((t,n)=>({id:`local-${n}-${t.name}`,name:t.name,contentType:t.type||void 0,fileSize:t.size,url:typeof URL<"u"&&URL.createObjectURL?URL.createObjectURL(t):void 0,isSafeImage:G.has(t.type)}))}var N=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 n=this.handlers.get(e);n&&n.delete(t)}emit(e,t){const n=this.handlers.get(e);n&&n.forEach(i=>{try{i(t)}catch(s){console.error(`Error in event handler for ${String(e)}:`,s)}})}once(e,t){const n=(i=>{this.off(e,n),t(i)});this.on(e,n)}waitFor(e,t){return new Promise((n,i)=>{let s;const o=(a=>{s&&clearTimeout(s),n(a)});this.once(e,o),t?.timeout&&(s=setTimeout(()=>{this.off(e,o),i(new Error(`waitFor("${String(e)}") timed out after ${t.timeout}ms`))},t.timeout))})}removeAllListeners(e){e?this.handlers.delete(e):this.handlers.clear()}},$='`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).',H=/^[a-z][a-z0-9+.-]*:\/\//i,x=!1;function E(e,t="AgoClient"){if(typeof e!="object"||e===null)throw new d(`${t}: ${$}`,"config_missing_base_url");const n=e.baseUrl;if(typeof n!="string"||n.trim()==="")throw new d(`${t}: ${$}`,"config_missing_base_url");!n.startsWith("/")&&!H.test(n)&&(x||(x=!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).`)))}function K(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function w(e){return e.split("/").filter(t=>t.startsWith(":")).map(t=>t.slice(1))}function X(e,t){const n=[];return{path:e.split("/").map(i=>{if(!i.startsWith(":"))return i;const s=i.slice(1),o=t[s];return o==null||o===""?(n.push(s),i):encodeURIComponent(String(o))}).join("/"),missing:n}}function Y(e,t){const n=t.find(s=>s.path===e);if(n)return n;const i=t.find(s=>{if(!s.path.includes(":"))return!1;const o="^"+s.path.split("/").map(a=>a.startsWith(":")?"[^/]+":K(a)).join("/")+"/?$";return new RegExp(o).test(e)});return i||t.filter(s=>s.path!=="/"&&e.startsWith(s.path)).sort((s,o)=>o.path.length-s.path.length)[0]}var V=class T{constructor(t){this.warnedEmptyConversations=new Set,this.pendingResumes=new Map,this.resumeGate=null,E(t,"AgoClient"),this.config=t,this.httpClient=new j(t),this.functionRegistry=new k({maxResultBytes:t.maxFunctionResultBytes}),this.contextRegistry=new F,this.eventEmitter=new N,t.debug&&l.enable(),l.log("AgoClient initialized")}async sendMessage(t,n){const i=this.functionRegistry.getSchemas(),s=this.contextRegistry.getSnapshot(),o=this.config.agent||this.config.defaultAgentId,a=this.resolveClientFunctionsMode(n),r={content:t,conversation_id:n?.conversationId,agent_id:n?.agentId||o};i.length>0&&(r.client_functions=i,a==="pause"&&(r.client_functions_mode=a)),s&&(r.client_context=s),n?.hidden&&(r.hidden=!0);let c;if(n?.files&&n.files.length>0){const u=new FormData;u.append("content",t),n.conversationId&&u.append("conversation_id",n.conversationId),(n.agentId||o)&&u.append("agent_id",n.agentId||o||""),i.length>0&&(u.append("client_functions",JSON.stringify(i)),a==="pause"&&u.append("client_functions_mode",a)),s&&u.append("client_context",JSON.stringify(s)),n.hidden&&u.append("hidden","true");for(const h of n.files)u.append("files",h);c=await this.httpClient.postFormData("/api/sdk/v1/messages",u)}else c=await this.httpClient.postStream("/api/sdk/v1/messages",r);return this.processSSEResponse(c)}async processSSEResponse(t){let n=!1;const i=await new I({onRawChunk:s=>{this.eventEmitter.emit("stream:message",s)},onStart:s=>{this.eventEmitter.emit("message:start",s)},onChunk:s=>{this.eventEmitter.emit("message:chunk",s)},onToolCall:s=>{this.eventEmitter.emit("toolCall:received",s),s.type==="form"&&this.eventEmitter.emit("toolCall:form",s)},onClientFunction:async s=>{n=!0,this.eventEmitter.emit("function:invoke",s),await this.handleClientFunctionInvocation(s)},onAnswerComplete:s=>{this.eventEmitter.emit("message:answer-complete",s)},onWaitingClient:s=>{this.eventEmitter.emit("message:waiting-client",s),this.markResumeSignal(s.messageId,s.conversationId,"waitingConfirmed")},onComplete:s=>{this.eventEmitter.emit("message:complete",s)},onError:s=>{this.eventEmitter.emit("message:error",{error:s.message,code:s instanceof d?s.code:void 0})}}).processStream(t);return i.status!=="WAITING_CLIENT"&&this.maybeFlagEmptyReply(i,n),i}resolveClientFunctionsMode(t){return t?.clientFunctionsMode??this.config.clientFunctionsMode??"pause"}registerResumeGate(t){return this.resumeGate=t,()=>{this.resumeGate===t&&(this.resumeGate=null)}}async continueMessage(t){const n=this.functionRegistry.getSchemas(),i=this.contextRegistry.getSnapshot(),s={};n.length>0&&(s.client_functions=n),i&&(s.client_context=i);const o=await this.httpClient.postStream(`/api/sdk/v1/messages/${t}/continue`,s);return this.processSSEResponse(o)}markResumeSignal(t,n,i){if(!t)return;const s=this.pendingResumes.get(t)??{conversationId:n,waitingConfirmed:!1,resultsReady:!1,started:!1};s[i]=!0,n&&(s.conversationId=n),this.pendingResumes.set(t,s),!(!s.waitingConfirmed||!s.resultsReady||s.started)&&(s.started=!0,this.runAutoResume(t,s.conversationId))}async runAutoResume(t,n){try{this.resumeGate&&await this.resumeGate({conversationId:n,messageId:t})}catch(i){l.warn("Resume gate threw; resuming anyway:",i)}this.pendingResumes.delete(t);try{await this.continueMessage(t)}catch(i){l.warn("Auto-resume of paused turn failed:",i)}}maybeFlagEmptyReply(t,n){t.status==="DONE"&&(t.content??"").trim()===""&&!n&&!(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 i=t.conversationId||"(no conversation)";if(this.warnedEmptyConversations.has(i))return;this.warnedEmptyConversations.add(i);const s=this.config.agent||this.config.defaultAgentId;t.id?console.warn("[AGO] Reply completed with empty content. Possible causes: "+(s?`the agent "${s}" 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: "+(s?`the agent "${s}" 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 n,i;try{n=await this.functionRegistry.execute(t.functionName,t.arguments)}catch(s){i=s instanceof Error?s.message:"Unknown error",l.error("Client function execution failed:",s)}if(t.invocationId)try{const s=await this.submitToolCallForm(t.invocationId,{result:n,error:i,_type:"client_function_result"});s?.resume?.ready&&this.markResumeSignal(s.resume.message_id,t.conversationId,"resultsReady")}catch(s){l.error("Failed to submit function result:",s)}this.eventEmitter.emit("function:result",{invocationId:t.invocationId,result:n,error:i})}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 n=await this.httpClient.get(`/api/sdk/v1/conversations/${t}`),i={id:n.id,title:n.title,lastMessageDate:new Date(n.last_message_date),messages:n.messages.map(s=>({id:s.id,conversationId:n.id,content:s.content,role:s.role,status:s.status,createdAt:new Date(s.created_at),hidden:s.hidden,toolCalls:T.mapPersistedToolCalls(s.tool_call_data),attachments:s.attachments&&s.attachments.length>0?s.attachments.map(J):void 0}))};return this.eventEmitter.emit("conversation:loaded",i),i}static mapPersistedToolCalls(t){if(!(!t||t.length===0))return t.map(n=>({id:String(n.id??""),type:n.type??"status_message",status:String(n.status??"unknown"),toolName:n.tool_name??"",toolDisplayName:n.tool_display_name,message:n.message,formSchema:n.form_schema,data:n.data,functionName:n.function_name,arguments:n.arguments}))}async getMessages(t){return(await this.getConversation(t)).messages||[]}async resumePendingClientFunctions(t){const n=[...t.messages??[]].reverse().find(r=>r.role==="assistant"&&r.status==="WAITING_CLIENT");if(!n)return null;const i=(n.toolCalls??[]).filter(r=>r.type==="client_function"&&r.status==="waiting_input"&&r.functionName),s=new Set(this.functionRegistry.getSchemas().map(r=>r.name)),o=i.filter(r=>!s.has(r.functionName));if(o.length>0)return l.warn("Cannot resume paused turn: function(s) not registered on this page:",o.map(r=>r.functionName)),null;let a=i.length===0;for(const r of i){let c,u;try{c=await this.functionRegistry.execute(r.functionName,r.arguments??{})}catch(h){u=h instanceof Error?h.message:"Unknown error",l.error("Client function execution failed:",h)}a=(await this.submitToolCallForm(r.id,{result:c,error:u,_type:"client_function_result"}))?.resume?.ready??a}return a?this.continueMessage(n.id):null}async submitToolCallForm(t,n){return this.httpClient.post(`/api/sdk/v1/tool-calls/${t}/submit`,{formData:n})}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 n=await this.httpClient.get(`/api/sdk/v1/forms/${encodeURIComponent(t)}`);return{name:n.name,description:n.description,schema:n.schema,submit:n.submit??void 0}}async submitFormCollector(t,n){return this.httpClient.post("/api/sdk/v1/forms/submit",{name:t,values:n})}async submitFeedback(t,n){await this.httpClient.post(`/api/sdk/v1/messages/${t}/feedback`,{rating:n})}registerFunction(t,n,i){typeof t=="object"?this.functionRegistry.register(t):this.functionRegistry.register(t,n,i)}register(t){if(Array.isArray(t))for(const n of t)this.functionRegistry.register(n);else this.functionRegistry.register(t)}unregisterFunction(t){return this.functionRegistry.unregister(t)}getRegisteredFunctions(){return this.functionRegistry.getSchemas()}registerNavigationFunction(t,n){const i=n.map(r=>r.name),s=n.map(r=>{const c=w(r.path),u=c.length>0?` (requires ${c.map(h=>`"${h}"`).join(", ")})`:"";return`- "${r.name}"${u}: ${r.description}`}).join(`
|
|
5
5
|
`),o=new Map;for(const r of n)for(const c of w(r.path))o.set(c,[...o.get(c)??[],r.name]);const a={page:{type:"string",description:"The page to navigate to",enum:i}};for(const[r,c]of o){if(r==="page"){l.error('registerNavigationFunction: a ":page" placeholder collides with the "page" argument and is ignored. Rename the placeholder.');continue}a[r]={type:"string",description:`Value for ":${r}" in the page path. Required when page is ${c.map(u=>`"${u}"`).join(" or ")}.`}}this.registerFunction("navigateToPage",async r=>{const c=r.page,u=n.find(m=>m.name===c);if(!u)return{success:!1,error:`Unknown page: ${c}`};let h={};if(typeof r.params=="string")try{h=JSON.parse(r.params)}catch{}else r.params&&typeof r.params=="object"&&(h=r.params);const _={};for(const m of w(u.path))_[m]=r[m]??h[m];const{path:R,missing:p}=X(u.path,_);if(p.length>0){const m=p.map(y=>`"${y}": "..."`).join(", ");return{success:!1,error:`Page "${c}" needs ${p.map(y=>`"${y}"`).join(", ")}. Retry with { "page": "${c}", ${m} }.`}}return t(R),{success:!0,navigatedTo:R}},{description:`Navigate the user to a page in the application. Available pages:
|
|
6
6
|
${s}`,parameters:{type:"object",properties:a,required:["page"]}}),this.addDynamicContext("current-page",()=>{if(typeof window>"u"||!window.location)return null;const r=window.location.href,c=typeof document<"u"?document.title:void 0,u=Y(window.location.pathname,n),h={url:r};return u&&(h.page=u.name),c&&(h.title=c),{name:"Current page",description:"The page the user is currently viewing.",data:h}})}unregisterNavigationFunction(){this.unregisterFunction("navigateToPage"),this.removeDynamicContext("current-page")}registerPageStateFunction(t,n){const i=n?.functionName??"setPageState",s=new Map(t.map(a=>[a.name,a])),o=t.map(a=>`- "${a.name}": ${a.description}`).join(`
|
|
7
7
|
`);this.registerFunction(i,async a=>{const r={};for(const[c,u]of Object.entries(a)){if(u===void 0)continue;const h=s.get(c);h&&(await h.set(u),r[c]=u)}return{success:!0,applied:r}},{description:`Change the state of the current page. Set ONLY the controls the user explicitly asked for; leave the others unset. Available controls:
|
package/dist/angular.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("./AgoClient-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("./AgoClient-wcPRaIMs.cjs"),c=require("./autoContinue-CcCTva-D.cjs");var n=class{constructor(t){this.client=new o.AgoClient(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")}fromEvent(t){const e=this.client;return{subscribe(i){const r=s=>{i.next?.(s)};return e.on(t,r),{unsubscribe:()=>e.off(t,r)}}}}sendMessage(t,e){return this.client.sendMessage(t,e)}getConversations(){return this.client.getConversations()}getConversation(t){return this.client.getConversation(t)}getMessages(t){return this.client.getMessages(t)}submitToolCallForm(t,e){return this.client.submitToolCallForm(t,e)}confirmToolCall(t){return this.client.confirmToolCall(t)}rejectToolCall(t){return this.client.rejectToolCall(t)}submitFeedback(t,e){return this.client.submitFeedback(t,e)}registerFunction(t,e,i){typeof t=="object"?this.client.registerFunction(t):this.client.registerFunction(t,e,i)}unregisterFunction(t){return this.client.unregisterFunction(t)}registerNavigationFunction(t,e){this.client.registerNavigationFunction(t,e)}unregisterNavigationFunction(){this.client.unregisterNavigationFunction()}enableAutoContinueAfterNavigation(t){return c.attachAutoContinueAfterNavigation(this.client,t)}registerPageStateFunction(t,e){this.client.registerPageStateFunction(t,e)}unregisterPageStateFunction(t){this.client.unregisterPageStateFunction(t)}on(t,e){this.client.on(t,e)}off(t,e){this.client.off(t,e)}updateConfig(t){this.client.updateConfig(t)}getClient(){return this.client}destroy(){this.client.destroy()}};function u(t){return{provide:n,useValue:new n(t)}}exports.AgoService=n;exports.provideAgo=u;
|
package/dist/angular.d.cts
CHANGED
|
@@ -28,6 +28,23 @@ declare interface AgoAttachment {
|
|
|
28
28
|
isSafeImage: boolean;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
export declare interface AgoAutoContinueOptions {
|
|
32
|
+
/** Function names whose invocation counts as "navigated". Default `["navigateToPage"]`. */
|
|
33
|
+
navigationFunctions?: string[];
|
|
34
|
+
/** Registered function that signals the destination has editable state. Default `"setPageState"`. */
|
|
35
|
+
pageStateFunctionName?: string;
|
|
36
|
+
/** The hidden continuation prompt. Override for i18n / tone. */
|
|
37
|
+
continuationPrompt?: string;
|
|
38
|
+
/** Max continuations per user gesture (guards navigate→continue→navigate loops). Default `1`. */
|
|
39
|
+
maxDepth?: number;
|
|
40
|
+
/** How long to wait for the destination page to register page-state before giving up. Default `4000`ms. */
|
|
41
|
+
readinessTimeoutMs?: number;
|
|
42
|
+
/** Extra settle delay after page-state appears, to let sibling state mount. Default `150`ms. */
|
|
43
|
+
settleMs?: number;
|
|
44
|
+
/** Opt-out escape hatch. Default `true`. */
|
|
45
|
+
enabled?: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
31
48
|
/**
|
|
32
49
|
* Main SDK client for AGO Chat integration
|
|
33
50
|
*/
|
|
@@ -476,11 +493,11 @@ export declare interface AgoConfig {
|
|
|
476
493
|
warnOnEmptyReply?: boolean;
|
|
477
494
|
/**
|
|
478
495
|
* How the agent loop treats client function calls (requires backend support):
|
|
479
|
-
* - `"
|
|
480
|
-
* result; the real result is only visible to the agent on later turns.
|
|
481
|
-
* - `"pause"`: the turn stops on client function call(s) with status
|
|
496
|
+
* - `"pause"` (default): the turn stops on client function call(s) with status
|
|
482
497
|
* `WAITING_CLIENT`; once every result is submitted, the SDK resumes the SAME
|
|
483
498
|
* turn via `POST /messages/{id}/continue`, so the agent sees the real results.
|
|
499
|
+
* - `"placeholder"` (legacy): the turn continues immediately on a placeholder
|
|
500
|
+
* result; the real result is only visible to the agent on later turns.
|
|
484
501
|
* Older backends ignore the flag, so `"pause"` degrades to legacy behavior.
|
|
485
502
|
*/
|
|
486
503
|
clientFunctionsMode?: ClientFunctionsMode;
|
|
@@ -609,6 +626,31 @@ export declare class AgoService {
|
|
|
609
626
|
}>): void;
|
|
610
627
|
/** Unregister navigation (function + current-page context) */
|
|
611
628
|
unregisterNavigationFunction(): void;
|
|
629
|
+
/**
|
|
630
|
+
* Auto-continue the agent after it navigates, so a "go to page B and change X"
|
|
631
|
+
* request completes in one user gesture. Enable once (e.g. in your root
|
|
632
|
+
* component) and call the returned function on teardown:
|
|
633
|
+
*
|
|
634
|
+
* ```ts
|
|
635
|
+
* export class AppComponent implements OnDestroy {
|
|
636
|
+
* private ago = inject(AgoService);
|
|
637
|
+
* private router = inject(Router);
|
|
638
|
+
* private stopAutoContinue = this.ago.enableAutoContinueAfterNavigation();
|
|
639
|
+
*
|
|
640
|
+
* constructor() {
|
|
641
|
+
* this.ago.registerNavigationFunction((path) => this.router.navigateByUrl(path), routes);
|
|
642
|
+
* }
|
|
643
|
+
*
|
|
644
|
+
* ngOnDestroy() {
|
|
645
|
+
* this.stopAutoContinue();
|
|
646
|
+
* }
|
|
647
|
+
* }
|
|
648
|
+
* ```
|
|
649
|
+
*
|
|
650
|
+
* See {@link attachAutoContinueAfterNavigation} for the pause-mode /
|
|
651
|
+
* placeholder-mode mechanics and the available options.
|
|
652
|
+
*/
|
|
653
|
+
enableAutoContinueAfterNavigation(options?: AgoAutoContinueOptions): () => void;
|
|
612
654
|
/** Register editable page-state controls (filters, sort, view mode…) */
|
|
613
655
|
registerPageStateFunction(controls: AgoStateControl[], opts?: AgoPageStateOptions): void;
|
|
614
656
|
/** Unregister a page-state function and its dynamic context */
|
package/dist/angular.d.ts
CHANGED
|
@@ -28,6 +28,23 @@ declare interface AgoAttachment {
|
|
|
28
28
|
isSafeImage: boolean;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
export declare interface AgoAutoContinueOptions {
|
|
32
|
+
/** Function names whose invocation counts as "navigated". Default `["navigateToPage"]`. */
|
|
33
|
+
navigationFunctions?: string[];
|
|
34
|
+
/** Registered function that signals the destination has editable state. Default `"setPageState"`. */
|
|
35
|
+
pageStateFunctionName?: string;
|
|
36
|
+
/** The hidden continuation prompt. Override for i18n / tone. */
|
|
37
|
+
continuationPrompt?: string;
|
|
38
|
+
/** Max continuations per user gesture (guards navigate→continue→navigate loops). Default `1`. */
|
|
39
|
+
maxDepth?: number;
|
|
40
|
+
/** How long to wait for the destination page to register page-state before giving up. Default `4000`ms. */
|
|
41
|
+
readinessTimeoutMs?: number;
|
|
42
|
+
/** Extra settle delay after page-state appears, to let sibling state mount. Default `150`ms. */
|
|
43
|
+
settleMs?: number;
|
|
44
|
+
/** Opt-out escape hatch. Default `true`. */
|
|
45
|
+
enabled?: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
31
48
|
/**
|
|
32
49
|
* Main SDK client for AGO Chat integration
|
|
33
50
|
*/
|
|
@@ -476,11 +493,11 @@ export declare interface AgoConfig {
|
|
|
476
493
|
warnOnEmptyReply?: boolean;
|
|
477
494
|
/**
|
|
478
495
|
* How the agent loop treats client function calls (requires backend support):
|
|
479
|
-
* - `"
|
|
480
|
-
* result; the real result is only visible to the agent on later turns.
|
|
481
|
-
* - `"pause"`: the turn stops on client function call(s) with status
|
|
496
|
+
* - `"pause"` (default): the turn stops on client function call(s) with status
|
|
482
497
|
* `WAITING_CLIENT`; once every result is submitted, the SDK resumes the SAME
|
|
483
498
|
* turn via `POST /messages/{id}/continue`, so the agent sees the real results.
|
|
499
|
+
* - `"placeholder"` (legacy): the turn continues immediately on a placeholder
|
|
500
|
+
* result; the real result is only visible to the agent on later turns.
|
|
484
501
|
* Older backends ignore the flag, so `"pause"` degrades to legacy behavior.
|
|
485
502
|
*/
|
|
486
503
|
clientFunctionsMode?: ClientFunctionsMode;
|
|
@@ -609,6 +626,31 @@ export declare class AgoService {
|
|
|
609
626
|
}>): void;
|
|
610
627
|
/** Unregister navigation (function + current-page context) */
|
|
611
628
|
unregisterNavigationFunction(): void;
|
|
629
|
+
/**
|
|
630
|
+
* Auto-continue the agent after it navigates, so a "go to page B and change X"
|
|
631
|
+
* request completes in one user gesture. Enable once (e.g. in your root
|
|
632
|
+
* component) and call the returned function on teardown:
|
|
633
|
+
*
|
|
634
|
+
* ```ts
|
|
635
|
+
* export class AppComponent implements OnDestroy {
|
|
636
|
+
* private ago = inject(AgoService);
|
|
637
|
+
* private router = inject(Router);
|
|
638
|
+
* private stopAutoContinue = this.ago.enableAutoContinueAfterNavigation();
|
|
639
|
+
*
|
|
640
|
+
* constructor() {
|
|
641
|
+
* this.ago.registerNavigationFunction((path) => this.router.navigateByUrl(path), routes);
|
|
642
|
+
* }
|
|
643
|
+
*
|
|
644
|
+
* ngOnDestroy() {
|
|
645
|
+
* this.stopAutoContinue();
|
|
646
|
+
* }
|
|
647
|
+
* }
|
|
648
|
+
* ```
|
|
649
|
+
*
|
|
650
|
+
* See {@link attachAutoContinueAfterNavigation} for the pause-mode /
|
|
651
|
+
* placeholder-mode mechanics and the available options.
|
|
652
|
+
*/
|
|
653
|
+
enableAutoContinueAfterNavigation(options?: AgoAutoContinueOptions): () => void;
|
|
612
654
|
/** Register editable page-state controls (filters, sort, view mode…) */
|
|
613
655
|
registerPageStateFunction(controls: AgoStateControl[], opts?: AgoPageStateOptions): void;
|
|
614
656
|
/** Unregister a page-state function and its dynamic context */
|
package/dist/angular.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { t as o } from "./AgoClient-
|
|
1
|
+
import { t as o } from "./AgoClient-q39FIjSC.js";
|
|
2
|
+
import { t as c } from "./autoContinue-D3Mmaaca.js";
|
|
2
3
|
var s = class {
|
|
3
4
|
constructor(t) {
|
|
4
5
|
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");
|
|
@@ -48,6 +49,9 @@ var s = class {
|
|
|
48
49
|
unregisterNavigationFunction() {
|
|
49
50
|
this.client.unregisterNavigationFunction();
|
|
50
51
|
}
|
|
52
|
+
enableAutoContinueAfterNavigation(t) {
|
|
53
|
+
return c(this.client, t);
|
|
54
|
+
}
|
|
51
55
|
registerPageStateFunction(t, e) {
|
|
52
56
|
this.client.registerPageStateFunction(t, e);
|
|
53
57
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var I="You have navigated the user to a new page. That page now exposes its editable state through the available setPageState function, and its current values in the page context. If the user's latest request also asked to change something on this page, call setPageState now to apply exactly those changes and nothing else. Otherwise, reply with a short confirmation only.";function p(e){return{navigationFunctions:e?.navigationFunctions??["navigateToPage"],pageStateFunctionName:e?.pageStateFunctionName??"setPageState",continuationPrompt:e?.continuationPrompt??I,maxDepth:e?.maxDepth??1,readinessTimeoutMs:e?.readinessTimeoutMs??4e3,settleMs:e?.settleMs??150,enabled:e?.enabled??!0}}function w(e,s){const a=typeof s=="function"?()=>p(s()):()=>p(s),t={navigated:!1,pausedTurnNavigated:!1,depth:0,runId:0,ourSendPending:!1},i=()=>e.getRegisteredFunctions().some(n=>n.name===a().pageStateFunctionName),g=n=>new Promise(o=>{let l=!1;const r=N=>{l||(l=!0,clearTimeout(S),e.off("context:changed",u),o(N))},v=()=>setTimeout(()=>r(n===t.runId&&i()),a().settleMs),u=()=>{if(n!==t.runId)return r(!1);i()&&(e.off("context:changed",u),v())},S=setTimeout(()=>r(!1),a().readinessTimeoutMs);i()?v():e.on("context:changed",u)}),P=async n=>{const o=++t.runId;if(!(!await g(o)||o!==t.runId)){t.depth+=1,t.ourSendPending=!0;try{await e.sendMessage(a().continuationPrompt,{conversationId:n,hidden:!0})}catch{t.ourSendPending=!1}}},d=n=>{a().navigationFunctions.includes(n.functionName)&&(t.navigated=!0)},T=e.registerResumeGate(async()=>{a().enabled&&t.pausedTurnNavigated&&(t.pausedTurnNavigated=!1,await g(++t.runId))}),f=()=>{t.pausedTurnNavigated=t.navigated,t.navigated=!1},c=()=>{t.ourSendPending||(t.depth=0,t.runId+=1)},h=n=>{t.ourSendPending=!1;const o=t.navigated;t.navigated=!1,a().enabled&&o&&(t.depth>=a().maxDepth||P(n.conversationId))},m=()=>{t.ourSendPending=!1};return e.on("function:invoke",d),e.on("message:start",c),e.on("message:waiting-client",f),e.on("message:complete",h),e.on("message:error",m),()=>{t.runId+=1,T(),e.off("function:invoke",d),e.off("message:start",c),e.off("message:waiting-client",f),e.off("message:complete",h),e.off("message:error",m)}}Object.defineProperty(exports,"attachAutoContinueAfterNavigation",{enumerable:!0,get:function(){return w}});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
var N = "You have navigated the user to a new page. That page now exposes its editable state through the available setPageState function, and its current values in the page context. If the user's latest request also asked to change something on this page, call setPageState now to apply exactly those changes and nothing else. Otherwise, reply with a short confirmation only.";
|
|
2
|
+
function p(e) {
|
|
3
|
+
return {
|
|
4
|
+
navigationFunctions: e?.navigationFunctions ?? ["navigateToPage"],
|
|
5
|
+
pageStateFunctionName: e?.pageStateFunctionName ?? "setPageState",
|
|
6
|
+
continuationPrompt: e?.continuationPrompt ?? N,
|
|
7
|
+
maxDepth: e?.maxDepth ?? 1,
|
|
8
|
+
readinessTimeoutMs: e?.readinessTimeoutMs ?? 4e3,
|
|
9
|
+
settleMs: e?.settleMs ?? 150,
|
|
10
|
+
enabled: e?.enabled ?? !0
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function w(e, o) {
|
|
14
|
+
const a = typeof o == "function" ? () => p(o()) : () => p(o), t = {
|
|
15
|
+
navigated: !1,
|
|
16
|
+
pausedTurnNavigated: !1,
|
|
17
|
+
depth: 0,
|
|
18
|
+
runId: 0,
|
|
19
|
+
ourSendPending: !1
|
|
20
|
+
}, i = () => e.getRegisteredFunctions().some((n) => n.name === a().pageStateFunctionName), g = (n) => new Promise((s) => {
|
|
21
|
+
let l = !1;
|
|
22
|
+
const r = (I) => {
|
|
23
|
+
l || (l = !0, clearTimeout(S), e.off("context:changed", u), s(I));
|
|
24
|
+
}, v = () => setTimeout(() => r(n === t.runId && i()), a().settleMs), u = () => {
|
|
25
|
+
if (n !== t.runId) return r(!1);
|
|
26
|
+
i() && (e.off("context:changed", u), v());
|
|
27
|
+
}, S = setTimeout(() => r(!1), a().readinessTimeoutMs);
|
|
28
|
+
i() ? v() : e.on("context:changed", u);
|
|
29
|
+
}), P = async (n) => {
|
|
30
|
+
const s = ++t.runId;
|
|
31
|
+
if (!(!await g(s) || s !== t.runId)) {
|
|
32
|
+
t.depth += 1, t.ourSendPending = !0;
|
|
33
|
+
try {
|
|
34
|
+
await e.sendMessage(a().continuationPrompt, {
|
|
35
|
+
conversationId: n,
|
|
36
|
+
hidden: !0
|
|
37
|
+
});
|
|
38
|
+
} catch {
|
|
39
|
+
t.ourSendPending = !1;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}, d = (n) => {
|
|
43
|
+
a().navigationFunctions.includes(n.functionName) && (t.navigated = !0);
|
|
44
|
+
}, T = e.registerResumeGate(async () => {
|
|
45
|
+
a().enabled && t.pausedTurnNavigated && (t.pausedTurnNavigated = !1, await g(++t.runId));
|
|
46
|
+
}), f = () => {
|
|
47
|
+
t.pausedTurnNavigated = t.navigated, t.navigated = !1;
|
|
48
|
+
}, c = () => {
|
|
49
|
+
t.ourSendPending || (t.depth = 0, t.runId += 1);
|
|
50
|
+
}, h = (n) => {
|
|
51
|
+
t.ourSendPending = !1;
|
|
52
|
+
const s = t.navigated;
|
|
53
|
+
t.navigated = !1, a().enabled && s && (t.depth >= a().maxDepth || P(n.conversationId));
|
|
54
|
+
}, m = () => {
|
|
55
|
+
t.ourSendPending = !1;
|
|
56
|
+
};
|
|
57
|
+
return e.on("function:invoke", d), e.on("message:start", c), e.on("message:waiting-client", f), e.on("message:complete", h), e.on("message:error", m), () => {
|
|
58
|
+
t.runId += 1, T(), e.off("function:invoke", d), e.off("message:start", c), e.off("message:waiting-client", f), e.off("message:complete", h), e.off("message:error", m);
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export {
|
|
62
|
+
w as t
|
|
63
|
+
};
|
package/dist/{createConversationSession-CUkNp6mZ.cjs → createConversationSession-BBYfQLLS.cjs}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
const d=require("./createFormCollector-
|
|
1
|
+
const d=require("./createFormCollector-eimwwWEi.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-QIBN0gLx.js → createConversationSession-DCSWShaW.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as d } from "./createFormCollector-
|
|
1
|
+
import { i as d } from "./createFormCollector-sB6rMccD.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) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const C=require("./AgoClient-
|
|
1
|
+
const C=require("./AgoClient-wcPRaIMs.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}});
|
package/dist/devtools.d.cts
CHANGED
|
@@ -476,11 +476,11 @@ declare interface AgoConfig {
|
|
|
476
476
|
warnOnEmptyReply?: boolean;
|
|
477
477
|
/**
|
|
478
478
|
* How the agent loop treats client function calls (requires backend support):
|
|
479
|
-
* - `"
|
|
480
|
-
* result; the real result is only visible to the agent on later turns.
|
|
481
|
-
* - `"pause"`: the turn stops on client function call(s) with status
|
|
479
|
+
* - `"pause"` (default): the turn stops on client function call(s) with status
|
|
482
480
|
* `WAITING_CLIENT`; once every result is submitted, the SDK resumes the SAME
|
|
483
481
|
* turn via `POST /messages/{id}/continue`, so the agent sees the real results.
|
|
482
|
+
* - `"placeholder"` (legacy): the turn continues immediately on a placeholder
|
|
483
|
+
* result; the real result is only visible to the agent on later turns.
|
|
484
484
|
* Older backends ignore the flag, so `"pause"` degrades to legacy behavior.
|
|
485
485
|
*/
|
|
486
486
|
clientFunctionsMode?: ClientFunctionsMode;
|
package/dist/devtools.d.ts
CHANGED
|
@@ -476,11 +476,11 @@ declare interface AgoConfig {
|
|
|
476
476
|
warnOnEmptyReply?: boolean;
|
|
477
477
|
/**
|
|
478
478
|
* How the agent loop treats client function calls (requires backend support):
|
|
479
|
-
* - `"
|
|
480
|
-
* result; the real result is only visible to the agent on later turns.
|
|
481
|
-
* - `"pause"`: the turn stops on client function call(s) with status
|
|
479
|
+
* - `"pause"` (default): the turn stops on client function call(s) with status
|
|
482
480
|
* `WAITING_CLIENT`; once every result is submitted, the SDK resumes the SAME
|
|
483
481
|
* turn via `POST /messages/{id}/continue`, so the agent sees the real results.
|
|
482
|
+
* - `"placeholder"` (legacy): the turn continues immediately on a placeholder
|
|
483
|
+
* result; the real result is only visible to the agent on later turns.
|
|
484
484
|
* Older backends ignore the flag, so `"pause"` degrades to legacy behavior.
|
|
485
485
|
*/
|
|
486
486
|
clientFunctionsMode?: ClientFunctionsMode;
|
package/dist/helpers.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("./functions-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("./functions-IGSjjvF4.cjs");exports.copyToClipboard=o.copyToClipboard;exports.getLocalStorage=o.getLocalStorage;exports.getUserLocation=o.getUserLocation;exports.highlightElement=o.highlightElement;exports.openUrl=o.openUrl;exports.scrollToElement=o.scrollToElement;exports.setLocalStorage=o.setLocalStorage;exports.setTheme=o.setTheme;exports.showConfirmDialog=o.showConfirmDialog;exports.showNotification=o.showNotification;exports.showToast=o.showToast;exports.submitForm=o.submitForm;exports.trackEvent=o.trackEvent;
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("./AgoClient-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("./AgoClient-wcPRaIMs.cjs"),h=require("./autoContinue-CcCTva-D.cjs"),f=require("./createFormCollector-eimwwWEi.cjs"),p=require("./createConversationSession-BBYfQLLS.cjs"),w=require("./createMockClient-CjdCqixj.cjs");require("./testing.cjs");const g=require("./functions-IGSjjvF4.cjs");function S(e){return e}function E(e,o){return e.on("message:complete",o),()=>e.off("message:complete",o)}function y(e,o){return e.on("message:chunk",o),()=>e.off("message:chunk",o)}function F(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 q(e,o){return e.on("form:submitted",o),()=>e.off("form:submitted",o)}function M(e,o){return e.on("form:error",o),()=>e.off("form:error",o)}function k(e,o){return e.on("function:invoke",o),()=>e.off("function:invoke",o)}async function*T(e,o,l){const a=[];let n=null,i=!1;const c=s=>{a.push(s),n?.()},m=s=>c({type:"start",data:s}),t=s=>c({type:"chunk",data:s}),u=s=>{c({type:"complete",data:s}),i=!0,n?.()},d=s=>{c({type:"error",data:s}),i=!0,n?.()},A=s=>c({type:"toolCall",data:s});e.on("message:start",m),e.on("message:chunk",t),e.on("message:complete",u),e.on("message:error",d),e.on("toolCall:received",A),e.sendMessage(o,l).catch(()=>{i=!0,n?.()});try{for(;!i||a.length>0;)a.length>0?yield a.shift():(await new Promise(s=>{n=s}),n=null)}finally{e.off("message:start",m),e.off("message:chunk",t),e.off("message:complete",u),e.off("message:error",d),e.off("toolCall:received",A)}}function C(e){let o,l,a,n,i,c;if(typeof window<"u"&&window.AGO){const t=window.AGO;o=t.basepath,l=t.widgetId,a=t.agent||t.defaultAgent,n=t.permission,i=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"),n=n||t("ago-permission"),i=i||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,n=n||u.getAttribute("data-ago-permission")||void 0,i=i||u.getAttribute("data-ago-user-email")||void 0)}o=e?.baseUrl||o,l=e?.widgetId||l,a=e?.agent||e?.defaultAgentId||a,n=e?.permission||n,i=e?.userEmail||i,c=e?.userJwt||c;const m=e?.debug;return o?{baseUrl:o,widgetId:l,agent:a,permission:n,userEmail:i,userJwt:c,debug:m}:null}function I(e){const o=C(e);if(!o)throw new r.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 r.AgoClient(o)}function U(e,o){return{...e,handler:o}}exports.AgoApiError=r.AgoApiError;exports.AgoClient=r.AgoClient;exports.AgoError=r.AgoError;exports.AgoFunctionError=r.AgoFunctionError;exports.AgoNetworkError=r.AgoNetworkError;exports.AgoStreamError=r.AgoStreamError;exports.ClientContextRegistry=r.ClientContextRegistry;exports.EventEmitter=r.EventEmitter;exports.FunctionRegistry=r.FunctionRegistry;exports.SSEHandler=r.SSEHandler;exports.attachAutoContinueAfterNavigation=h.attachAutoContinueAfterNavigation;exports.autoDetectConfig=C;exports.canInlineImage=r.canInlineImage;exports.copyToClipboard=g.copyToClipboard;exports.createAgo=I;exports.createConversationSession=p.createConversationSession;exports.createFormCollector=f.createFormCollector;exports.createMessageStream=T;exports.createMockClient=w.createMockClient;exports.createStore=f.createStore;exports.defineFunction=S;exports.deriveFormStatus=f.deriveFormStatus;exports.formatFileSize=r.formatFileSize;exports.getLocalStorage=g.getLocalStorage;exports.getUserLocation=g.getUserLocation;exports.highlightElement=g.highlightElement;exports.isStreamNetworkError=r.isStreamNetworkError;exports.loadFormCollector=f.loadFormCollector;exports.logger=r.logger;exports.onFormError=M;exports.onFormSubmitted=q;exports.onFunctionInvoke=k;exports.onMessage=E;exports.onMessageChunk=y;exports.onMessageError=v;exports.onMessageStart=F;exports.onToolCall=b;exports.openUrl=g.openUrl;exports.safeAttachmentUrl=r.safeAttachmentUrl;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=r.validateConfig;exports.withHandler=U;
|
package/dist/index.d.cts
CHANGED
|
@@ -39,6 +39,23 @@ export declare interface AgoAttachment {
|
|
|
39
39
|
isSafeImage: boolean;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
export declare interface AgoAutoContinueOptions {
|
|
43
|
+
/** Function names whose invocation counts as "navigated". Default `["navigateToPage"]`. */
|
|
44
|
+
navigationFunctions?: string[];
|
|
45
|
+
/** Registered function that signals the destination has editable state. Default `"setPageState"`. */
|
|
46
|
+
pageStateFunctionName?: string;
|
|
47
|
+
/** The hidden continuation prompt. Override for i18n / tone. */
|
|
48
|
+
continuationPrompt?: string;
|
|
49
|
+
/** Max continuations per user gesture (guards navigate→continue→navigate loops). Default `1`. */
|
|
50
|
+
maxDepth?: number;
|
|
51
|
+
/** How long to wait for the destination page to register page-state before giving up. Default `4000`ms. */
|
|
52
|
+
readinessTimeoutMs?: number;
|
|
53
|
+
/** Extra settle delay after page-state appears, to let sibling state mount. Default `150`ms. */
|
|
54
|
+
settleMs?: number;
|
|
55
|
+
/** Opt-out escape hatch. Default `true`. */
|
|
56
|
+
enabled?: boolean;
|
|
57
|
+
}
|
|
58
|
+
|
|
42
59
|
/**
|
|
43
60
|
* Main SDK client for AGO Chat integration
|
|
44
61
|
*/
|
|
@@ -487,11 +504,11 @@ export declare interface AgoConfig {
|
|
|
487
504
|
warnOnEmptyReply?: boolean;
|
|
488
505
|
/**
|
|
489
506
|
* How the agent loop treats client function calls (requires backend support):
|
|
490
|
-
* - `"
|
|
491
|
-
* result; the real result is only visible to the agent on later turns.
|
|
492
|
-
* - `"pause"`: the turn stops on client function call(s) with status
|
|
507
|
+
* - `"pause"` (default): the turn stops on client function call(s) with status
|
|
493
508
|
* `WAITING_CLIENT`; once every result is submitted, the SDK resumes the SAME
|
|
494
509
|
* turn via `POST /messages/{id}/continue`, so the agent sees the real results.
|
|
510
|
+
* - `"placeholder"` (legacy): the turn continues immediately on a placeholder
|
|
511
|
+
* result; the real result is only visible to the agent on later turns.
|
|
495
512
|
* Older backends ignore the flag, so `"pause"` degrades to legacy behavior.
|
|
496
513
|
*/
|
|
497
514
|
clientFunctionsMode?: ClientFunctionsMode;
|
|
@@ -659,6 +676,38 @@ declare interface ApiErrorResponse {
|
|
|
659
676
|
};
|
|
660
677
|
}
|
|
661
678
|
|
|
679
|
+
/**
|
|
680
|
+
* Auto-continue the agent after it navigates, so a "go to page B and change X"
|
|
681
|
+
* request completes in one user gesture. Attach once per client; returns a
|
|
682
|
+
* detach function that removes every listener.
|
|
683
|
+
*
|
|
684
|
+
* Two mechanisms, picked by the turn's client-functions mode:
|
|
685
|
+
*
|
|
686
|
+
* - **Pause mode** (`clientFunctionsMode: "pause"`, the default): the backend pauses the turn
|
|
687
|
+
* on the navigation call (`message:waiting-client`) and the SDK resumes it once
|
|
688
|
+
* the result is submitted. This helper only registers a *resume gate* that delays
|
|
689
|
+
* the resume until the destination page's page-state registered (plus a settle
|
|
690
|
+
* delay), so the continued turn ships the new page's `setPageState` and current
|
|
691
|
+
* state. Same turn, no extra prompt, backend budget as the guardrail —
|
|
692
|
+
* `continuationPrompt` and `maxDepth` are unused here.
|
|
693
|
+
*
|
|
694
|
+
* - **Placeholder mode** (legacy, `clientFunctionsMode: "placeholder"`): when the agent calls the navigation
|
|
695
|
+
* function and that turn ends, this waits for the destination page's state to
|
|
696
|
+
* register, then sends a hidden continuation message; the agent applies the
|
|
697
|
+
* state in a second turn (requires backend support for the `hidden` flag).
|
|
698
|
+
*
|
|
699
|
+
* ```ts
|
|
700
|
+
* const detach = attachAutoContinueAfterNavigation(client);
|
|
701
|
+
* // later, on teardown:
|
|
702
|
+
* detach();
|
|
703
|
+
* ```
|
|
704
|
+
*
|
|
705
|
+
* Options can be a plain object, or a getter for live options: the getter is
|
|
706
|
+
* re-read on every event, so changing what it returns never re-subscribes the
|
|
707
|
+
* listeners (this is how the React hook keeps options reactive).
|
|
708
|
+
*/
|
|
709
|
+
export declare function attachAutoContinueAfterNavigation(client: AgoClient, options?: AgoAutoContinueOptions | (() => AgoAutoContinueOptions | undefined)): () => void;
|
|
710
|
+
|
|
662
711
|
/**
|
|
663
712
|
* Auto-detect AGO configuration from the DOM environment.
|
|
664
713
|
*
|