@useago/sdk 1.5.2 → 1.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/README.md +10 -0
- package/dist/AgoClient-BW0YZsis.js +1681 -0
- package/dist/AgoClient-Ba3ICq4K.cjs +8 -0
- package/dist/angular.cjs +1 -1
- package/dist/angular.d.cts +352 -0
- package/dist/angular.d.ts +352 -0
- package/dist/angular.js +1 -1
- package/dist/{createConversationSession-DPrrr70o.js → createConversationSession-BjsgjL-v.js} +3 -3
- package/dist/createConversationSession-okoz3teC.cjs +1 -0
- package/dist/createFormCollector-BWIh1pMl.cjs +1 -0
- package/dist/createFormCollector-Bay2UpRv.js +279 -0
- package/dist/createMockClient-DcuNlufr.js +97 -0
- package/dist/createMockClient-Dw-cyISN.cjs +1 -0
- package/dist/devtools.d.cts +352 -0
- package/dist/devtools.d.ts +352 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +564 -0
- package/dist/index.d.ts +564 -0
- package/dist/index.js +97 -90
- package/dist/react.cjs +3 -3
- package/dist/react.d.cts +430 -0
- package/dist/react.d.ts +430 -0
- package/dist/react.js +600 -443
- package/dist/testing.cjs +1 -1
- package/dist/testing.d.cts +352 -0
- package/dist/testing.d.ts +352 -0
- package/dist/testing.js +1 -1
- package/dist/vue.cjs +1 -1
- package/dist/vue.d.cts +373 -0
- package/dist/vue.d.ts +373 -0
- package/dist/vue.js +133 -110
- package/dist/widget.cjs +1 -1
- package/dist/widget.d.cts +349 -0
- package/dist/widget.d.ts +349 -0
- package/dist/widget.js +7 -7
- package/llms-full.txt +29 -0
- package/package.json +1 -1
- package/dist/AgoClient-5VGybmvz.cjs +0 -8
- package/dist/AgoClient-DXrH2AMi.js +0 -964
- package/dist/createConversationSession-0WTqcCLK.cjs +0 -1
- package/dist/createFormCollector-1GKKwQV_.cjs +0 -1
- package/dist/createFormCollector-DYh90kiG.js +0 -305
- package/dist/createMockClient-C-wrjb2C.cjs +0 -1
- package/dist/createMockClient-we-XoPom.js +0 -79
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
var Y=10,T=1e3;function z(e){if(typeof e!="object"||e===null)return!1;const t=Object.getPrototypeOf(e);return t===Object.prototype||t===null}function S(e,t){if(typeof e=="string"){if(e.length<=1e3)return e;const i=e.length-T;return e.slice(0,T)+`…[truncated ${i} chars]`}if(Array.isArray(e)){if(t.has(e))return"…[circular]";t.add(e);const i=e.slice(0,50).map(s=>S(s,t));return e.length>50&&i.push(`…[+${e.length-50} more]`),t.delete(e),i}if(z(e)){if(t.has(e))return"…[circular]";t.add(e);const i={};for(const[s,n]of Object.entries(e))i[s]=S(n,t);return t.delete(e),i}return e}function J(e){return S(e,new WeakSet)}var D=class{constructor(e=10){this.entries=[];const t=Math.floor(e);this.maxEntries=Number.isFinite(t)?Math.max(1,t):10}add(e){const t={ts:e.ts??Date.now(),actor:e.actor??"user",name:e.name,summary:e.summary,...e.data!==void 0?{data:J(e.data)}:{}};this.entries.push(t);const i=this.entries.length-this.maxEntries;return i>0&&this.entries.splice(0,i),t}getRecent(){return[...this.entries]}get size(){return this.entries.length}clear(){this.entries=[]}},d=class extends Error{constructor(e,t,i){super(e),this.code=t,this.statusCode=i,this.name="AgoError"}},p=class U extends d{constructor(t,i,s,n,o,a){super(t,i,s),this.type=n,this.param=o,this.docUrl=a,this.name="AgoApiError"}static fromResponse(t,i){const s=t.error;return new U(s.doc_url?`${s.message} See ${s.doc_url}`:s.message,s.code,i,s.type,s.param,s.doc_url)}},y=class extends d{constructor(e,t){super(e,"network_error"),this.originalError=t,this.name="AgoNetworkError"}},A=class extends d{constructor(e,t="stream_error"){super(e,t),this.name="AgoStreamError"}},E=class extends d{constructor(e,t,i){super(e,"function_error"),this.functionName=t,this.originalError=i,this.name="AgoFunctionError"}},V=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)}},c=new V,F="ago_widget_id";function Q(){if(typeof localStorage<"u"){const t=localStorage.getItem(F);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(F,e)}catch{}return e}var Z=class{constructor(e){this.baseUrl=e.baseUrl.replace(/\/$/,""),this.headers={"X-User-Anon-Id":e.widgetId||Q()},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,i){return this.request("POST",e,t,i)}async postStream(e,t){const i=`${this.baseUrl}${e}`;c.debug("POST (stream)",i,t);try{const s=await fetch(i,{method:"POST",headers:{...this.headers,"Content-Type":"application/json"},body:t?JSON.stringify(t):void 0});return s.ok||await this.handleErrorResponse(s),s}catch(s){throw s instanceof p?s:new y(`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 postFormData(e,t){const i=`${this.baseUrl}${e}`;c.debug("POST (formData)",i);const s={...this.headers};try{const n=await fetch(i,{method:"POST",headers:s,body:t});return n.ok||await this.handleErrorResponse(n),n}catch(n){throw n instanceof p?n:new y(`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 request(e,t,i,s){const n=`${this.baseUrl}${t}`;c.debug(e,n,i);try{const o=await fetch(n,{method:e,headers:{...this.headers,"Content-Type":"application/json"},body:i?JSON.stringify(i):void 0,...s?.keepalive?{keepalive:!0}:{}});return o.ok||await this.handleErrorResponse(o),o.status===204?void 0:o.json()}catch(o){throw o instanceof p?o:new y(`Network error: ${o instanceof Error?o.message:"Unknown error"}. Check that \`baseUrl\` is reachable and includes the protocol (https://).`,o instanceof Error?o:void 0)}}async handleErrorResponse(e){let t;try{t=await e.json()}catch{}if(t?.error)throw p.fromResponse(t,e.status);let i="";throw e.status===401||e.status===403?i=" Check `userJwt` (and that it has not expired).":e.status===404&&(i=" Check that `baseUrl` points at your AGO API root."),new p(`HTTP ${e.status}: ${e.statusText}.${i}`,"http_error",e.status,"api_error")}},ee=5e4,te=2e4,ie=2e3,M=class{constructor(e){this.functions=new Map,this.defaultMaxResultBytes=e?.maxResultBytes??ee}setDefaultMaxResultBytes(e){this.defaultMaxResultBytes=e}register(e,t,i){if(typeof e=="object"){const{name:r,handler:l,description:u,parameters:h,maxResultBytes:g,requiresApproval:m}=e;return this.register(r,l,{description:u,parameters:h,maxResultBytes:g,requiresApproval:m})}const s=e;if(!t||!i)throw new d(`registerFunction("${s}"): 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(s)&&c.warn(`Function "${s}" is being overwritten`);const{maxResultBytes:n,requiresApproval:o,...a}=i;this.functions.set(s,{schema:{...a,name:s},handler:t,maxResultBytes:n,requiresApproval:o}),c.log(`Registered function: ${s}`)}unregister(e){const t=this.functions.delete(e);return t&&c.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 i=this.functions.get(e);if(!i)throw new E(`Function "${e}" is not registered`,e);c.log(`Executing function: ${e}`,t);try{const s=await i.handler(t);return c.log(`Function ${e} completed:`,s),this.guardResultSize(e,s,i.maxResultBytes)}catch(s){throw c.error(`Function ${e} failed:`,s),new E(`Function "${e}" execution failed: ${s instanceof Error?s.message:"Unknown error"}`,e,s instanceof Error?s:void 0)}}guardResultSize(e,t,i){let s;try{s=JSON.stringify(t)}catch{return t}if(s===void 0)return t;const n=new TextEncoder().encode(s).length,o=i??this.defaultMaxResultBytes;return n>o?(c.warn(`Function "${e}" result is ${n} 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:n,maxBytes:o,preview:s.slice(0,ie),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.`}):(n>te&&c.warn(`Function "${e}" returned a large result (${Math.round(n/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(),c.log("Cleared all registered functions")}get size(){return this.functions.size}},se=250,ne=3,re=1e3,oe=24,ae=10,ce=50,le=["mousemove","mousedown","keydown","scroll","touchstart"],j=class{constructor(e={}){this.started=!1,this.teardowns=[],this.route="",this.routeEnteredAt=0,this.lastActivityAt=0,this.lastHeavyActivityAt=0,this.recentRoutes=[],this.routeBounces=0,this.rageClicks=0,this.fieldErrors={},this.recentClicks=[],this.signals=[],this.listeners=new Set,this.getPageState=e.getPageState,this.activityThrottleMs=e.activityThrottleMs??se,this.rageClickCount=e.rageClickCount??ne,this.rageClickWindowMs=e.rageClickWindowMs??re,this.rageClickRadiusPx=e.rageClickRadiusPx??oe,this.maxRecentRoutes=e.maxRecentRoutes??ae,this.maxSignals=e.maxSignals??ce}start(){if(this.started||typeof window>"u"||typeof document>"u")return;this.started=!0;const e=Date.now();this.route=this.currentPathname(),this.routeEnteredAt=e,this.lastActivityAt=e,this.recentRoutes=[this.route];const t=(s,n,o)=>{s.addEventListener(n,o,{passive:!0}),this.teardowns.push(()=>s.removeEventListener(n,o))},i=()=>this.recordActivity();for(const s of le)t(window,s,i);t(document,"visibilitychange",()=>{document.visibilityState==="visible"&&this.recordActivity()}),t(window,"popstate",()=>this.checkRoute()),t(window,"hashchange",()=>this.checkRoute()),t(window,"click",s=>this.onClick(s)),c.log("SignalCollector started")}destroy(){for(const e of this.teardowns)e();this.teardowns=[],this.listeners.clear(),this.recentClicks=[],this.started=!1,c.log("SignalCollector destroyed")}signalFieldError(e){this.checkRoute();const t=(this.fieldErrors[e]??0)+1;this.fieldErrors[e]=t,this.pushSignal({type:"field_error",route:this.route,at:Date.now(),count:t,meta:{field:e}})}subscribe(e){return this.listeners.add(e),()=>{this.listeners.delete(e)}}getSignals(){return[...this.signals]}getSnapshot(e){this.checkRoute();const t=Date.now();return{route:this.route,dwellMs:this.started?t-this.routeEnteredAt:0,idleMs:this.started?t-this.lastActivityAt:0,rageClicks:this.rageClicks,routeBounces:this.routeBounces,fieldErrors:{...this.fieldErrors},recentRoutes:[...this.recentRoutes],pageState:e?.pageState===!1?null:this.readPageState()}}currentPathname(){return typeof window<"u"&&window.location?window.location.pathname:""}readPageState(){if(!this.getPageState)return null;try{return this.getPageState()??null}catch(e){return c.warn("SignalCollector getPageState threw:",e),null}}recordActivity(){const e=Date.now();this.lastActivityAt=e,e-this.lastHeavyActivityAt>=this.activityThrottleMs&&(this.lastHeavyActivityAt=e,this.checkRoute())}checkRoute(){if(!this.started)return;const e=this.currentPathname();if(e===this.route)return;const t=Date.now(),i=this.route;this.route=e,this.routeEnteredAt=t,this.rageClicks=0,this.fieldErrors={},this.recentClicks=[],this.recentRoutes.push(e),this.recentRoutes.length>this.maxRecentRoutes&&this.recentRoutes.splice(0,this.recentRoutes.length-this.maxRecentRoutes),this.pushSignal({type:"route_change",route:e,at:t,meta:{from:i}});const s=this.recentRoutes,n=s.length;n>=4&&s[n-1]===s[n-3]&&s[n-2]===s[n-4]&&s[n-1]!==s[n-2]&&(this.routeBounces+=1,this.pushSignal({type:"route_bounce",route:e,at:t,count:this.routeBounces,meta:{between:`${s[n-2]}↔${s[n-1]}`}}))}onClick(e){this.recordActivity();const t=Date.now(),i=this.pageStateKey(),s={at:t,x:e.clientX,y:e.clientY,stateKey:i};this.recentClicks=this.recentClicks.filter(r=>t-r.at<=this.rageClickWindowMs);const n=this.recentClicks[0],o=!n||Math.abs(s.x-n.x)<=this.rageClickRadiusPx&&Math.abs(s.y-n.y)<=this.rageClickRadiusPx,a=!n||n.stateKey===i;if(!o||!a){this.recentClicks=[s];return}this.recentClicks.push(s),this.recentClicks.length>=this.rageClickCount&&(this.rageClicks+=1,this.recentClicks=[],this.pushSignal({type:"rage_click",route:this.route,at:t,count:this.rageClicks}))}pageStateKey(){const e=this.readPageState();if(!e)return"";try{return JSON.stringify(e)}catch{return""}}pushSignal(e){this.signals.push(e),this.signals.length>this.maxSignals&&this.signals.splice(0,this.signals.length-this.maxSignals);for(const t of this.listeners)try{t(e)}catch(i){c.error("Signal listener threw:",i)}}};function L(e,t){const i=t?t.storage??(typeof localStorage<"u"?localStorage:void 0):void 0;let s=e;if(t&&i)try{const r=i.getItem(t.key);r!=null&&(s=JSON.parse(r))}catch{}const n=new Set,o=()=>n.forEach(r=>r(s));function a(){if(t&&i)try{i.setItem(t.key,JSON.stringify(s))}catch{}}return{get:()=>s,set:r=>{s=r,a(),o()},subscribe:r=>(n.add(r),()=>{n.delete(r)})}}var ue=6e4,he=1440*60*1e3,de="ago_proactive";function C(e){const t=new Date(e),i=String(t.getMonth()+1).padStart(2,"0"),s=String(t.getDate()).padStart(2,"0");return`${t.getFullYear()}-${i}-${s}`}var ge=class{constructor(e={}){this.killSwitchEnabled=!1,this.engaged=!1,this.turnInFlight=!1,this.slotOccupied=!1,this.sessionCount=0,this.perSessionCap=e.frequencyCap?.perSession,this.perDayCap=e.frequencyCap?.perDay,this.store=L({optOut:!1,lastFiredAt:{},dismissedAt:{},day:{date:C(Date.now()),count:0}},{key:e.storageKey??de,storage:e.storage})}setKillSwitch(e){this.killSwitchEnabled=e}optOut(){this.store.set({...this.store.get(),optOut:!0})}optIn(){this.store.set({...this.store.get(),optOut:!1})}isOptedOut(){return this.store.get().optOut}setEngaged(e){this.engaged=e}setTurnInFlight(e){this.turnInFlight=e}isSlotOccupied(){return this.slotOccupied}occupySlot(){this.slotOccupied=!0}releaseSlot(){this.slotOccupied=!1}canFire(e,t=Date.now()){if(!this.killSwitchEnabled)return this.deny(e,"kill-switch (proactive disabled server-side)");const i=this.store.get();if(i.optOut)return this.deny(e,"user opted out");if(this.engaged)return this.deny(e,"user engaged (widget open)");if(this.turnInFlight)return this.deny(e,"message turn in flight");if(this.slotOccupied)return this.deny(e,"nudge slot occupied");const s=i.lastFiredAt[e.id],n=e.cooldownMs??ue;if(s!==void 0&&t-s<n)return this.deny(e,"cooldown");const o=i.dismissedAt[e.id],a=e.resurfaceAfterMs??he;return o!==void 0&&t-o<a?this.deny(e,"dismissed recently"):this.perSessionCap!==void 0&&this.sessionCount>=this.perSessionCap?this.deny(e,"perSession cap"):this.perDayCap!==void 0&&this.dayCount(t)>=this.perDayCap?this.deny(e,"perDay cap"):!0}recordFired(e,t=Date.now()){const i=this.store.get();this.store.set({...i,lastFiredAt:{...i.lastFiredAt,[e]:t}})}recordNudgeShown(e=Date.now()){this.sessionCount+=1;const t=this.store.get(),i=C(e),s=t.day.date===i?{date:i,count:t.day.count+1}:{date:i,count:1};this.store.set({...t,day:s})}recordDismissed(e,t=Date.now()){const i=this.store.get();this.store.set({...i,dismissedAt:{...i.dismissedAt,[e]:t}})}dayCount(e){const t=this.store.get();return t.day.date===C(e)?t.day.count:0}deny(e,t){return c.debug(`NudgeGovernor: trigger "${e.id}" blocked — ${t}`),!1}};function P(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const t=Math.floor(Math.random()*16);return(e==="x"?t:t&3|8).toString(16)})}function fe(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function B(e,t){if(e==="*"||e===t)return!0;const i=e.split("/"),s=i.map((n,o)=>{const a=o===i.length-1;return n==="*"?a?"[^/]+(?:/[^/]+)*":"[^/]+":n.startsWith(":")?"[^/]+":fe(n)});return new RegExp("^"+s.join("/")+"/?$").test(t)}function x(e,t){return e===t?!0:e===null||t===null?!1:Array.isArray(e)?!Array.isArray(t)||t.length!==e.length?!1:e.every((i,s)=>_(i,t[s])):typeof e=="object"?typeof t!="object"||Array.isArray(t)?!1:Object.entries(e).every(([i,s])=>x(s,t[i])):!1}function _(e,t){if(e===t)return!0;if(e===null||t===null||typeof e!=typeof t)return!1;if(Array.isArray(e))return!Array.isArray(t)||e.length!==t.length?!1:e.every((i,s)=>_(i,t[s]));if(typeof e=="object"){const i=Object.keys(e),s=Object.keys(t);return i.length!==s.length?!1:i.every(n=>_(e[n],t[n]))}return!1}function G(e,t){if(e.route!==void 0&&!B(e.route,t.route)||e.idleMs!==void 0&&t.idleMs<e.idleMs||e.dwellMs!==void 0&&t.dwellMs<e.dwellMs||e.rageClicks!==void 0&&t.rageClicks<e.rageClicks||e.routeBounces!==void 0&&t.routeBounces<e.routeBounces)return!1;if(e.fieldErrors!==void 0){for(const[i,s]of Object.entries(e.fieldErrors))if((t.fieldErrors[i]??0)<s)return!1}return!(e.pageState!==void 0&&!x(e.pageState,t.pageState??{}))}var pe=1e3,me="/api/sdk/v1/config",ye="/api/sdk/v1/proactive/evaluate",ve=class{constructor(e){this.tickTimer=null,this.teardowns=[],this.started=!1,this.activeNudge=null,this.lastConversationId=null,this.shownIds=new Set,this.resolvedIds=new Set,this.lastEvaluationAt=0,this.client=e.client,this.collector=e.collector,this.governor=e.governor,this.tracker=e.tracker,this.options=e.options,this.tickMs=e.options.tickMs??pe}start(){if(this.started)return;this.started=!0,this.options.enabledOverride!==void 0?this.governor.setKillSwitch(this.options.enabledOverride):this.loadKillSwitch();const e=n=>{n.conversationId&&(this.lastConversationId=n.conversationId),this.governor.setTurnInFlight(!0)},t=()=>this.governor.setTurnInFlight(!1),i=()=>this.governor.setTurnInFlight(!1),s=n=>{n?.id&&(this.lastConversationId=n.id)};this.client.on("message:start",e),this.client.on("message:complete",t),this.client.on("message:error",i),this.client.on("conversation:loaded",s),this.teardowns.push(()=>{this.client.off("message:start",e),this.client.off("message:complete",t),this.client.off("message:error",i),this.client.off("conversation:loaded",s)}),this.tickTimer=setInterval(()=>this.evaluate(),this.tickMs),this.teardowns.push(this.collector.subscribe(()=>{Date.now()-this.lastEvaluationAt>=this.tickMs&&this.evaluate()}))}stop(){this.tickTimer!==null&&(clearInterval(this.tickTimer),this.tickTimer=null);for(const e of this.teardowns)e();this.teardowns=[],this.started=!1}getActiveNudge(){return this.activeNudge}async loadKillSwitch(){try{const e=await this.client.getHttp().get(me);this.governor.setKillSwitch(e?.proactive?.enabled===!0)}catch(e){c.debug("Proactive config fetch failed; staying disabled:",e),this.governor.setKillSwitch(!1)}}evaluate(){if(this.lastEvaluationAt=Date.now(),typeof document<"u"&&document.visibilityState==="hidden"||this.governor.isSlotOccupied())return;const e=this.collector.getSnapshot();for(const t of this.options.triggers)if(G(t.when,e)&&this.governor.canFire(t)){this.fire(t);return}}fire(e){if(this.governor.recordFired(e.id),this.governor.occupySlot(),e.intervene==="agent"){this.evaluateWithAgent(e);return}this.setReady({id:P(),triggerId:e.id,source:"static",message:e.intervene.message,action:e.intervene.action,goal:e.goal,kickoff:e.kickoff,createdAt:new Date})}async evaluateWithAgent(e){const t=P(),i={client_nudge_id:t,trigger_id:e.id,goal:e.goal??"",agent:this.client.getConfiguredAgent(),signals:this.collector.getSnapshot(),client_context:this.client.getContextSnapshot()??{entries:{}},client_functions:this.client.getRegisteredFunctions()};let s;try{s=await this.client.getHttp().post(ye,i)}catch(n){c.debug("Proactive evaluate failed (treated as no-intervene):",n),this.governor.releaseSlot();return}if(!s?.intervene||!s.message){this.governor.releaseSlot();return}this.setReady({id:t,triggerId:e.id,source:"agent",message:s.message,action:we(s.action),goal:e.goal,kickoff:e.kickoff,createdAt:new Date,serverNudgeId:s.nudge_id})}setReady(e){this.activeNudge=e,this.client.emitProactiveEvent("nudge:ready",e)}shown(e){this.shownIds.has(e.id)||(this.shownIds.add(e.id),this.governor.recordNudgeShown(),this.tracker.track({clientNudgeId:e.id,triggerId:e.triggerId,source:e.source,type:"shown",message:e.message,occurredAt:Date.now()}),this.client.emitProactiveEvent("nudge:shown",{nudge:e}))}dismiss(e){this.resolvedIds.has(e.id)||(this.resolvedIds.add(e.id),this.governor.recordDismissed(e.triggerId),this.tracker.track({clientNudgeId:e.id,triggerId:e.triggerId,source:e.source,type:"dismissed",occurredAt:Date.now()}),this.clearActive(e),this.client.emitProactiveEvent("nudge:dismissed",{nudge:e}))}async accept(e){this.resolvedIds.has(e.id)||(this.resolvedIds.add(e.id),this.tracker.track({clientNudgeId:e.id,triggerId:e.triggerId,source:e.source,type:"accepted",occurredAt:Date.now()}),this.clearActive(e),this.client.emitProactiveEvent("nudge:accepted",{nudge:e}),(e.source==="agent"||e.goal)&&(this.lastConversationId?this.client.sendMessage(Ce(e),{hidden:!0,conversationId:this.lastConversationId}).catch(t=>c.debug("Nudge kickoff message failed:",t)):e.kickoff?this.client.sendMessage(e.kickoff).catch(t=>c.debug("Nudge kickoff message failed:",t)):c.debug("Nudge accepted with no open conversation and no trigger.kickoff; skipping the chat pre-seed (a hidden message cannot open a conversation).")),e.action&&await this.runAction(e.action))}markConverted(e){this.tracker.track({clientNudgeId:e.id,triggerId:e.triggerId,source:e.source,type:"converted",occurredAt:Date.now()})}clearActive(e){this.activeNudge?.id===e.id&&(this.activeNudge=null),this.governor.releaseSlot()}async runAction(e){try{"functionName"in e?await this.client.executeClientFunction(e.functionName,e.arguments??{}):await this.client.executeClientFunction("setPageState",e.setPageState)}catch(t){c.warn("Nudge action failed:",t)}}};function we(e){if(e)return{label:e.label,functionName:e.function_name,arguments:e.arguments}}function Ce(e){const t=e.goal?` Goal: ${e.goal}.`:"";return`The user accepted a proactive nudge that said: "${e.message}".${t} Recent friction signals are in the client context (friction_signals). Start helping with this now: briefly explain what you can do and take the first concrete step.`}var be=5e3,Se="/api/sdk/v1/proactive/events",Ae=class{constructor(e){if(this.queue=[],this.flushTimer=null,this.teardowns=[],this.destroyed=!1,this.post=e.post,this.agent=e.agent,this.flushIntervalMs=e.flushIntervalMs??be,typeof document<"u"){const t=()=>{document.visibilityState==="hidden"&&this.flush({keepalive:!0})};document.addEventListener("visibilitychange",t),this.teardowns.push(()=>document.removeEventListener("visibilitychange",t))}if(typeof window<"u"){const t=()=>this.flush({keepalive:!0});window.addEventListener("pagehide",t),this.teardowns.push(()=>window.removeEventListener("pagehide",t))}}track(e){this.destroyed||(this.queue.push(e),this.flushTimer===null&&(this.flushTimer=setTimeout(()=>this.flush(),this.flushIntervalMs)))}flush(e){if(this.flushTimer!==null&&(clearTimeout(this.flushTimer),this.flushTimer=null),this.queue.length===0)return;const t=this.queue;this.queue=[];const i={events:t.map(s=>({client_nudge_id:s.clientNudgeId,trigger_id:s.triggerId,source:s.source,type:s.type,...s.message!==void 0?{message:s.message}:{},...this.agent?{agent:this.agent}:{},occurred_at:new Date(s.occurredAt).toISOString()}))};this.post(Se,i,e).catch(s=>{c.debug("Proactive event batch failed:",s)})}destroy(){if(!this.destroyed){this.flush({keepalive:!0}),this.destroyed=!0;for(const e of this.teardowns)e();this.teardowns=[]}}},O="friction_signals";function k(e,t){e.proactive&&(c.warn("createAgoProactive: a proactive controller is already attached; replacing it."),e.proactive.destroy());const i=new j({...t.signals,getPageState:()=>{const r=e.getContextSnapshot();if(!r)return null;const l={};for(const[u,h]of Object.entries(r.entries))u.startsWith("page-state:")&&h.data&&Object.assign(l,h.data);return Object.keys(l).length>0?l:null}});i.start(),e.addDynamicContext(O,()=>{const{pageState:r,...l}=i.getSnapshot({pageState:!1});return{name:"Friction signals",description:"Behavioral friction observed on the current page: dwell/idle times, rage clicks, route bounces, repeated field errors (names and counters only).",data:l}});const s=new ge({frequencyCap:t.frequencyCap,storage:t.storage,storageKey:t.storageKey}),n=new Ae({post:(r,l,u)=>e.getHttp().post(r,l,u),agent:e.getConfiguredAgent()}),o=new ve({client:e,collector:i,governor:s,tracker:n,options:t});o.start();const a={optOut:()=>s.optOut(),optIn:()=>s.optIn(),isOptedOut:()=>s.isOptedOut(),setEngaged:r=>s.setEngaged(r),signalFieldError:r=>i.signalFieldError(r),getSignalsSnapshot:()=>i.getSnapshot(),getActiveNudge:()=>o.getActiveNudge(),shown:r=>o.shown(r),accept:r=>o.accept(r),dismiss:r=>o.dismiss(r),markConverted:r=>o.markConverted(r),destroy:()=>{o.stop(),n.destroy(),i.destroy(),e.removeDynamicContext(O),e.proactive===a&&(e.proactive=null)}};return e.proactive=a,a}var W=class{constructor(){this.entries=new Map,this.dynamicProviders=new Map}set(e,t){this.entries.set(e,t),c.log(`ClientContext set: ${e}`)}remove(e){const t=this.entries.delete(e);return t&&c.log(`ClientContext removed: ${e}`),t}addDynamicProvider(e,t){this.dynamicProviders.set(e,t),c.log(`DynamicContext provider added: ${e}`)}removeDynamicProvider(e){const t=this.dynamicProviders.delete(e);return t&&c.log(`DynamicContext provider removed: ${e}`),t}getSnapshot(){if(this.entries.size===0&&this.dynamicProviders.size===0)return null;const e={};for(const[t,i]of this.entries)e[t]=i;for(const[t,i]of this.dynamicProviders)try{const s=i();s&&(e[t]=s)}catch(s){c.error(`DynamicContext provider "${t}" threw:`,s)}return Object.keys(e).length===0?null:{entries:e}}clear(){this.entries.clear(),this.dynamicProviders.clear(),c.log("ClientContext cleared")}},Ee="page-state:";function _e(e){const t={};for(const[i,s]of Object.entries(e))i.startsWith(Ee)&&s.data&&Object.assign(t,s.data);return t}function ke(e){const t=e["current-page"]?.data;return t?.page?t.page:t?.url?t.url:e["browser-page"]?.data?.url}function Re(e,t){return e===t?!0:JSON.stringify(e)===JSON.stringify(t)}function Ie(e,t){const i=[];for(const s of Object.keys(t))s in e&&(Re(e[s],t[s])||i.push({field:s,from:e[s],to:t[s]}));return i}function xe(e,t){const i=_e(t),s=ke(t);return{changes:e&&e.page===s?Ie(e.state,i):[],baseline:{page:s,state:i}}}function R(e){return e===null||typeof e!="object"?JSON.stringify(e)??"null":Array.isArray(e)?`[${e.map(R).join(",")}]`:`{${Object.keys(e).sort().map(t=>`${JSON.stringify(t)}:${R(e[t])}`).join(",")}}`}var K=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 A("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(),i=new TextDecoder;try{for(;;){const{done:s,value:n}=await t.read();if(s)break;const o=i.decode(n,{stream:!0});this.buffer+=o,this.processBuffer()}return this.buffer.trim()&&this.processBuffer(),this.buildFinalMessage()}catch(s){const n=s instanceof Error?s:new A("Stream processing failed mid-flight. The connection may have dropped; retry the message.");throw this.callbacks.onError?.(n),n}finally{t.releaseLock()}}processBuffer(){const e=this.buffer.split(`
|
|
2
|
+
|
|
3
|
+
`);this.buffer=e.pop()||"";for(const t of e){if(!t.trim())continue;const i=t.split(`
|
|
4
|
+
`);for(const s of i)if(!s.startsWith(": ")&&s.startsWith("data: ")){const n=s.slice(6);try{const o=JSON.parse(n);this.handleChunk(o)}catch(o){c.warn("Failed to parse SSE data:",n,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 i=t.knowledge_document;return{id:i.id,title:i.title,url:i.use_external_link?i.external_link_url:i.internal_link_url}})),e.type==="client_function"&&e.function_name){const t=`${e.function_name}::${R(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 i=this.toolCalls.findIndex(s=>s.id===t.id);i>=0?this.toolCalls[i]=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 Te(e){const t=e.message.toLowerCase();return t.includes("load failed")||t.includes("failed to fetch")||t.includes("network")||t.includes("abort")}var Fe=new Set(["image/jpeg","image/png","image/gif","image/webp","image/bmp","image/tiff"]);function q(e){if(!e)return;const t=typeof window<"u"&&window.location?window.location.href:"https://ago.local/";try{const i=new URL(e,t);if(i.protocol==="http:"||i.protocol==="https:"||i.protocol==="blob:")return e}catch{}}function Pe(e){return e.isSafeImage===!0&&typeof e.contentType=="string"&&e.contentType.startsWith("image/")&&q(e.url)!==void 0}function Oe(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 $e(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 Ne(e){return e.map((t,i)=>({id:`local-${i}-${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:Fe.has(t.type)}))}var H=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 i=this.handlers.get(e);i&&i.delete(t)}emit(e,t){const i=this.handlers.get(e);i&&i.forEach(s=>{try{s(t)}catch(n){console.error(`Error in event handler for ${String(e)}:`,n)}})}once(e,t){const i=(s=>{this.off(e,i),t(s)});this.on(e,i)}waitFor(e,t){return new Promise((i,s)=>{let n;const o=(a=>{n&&clearTimeout(n),i(a)});this.once(e,o),t?.timeout&&(n=setTimeout(()=>{this.off(e,o),s(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).',De=/^[a-z][a-z0-9+.-]*:\/\//i,N=!1;function I(e,t="AgoClient"){if(typeof e!="object"||e===null)throw new d(`${t}: ${$}`,"config_missing_base_url");const i=e.baseUrl;if(typeof i!="string"||i.trim()==="")throw new d(`${t}: ${$}`,"config_missing_base_url");!i.startsWith("/")&&!De.test(i)&&(N||(N=!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 Ue(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function b(e){return e.split("/").filter(t=>t.startsWith(":")).map(t=>t.slice(1))}function Me(e,t){const i=[];return{path:e.split("/").map(s=>{if(!s.startsWith(":"))return s;const n=s.slice(1),o=t[n];return o==null||o===""?(i.push(n),s):encodeURIComponent(String(o))}).join("/"),missing:i}}function je(e,t){const i=t.find(n=>n.path===e);if(i)return i;const s=t.find(n=>{if(!n.path.includes(":"))return!1;const o="^"+n.path.split("/").map(a=>a.startsWith(":")?"[^/]+":Ue(a)).join("/")+"/?$";return new RegExp(o).test(e)});return s||t.filter(n=>n.path!=="/"&&e.startsWith(n.path)).sort((n,o)=>o.path.length-n.path.length)[0]}var Le="activity:recent",Be="state:delta",Ge=class X{constructor(t){this.lastSentPageState=null,this.warnedEmptyConversations=new Set,this.pendingResumes=new Map,this.pendingApprovals=new Map,this.resumeGate=null,this.proactive=null,I(t,"AgoClient"),this.config=t,this.activityLedger=new D(t.maxActivityEntries),this.httpClient=new Z(t),this.functionRegistry=new M({maxResultBytes:t.maxFunctionResultBytes}),this.contextRegistry=new W,this.eventEmitter=new H,this.registerActivityContext(),t.debug&&c.enable(),t.proactive&&k(this,t.proactive),c.log("AgoClient initialized")}async sendMessage(t,i){const s=this.functionRegistry.getSchemas(),n=this.buildSendContext(),o=this.config.agent||this.config.defaultAgentId,a=this.resolveClientFunctionsMode(i),r={content:t,conversation_id:i?.conversationId,agent_id:i?.agentId||o};s.length>0&&(r.client_functions=s,a==="pause"&&(r.client_functions_mode=a)),n&&(r.client_context=n),i?.hidden&&(r.hidden=!0);let l;if(i?.files&&i.files.length>0){const u=new FormData;u.append("content",t),i.conversationId&&u.append("conversation_id",i.conversationId),(i.agentId||o)&&u.append("agent_id",i.agentId||o||""),s.length>0&&(u.append("client_functions",JSON.stringify(s)),a==="pause"&&u.append("client_functions_mode",a)),n&&u.append("client_context",JSON.stringify(n)),i.hidden&&u.append("hidden","true");for(const h of i.files)u.append("files",h);l=await this.httpClient.postFormData("/api/sdk/v1/messages",u)}else l=await this.httpClient.postStream("/api/sdk/v1/messages",r);return this.processSSEResponse(l)}async processSSEResponse(t){let i=!1;const s=await new K({onRawChunk:n=>{this.eventEmitter.emit("stream:message",n)},onStart:n=>{this.eventEmitter.emit("message:start",n)},onChunk:n=>{this.eventEmitter.emit("message:chunk",n)},onToolCall:n=>{this.eventEmitter.emit("toolCall:received",n),n.type==="form"&&this.eventEmitter.emit("toolCall:form",n)},onClientFunction:async n=>{if(i=!0,this.eventEmitter.emit("function:invoke",n),this.recordAgentAction(n),n.invocationId&&this.requiresApproval(n)){this.pendingApprovals.set(n.invocationId,n),this.eventEmitter.emit("function:awaiting-approval",n);return}await this.handleClientFunctionInvocation(n)},onAnswerComplete:n=>{this.eventEmitter.emit("message:answer-complete",n)},onWaitingClient:n=>{this.eventEmitter.emit("message:waiting-client",n),this.markResumeSignal(n.messageId,n.conversationId,"waitingConfirmed")},onComplete:n=>{this.eventEmitter.emit("message:complete",n)},onError:n=>{this.eventEmitter.emit("message:error",{error:n.message,code:n instanceof d?n.code:void 0})}}).processStream(t);return s.status!=="WAITING_CLIENT"&&this.maybeFlagEmptyReply(s,i),s}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 i=this.functionRegistry.getSchemas(),s=this.buildSendContext(),n={};i.length>0&&(n.client_functions=i),s&&(n.client_context=s);const o=await this.httpClient.postStream(`/api/sdk/v1/messages/${t}/continue`,n);return this.processSSEResponse(o)}markResumeSignal(t,i,s){if(!t)return;const n=this.pendingResumes.get(t)??{conversationId:i,waitingConfirmed:!1,resultsReady:!1,started:!1};n[s]=!0,i&&(n.conversationId=i),this.pendingResumes.set(t,n),!(!n.waitingConfirmed||!n.resultsReady||n.started)&&(n.started=!0,this.runAutoResume(t,n.conversationId))}async runAutoResume(t,i){try{this.resumeGate&&await this.resumeGate({conversationId:i,messageId:t})}catch(s){c.warn("Resume gate threw; resuming anyway:",s)}this.pendingResumes.delete(t);try{await this.continueMessage(t)}catch(s){c.warn("Auto-resume of paused turn failed:",s)}}maybeFlagEmptyReply(t,i){t.status==="DONE"&&(t.content??"").trim()===""&&!i&&!(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 s=t.conversationId||"(no conversation)";if(this.warnedEmptyConversations.has(s))return;this.warnedEmptyConversations.add(s);const n=this.config.agent||this.config.defaultAgentId;t.id?console.warn("[AGO] Reply completed with empty content. Possible causes: "+(n?`the agent "${n}" 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: "+(n?`the agent "${n}" 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 i,s;try{i=await this.functionRegistry.execute(t.functionName,t.arguments)}catch(n){s=n instanceof Error?n.message:"Unknown error",c.error("Client function execution failed:",n)}if(t.invocationId)try{const n=await this.submitToolCallForm(t.invocationId,{result:i,error:s,_type:"client_function_result"});n?.resume?.ready&&this.markResumeSignal(n.resume.message_id,t.conversationId,"resultsReady")}catch(n){c.error("Failed to submit function result:",n)}this.eventEmitter.emit("function:result",{invocationId:t.invocationId,result:i,error:s})}requiresApproval(t){const i=this.config.approvalPolicy;if(i)try{if(i(t))return!0}catch(s){c.warn("approvalPolicy threw; not gating this call:",s)}return this.functionRegistry.get(t.functionName)?.requiresApproval===!0}async approveFunction(t){const i=this.pendingApprovals.get(t);i&&(this.pendingApprovals.delete(t),await this.handleClientFunctionInvocation(i))}async rejectFunction(t){const i=this.pendingApprovals.get(t);if(!i)return;this.pendingApprovals.delete(t);const s={approved:!1,reason:"user_rejected"};try{const n=await this.submitToolCallForm(t,{result:s,_type:"client_function_result"});n?.resume?.ready&&this.markResumeSignal(n.resume.message_id,i.conversationId,"resultsReady")}catch(n){c.error("Failed to submit function rejection:",n)}this.eventEmitter.emit("function:result",{invocationId:t,result:s})}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 i=await this.httpClient.get(`/api/sdk/v1/conversations/${t}`),s={id:i.id,title:i.title,lastMessageDate:new Date(i.last_message_date),messages:i.messages.map(n=>({id:n.id,conversationId:i.id,content:n.content,role:n.role,status:n.status,createdAt:new Date(n.created_at),hidden:n.hidden,toolCalls:X.mapPersistedToolCalls(n.tool_call_data),attachments:n.attachments&&n.attachments.length>0?n.attachments.map($e):void 0}))};return this.eventEmitter.emit("conversation:loaded",s),s}static mapPersistedToolCalls(t){if(!(!t||t.length===0))return t.map(i=>({id:String(i.id??""),type:i.type??"status_message",status:String(i.status??"unknown"),toolName:i.tool_name??"",toolDisplayName:i.tool_display_name,message:i.message,formSchema:i.form_schema,data:i.data,functionName:i.function_name,arguments:i.arguments}))}async getMessages(t){return(await this.getConversation(t)).messages||[]}async resumePendingClientFunctions(t){const i=[...t.messages??[]].reverse().find(r=>r.role==="assistant"&&r.status==="WAITING_CLIENT");if(!i)return null;const s=(i.toolCalls??[]).filter(r=>r.type==="client_function"&&r.status==="waiting_input"&&r.functionName),n=new Set(this.functionRegistry.getSchemas().map(r=>r.name)),o=s.filter(r=>!n.has(r.functionName));if(o.length>0)return c.warn("Cannot resume paused turn: function(s) not registered on this page:",o.map(r=>r.functionName)),null;let a=s.length===0;for(const r of s){const l={invocationId:r.id,functionName:r.functionName,arguments:r.arguments??{},conversationId:t.id};if(this.requiresApproval(l)){this.pendingApprovals.set(r.id,l),this.markResumeSignal(i.id,t.id,"waitingConfirmed"),this.eventEmitter.emit("function:awaiting-approval",l),a=!1;continue}let u,h;try{u=await this.functionRegistry.execute(r.functionName,r.arguments??{})}catch(g){h=g instanceof Error?g.message:"Unknown error",c.error("Client function execution failed:",g)}a=(await this.submitToolCallForm(r.id,{result:u,error:h,_type:"client_function_result"}))?.resume?.ready??a}return a?this.continueMessage(i.id):null}async submitToolCallForm(t,i){return this.httpClient.post(`/api/sdk/v1/tool-calls/${t}/submit`,{formData:i})}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 i=await this.httpClient.get(`/api/sdk/v1/forms/${encodeURIComponent(t)}`);return{name:i.name,description:i.description,schema:i.schema,submit:i.submit??void 0}}async submitFormCollector(t,i){return this.httpClient.post("/api/sdk/v1/forms/submit",{name:t,values:i})}async submitFeedback(t,i){await this.httpClient.post(`/api/sdk/v1/messages/${t}/feedback`,{rating:i})}registerFunction(t,i,s){typeof t=="object"?this.functionRegistry.register(t):this.functionRegistry.register(t,i,s)}register(t){if(Array.isArray(t))for(const i of t)this.functionRegistry.register(i);else this.functionRegistry.register(t)}unregisterFunction(t){return this.functionRegistry.unregister(t)}getRegisteredFunctions(){return this.functionRegistry.getSchemas()}async executeClientFunction(t,i){return this.functionRegistry.execute(t,i)}registerNavigationFunction(t,i){const s=i.map(r=>r.name),n=i.map(r=>{const l=b(r.path),u=l.length>0?` (requires ${l.map(h=>`"${h}"`).join(", ")})`:"";return`- "${r.name}"${u}: ${r.description}`}).join(`
|
|
5
|
+
`),o=new Map;for(const r of i)for(const l of b(r.path))o.set(l,[...o.get(l)??[],r.name]);const a={page:{type:"string",description:"The page to navigate to",enum:s}};for(const[r,l]of o){if(r==="page"){c.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 ${l.map(u=>`"${u}"`).join(" or ")}.`}}this.registerFunction("navigateToPage",async r=>{const l=r.page,u=i.find(f=>f.name===l);if(!u)return{success:!1,error:`Unknown page: ${l}`};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 g={};for(const f of b(u.path))g[f]=r[f]??h[f];const{path:m,missing:v}=Me(u.path,g);if(v.length>0){const f=v.map(w=>`"${w}": "..."`).join(", ");return{success:!1,error:`Page "${l}" needs ${v.map(w=>`"${w}"`).join(", ")}. Retry with { "page": "${l}", ${f} }.`}}return t(m),{success:!0,navigatedTo:m}},{description:`Navigate the user to a page in the application. Available pages:
|
|
6
|
+
${n}`,parameters:{type:"object",properties:a,required:["page"]}}),this.addDynamicContext("current-page",()=>{if(typeof window>"u"||!window.location)return null;const r=window.location.href,l=typeof document<"u"?document.title:void 0,u=je(window.location.pathname,i),h={url:r};return u&&(h.page=u.name),l&&(h.title=l),{name:"Current page",description:"The page the user is currently viewing.",data:h}})}unregisterNavigationFunction(){this.unregisterFunction("navigateToPage"),this.removeDynamicContext("current-page")}registerPageStateFunction(t,i){const s=i?.functionName??"setPageState",n=new Map(t.map(a=>[a.name,a])),o=t.map(a=>`- "${a.name}": ${a.description}`).join(`
|
|
7
|
+
`);this.registerFunction(s,async a=>{const r={};for(const[l,u]of Object.entries(a)){if(u===void 0)continue;const h=n.get(l);h&&(await h.set(u),r[l]=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:
|
|
8
|
+
${o}`,parameters:{type:"object",properties:Object.fromEntries(t.map(a=>[a.name,{...a.schema,description:a.description}]))},requiresApproval:i?.requiresApproval}),this.addDynamicContext(`page-state:${s}`,()=>{const a=t.filter(l=>typeof l.get=="function");if(a.length===0)return null;const r={};for(const l of a)r[l.name]=l.get();return{name:"Page state",description:"Current, editable state of the page the user is viewing.",data:r}})}unregisterPageStateFunction(t){const i=t??"setPageState";this.unregisterFunction(i),this.removeDynamicContext(`page-state:${i}`)}setContext(t,i){this.contextRegistry.set(t,i),this.notifyContextChanged()}removeContext(t){const i=this.contextRegistry.remove(t);return i&&this.notifyContextChanged(),i}addDynamicContext(t,i){this.contextRegistry.addDynamicProvider(t,i),this.notifyContextChanged()}removeDynamicContext(t){const i=this.contextRegistry.removeDynamicProvider(t);return i&&this.notifyContextChanged(),i}notifyContextChanged(){this.eventEmitter.emit("context:changed",this.contextRegistry.getSnapshot())}emitFormEvent(t,i){this.eventEmitter.emit(t,i)}emitProactiveEvent(t,i){this.eventEmitter.emit(t,i)}getHttp(){return this.httpClient}getConfiguredAgent(){return this.config.agent||this.config.defaultAgentId}enableAutoPageContext(){this.addDynamicContext("browser-page",()=>{const t=typeof window<"u"?window.location.href:void 0,i=typeof document<"u"?document.title:void 0;return!t&&!i?null:{name:"Browser page",description:"Current page the user is viewing",data:{url:t,title:i}}})}getContextSnapshot(){return this.contextRegistry.getSnapshot()}buildSendContext(){const t=this.contextRegistry.getSnapshot(),i=t?.entries??{},{changes:s,baseline:n}=xe(this.lastSentPageState,i);return this.lastSentPageState=n,s.length===0?t:{entries:{...i,[Be]:{name:"State changes",description:"Fields in the page state that changed since your last message, as field: <old> -> <new>.",data:{changes:s}}}}}recordActivity(t){this.addActivity(t)}getRecentActivity(){return this.activityLedger.getRecent()}clearActivity(){this.activityLedger.clear()}addActivity(t){const i=this.activityLedger.add(t);this.eventEmitter.emit("activity:recorded",i)}recordAgentAction(t){const i=t.functionName,s=t.arguments;let n,o;if(i==="navigateToPage"){const a=s?.page;n="agent.navigate",o=a?`Agent navigated to "${String(a)}"`:"Agent navigated"}else i==="setPageState"?(n="agent.page_state",o="Agent changed the page state"):(n=`agent.${i}`,o=`Agent ran "${i}"`);this.addActivity({actor:"agent",name:n,summary:o,...s&&Object.keys(s).length>0?{data:{arguments:s}}:{}})}registerActivityContext(){this.contextRegistry.addDynamicProvider(Le,()=>{const t=this.activityLedger.getRecent();return t.length===0?null:{name:"Recent activity",description:"Recent actions by the user and the agent in the app, oldest first. Use this to understand what just happened before this message.",data:{events:t}}})}on(t,i){this.eventEmitter.on(t,i)}off(t,i){this.eventEmitter.off(t,i)}once(t,i){this.eventEmitter.once(t,i)}waitFor(t,i){return this.eventEmitter.waitFor(t,i)}updateConfig(t){const i={};for(const[n,o]of Object.entries(t))o!==void 0&&(i[n]=o);const s={...this.config,...i};I(s,"AgoClient.updateConfig"),this.config=s,this.httpClient.updateConfig(i),i.debug!==void 0&&(i.debug?c.enable():c.disable()),i.maxFunctionResultBytes!==void 0&&this.functionRegistry.setDefaultMaxResultBytes(i.maxFunctionResultBytes)}destroy(){this.proactive?.destroy(),this.proactive=null,this.eventEmitter.removeAllListeners(),this.functionRegistry.clear(),this.contextRegistry.clear(),this.pendingResumes.clear(),this.pendingApprovals.clear(),this.resumeGate=null,c.log("AgoClient destroyed")}reviveAfterDestroy(){this.registerActivityContext(),this.config.proactive&&!this.proactive&&k(this,this.config.proactive)}};Object.defineProperty(exports,"ActivityLedger",{enumerable:!0,get:function(){return D}});Object.defineProperty(exports,"AgoApiError",{enumerable:!0,get:function(){return p}});Object.defineProperty(exports,"AgoClient",{enumerable:!0,get:function(){return Ge}});Object.defineProperty(exports,"AgoError",{enumerable:!0,get:function(){return d}});Object.defineProperty(exports,"AgoFunctionError",{enumerable:!0,get:function(){return E}});Object.defineProperty(exports,"AgoNetworkError",{enumerable:!0,get:function(){return y}});Object.defineProperty(exports,"AgoStreamError",{enumerable:!0,get:function(){return A}});Object.defineProperty(exports,"ClientContextRegistry",{enumerable:!0,get:function(){return W}});Object.defineProperty(exports,"DEFAULT_MAX_ACTIVITY_ENTRIES",{enumerable:!0,get:function(){return Y}});Object.defineProperty(exports,"EventEmitter",{enumerable:!0,get:function(){return H}});Object.defineProperty(exports,"FunctionRegistry",{enumerable:!0,get:function(){return M}});Object.defineProperty(exports,"SSEHandler",{enumerable:!0,get:function(){return K}});Object.defineProperty(exports,"SignalCollector",{enumerable:!0,get:function(){return j}});Object.defineProperty(exports,"attachmentsFromFiles",{enumerable:!0,get:function(){return Ne}});Object.defineProperty(exports,"canInlineImage",{enumerable:!0,get:function(){return Pe}});Object.defineProperty(exports,"createAgoProactive",{enumerable:!0,get:function(){return k}});Object.defineProperty(exports,"createStore",{enumerable:!0,get:function(){return L}});Object.defineProperty(exports,"formatFileSize",{enumerable:!0,get:function(){return Oe}});Object.defineProperty(exports,"isStreamNetworkError",{enumerable:!0,get:function(){return Te}});Object.defineProperty(exports,"logger",{enumerable:!0,get:function(){return c}});Object.defineProperty(exports,"matchesRoutePattern",{enumerable:!0,get:function(){return B}});Object.defineProperty(exports,"matchesWhen",{enumerable:!0,get:function(){return G}});Object.defineProperty(exports,"partialDeepMatch",{enumerable:!0,get:function(){return x}});Object.defineProperty(exports,"safeAttachmentUrl",{enumerable:!0,get:function(){return q}});Object.defineProperty(exports,"validateConfig",{enumerable:!0,get:function(){return I}});
|
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-Ba3ICq4K.cjs"),c=require("./autoContinue-CP9pJR-V.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
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
declare interface ActivityEntry {
|
|
2
|
+
ts: number;
|
|
3
|
+
actor: "user" | "agent";
|
|
4
|
+
name: string;
|
|
5
|
+
summary: string;
|
|
6
|
+
data?: Record<string, unknown>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
declare interface ActivityInput {
|
|
10
|
+
actor?: "user" | "agent";
|
|
11
|
+
name: string;
|
|
12
|
+
summary: string;
|
|
13
|
+
data?: Record<string, unknown>;
|
|
14
|
+
ts?: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
1
17
|
/**
|
|
2
18
|
* Agent information
|
|
3
19
|
*/
|
|
@@ -53,6 +69,8 @@ declare class AgoClient {
|
|
|
53
69
|
private functionRegistry;
|
|
54
70
|
private contextRegistry;
|
|
55
71
|
private eventEmitter;
|
|
72
|
+
private activityLedger;
|
|
73
|
+
private lastSentPageState;
|
|
56
74
|
private config;
|
|
57
75
|
/** Conversations already warned about an empty reply (one warning each). */
|
|
58
76
|
private warnedEmptyConversations;
|
|
@@ -66,6 +84,12 @@ declare class AgoClient {
|
|
|
66
84
|
private pendingApprovals;
|
|
67
85
|
/** App-provided gate deciding WHEN a paused turn resumes (see {@link registerResumeGate}). */
|
|
68
86
|
private resumeGate;
|
|
87
|
+
/**
|
|
88
|
+
* Proactive-mode controller, set when the client was created with
|
|
89
|
+
* `proactive` options or when `createAgoProactive(client, opts)` was called.
|
|
90
|
+
* `null` otherwise.
|
|
91
|
+
*/
|
|
92
|
+
proactive: ProactiveController | null;
|
|
69
93
|
constructor(config: AgoConfig);
|
|
70
94
|
/**
|
|
71
95
|
* Send a message and receive a streaming response
|
|
@@ -236,6 +260,14 @@ declare class AgoClient {
|
|
|
236
260
|
* Get all registered function schemas
|
|
237
261
|
*/
|
|
238
262
|
getRegisteredFunctions(): ClientFunctionSchema[];
|
|
263
|
+
/**
|
|
264
|
+
* Execute a registered client function locally, through the same
|
|
265
|
+
* FunctionRegistry path (result-size guard, error wrapping) as agent-invoked
|
|
266
|
+
* calls. Used by proactive nudge actions, where the user's explicit click on
|
|
267
|
+
* the action button constitutes the approval — nothing is submitted to the
|
|
268
|
+
* backend (there is no tool call to answer).
|
|
269
|
+
*/
|
|
270
|
+
executeClientFunction(name: string, args: Record<string, unknown>): Promise<unknown>;
|
|
239
271
|
/**
|
|
240
272
|
* Register a navigation function that lets AGO navigate users to different pages.
|
|
241
273
|
*
|
|
@@ -337,6 +369,9 @@ declare class AgoClient {
|
|
|
337
369
|
*/
|
|
338
370
|
notifyContextChanged(): void;
|
|
339
371
|
/* Excluded from this release type: emitFormEvent */
|
|
372
|
+
/* Excluded from this release type: emitProactiveEvent */
|
|
373
|
+
/* Excluded from this release type: getHttp */
|
|
374
|
+
/* Excluded from this release type: getConfiguredAgent */
|
|
340
375
|
/**
|
|
341
376
|
* Enable automatic capture of the current browser page (URL + title).
|
|
342
377
|
* Injected as a dynamic context entry named `browser-page`.
|
|
@@ -346,6 +381,19 @@ declare class AgoClient {
|
|
|
346
381
|
* Get the current context snapshot.
|
|
347
382
|
*/
|
|
348
383
|
getContextSnapshot(): ContextSnapshot | null;
|
|
384
|
+
private buildSendContext;
|
|
385
|
+
/**
|
|
386
|
+
* Record something the user just did so the agent is aware of it on the next
|
|
387
|
+
* message. `actor` defaults to `"user"`. Keep `summary` short and high-signal
|
|
388
|
+
* (no raw payloads, secrets, or PII).
|
|
389
|
+
*/
|
|
390
|
+
recordActivity(entry: ActivityInput): void;
|
|
391
|
+
/** Recent user + agent actions, oldest first (a copy). */
|
|
392
|
+
getRecentActivity(): ActivityEntry[];
|
|
393
|
+
clearActivity(): void;
|
|
394
|
+
private addActivity;
|
|
395
|
+
private recordAgentAction;
|
|
396
|
+
private registerActivityContext;
|
|
349
397
|
/**
|
|
350
398
|
* Subscribe to an event
|
|
351
399
|
*/
|
|
@@ -381,6 +429,7 @@ declare class AgoClient {
|
|
|
381
429
|
* Clean up resources
|
|
382
430
|
*/
|
|
383
431
|
destroy(): void;
|
|
432
|
+
/* Excluded from this release type: reviveAfterDestroy */
|
|
384
433
|
}
|
|
385
434
|
|
|
386
435
|
/**
|
|
@@ -497,6 +546,27 @@ export declare interface AgoClientEvents {
|
|
|
497
546
|
"connection:status": {
|
|
498
547
|
connected: boolean;
|
|
499
548
|
};
|
|
549
|
+
/**
|
|
550
|
+
* A proactive nudge passed every governor gate and is ready to display.
|
|
551
|
+
* `useProactiveNudge` / `<AgoNudge>` consume this; custom UIs should call
|
|
552
|
+
* `client.proactive.shown(nudge)` when they render it (impression tracking
|
|
553
|
+
* + frequency caps), then `accept(nudge)` / `dismiss(nudge)`.
|
|
554
|
+
*/
|
|
555
|
+
"nudge:ready": ProactiveNudgeInstance;
|
|
556
|
+
/** A nudge was displayed to the user (fires once per nudge). */
|
|
557
|
+
"nudge:shown": {
|
|
558
|
+
nudge: ProactiveNudgeInstance;
|
|
559
|
+
};
|
|
560
|
+
/** The user dismissed the nudge (its trigger is suppressed, see governor). */
|
|
561
|
+
"nudge:dismissed": {
|
|
562
|
+
nudge: ProactiveNudgeInstance;
|
|
563
|
+
};
|
|
564
|
+
/** The user accepted the nudge (its action ran / the chat is being seeded). */
|
|
565
|
+
"nudge:accepted": {
|
|
566
|
+
nudge: ProactiveNudgeInstance;
|
|
567
|
+
};
|
|
568
|
+
/** A user- or agent-action was recorded into the activity ledger. */
|
|
569
|
+
"activity:recorded": ActivityEntry;
|
|
500
570
|
}
|
|
501
571
|
|
|
502
572
|
/**
|
|
@@ -558,6 +628,24 @@ export declare interface AgoConfig {
|
|
|
558
628
|
* with `maxResultBytes` on its definition; `Infinity` disables the guard.
|
|
559
629
|
*/
|
|
560
630
|
maxFunctionResultBytes?: number;
|
|
631
|
+
/**
|
|
632
|
+
* Max number of recent actions kept in the in-memory activity ledger that
|
|
633
|
+
* rides along as the `activity:recent` context entry. Older entries drop
|
|
634
|
+
* first. Default `10`. Larger values give the agent more history at the cost
|
|
635
|
+
* of tokens on every message. Each entry's `data` is also size-clamped (long
|
|
636
|
+
* strings truncated, large arrays capped) so one event can't bloat the context.
|
|
637
|
+
*/
|
|
638
|
+
maxActivityEntries?: number;
|
|
639
|
+
/**
|
|
640
|
+
* Enable the proactive mode: declarative triggers evaluated client-side
|
|
641
|
+
* against friction signals (dwell/idle time, rage clicks, route bounces…)
|
|
642
|
+
* that can surface a nudge before the user opens the chat. Equivalent to
|
|
643
|
+
* calling `createAgoProactive(client, proactive)` — the resulting controller
|
|
644
|
+
* is available as `client.proactive`. Requires the tenant-level kill-switch
|
|
645
|
+
* (`GET /api/sdk/v1/config` → `proactive.enabled`) to be on; disabled by
|
|
646
|
+
* default otherwise.
|
|
647
|
+
*/
|
|
648
|
+
proactive?: ProactiveOptions;
|
|
561
649
|
}
|
|
562
650
|
|
|
563
651
|
declare type AgoEventHandler<K extends AgoEventName> = (data: AgoClientEvents[K]) => void;
|
|
@@ -992,6 +1080,43 @@ declare interface FormSchema {
|
|
|
992
1080
|
required?: string[];
|
|
993
1081
|
}
|
|
994
1082
|
|
|
1083
|
+
/**
|
|
1084
|
+
* HTTP client with authentication headers
|
|
1085
|
+
*/
|
|
1086
|
+
declare class HttpClient {
|
|
1087
|
+
private baseUrl;
|
|
1088
|
+
private headers;
|
|
1089
|
+
constructor(config: AgoConfig);
|
|
1090
|
+
/**
|
|
1091
|
+
* Update configuration (e.g., JWT token)
|
|
1092
|
+
*/
|
|
1093
|
+
updateConfig(config: Partial<AgoConfig>): void;
|
|
1094
|
+
/**
|
|
1095
|
+
* Make a GET request
|
|
1096
|
+
*/
|
|
1097
|
+
get<T>(path: string): Promise<T>;
|
|
1098
|
+
/**
|
|
1099
|
+
* Make a POST request with JSON body.
|
|
1100
|
+
*
|
|
1101
|
+
* `keepalive: true` lets the request survive a page unload (used by the
|
|
1102
|
+
* proactive event tracker's flush-on-hide — sendBeacon can't carry the auth
|
|
1103
|
+
* headers, so it's a keepalive fetch instead).
|
|
1104
|
+
*/
|
|
1105
|
+
post<T>(path: string, body?: unknown, options?: {
|
|
1106
|
+
keepalive?: boolean;
|
|
1107
|
+
}): Promise<T>;
|
|
1108
|
+
/**
|
|
1109
|
+
* Make a POST request and return the raw Response (for streaming)
|
|
1110
|
+
*/
|
|
1111
|
+
postStream(path: string, body?: unknown): Promise<Response>;
|
|
1112
|
+
/**
|
|
1113
|
+
* Make a POST request with FormData (for file uploads)
|
|
1114
|
+
*/
|
|
1115
|
+
postFormData(path: string, formData: FormData): Promise<Response>;
|
|
1116
|
+
private request;
|
|
1117
|
+
private handleErrorResponse;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
995
1120
|
declare type MessageStatus = "IN_PROGRESS" | "DONE" | "ERROR" | "TODO" | "CANCELED"
|
|
996
1121
|
/**
|
|
997
1122
|
* The turn paused on client function call(s) (pause mode): the agent waits for
|
|
@@ -1000,6 +1125,21 @@ declare type MessageStatus = "IN_PROGRESS" | "DONE" | "ERROR" | "TODO" | "CANCEL
|
|
|
1000
1125
|
*/
|
|
1001
1126
|
| "WAITING_CLIENT";
|
|
1002
1127
|
|
|
1128
|
+
/**
|
|
1129
|
+
* Optional action attached to a nudge. Accepting a `functionName` action
|
|
1130
|
+
* executes the registered client function (same `FunctionRegistry` path as
|
|
1131
|
+
* agent-invoked calls); `setPageState` applies values through the registered
|
|
1132
|
+
* page-state controls.
|
|
1133
|
+
*/
|
|
1134
|
+
declare type NudgeAction = {
|
|
1135
|
+
label: string;
|
|
1136
|
+
functionName: string;
|
|
1137
|
+
arguments?: Record<string, unknown>;
|
|
1138
|
+
} | {
|
|
1139
|
+
label: string;
|
|
1140
|
+
setPageState: Record<string, unknown>;
|
|
1141
|
+
};
|
|
1142
|
+
|
|
1003
1143
|
declare interface Observable<T> {
|
|
1004
1144
|
subscribe(observer: {
|
|
1005
1145
|
next?: (value: T) => void;
|
|
@@ -1009,6 +1149,167 @@ declare interface Observable<T> {
|
|
|
1009
1149
|
};
|
|
1010
1150
|
}
|
|
1011
1151
|
|
|
1152
|
+
/**
|
|
1153
|
+
* Controller returned by {@link createAgoProactive} (also available as
|
|
1154
|
+
* `client.proactive`).
|
|
1155
|
+
*/
|
|
1156
|
+
declare interface ProactiveController {
|
|
1157
|
+
/** Persistently opt this browser out of proactive nudges. */
|
|
1158
|
+
optOut(): void;
|
|
1159
|
+
/** Undo {@link optOut}. */
|
|
1160
|
+
optIn(): void;
|
|
1161
|
+
isOptedOut(): boolean;
|
|
1162
|
+
/**
|
|
1163
|
+
* Tell the governor the user is already engaged (e.g. the chat widget is
|
|
1164
|
+
* open) — nudges are suppressed while `true`. Call from the UI layer.
|
|
1165
|
+
*/
|
|
1166
|
+
setEngaged(engaged: boolean): void;
|
|
1167
|
+
/** App-pushed field error counter (field NAME only — never values). */
|
|
1168
|
+
signalFieldError(fieldName: string): void;
|
|
1169
|
+
/** Current aggregated friction snapshot. */
|
|
1170
|
+
getSignalsSnapshot(): SignalsSnapshot;
|
|
1171
|
+
/** The nudge currently occupying the single-nudge slot, if any. */
|
|
1172
|
+
getActiveNudge(): ProactiveNudgeInstance | null;
|
|
1173
|
+
/**
|
|
1174
|
+
* Mark the nudge as displayed. Idempotent per nudge; counts it against the
|
|
1175
|
+
* frequency caps, tracks the impression, and emits `nudge:shown`.
|
|
1176
|
+
*/
|
|
1177
|
+
shown(nudge: ProactiveNudgeInstance): void;
|
|
1178
|
+
/**
|
|
1179
|
+
* Accept the nudge: run its action (if any), emit `nudge:accepted`, track,
|
|
1180
|
+
* and — for agent nudges or nudges with a `goal` — pre-seed the chat with a
|
|
1181
|
+
* hidden kickoff message so the agent starts the turn with full context.
|
|
1182
|
+
*/
|
|
1183
|
+
accept(nudge: ProactiveNudgeInstance): Promise<void>;
|
|
1184
|
+
/** Dismiss the nudge: suppress the trigger, track, emit `nudge:dismissed`. */
|
|
1185
|
+
dismiss(nudge: ProactiveNudgeInstance): void;
|
|
1186
|
+
/** Report a downstream conversion attributable to the nudge (analytics only). */
|
|
1187
|
+
markConverted(nudge: ProactiveNudgeInstance): void;
|
|
1188
|
+
/** Detach everything: listeners, timers, context provider, event flush. */
|
|
1189
|
+
destroy(): void;
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
/** A concrete nudge produced by the engine, carried by every `nudge:*` event. */
|
|
1193
|
+
declare interface ProactiveNudgeInstance {
|
|
1194
|
+
/** Client-generated uuid for this nudge instance (`client_nudge_id`). */
|
|
1195
|
+
id: string;
|
|
1196
|
+
triggerId: string;
|
|
1197
|
+
/** `static` = configured message; `agent` = backend LLM evaluation. */
|
|
1198
|
+
source: "static" | "agent";
|
|
1199
|
+
message: string;
|
|
1200
|
+
action?: NudgeAction;
|
|
1201
|
+
goal?: string;
|
|
1202
|
+
/** See {@link ProactiveTrigger.kickoff}. */
|
|
1203
|
+
kickoff?: string;
|
|
1204
|
+
createdAt: Date;
|
|
1205
|
+
/** Server-side nudge id (agent nudges only). */
|
|
1206
|
+
serverNudgeId?: string;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
/** Options for {@link createAgoProactive} / `AgoConfig.proactive`. */
|
|
1210
|
+
declare interface ProactiveOptions {
|
|
1211
|
+
triggers: ProactiveTrigger[];
|
|
1212
|
+
/**
|
|
1213
|
+
* Global caps on nudges actually shown. `perSession` is in-memory (page
|
|
1214
|
+
* lifetime); `perDay` is persisted in localStorage with a date rollover —
|
|
1215
|
+
* best-effort by design (private browsing resets it); the costly LLM path is
|
|
1216
|
+
* additionally capped server-side.
|
|
1217
|
+
*/
|
|
1218
|
+
frequencyCap?: {
|
|
1219
|
+
perSession?: number;
|
|
1220
|
+
perDay?: number;
|
|
1221
|
+
};
|
|
1222
|
+
/** Evaluation tick, ms. Default 1000. Zero network per tick. */
|
|
1223
|
+
tickMs?: number;
|
|
1224
|
+
/** Storage backend for persisted governance state. Default `localStorage`. */
|
|
1225
|
+
storage?: StorageLike;
|
|
1226
|
+
/** Storage key for persisted governance state. Default `"ago_proactive"`. */
|
|
1227
|
+
storageKey?: string;
|
|
1228
|
+
/** Tuning of the signal detectors (rage-click window, throttles…). */
|
|
1229
|
+
signals?: SignalCollectorTuning;
|
|
1230
|
+
/**
|
|
1231
|
+
* Local demos and tests ONLY: bypass the remote kill-switch. When set, the
|
|
1232
|
+
* `GET /config` fetch is skipped and the kill-switch takes this value.
|
|
1233
|
+
* Leave unset in production — the tenant admin controls the feature, and
|
|
1234
|
+
* the evaluate endpoint stays gated server-side regardless.
|
|
1235
|
+
*/
|
|
1236
|
+
enabledOverride?: boolean;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
/** A declarative proactive trigger. */
|
|
1240
|
+
declare interface ProactiveTrigger {
|
|
1241
|
+
/** Stable id, used for cooldowns, dismissal memory, and analytics. */
|
|
1242
|
+
id: string;
|
|
1243
|
+
/** Conjunction of matchers (see {@link ProactiveTriggerWhen}). */
|
|
1244
|
+
when: ProactiveTriggerWhen;
|
|
1245
|
+
/**
|
|
1246
|
+
* Min time between two firings of this trigger (persisted across reloads).
|
|
1247
|
+
* A firing is the moment the trigger passes the gates — including an agent
|
|
1248
|
+
* evaluation that decides NOT to intervene, so a persistent condition can't
|
|
1249
|
+
* hammer the evaluate endpoint. Default 60 000.
|
|
1250
|
+
*/
|
|
1251
|
+
cooldownMs?: number;
|
|
1252
|
+
/**
|
|
1253
|
+
* `'agent'` asks the backend's fast-LLM second filter whether (and how) to
|
|
1254
|
+
* intervene; a `static` object shows the given message with no network call.
|
|
1255
|
+
*/
|
|
1256
|
+
intervene: "agent" | {
|
|
1257
|
+
type: "static";
|
|
1258
|
+
message: string;
|
|
1259
|
+
action?: NudgeAction;
|
|
1260
|
+
};
|
|
1261
|
+
/**
|
|
1262
|
+
* What the agent should help with. Sent to the evaluate endpoint, and used to
|
|
1263
|
+
* pre-seed the chat turn when the user accepts the nudge.
|
|
1264
|
+
*/
|
|
1265
|
+
goal?: string;
|
|
1266
|
+
/**
|
|
1267
|
+
* User-voice line used to OPEN the conversation when the user accepts this
|
|
1268
|
+
* nudge and no conversation exists yet — a hidden kickoff cannot start one
|
|
1269
|
+
* (the backend rejects it). Sent as a visible user message, so write it in
|
|
1270
|
+
* the app's language ("Oui, aidez-moi à choisir !"). When a conversation is
|
|
1271
|
+
* already open, the SDK sends a hidden meta kickoff instead and this field
|
|
1272
|
+
* is unused. Without it, accepting with no open conversation skips the chat
|
|
1273
|
+
* pre-seed (the nudge action still runs).
|
|
1274
|
+
*/
|
|
1275
|
+
kickoff?: string;
|
|
1276
|
+
/**
|
|
1277
|
+
* How long a dismissal of this trigger's nudge suppresses it (persisted).
|
|
1278
|
+
* Default 24h.
|
|
1279
|
+
*/
|
|
1280
|
+
resurfaceAfterMs?: number;
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
/**
|
|
1284
|
+
* Conditions of a proactive trigger. Pure conjunction: EVERY specified matcher
|
|
1285
|
+
* must pass against the current {@link SignalsSnapshot}. Numeric fields are
|
|
1286
|
+
* `>=` thresholds.
|
|
1287
|
+
*/
|
|
1288
|
+
declare interface ProactiveTriggerWhen {
|
|
1289
|
+
/**
|
|
1290
|
+
* Route pattern the current pathname must match. Supports the SDK's route
|
|
1291
|
+
* styles: exact (`/billing`), `:param` segments (`/users/:id`), single-segment
|
|
1292
|
+
* wildcards (`/a/*` / `/b`), and a trailing `/*` that matches one or more
|
|
1293
|
+
* remaining segments (`/dossier/*`).
|
|
1294
|
+
*/
|
|
1295
|
+
route?: string;
|
|
1296
|
+
/** Min time since the last user activity, ms. */
|
|
1297
|
+
idleMs?: number;
|
|
1298
|
+
/** Min time spent on the current route, ms. */
|
|
1299
|
+
dwellMs?: number;
|
|
1300
|
+
/**
|
|
1301
|
+
* Partial deep match against the registered page state (funnel step,
|
|
1302
|
+
* filters…). Every key present here must deep-match the live value.
|
|
1303
|
+
*/
|
|
1304
|
+
pageState?: Partial<Record<string, unknown>>;
|
|
1305
|
+
/** Min rage-click bursts on the current route. */
|
|
1306
|
+
rageClicks?: number;
|
|
1307
|
+
/** Min A→B→A→B route oscillations this session. */
|
|
1308
|
+
routeBounces?: number;
|
|
1309
|
+
/** Per-field minimum error counts (field NAME → min count). */
|
|
1310
|
+
fieldErrors?: Record<string, number>;
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1012
1313
|
/**
|
|
1013
1314
|
* Factory function that returns an Angular-compatible provider.
|
|
1014
1315
|
* Use in your app's `providers` array or `bootstrapApplication`:
|
|
@@ -1065,6 +1366,54 @@ declare interface SendMessageOptions {
|
|
|
1065
1366
|
clientFunctionsMode?: ClientFunctionsMode;
|
|
1066
1367
|
}
|
|
1067
1368
|
|
|
1369
|
+
/** Tuning knobs for the collector's detectors. All optional. */
|
|
1370
|
+
declare interface SignalCollectorTuning {
|
|
1371
|
+
/** Min gap between heavy activity work (route checks, notifications), ms. Default 250. */
|
|
1372
|
+
activityThrottleMs?: number;
|
|
1373
|
+
/** Rage click: clicks needed to count a burst. Default 3. */
|
|
1374
|
+
rageClickCount?: number;
|
|
1375
|
+
/** Rage click: max time between first and last click of a burst, ms. Default 1000. */
|
|
1376
|
+
rageClickWindowMs?: number;
|
|
1377
|
+
/** Rage click: max radius between clicks of a burst, px. Default 24. */
|
|
1378
|
+
rageClickRadiusPx?: number;
|
|
1379
|
+
/** Max routes kept in the recent-route sequence. Default 10. */
|
|
1380
|
+
maxRecentRoutes?: number;
|
|
1381
|
+
/** Max typed signals kept in the ring buffer. Default 50. */
|
|
1382
|
+
maxSignals?: number;
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
/**
|
|
1386
|
+
* Aggregated view of the current friction state. Registered as a dynamic
|
|
1387
|
+
* context provider (key `friction_signals`) so it rides along as
|
|
1388
|
+
* `client_context` on every message, and sent as `signals` to the proactive
|
|
1389
|
+
* evaluate endpoint.
|
|
1390
|
+
*/
|
|
1391
|
+
declare interface SignalsSnapshot {
|
|
1392
|
+
/** Current pathname. */
|
|
1393
|
+
route: string;
|
|
1394
|
+
/** Time spent on the current route, ms. */
|
|
1395
|
+
dwellMs: number;
|
|
1396
|
+
/** Time since the last user activity (pointer/key/scroll/touch), ms. */
|
|
1397
|
+
idleMs: number;
|
|
1398
|
+
/** Rage-click bursts detected on the current route. */
|
|
1399
|
+
rageClicks: number;
|
|
1400
|
+
/**
|
|
1401
|
+
* A→B→A→B route oscillations detected in the recent route sequence. Rolling
|
|
1402
|
+
* session counter (cross-route by nature); trigger cooldowns and dismissal
|
|
1403
|
+
* memory keep it from re-firing forever.
|
|
1404
|
+
*/
|
|
1405
|
+
routeBounces: number;
|
|
1406
|
+
/** App-pushed field error counters (field NAME → count), current route only. */
|
|
1407
|
+
fieldErrors: Record<string, number>;
|
|
1408
|
+
/** Most recent pathnames, oldest first (bounded). */
|
|
1409
|
+
recentRoutes: string[];
|
|
1410
|
+
/**
|
|
1411
|
+
* Merged `AgoStateControl.get()` snapshot of the registered page-state
|
|
1412
|
+
* controls (funnel step, filters…), or `null` when none are registered.
|
|
1413
|
+
*/
|
|
1414
|
+
pageState: Record<string, unknown> | null;
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1068
1417
|
/**
|
|
1069
1418
|
* SSE stream chunk data
|
|
1070
1419
|
*/
|
|
@@ -1108,6 +1457,9 @@ declare interface SSEChunkData {
|
|
|
1108
1457
|
waiting_tool_call_ids?: string[];
|
|
1109
1458
|
}
|
|
1110
1459
|
|
|
1460
|
+
/** The slice of the Web Storage API that {@link createStore} relies on. */
|
|
1461
|
+
declare type StorageLike = Pick<Storage, "getItem" | "setItem">;
|
|
1462
|
+
|
|
1111
1463
|
/**
|
|
1112
1464
|
* Where a completed form is sent.
|
|
1113
1465
|
* - `"https://..."` (a bare string) — shorthand for `{ via: "client", url }`: the browser
|