@useago/sdk 1.13.1 → 1.13.3

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/llms-full.txt CHANGED
@@ -1465,6 +1465,11 @@ This registers a `navigateToPage` function whose `page` parameter is an enum of
1465
1465
  your route names. React/Vue offer `useAgoNavigation(navigate, routes)` with
1466
1466
  lifecycle cleanup.
1467
1467
 
1468
+ Functions and page state can change with the route. The navigation tool
1469
+ instructs the agent to use the destination's functions for remaining actions.
1470
+ Use the [auto-continue helper](#navigate-then-change-the-page-in-one-go) to wait
1471
+ for destination page-state registration before continuing.
1472
+
1468
1473
  Paths can contain `:param` placeholders. Each placeholder becomes an explicit
1469
1474
  top-level argument of `navigateToPage` (`{ page: "orderDetail", id: "42" }`
1470
1475
  navigates to `/orders/42`), so one route covers every detail page of an entity.
@@ -1473,8 +1478,11 @@ and returns an error telling the agent what to retry with. Placeholders cannot
1473
1478
  be named `page` (that argument selects the route).
1474
1479
 
1475
1480
  It also registers a dynamic context entry ("Current page") that reports the page
1476
- the user is on (by route name, plus URL and title) on every message, so the agent
1477
- knows where it is after it navigates, not just how to navigate away. Call
1481
+ the user is on (by route name when matched, plus URL) on every message, so the
1482
+ agent knows where it is after it navigates, not just how to navigate away. It
1483
+ does not include `document.title`, which may describe a conversation instead of
1484
+ the application route. The separate opt-in `enableAutoPageContext()` feature
1485
+ still includes the URL and document title. Call
1478
1486
  `client.unregisterNavigationFunction()` to remove both the function and that
1479
1487
  context.
1480
1488
 
@@ -1496,7 +1504,8 @@ client.registerNavigationFunction(
1496
1504
  ```
1497
1505
 
1498
1506
  `navigateToPage` keeps the page names as its `page` enum, so a recognisable name
1499
- can still navigate in one call.
1507
+ can still navigate in one call. Both catalogue modes instruct the agent to use
1508
+ the destination page's functions and state to finish remaining requested actions.
1500
1509
 
1501
1510
  Give routes a `section` and `listPages` takes it as a required argument, so the
1502
1511
  agent asks for one group at a time and no single answer has to carry the whole
@@ -1564,6 +1573,12 @@ control names are rejected with the list of available controls. A value that
1564
1573
  matches the current `get()` value is skipped without calling `set()` and
1565
1574
  reported as `unchanged`.
1566
1575
 
1576
+ Empty enums are omitted from the advertised tool schema because providers
1577
+ reject them. Execution still rejects selections when no values are available.
1578
+ An empty array remains valid when only its item enum is empty, and a clearable
1579
+ string can still be cleared with `""`. Independent fields in the same request
1580
+ can still be applied.
1581
+
1567
1582
  React/Vue offer `useAgoPageState(controls, opts?)` with lifecycle cleanup;
1568
1583
  Angular exposes `agoService.registerPageStateFunction(...)`.
1569
1584
 
@@ -1704,7 +1719,8 @@ of reporting a diff, so a page switch never shows up as a spurious change.
1704
1719
  A request like "open the invoices page and show only the overdue ones" needs two
1705
1720
  steps: the agent navigates, then changes the new page's state. But the
1706
1721
  destination page only registers its `setPageState` (and current state) once it
1707
- mounts, so the agent can't do both in a single turn.
1722
+ mounts. Continuation must wait for that registration to use the new page's
1723
+ capabilities.
1708
1724
 
1709
1725
  `useAgoAutoContinueAfterNavigation()` closes the gap. Mount it once inside
1710
1726
  `AgoProvider`:
@@ -1814,11 +1830,28 @@ client.registerFunction({
1814
1830
  description: "The flights currently on screen, after filtering.",
1815
1831
  parameters: { type: "object", properties: {} },
1816
1832
  handler: () => visibleFlights(),
1817
- // Optional, WebMCP only: MCP behavior hints.
1833
+ // Optional, WebMCP only: tool annotations.
1818
1834
  webmcp: { annotations: { readOnlyHint: true } },
1819
1835
  });
1820
1836
  ```
1821
1837
 
1838
+ The optional `webmcp.annotations` fields describe the tool to browser agents:
1839
+
1840
+ | Annotation | Meaning when `true` |
1841
+ | --- | --- |
1842
+ | `readOnlyHint` | Only reads data and does not change state. |
1843
+ | `untrustedContentHint` | Returns content untrusted by the tool author, such as customer reviews. |
1844
+ | `consequentialHint` | Has significant, real-world, or irreversible effects, such as purchasing a flight. |
1845
+ | `debugging` | Is intended for debugging and developer tooling. |
1846
+
1847
+ All four default to `false` in WebMCP. For example, a purchase tool can set
1848
+ `webmcp: { annotations: { consequentialHint: true } }`, and a diagnostic tool
1849
+ can set `webmcp: { annotations: { debugging: true } }`.
1850
+
1851
+ The SDK forwards these annotations to the browser without changing AGO's
1852
+ in-app agent behavior. `consequentialHint` and `requiresApproval` are independent:
1853
+ neither sets the other, and annotations do not enforce confirmation dialogs.
1854
+
1822
1855
  In React it is a prop:
1823
1856
 
1824
1857
  ```tsx
@@ -2176,6 +2209,7 @@ const final = await client.waitFor("message:complete", { timeout: 10_000 });
2176
2209
  | `function:result` | `{ invocationId, result, error? }` |
2177
2210
  | `activity:recorded` | `ActivityEntry`: a user- or agent-action was recorded into the activity ledger (via `recordActivity`, or auto-recorded when the agent calls a client function). See [Activity ledger](functions-and-context.md#activity-ledger) |
2178
2211
  | `error:captured` | `CapturedError`: the error watcher captured a JavaScript error (uncaught exception, unhandled rejection, `console.error`, or `reportError`). Fires on repeats too, with the updated `count`. Needs `errorWatcher` in the config. See [JavaScript errors](functions-and-context.md#javascript-errors) |
2212
+ | `form:submitting` | `{ name, values }`: validated submission is about to start |
2179
2213
  | `form:submitted` | `{ name, values, result }`: a form collector submitted successfully; `result` is the submit response (the third-party API's answer) |
2180
2214
  | `form:error` | `{ name, values, error }`: a form collector submit failed at the network/server level (HTTP non-2xx or a thrown handler/fetch). Validation pre-checks (missing fields, no submit target) do not fire this |
2181
2215
  | `connection:status` | `{ connected }` |
@@ -2216,6 +2250,7 @@ onMessage(client, (msg) => console.log("complete:", msg.content));
2216
2250
  | `onMessageError(client, cb)` | `message:error` |
2217
2251
  | `onToolCall(client, cb)` | `toolCall:received` |
2218
2252
  | `onFunctionInvoke(client, cb)` | `function:invoke` |
2253
+ | `onFormSubmitting(client, cb)` | `form:submitting` |
2219
2254
  | `onFormSubmitted(client, cb)` | `form:submitted` |
2220
2255
  | `onFormError(client, cb)` | `form:error` |
2221
2256
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@useago/sdk",
3
- "version": "1.13.1",
3
+ "version": "1.13.3",
4
4
  "description": "Create In-APP AI agents that answers question and performs actions. Works with React, Vue, Angular and vanilla JS/TS.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -1 +0,0 @@
1
- const C=require("./types-Ck3iZG0K.cjs");var W=/^[a-zA-Z0-9_-]{1,40}$/,V=n=>n==null||n==="";function S(n,e){if("anyOf"in n)return n.anyOf.some(c=>S(c,e));if("allOf"in n)return n.allOf.every(c=>S(c,e));const l=e[n.property];if(V(l))return!1;if(Array.isArray(n.value))return n.value.some(c=>String(l)===String(c));if(!n.op)return String(l)===String(n.value);const i=Number(l),a=Number(n.value);if(Number.isNaN(i)||Number.isNaN(a))return!1;switch(n.op){case">=":return i>=a;case"<=":return i<=a;case">":return i>a;case"<":return i<a;default:return C.logger.warn(`Unknown requiredWhen op "${n.op}"; treating as not required.`),!1}}function h(n,e){const l=n.properties??{},i=new Set;for(const c of n.required??[]){const d=l[c]?.requiredWhen;(!d||S(d,e))&&i.add(c)}for(const[c,d]of Object.entries(l))d.requiredWhen&&S(d.requiredWhen,e)&&i.add(c);const a=[...i].filter(c=>V(e[c]));return{missing:a,complete:a.length===0}}function B(n){const e={};for(const[l,i]of Object.entries(n.properties??{}))e[l]={type:i.type,...i.description!=null&&{description:i.description},...i.enum&&{enum:i.enum},...i.default!==void 0&&{default:i.default}};return{type:"object",properties:e,required:[]}}function A(n){const{name:e,description:l,schema:i}=n;if(!W.test(e))throw new Error(`Invalid form collector name "${e}": must match ${W} (it becomes the update_/submit_ function names).`);const a=typeof n.submit=="string"?n.submit?{via:"client",url:n.submit}:!1:n.submit??!1;n.autoSubmit===!0&&!a&&C.logger.warn(`FormCollector "${e}": autoSubmit is ignored because no submit target is configured.`);const c=(n.autoSubmit??!0)&&!!a,d=i.properties??{},$=t=>{const r={};for(const[o,s]of Object.entries(t)){const u=d[o];if(u)if(s==null)r[o]=s;else if(u.type==="number"||u.type==="integer"){const f=typeof s=="number"?s:Number(s);r[o]=Number.isNaN(f)?s:f}else u.type==="boolean"?r[o]=typeof s=="boolean"?s:s==="true"?!0:s==="false"?!1:!!s:r[o]=s}return r},N=n.initialValues??{},m=C.createStore({values:N,submitted:!1}),w=t=>{const r=m.get(),o={...r,values:{...r.values,...t}};return m.set(o),o};let b=null;const k=(t,r)=>{b?.emitFormEvent("form:error",{name:e,values:t,error:r})};let p=null;const I=async()=>{const{values:t}=m.get(),{complete:r,missing:o}=h(i,t);if(!r)return{ok:!1,missing:o};if(!a)return{ok:!1,error:"No submit target configured for this form collector."};const s=t;try{let u;if(a.via==="client")if("handler"in a)u=await a.handler(s);else{const f=await fetch(a.url,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify(s)});if(!f.ok){const j=`Submit failed: HTTP ${f.status}`;return k(s,j),{ok:!1,error:j}}u=await f.json().catch(()=>{})}else{if(!b)return{ok:!1,error:"Backend submit requires install(client) (or useFormCollector) first."};u=await b.submitFormCollector(e,s)}return m.set({...m.get(),submitted:!0,submitResult:u}),b?.emitFormEvent("form:submitted",{name:e,values:s,result:u}),{ok:!0,result:u}}catch(u){const f=u instanceof Error?u.message:"Submit failed";return C.logger.error(`FormCollector "${e}" submit failed:`,u),k(s,f),{ok:!1,error:f}}},F=()=>p||(p=(async()=>{try{return await I()}finally{p=null}})(),p),q=async()=>{if(!c||!a)return null;const{values:t,submitted:r}=m.get();return r||!h(i,t).complete?null:F()},O={name:`update_${e}`,description:`Fill or update fields of the "${e}" form. ${l} 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:B(i),handler:async t=>{const r=w($(t)),{missing:o}=h(i,r.values),s=await q();return s?s.ok?{ok:!0,values:r.values,missing:o,submitted:!0}:{ok:!0,values:r.values,missing:o,submitted:!1,submitError:s.error}:{ok:!0,values:r.values,missing:o}}},y=[O];a&&!c&&y.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:()=>F()});const D="client__",_=a?`submit_${e}`:null,R=t=>{const r=t.functionName??t.toolName;if(r)return r.startsWith(D)?r.slice(8):r},x=t=>{let r={...N},o=!1;for(const s of t){const u=R(s);u===O.name?s.arguments&&(r={...r,...$(s.arguments)}):_&&u===_&&s.status?.toLowerCase()==="done"&&(o=!0)}m.set({values:r,submitted:o})},g=`form:${e}`,v=`${g}:definition`,P=()=>({name:`Form definition: ${e}`,description:`${l} Some fields are conditionally required; the requiredWhen field in the schema tells you when each becomes required. The form schema is the following: ${JSON.stringify(i)}.`,stable:!0}),T=()=>{const{values:t,submitted:r}=m.get(),{missing:o,complete:s}=h(i,t),u=a?c?" The form is submitted automatically as soon as every required field is filled, so there is no submit function to call.":` When nothing is missing and the user confirms, call submit_${e}.`:"",f=r?`The "${e}" form has been submitted.`:`You are currently collecting information from the user to fill the "${e}" form.`;return{name:`Form: ${e}`,description:`${f} The form's schema and field requirements are in the "${v}" client context. Call update_${e} with any fields the user provides; ask only for what is still missing.${u}`,data:{values:t,missing:o,complete:s,submitted:r}}},E=t=>{x((t.messages??[]).flatMap(r=>r.toolCalls??[]))};return{name:e,store:m,functions:y,contextKey:g,contextProvider:T,definitionContextKey:v,definitionContextProvider:P,install:t=>{b=t;for(const o of y)t.register(o);t.addDynamicContext(v,P),t.addDynamicContext(g,T);const r=m.subscribe(()=>t.notifyContextChanged());return t.on("conversation:loaded",E),()=>{r();for(const o of y)t.unregisterFunction(o.name);t.removeDynamicContext(g),t.removeDynamicContext(v),t.off("conversation:loaded",E),b===t&&(b=null)}},hydrate:x,submit:F,getValues:()=>m.get().values,setValues:t=>{w($(t)),q()},reset:()=>{m.set({values:N,submitted:!1})},getStatus:()=>h(i,m.get().values)}}async function L(n,e){const l=await n.getFormCollector(e.name);return A({name:l.name,description:e.description??l.description,schema:e.schema??l.schema,submit:e.submit??l.submit,initialValues:e.initialValues,autoSubmit:e.autoSubmit??l.autoSubmit})}Object.defineProperty(exports,"createFormCollector",{enumerable:!0,get:function(){return A}});Object.defineProperty(exports,"deriveFormStatus",{enumerable:!0,get:function(){return h}});Object.defineProperty(exports,"loadFormCollector",{enumerable:!0,get:function(){return L}});