@useago/sdk 1.0.4 → 1.0.6

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.
Files changed (41) hide show
  1. package/README.md +3 -3
  2. package/dist/{AgoClient-1jgDndKs.cjs → AgoClient--y10UXUV.cjs} +1 -1
  3. package/dist/{AgoClient-j1sk1hK5.js → AgoClient-DYdIe_R4.js} +9 -6
  4. package/dist/angular.cjs +1 -1
  5. package/dist/angular.d.cts +49 -2
  6. package/dist/angular.d.ts +49 -2
  7. package/dist/angular.js +1 -1
  8. package/dist/{createConversationSession-CZhoTCtD.js → createConversationSession-Boi0PjDU.js} +1 -1
  9. package/dist/{createConversationSession-BYNhYbkY.cjs → createConversationSession-lJWANT6T.cjs} +1 -1
  10. package/dist/{createFormCollector-BPKzKDeX.js → createFormCollector-BUqSA2Qw.js} +1 -1
  11. package/dist/{createFormCollector-B8V9YSGe.cjs → createFormCollector-CSG5ioym.cjs} +1 -1
  12. package/dist/devtools.cjs +41 -27
  13. package/dist/devtools.d.cts +48 -0
  14. package/dist/devtools.d.ts +48 -0
  15. package/dist/devtools.js +98 -70
  16. package/dist/{functions-C1-5vadY.js → functions-CApuQZ2b.js} +50 -43
  17. package/dist/{functions-DsmzgHjC.cjs → functions-Djnq-JAo.cjs} +1 -1
  18. package/dist/helpers.cjs +1 -1
  19. package/dist/helpers.js +1 -1
  20. package/dist/index.cjs +1 -1
  21. package/dist/index.d.cts +12 -0
  22. package/dist/index.d.ts +12 -0
  23. package/dist/index.js +35 -35
  24. package/dist/react.cjs +3 -31
  25. package/dist/react.d.cts +49 -2
  26. package/dist/react.d.ts +49 -2
  27. package/dist/react.js +599 -8156
  28. package/dist/rolldown-runtime-16Nf8qvH.cjs +1 -0
  29. package/dist/testing.d.cts +48 -0
  30. package/dist/testing.d.ts +48 -0
  31. package/dist/vue.cjs +1 -1
  32. package/dist/vue.d.cts +49 -2
  33. package/dist/vue.d.ts +49 -2
  34. package/dist/vue.js +1 -1
  35. package/dist/widget.cjs +1 -1
  36. package/dist/widget.d.cts +48 -0
  37. package/dist/widget.d.ts +48 -0
  38. package/dist/widget.js +3 -3
  39. package/package.json +6 -6
  40. package/dist/rolldown-runtime-CaekUdDJ.cjs +0 -1
  41. package/dist/rolldown-runtime-DxmricAu.js +0 -25
package/README.md CHANGED
@@ -22,12 +22,12 @@ the same two steps through its own guide:
22
22
  ## <img src="https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/react/react-original.svg" width="22" /> Try it in 30 seconds
23
23
 
24
24
  ```bash
25
- npm install @useago/sdk
25
+ npm install @useago/sdk react react-dom
26
26
  ```
27
27
 
28
28
  The SDK has no framework dependency of its own. The React bindings need `react`
29
- and `react-dom` (>=17): already there in an existing React app, otherwise
30
- install them too.
29
+ and `react-dom` (>=17): already there in an existing React app, so you can drop
30
+ them from the command above.
31
31
 
32
32
  ```tsx
33
33
  // App.tsx
@@ -1,6 +1,6 @@
1
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
2
 
3
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(`
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(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 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 _({onRawChunk:r=>{this.eventEmitter.emit("stream:message",r)},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
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
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}});
@@ -262,7 +262,7 @@ var x = class {
262
262
  }
263
263
  }
264
264
  handleChunk(e) {
265
- 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?.({
265
+ 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?.({
266
266
  conversationId: this.message.conversationId,
267
267
  messageId: this.message.id
268
268
  }))), e.content !== void 0 && (this.message.content = (this.message.content || "") + e.content, this.message.conversationId && this.message.id && this.callbacks.onChunk?.({
@@ -334,7 +334,7 @@ function U(e) {
334
334
  const t = e.message.toLowerCase();
335
335
  return t.includes("load failed") || t.includes("failed to fetch") || t.includes("network") || t.includes("abort");
336
336
  }
337
- var I = class {
337
+ var R = class {
338
338
  constructor() {
339
339
  this.handlers = /* @__PURE__ */ new Map();
340
340
  }
@@ -375,16 +375,16 @@ var I = class {
375
375
  removeAllListeners(e) {
376
376
  e ? this.handlers.delete(e) : this.handlers.clear();
377
377
  }
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;
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).', I = /^[a-z][a-z0-9+.-]*:\/\//i, w = !1;
379
379
  function v(e, t = "AgoClient") {
380
380
  if (typeof e != "object" || e === null) throw new h(`${t}: ${y}`, "config_missing_base_url");
381
381
  const s = e.baseUrl;
382
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).`)));
383
+ !s.startsWith("/") && !I.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).`)));
384
384
  }
385
385
  var F = class b {
386
386
  constructor(t) {
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");
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 R(), t.debug && o.enable(), o.log("AgoClient initialized");
388
388
  }
389
389
  async sendMessage(t, s) {
390
390
  const n = this.functionRegistry.getSchemas(), i = this.contextRegistry.getSnapshot(), r = this.config.agent || this.config.defaultAgentId, a = {
@@ -405,6 +405,9 @@ var F = class b {
405
405
  async processSSEResponse(t) {
406
406
  let s = !1;
407
407
  const n = await new x({
408
+ onRawChunk: (i) => {
409
+ this.eventEmitter.emit("stream:message", i);
410
+ },
408
411
  onStart: (i) => {
409
412
  this.eventEmitter.emit("message:start", i);
410
413
  },
@@ -643,7 +646,7 @@ export {
643
646
  v as n,
644
647
  A as o,
645
648
  g as p,
646
- I as r,
649
+ R as r,
647
650
  $ as s,
648
651
  F as t,
649
652
  h as u
package/dist/angular.cjs CHANGED
@@ -1 +1 @@
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;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("./AgoClient--y10UXUV.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;
@@ -243,6 +243,13 @@ export declare interface AgoClientEvents {
243
243
  conversationId: string;
244
244
  messageId: string;
245
245
  };
246
+ /**
247
+ * Every raw SSE message parsed off the stream, before the handler interprets it
248
+ * into the higher-level `message:*` / `toolCall:*` / `function:*` events. Carries
249
+ * the chunk verbatim so debugging tools (notably the dev panel) can log the exact
250
+ * wire payload. Heartbeat comments are not messages and don't fire this.
251
+ */
252
+ "stream:message": SSEChunkData;
246
253
  /**
247
254
  * The main answer text is done (backend emitted `status: "DONE"`), but follow-up
248
255
  * replies may still be pending. Fires once, before `message:complete`.
@@ -337,8 +344,7 @@ export declare interface AgoMessage {
337
344
  createdAt: Date;
338
345
  }
339
346
 
340
- export declare interface AgoProvideOptions extends AgoConfig {
341
- }
347
+ export declare type AgoProvideOptions = AgoConfig;
342
348
 
343
349
  /**
344
350
  * Angular-style service wrapping AgoClient.
@@ -651,6 +657,47 @@ declare interface SendMessageOptions {
651
657
  files?: File[];
652
658
  }
653
659
 
660
+ /**
661
+ * SSE stream chunk data
662
+ */
663
+ declare interface SSEChunkData {
664
+ content?: string;
665
+ full_content?: string;
666
+ message_id?: string;
667
+ status?: MessageStatus;
668
+ thread?: {
669
+ id: string;
670
+ };
671
+ agent?: {
672
+ id: string;
673
+ name: string;
674
+ display_name?: string;
675
+ };
676
+ knowledge_sources?: Array<{
677
+ knowledge_document: {
678
+ id: string;
679
+ title: string;
680
+ use_external_link: boolean;
681
+ external_link_url?: string;
682
+ internal_link_url?: string;
683
+ };
684
+ position: number;
685
+ }>;
686
+ tool_call_data?: boolean;
687
+ type?: string;
688
+ id?: string;
689
+ tool_name?: string;
690
+ tool_display_name?: string;
691
+ function_name?: string;
692
+ arguments?: Record<string, unknown>;
693
+ form_schema?: FormSchema;
694
+ message?: string;
695
+ data?: Record<string, unknown>;
696
+ follow_up_replies?: string[];
697
+ satisfaction_feedback?: unknown;
698
+ ask_to_talk_to_human?: boolean;
699
+ }
700
+
654
701
  /**
655
702
  * Where a completed form is sent.
656
703
  * - `"https://..."` (a bare string) — shorthand for `{ via: "client", url }`: the browser
package/dist/angular.d.ts CHANGED
@@ -243,6 +243,13 @@ export declare interface AgoClientEvents {
243
243
  conversationId: string;
244
244
  messageId: string;
245
245
  };
246
+ /**
247
+ * Every raw SSE message parsed off the stream, before the handler interprets it
248
+ * into the higher-level `message:*` / `toolCall:*` / `function:*` events. Carries
249
+ * the chunk verbatim so debugging tools (notably the dev panel) can log the exact
250
+ * wire payload. Heartbeat comments are not messages and don't fire this.
251
+ */
252
+ "stream:message": SSEChunkData;
246
253
  /**
247
254
  * The main answer text is done (backend emitted `status: "DONE"`), but follow-up
248
255
  * replies may still be pending. Fires once, before `message:complete`.
@@ -337,8 +344,7 @@ export declare interface AgoMessage {
337
344
  createdAt: Date;
338
345
  }
339
346
 
340
- export declare interface AgoProvideOptions extends AgoConfig {
341
- }
347
+ export declare type AgoProvideOptions = AgoConfig;
342
348
 
343
349
  /**
344
350
  * Angular-style service wrapping AgoClient.
@@ -651,6 +657,47 @@ declare interface SendMessageOptions {
651
657
  files?: File[];
652
658
  }
653
659
 
660
+ /**
661
+ * SSE stream chunk data
662
+ */
663
+ declare interface SSEChunkData {
664
+ content?: string;
665
+ full_content?: string;
666
+ message_id?: string;
667
+ status?: MessageStatus;
668
+ thread?: {
669
+ id: string;
670
+ };
671
+ agent?: {
672
+ id: string;
673
+ name: string;
674
+ display_name?: string;
675
+ };
676
+ knowledge_sources?: Array<{
677
+ knowledge_document: {
678
+ id: string;
679
+ title: string;
680
+ use_external_link: boolean;
681
+ external_link_url?: string;
682
+ internal_link_url?: string;
683
+ };
684
+ position: number;
685
+ }>;
686
+ tool_call_data?: boolean;
687
+ type?: string;
688
+ id?: string;
689
+ tool_name?: string;
690
+ tool_display_name?: string;
691
+ function_name?: string;
692
+ arguments?: Record<string, unknown>;
693
+ form_schema?: FormSchema;
694
+ message?: string;
695
+ data?: Record<string, unknown>;
696
+ follow_up_replies?: string[];
697
+ satisfaction_feedback?: unknown;
698
+ ask_to_talk_to_human?: boolean;
699
+ }
700
+
654
701
  /**
655
702
  * Where a completed form is sent.
656
703
  * - `"https://..."` (a bare string) — shorthand for `{ via: "client", url }`: the browser
package/dist/angular.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as o } from "./AgoClient-j1sk1hK5.js";
1
+ import { t as o } from "./AgoClient-DYdIe_R4.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,4 +1,4 @@
1
- import { i as d } from "./createFormCollector-BPKzKDeX.js";
1
+ import { i as d } from "./createFormCollector-BUqSA2Qw.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 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
+ const d=require("./createFormCollector-CSG5ioym.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 { c as _ } from "./AgoClient-j1sk1hK5.js";
1
+ import { c as _ } from "./AgoClient-DYdIe_R4.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;
@@ -1 +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
+ const O=require("./AgoClient--y10UXUV.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}});
package/dist/devtools.cjs CHANGED
@@ -1,20 +1,28 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});var p,d,x=()=>({});function i(){p&&(p.textContent=JSON.stringify(x(),null,2))}function s(n,e){if(!d)return;const o=document.createElement("div");o.className=`dev-log-line dev-log-${e}`,o.textContent=`${new Date().toLocaleTimeString()} ${n}`,d.appendChild(o),d.scrollTop=d.scrollHeight}var u="ago_dev_panel_collapsed";function c(n,e){n.classList.toggle("collapsed",e);const o=n.querySelector(".dev-toggle");o&&(o.textContent=e?"▢":"—",o.title=e?"Expand dev tools":"Collapse dev tools",o.setAttribute("aria-expanded",String(!e)));try{localStorage.setItem(u,e?"1":"0")}catch{}}function h(n){const{client:e,target:o}=n;x=()=>{const t=e.getContextSnapshot()?.entries??{};return Object.fromEntries(Object.entries(t).map(([l,{name:r,data:b}])=>[l,{name:r,data:b}]))},m();const f=typeof o=="string"?document.querySelector(o):o instanceof Element?o:document.body,a=document.createElement("aside");a.id="ago-dev-panel",(f??document.body).appendChild(a),a.innerHTML=`
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});var v,x,p,h=()=>({});function s(){v&&(v.textContent=JSON.stringify(h(),null,2))}function m(e,t,d){if(!e)return;const n=document.createElement("div");n.className=`dev-log-line dev-log-${d}`,n.textContent=`${new Date().toLocaleTimeString()} ${t}`,e.appendChild(n),e.scrollTop=e.scrollHeight}function c(e,t){m(x,e,t)}function y(e){let t;return e.type?t=e.type:e.content!==void 0?t="content":e.full_content!==void 0?t="full_content":e.status?t=`status:${e.status}`:t="event",`${t} ${JSON.stringify(e)}`}var S="ago_dev_panel_collapsed",E="ago_dev_events_collapsed";function g(e,t,d,n){e.classList.toggle("collapsed",t);const o=e.querySelector(".dev-toggle");o&&(o.textContent=t?"▢":"—",o.title=t?`Expand ${n}`:`Collapse ${n}`,o.setAttribute("aria-expanded",String(!t)));try{localStorage.setItem(d,t?"1":"0")}catch{}}function u(e,t,d){e.querySelector(".dev-toggle")?.addEventListener("click",o=>{o.stopPropagation(),g(e,!e.classList.contains("collapsed"),t,d)}),e.addEventListener("click",()=>{e.classList.contains("collapsed")&&g(e,!1,t,d)});let n=!1;try{n=localStorage.getItem(t)==="1"}catch{}g(e,n,t,d)}function C(e){const{client:t,target:d}=e;h=()=>{const a=t.getContextSnapshot()?.entries??{};return Object.fromEntries(Object.entries(a).map(([l,{name:r,data:b}])=>[l,{name:r,data:b}]))},w();const n=typeof d=="string"?document.querySelector(d):d instanceof Element?d:document.body,o=document.createElement("aside");o.id="ago-dev-panel",o.className="ago-dev-card",(n??document.body).appendChild(o),o.innerHTML=`
2
2
  <div class="dev-head">
3
3
  <span class="dev-badge">DEV</span>
4
4
  <span class="dev-title">DEV TOOLS · client-side function state — not for production</span>
5
5
  <button type="button" class="dev-toggle" aria-label="Toggle dev tools">—</button>
6
6
  </div>
7
7
  <div class="dev-body">
8
- <div class="dev-fns">Registered functions: <code>${(e.getRegisteredFunctions?.()??[]).map(t=>t.name).join(", ")||"—"}</code></div>
8
+ <div class="dev-fns">Registered functions: <code>${(t.getRegisteredFunctions?.()??[]).map(a=>a.name).join(", ")||"—"}</code></div>
9
9
  <div class="dev-section-label">JSON object (built by the agent)</div>
10
10
  <pre class="dev-state" id="ago-dev-state"></pre>
11
11
  <div class="dev-section-label">Function calls</div>
12
12
  <div class="dev-log" id="ago-dev-log"></div>
13
13
  </div>
14
- `,p=a.querySelector("#ago-dev-state"),d=a.querySelector("#ago-dev-log"),a.querySelector(".dev-toggle")?.addEventListener("click",t=>{t.stopPropagation(),c(a,!a.classList.contains("collapsed"))}),a.addEventListener("click",()=>{a.classList.contains("collapsed")&&c(a,!1)});let g=!1;try{g=localStorage.getItem(u)==="1"}catch{}c(a,g),e.on("function:invoke",({functionName:t,arguments:l})=>{s(`→ ${t}(${JSON.stringify(l??{})})`,"invoke")}),e.on("function:result",({result:t,error:l})=>{s(l?`✗ error: ${l}`:`← ${JSON.stringify(t)}`,l?"error":"result"),i()}),e.on("context:changed",i),e.on("conversation:loaded",t=>{const l=(t.messages??[]).flatMap(r=>r.toolCalls??[]);s(`⟳ hydrated "${t.title||t.id}" — replayed ${l.length} tool call${l.length===1?"":"s"}`,"hydrate"),i()}),i()}var v="ago-dev-panel-styles";function m(){if(document.getElementById(v))return;const n=document.createElement("style");n.id=v,n.textContent=y,document.head.appendChild(n)}var y=`
15
- #ago-dev-panel {
14
+ `,v=o.querySelector("#ago-dev-state"),x=o.querySelector("#ago-dev-log"),u(o,S,"dev tools");const i=document.createElement("aside");i.id="ago-dev-events",i.className="ago-dev-card",(n??document.body).appendChild(i),i.innerHTML=`
15
+ <div class="dev-head">
16
+ <span class="dev-badge">SSE</span>
17
+ <span class="dev-title">SSE EVENT LOG · raw stream messages</span>
18
+ <button type="button" class="dev-toggle" aria-label="Toggle SSE event log">—</button>
19
+ </div>
20
+ <div class="dev-body">
21
+ <div class="dev-log" id="ago-dev-event-log"></div>
22
+ </div>
23
+ `,p=i.querySelector("#ago-dev-event-log"),u(i,E,"SSE event log"),t.on("stream:message",a=>{m(p,y(a),"event")}),t.on("function:invoke",({functionName:a,arguments:l})=>{c(`→ ${a}(${JSON.stringify(l??{})})`,"invoke")}),t.on("function:result",({result:a,error:l})=>{c(l?`✗ error: ${l}`:`← ${JSON.stringify(a)}`,l?"error":"result"),s()}),t.on("context:changed",s),t.on("conversation:loaded",a=>{const l=(a.messages??[]).flatMap(r=>r.toolCalls??[]);c(`⟳ hydrated "${a.title||a.id}" — replayed ${l.length} tool call${l.length===1?"":"s"}`,"hydrate"),s()}),s()}var f="ago-dev-panel-styles";function w(){if(document.getElementById(f))return;const e=document.createElement("style");e.id=f,e.textContent=L,document.head.appendChild(e)}var L=`
24
+ .ago-dev-card {
16
25
  position: fixed;
17
- top: 16px;
18
26
  right: 16px;
19
27
  width: min(360px, calc(100vw - 32px));
20
28
  max-width: calc(100vw - 32px);
@@ -33,8 +41,11 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});var p,d,x=()=
33
41
  font-size: 12px;
34
42
  z-index: 1000;
35
43
  }
36
- #ago-dev-panel .dev-head { display: flex; align-items: center; gap: 8px; }
37
- #ago-dev-panel .dev-toggle {
44
+ /* The main panel pins to the top, the SSE log to the bottom, so they don't overlap. */
45
+ #ago-dev-panel { top: 16px; }
46
+ #ago-dev-events { bottom: 16px; }
47
+ .ago-dev-card .dev-head { display: flex; align-items: center; gap: 8px; }
48
+ .ago-dev-card .dev-toggle {
38
49
  margin-left: auto;
39
50
  flex: none;
40
51
  width: 22px;
@@ -50,19 +61,19 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});var p,d,x=()=
50
61
  line-height: 1;
51
62
  cursor: pointer;
52
63
  }
53
- #ago-dev-panel .dev-toggle:hover { color: #d7e0e8; border-color: #3a4655; }
54
- #ago-dev-panel .dev-body { display: flex; flex-direction: column; gap: 8px; }
64
+ .ago-dev-card .dev-toggle:hover { color: #d7e0e8; border-color: #3a4655; }
65
+ .ago-dev-card .dev-body { display: flex; flex-direction: column; gap: 8px; }
55
66
 
56
- /* Collapsed: shrink to a small clickable widget showing only the DEV badge. */
57
- #ago-dev-panel.collapsed {
67
+ /* Collapsed: shrink to a small clickable widget showing only the badge. */
68
+ .ago-dev-card.collapsed {
58
69
  width: auto;
59
70
  cursor: pointer;
60
71
  padding: 8px 10px;
61
72
  }
62
- #ago-dev-panel.collapsed .dev-title,
63
- #ago-dev-panel.collapsed .dev-body { display: none; }
64
- #ago-dev-panel.collapsed .dev-toggle { margin-left: 6px; }
65
- #ago-dev-panel .dev-badge {
73
+ .ago-dev-card.collapsed .dev-title,
74
+ .ago-dev-card.collapsed .dev-body { display: none; }
75
+ .ago-dev-card.collapsed .dev-toggle { margin-left: 6px; }
76
+ .ago-dev-card .dev-badge {
66
77
  background: #f59e0b;
67
78
  color: #1c1206;
68
79
  font-weight: 700;
@@ -71,17 +82,17 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});var p,d,x=()=
71
82
  padding: 2px 6px;
72
83
  border-radius: 5px;
73
84
  }
74
- #ago-dev-panel .dev-title { color: #f59e0b; font-size: 11px; line-height: 1.3; }
75
- #ago-dev-panel .dev-fns { color: #7c8a99; font-size: 11px; }
76
- #ago-dev-panel .dev-fns code { color: #9ecbff; }
77
- #ago-dev-panel .dev-section-label {
85
+ .ago-dev-card .dev-title { color: #f59e0b; font-size: 11px; line-height: 1.3; }
86
+ .ago-dev-card .dev-fns { color: #7c8a99; font-size: 11px; }
87
+ .ago-dev-card .dev-fns code { color: #9ecbff; }
88
+ .ago-dev-card .dev-section-label {
78
89
  color: #7c8a99;
79
90
  text-transform: uppercase;
80
91
  letter-spacing: .08em;
81
92
  font-size: 10px;
82
93
  margin-top: 4px;
83
94
  }
84
- #ago-dev-panel .dev-state {
95
+ .ago-dev-card .dev-state {
85
96
  margin: 0;
86
97
  padding: 8px;
87
98
  background: #060a0e;
@@ -91,7 +102,7 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});var p,d,x=()=
91
102
  white-space: pre;
92
103
  color: #c8e6c9;
93
104
  }
94
- #ago-dev-panel .dev-log {
105
+ .ago-dev-card .dev-log {
95
106
  display: flex;
96
107
  flex-direction: column;
97
108
  gap: 2px;
@@ -101,9 +112,12 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});var p,d,x=()=
101
112
  border-radius: 8px;
102
113
  padding: 6px 8px;
103
114
  }
104
- #ago-dev-panel .dev-log-line { white-space: pre-wrap; word-break: break-word; line-height: 1.4; }
105
- #ago-dev-panel .dev-log-invoke { color: #9ecbff; }
106
- #ago-dev-panel .dev-log-result { color: #86efac; }
107
- #ago-dev-panel .dev-log-error { color: #fca5a5; }
108
- #ago-dev-panel .dev-log-hydrate { color: #d8b4fe; }
109
- `;exports.initDevPanel=h;
115
+ /* The SSE log is the events panel's only content, so give it more room. */
116
+ #ago-dev-events .dev-log { max-height: 60vh; }
117
+ .ago-dev-card .dev-log-line { white-space: pre-wrap; word-break: break-word; line-height: 1.4; }
118
+ .ago-dev-card .dev-log-invoke { color: #9ecbff; }
119
+ .ago-dev-card .dev-log-result { color: #86efac; }
120
+ .ago-dev-card .dev-log-error { color: #fca5a5; }
121
+ .ago-dev-card .dev-log-hydrate { color: #d8b4fe; }
122
+ .ago-dev-card .dev-log-event { color: #7c8a99; }
123
+ `;exports.initDevPanel=C;
@@ -243,6 +243,13 @@ declare interface AgoClientEvents {
243
243
  conversationId: string;
244
244
  messageId: string;
245
245
  };
246
+ /**
247
+ * Every raw SSE message parsed off the stream, before the handler interprets it
248
+ * into the higher-level `message:*` / `toolCall:*` / `function:*` events. Carries
249
+ * the chunk verbatim so debugging tools (notably the dev panel) can log the exact
250
+ * wire payload. Heartbeat comments are not messages and don't fire this.
251
+ */
252
+ "stream:message": SSEChunkData;
246
253
  /**
247
254
  * The main answer text is done (backend emitted `status: "DONE"`), but follow-up
248
255
  * replies may still be pending. Fires once, before `message:complete`.
@@ -542,6 +549,47 @@ declare interface SendMessageOptions {
542
549
  files?: File[];
543
550
  }
544
551
 
552
+ /**
553
+ * SSE stream chunk data
554
+ */
555
+ declare interface SSEChunkData {
556
+ content?: string;
557
+ full_content?: string;
558
+ message_id?: string;
559
+ status?: MessageStatus;
560
+ thread?: {
561
+ id: string;
562
+ };
563
+ agent?: {
564
+ id: string;
565
+ name: string;
566
+ display_name?: string;
567
+ };
568
+ knowledge_sources?: Array<{
569
+ knowledge_document: {
570
+ id: string;
571
+ title: string;
572
+ use_external_link: boolean;
573
+ external_link_url?: string;
574
+ internal_link_url?: string;
575
+ };
576
+ position: number;
577
+ }>;
578
+ tool_call_data?: boolean;
579
+ type?: string;
580
+ id?: string;
581
+ tool_name?: string;
582
+ tool_display_name?: string;
583
+ function_name?: string;
584
+ arguments?: Record<string, unknown>;
585
+ form_schema?: FormSchema;
586
+ message?: string;
587
+ data?: Record<string, unknown>;
588
+ follow_up_replies?: string[];
589
+ satisfaction_feedback?: unknown;
590
+ ask_to_talk_to_human?: boolean;
591
+ }
592
+
545
593
  /**
546
594
  * Where a completed form is sent.
547
595
  * - `"https://..."` (a bare string) — shorthand for `{ via: "client", url }`: the browser
@@ -243,6 +243,13 @@ declare interface AgoClientEvents {
243
243
  conversationId: string;
244
244
  messageId: string;
245
245
  };
246
+ /**
247
+ * Every raw SSE message parsed off the stream, before the handler interprets it
248
+ * into the higher-level `message:*` / `toolCall:*` / `function:*` events. Carries
249
+ * the chunk verbatim so debugging tools (notably the dev panel) can log the exact
250
+ * wire payload. Heartbeat comments are not messages and don't fire this.
251
+ */
252
+ "stream:message": SSEChunkData;
246
253
  /**
247
254
  * The main answer text is done (backend emitted `status: "DONE"`), but follow-up
248
255
  * replies may still be pending. Fires once, before `message:complete`.
@@ -542,6 +549,47 @@ declare interface SendMessageOptions {
542
549
  files?: File[];
543
550
  }
544
551
 
552
+ /**
553
+ * SSE stream chunk data
554
+ */
555
+ declare interface SSEChunkData {
556
+ content?: string;
557
+ full_content?: string;
558
+ message_id?: string;
559
+ status?: MessageStatus;
560
+ thread?: {
561
+ id: string;
562
+ };
563
+ agent?: {
564
+ id: string;
565
+ name: string;
566
+ display_name?: string;
567
+ };
568
+ knowledge_sources?: Array<{
569
+ knowledge_document: {
570
+ id: string;
571
+ title: string;
572
+ use_external_link: boolean;
573
+ external_link_url?: string;
574
+ internal_link_url?: string;
575
+ };
576
+ position: number;
577
+ }>;
578
+ tool_call_data?: boolean;
579
+ type?: string;
580
+ id?: string;
581
+ tool_name?: string;
582
+ tool_display_name?: string;
583
+ function_name?: string;
584
+ arguments?: Record<string, unknown>;
585
+ form_schema?: FormSchema;
586
+ message?: string;
587
+ data?: Record<string, unknown>;
588
+ follow_up_replies?: string[];
589
+ satisfaction_feedback?: unknown;
590
+ ask_to_talk_to_human?: boolean;
591
+ }
592
+
545
593
  /**
546
594
  * Where a completed form is sent.
547
595
  * - `"https://..."` (a bare string) — shorthand for `{ via: "client", url }`: the browser