@useago/sdk 0.3.4 → 0.4.2

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/dist/AgoClient-D4EL6nva.cjs +6 -0
  2. package/dist/{AgoClient-DF7M2WAz.js → AgoClient-rvTz2thR.js} +239 -218
  3. package/dist/angular.cjs +1 -1
  4. package/dist/angular.d.cts +27 -0
  5. package/dist/angular.d.ts +27 -0
  6. package/dist/angular.js +1 -1
  7. package/dist/createConversationSession-D9FesBQS.js +58 -0
  8. package/dist/createConversationSession-DtaeA418.cjs +1 -0
  9. package/dist/createFormCollector-BwvkKi48.js +211 -0
  10. package/dist/createFormCollector-Wb8Rr-U2.cjs +1 -0
  11. package/dist/{createMockClient-C50gjV0b.js → createMockClient-DwdxNwEn.js} +5 -2
  12. package/dist/createMockClient-zHQ0cjUH.cjs +1 -0
  13. package/dist/devtools.cjs +5 -4
  14. package/dist/devtools.d.cts +31 -26
  15. package/dist/devtools.d.ts +31 -26
  16. package/dist/devtools.js +39 -35
  17. package/dist/helpers.cjs +1 -1
  18. package/dist/helpers.js +1 -1
  19. package/dist/index.cjs +1 -1
  20. package/dist/index.d.cts +117 -1
  21. package/dist/index.d.ts +117 -1
  22. package/dist/index.js +44 -42
  23. package/dist/react.cjs +1 -1
  24. package/dist/react.d.cts +35 -0
  25. package/dist/react.d.ts +35 -0
  26. package/dist/react.js +4 -4
  27. package/dist/vue.cjs +1 -1
  28. package/dist/vue.d.cts +27 -0
  29. package/dist/vue.d.ts +27 -0
  30. package/dist/vue.js +1 -1
  31. package/dist/widget.cjs +1 -1
  32. package/dist/widget.d.cts +114 -0
  33. package/dist/widget.d.ts +114 -0
  34. package/dist/widget.js +170 -150
  35. package/package.json +58 -15
  36. package/dist/AgoClient-bjN-z2ZQ.cjs +0 -6
  37. package/dist/createFormCollector-ClzHfaBd.js +0 -185
  38. package/dist/createFormCollector-H4Me_2uj.cjs +0 -1
  39. package/dist/createMockClient-DQvFFWnc.cjs +0 -1
  40. /package/dist/{functions-CWSRi3xv.js → functions-C1-5vadY.js} +0 -0
  41. /package/dist/{functions-BH1X5kas.cjs → functions-DsmzgHjC.cjs} +0 -0
package/dist/angular.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("./AgoClient-bjN-z2ZQ.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-D4EL6nva.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;
@@ -31,6 +31,12 @@ declare class AgoClient {
31
31
  * Get a specific conversation with messages
32
32
  */
33
33
  getConversation(conversationId: string): Promise<Conversation>;
34
+ /**
35
+ * Map the backend's snake_case `tool_call_data` entries onto the camelCase
36
+ * {@link ToolCallData} shape used everywhere else in the SDK. Mirrors
37
+ * `SSEHandler.parseToolCall` so persisted and streamed tool calls match.
38
+ */
39
+ private static mapPersistedToolCalls;
34
40
  /**
35
41
  * Get messages for a conversation
36
42
  */
@@ -141,6 +147,13 @@ declare class AgoClient {
141
147
  * Remove a dynamic context provider.
142
148
  */
143
149
  removeDynamicContext(key: string): boolean;
150
+ /**
151
+ * Broadcast that the client-side context changed by emitting `context:changed`
152
+ * with the fresh snapshot. The context-mutating methods call this automatically;
153
+ * stateful helpers whose context lives in a store (e.g. `createFormCollector`)
154
+ * call it when their store updates so observers like the dev panel stay live.
155
+ */
156
+ notifyContextChanged(): void;
144
157
  /**
145
158
  * Enable automatic capture of the current browser page (URL + title).
146
159
  * Injected as a dynamic context entry named `browser-page`.
@@ -186,6 +199,14 @@ declare class AgoClient {
186
199
  * SDK Events
187
200
  */
188
201
  export declare interface AgoClientEvents {
202
+ /**
203
+ * The client-side context changed: a context entry or dynamic provider was
204
+ * added/removed, or a stateful helper (e.g. `createFormCollector`) updated its
205
+ * store. Carries the fresh snapshot so observers — notably the dev panel — can
206
+ * repaint without waiting for the next message. Fires for the form collector's
207
+ * initial install too, so its missing fields show from the start of a conversation.
208
+ */
209
+ "context:changed": ContextSnapshot | null;
189
210
  "message:start": {
190
211
  conversationId: string;
191
212
  messageId: string;
@@ -201,6 +222,12 @@ export declare interface AgoClientEvents {
201
222
  conversationId?: string;
202
223
  messageId?: string;
203
224
  };
225
+ /**
226
+ * A full conversation was loaded from the server (e.g. after a page reload).
227
+ * Carries the messages and their persisted tool calls so stateful helpers —
228
+ * notably `createFormCollector` — can replay them to restore their state.
229
+ */
230
+ "conversation:loaded": Conversation;
204
231
  "toolCall:received": ToolCallData;
205
232
  "toolCall:form": ToolCallData;
206
233
  "function:invoke": ClientFunctionInvocation;
package/dist/angular.d.ts CHANGED
@@ -31,6 +31,12 @@ declare class AgoClient {
31
31
  * Get a specific conversation with messages
32
32
  */
33
33
  getConversation(conversationId: string): Promise<Conversation>;
34
+ /**
35
+ * Map the backend's snake_case `tool_call_data` entries onto the camelCase
36
+ * {@link ToolCallData} shape used everywhere else in the SDK. Mirrors
37
+ * `SSEHandler.parseToolCall` so persisted and streamed tool calls match.
38
+ */
39
+ private static mapPersistedToolCalls;
34
40
  /**
35
41
  * Get messages for a conversation
36
42
  */
@@ -141,6 +147,13 @@ declare class AgoClient {
141
147
  * Remove a dynamic context provider.
142
148
  */
143
149
  removeDynamicContext(key: string): boolean;
150
+ /**
151
+ * Broadcast that the client-side context changed by emitting `context:changed`
152
+ * with the fresh snapshot. The context-mutating methods call this automatically;
153
+ * stateful helpers whose context lives in a store (e.g. `createFormCollector`)
154
+ * call it when their store updates so observers like the dev panel stay live.
155
+ */
156
+ notifyContextChanged(): void;
144
157
  /**
145
158
  * Enable automatic capture of the current browser page (URL + title).
146
159
  * Injected as a dynamic context entry named `browser-page`.
@@ -186,6 +199,14 @@ declare class AgoClient {
186
199
  * SDK Events
187
200
  */
188
201
  export declare interface AgoClientEvents {
202
+ /**
203
+ * The client-side context changed: a context entry or dynamic provider was
204
+ * added/removed, or a stateful helper (e.g. `createFormCollector`) updated its
205
+ * store. Carries the fresh snapshot so observers — notably the dev panel — can
206
+ * repaint without waiting for the next message. Fires for the form collector's
207
+ * initial install too, so its missing fields show from the start of a conversation.
208
+ */
209
+ "context:changed": ContextSnapshot | null;
189
210
  "message:start": {
190
211
  conversationId: string;
191
212
  messageId: string;
@@ -201,6 +222,12 @@ export declare interface AgoClientEvents {
201
222
  conversationId?: string;
202
223
  messageId?: string;
203
224
  };
225
+ /**
226
+ * A full conversation was loaded from the server (e.g. after a page reload).
227
+ * Carries the messages and their persisted tool calls so stateful helpers —
228
+ * notably `createFormCollector` — can replay them to restore their state.
229
+ */
230
+ "conversation:loaded": Conversation;
204
231
  "toolCall:received": ToolCallData;
205
232
  "toolCall:form": ToolCallData;
206
233
  "function:invoke": ClientFunctionInvocation;
package/dist/angular.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as o } from "./AgoClient-DF7M2WAz.js";
1
+ import { t as o } from "./AgoClient-rvTz2thR.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");
@@ -0,0 +1,58 @@
1
+ import { n as d } from "./createFormCollector-BwvkKi48.js";
2
+ var f = "ago_widget_id", g = "ago_last_thread", y = 7200 * 1e3;
3
+ function v() {
4
+ return typeof crypto < "u" && typeof crypto.randomUUID == "function" ? crypto.randomUUID() : "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (e) => {
5
+ const r = Math.floor(Math.random() * 16);
6
+ return (e === "x" ? r : r & 3 | 8).toString(16);
7
+ });
8
+ }
9
+ function D(e = {}) {
10
+ const r = e.key ?? f, c = e.threadKey ?? g, u = e.ttlMs ?? y, l = e.storage ?? (typeof localStorage < "u" ? localStorage : void 0);
11
+ let n = e.widgetId ?? "";
12
+ if (!n && l) try {
13
+ n = l.getItem(r) ?? "";
14
+ } catch {
15
+ }
16
+ if (n || (n = v()), l) try {
17
+ l.setItem(r, n);
18
+ } catch {
19
+ }
20
+ const o = d({
21
+ value: null,
22
+ lastMessageAt: null
23
+ }, {
24
+ key: c,
25
+ storage: l
26
+ }), s = o.get();
27
+ s.value !== null && x(s.lastMessageAt) && o.set({
28
+ value: null,
29
+ lastMessageAt: null
30
+ });
31
+ function x(t) {
32
+ return t === null ? !0 : Number.isFinite(u) ? Date.now() - t > u : !1;
33
+ }
34
+ return {
35
+ widgetId: n,
36
+ key: r,
37
+ getLastActiveThread() {
38
+ const { value: t, lastMessageAt: a } = o.get();
39
+ return t === null || x(a) ? null : t;
40
+ },
41
+ setActiveThread(t, a) {
42
+ const i = a == null ? null : a instanceof Date ? a.getTime() : a;
43
+ o.set({
44
+ value: t,
45
+ lastMessageAt: i
46
+ });
47
+ },
48
+ clear() {
49
+ o.set({
50
+ value: null,
51
+ lastMessageAt: null
52
+ });
53
+ }
54
+ };
55
+ }
56
+ export {
57
+ D as t
58
+ };
@@ -0,0 +1 @@
1
+ const d=require("./createFormCollector-Wb8Rr-U2.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 _}});
@@ -0,0 +1,211 @@
1
+ import { s as I } from "./AgoClient-rvTz2thR.js";
2
+ function O(u, o) {
3
+ const m = o ? o.storage ?? (typeof localStorage < "u" ? localStorage : void 0) : void 0;
4
+ let c = u;
5
+ if (o && m) try {
6
+ const a = m.getItem(o.key);
7
+ a != null && (c = JSON.parse(a));
8
+ } catch {
9
+ }
10
+ const r = /* @__PURE__ */ new Set(), p = () => r.forEach((a) => a(c));
11
+ function y() {
12
+ if (o && m) try {
13
+ m.setItem(o.key, JSON.stringify(c));
14
+ } catch {
15
+ }
16
+ }
17
+ return {
18
+ get: () => c,
19
+ set: (a) => {
20
+ c = a, y(), p();
21
+ },
22
+ subscribe: (a) => (r.add(a), () => {
23
+ r.delete(a);
24
+ })
25
+ };
26
+ }
27
+ var E = /^[a-zA-Z0-9_-]{1,40}$/;
28
+ function M(u) {
29
+ const { name: o, description: m, schema: c } = u;
30
+ if (!E.test(o)) throw new Error(`Invalid form collector name "${o}": must match ${E} (it becomes the update_/submit_ function names).`);
31
+ const r = typeof u.submit == "string" ? u.submit ? {
32
+ via: "client",
33
+ url: u.submit
34
+ } : !1 : u.submit ?? !1, p = c.properties ?? {}, y = c.required ?? [], a = (e) => e == null || e === "", b = (e) => y.filter((t) => a(e[t])), v = (e) => {
35
+ const t = {};
36
+ for (const [n, s] of Object.entries(e)) {
37
+ const i = p[n];
38
+ if (i)
39
+ if (s == null) t[n] = s;
40
+ else if (i.type === "number" || i.type === "integer") {
41
+ const g = typeof s == "number" ? s : Number(s);
42
+ t[n] = Number.isNaN(g) ? s : g;
43
+ } else i.type === "boolean" ? t[n] = typeof s == "boolean" ? s : s === "true" ? !0 : s === "false" ? !1 : !!s : t[n] = s;
44
+ }
45
+ return t;
46
+ }, f = u.initialValues ?? {}, N = b(f), l = O({
47
+ values: f,
48
+ missing: N,
49
+ complete: N.length === 0,
50
+ submitted: !1
51
+ }), $ = (e) => {
52
+ const t = l.get(), n = {
53
+ ...t.values,
54
+ ...e
55
+ }, s = b(n), i = {
56
+ ...t,
57
+ values: n,
58
+ missing: s,
59
+ complete: s.length === 0
60
+ };
61
+ return l.set(i), i;
62
+ };
63
+ let d = null;
64
+ const k = async () => {
65
+ const e = l.get();
66
+ if (!e.complete) return {
67
+ ok: !1,
68
+ missing: e.missing
69
+ };
70
+ if (!r) return {
71
+ ok: !1,
72
+ error: "No submit target configured for this form collector."
73
+ };
74
+ const t = e.values;
75
+ try {
76
+ let n;
77
+ if (r.via === "client") if ("handler" in r) n = await r.handler(t);
78
+ else {
79
+ const s = await fetch(r.url, {
80
+ method: "POST",
81
+ headers: { "Content-Type": "application/json" },
82
+ credentials: "include",
83
+ body: JSON.stringify(t)
84
+ });
85
+ if (!s.ok) return {
86
+ ok: !1,
87
+ error: `Submit failed: HTTP ${s.status}`
88
+ };
89
+ n = await s.json().catch(() => {
90
+ });
91
+ }
92
+ else {
93
+ if (!d) return {
94
+ ok: !1,
95
+ error: "Backend submit requires install(client) (or useFormCollector) first."
96
+ };
97
+ n = await d.submitFormCollector(r.destination, t);
98
+ }
99
+ return l.set({
100
+ ...l.get(),
101
+ submitted: !0
102
+ }), {
103
+ ok: !0,
104
+ result: n
105
+ };
106
+ } catch (n) {
107
+ const s = n instanceof Error ? n.message : "Submit failed";
108
+ return I.error(`FormCollector "${o}" submit failed:`, n), {
109
+ ok: !1,
110
+ error: s
111
+ };
112
+ }
113
+ }, w = {
114
+ name: `update_${o}`,
115
+ description: `Fill or update fields of the "${o}" form. ${m} 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.`,
116
+ parameters: {
117
+ ...c,
118
+ required: []
119
+ },
120
+ handler: (e) => {
121
+ const t = $(v(e));
122
+ return {
123
+ ok: !0,
124
+ values: t.values,
125
+ missing: t.missing,
126
+ complete: t.complete
127
+ };
128
+ }
129
+ }, h = [w];
130
+ r && h.push({
131
+ name: `submit_${o}`,
132
+ description: `Submit the completed "${o}" 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.`,
133
+ parameters: {
134
+ type: "object",
135
+ properties: {}
136
+ },
137
+ handler: () => k()
138
+ });
139
+ const T = "client__", S = r ? `submit_${o}` : null, q = (e) => {
140
+ const t = e.functionName ?? e.toolName;
141
+ if (t)
142
+ return t.startsWith(T) ? t.slice(8) : t;
143
+ }, _ = (e) => {
144
+ let t = { ...f }, n = !1;
145
+ for (const i of e) {
146
+ const g = q(i);
147
+ g === w.name ? i.arguments && (t = {
148
+ ...t,
149
+ ...v(i.arguments)
150
+ }) : S && g === S && i.status?.toLowerCase() === "done" && (n = !0);
151
+ }
152
+ const s = b(t);
153
+ l.set({
154
+ values: t,
155
+ missing: s,
156
+ complete: s.length === 0,
157
+ submitted: n
158
+ });
159
+ }, C = `form:${o}`, F = () => {
160
+ const e = l.get(), t = r ? ` When all required fields are present and the user confirms, call submit_${o}.` : "";
161
+ return {
162
+ name: `Form: ${o}`,
163
+ description: `Conversational form the assistant is helping fill. ${m} Use update_${o} to record fields; ask the user only for what is still missing.${t}`,
164
+ data: {
165
+ values: e.values,
166
+ missing: e.missing,
167
+ complete: e.complete,
168
+ submitted: e.submitted
169
+ }
170
+ };
171
+ }, x = (e) => {
172
+ _((e.messages ?? []).flatMap((t) => t.toolCalls ?? []));
173
+ };
174
+ return {
175
+ name: o,
176
+ store: l,
177
+ functions: h,
178
+ contextKey: C,
179
+ contextProvider: F,
180
+ install: (e) => {
181
+ d = e;
182
+ for (const n of h) e.register(n);
183
+ e.addDynamicContext(C, F);
184
+ const t = l.subscribe(() => e.notifyContextChanged());
185
+ return e.on("conversation:loaded", x), () => {
186
+ t();
187
+ for (const n of h) e.unregisterFunction(n.name);
188
+ e.removeDynamicContext(C), e.off("conversation:loaded", x), d === e && (d = null);
189
+ };
190
+ },
191
+ hydrate: _,
192
+ submit: k,
193
+ getValues: () => l.get().values,
194
+ setValues: (e) => {
195
+ $(v(e));
196
+ },
197
+ reset: () => {
198
+ const e = b(f);
199
+ l.set({
200
+ values: f,
201
+ missing: e,
202
+ complete: e.length === 0,
203
+ submitted: !1
204
+ });
205
+ }
206
+ };
207
+ }
208
+ export {
209
+ O as n,
210
+ M as t
211
+ };
@@ -0,0 +1 @@
1
+ const x=require("./AgoClient-D4EL6nva.cjs");function P(u,n){const m=n?n.storage??(typeof localStorage<"u"?localStorage:void 0):void 0;let c=u;if(n&&m)try{const a=m.getItem(n.key);a!=null&&(c=JSON.parse(a))}catch{}const o=new Set,p=()=>o.forEach(a=>a(c));function y(){if(n&&m)try{m.setItem(n.key,JSON.stringify(c))}catch{}}return{get:()=>c,set:a=>{c=a,y(),p()},subscribe:a=>(o.add(a),()=>{o.delete(a)})}}var O=/^[a-zA-Z0-9_-]{1,40}$/;function I(u){const{name:n,description:m,schema:c}=u;if(!O.test(n))throw new Error(`Invalid form collector name "${n}": must match ${O} (it becomes the update_/submit_ function names).`);const o=typeof u.submit=="string"?u.submit?{via:"client",url:u.submit}:!1:u.submit??!1,p=c.properties??{},y=c.required??[],a=e=>e==null||e==="",b=e=>y.filter(t=>a(e[t])),v=e=>{const t={};for(const[r,s]of Object.entries(e)){const i=p[r];if(i)if(s==null)t[r]=s;else if(i.type==="number"||i.type==="integer"){const g=typeof s=="number"?s:Number(s);t[r]=Number.isNaN(g)?s:g}else i.type==="boolean"?t[r]=typeof s=="boolean"?s:s==="true"?!0:s==="false"?!1:!!s:t[r]=s}return t},f=u.initialValues??{},N=b(f),l=P({values:f,missing:N,complete:N.length===0,submitted:!1}),$=e=>{const t=l.get(),r={...t.values,...e},s=b(r),i={...t,values:r,missing:s,complete:s.length===0};return l.set(i),i};let d=null;const S=async()=>{const e=l.get();if(!e.complete)return{ok:!1,missing:e.missing};if(!o)return{ok:!1,error:"No submit target configured for this form collector."};const t=e.values;try{let r;if(o.via==="client")if("handler"in o)r=await o.handler(t);else{const s=await fetch(o.url,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify(t)});if(!s.ok)return{ok:!1,error:`Submit failed: HTTP ${s.status}`};r=await s.json().catch(()=>{})}else{if(!d)return{ok:!1,error:"Backend submit requires install(client) (or useFormCollector) first."};r=await d.submitFormCollector(o.destination,t)}return l.set({...l.get(),submitted:!0}),{ok:!0,result:r}}catch(r){const s=r instanceof Error?r.message:"Submit failed";return x.logger.error(`FormCollector "${n}" submit failed:`,r),{ok:!1,error:s}}},_={name:`update_${n}`,description:`Fill or update fields of the "${n}" form. ${m} 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:{...c,required:[]},handler:e=>{const t=$(v(e));return{ok:!0,values:t.values,missing:t.missing,complete:t.complete}}},h=[_];o&&h.push({name:`submit_${n}`,description:`Submit the completed "${n}" 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:()=>S()});const E="client__",k=o?`submit_${n}`:null,T=e=>{const t=e.functionName??e.toolName;if(t)return t.startsWith(E)?t.slice(8):t},w=e=>{let t={...f},r=!1;for(const i of e){const g=T(i);g===_.name?i.arguments&&(t={...t,...v(i.arguments)}):k&&g===k&&i.status?.toLowerCase()==="done"&&(r=!0)}const s=b(t);l.set({values:t,missing:s,complete:s.length===0,submitted:r})},C=`form:${n}`,F=()=>{const e=l.get(),t=o?` When all required fields are present and the user confirms, call submit_${n}.`:"";return{name:`Form: ${n}`,description:`Conversational form the assistant is helping fill. ${m} Use update_${n} to record fields; ask the user only for what is still missing.${t}`,data:{values:e.values,missing:e.missing,complete:e.complete,submitted:e.submitted}}},q=e=>{w((e.messages??[]).flatMap(t=>t.toolCalls??[]))};return{name:n,store:l,functions:h,contextKey:C,contextProvider:F,install:e=>{d=e;for(const r of h)e.register(r);e.addDynamicContext(C,F);const t=l.subscribe(()=>e.notifyContextChanged());return e.on("conversation:loaded",q),()=>{t();for(const r of h)e.unregisterFunction(r.name);e.removeDynamicContext(C),e.off("conversation:loaded",q),d===e&&(d=null)}},hydrate:w,submit:S,getValues:()=>l.get().values,setValues:e=>{$(v(e))},reset:()=>{const e=b(f);l.set({values:f,missing:e,complete:e.length===0,submitted:!1})}}}Object.defineProperty(exports,"createFormCollector",{enumerable:!0,get:function(){return I}});Object.defineProperty(exports,"createStore",{enumerable:!0,get:function(){return P}});
@@ -1,4 +1,4 @@
1
- function w(g = {}) {
1
+ function C(g = {}) {
2
2
  const { overrides: m = {} } = g, d = {
3
3
  id: "mock-msg-1",
4
4
  conversationId: "mock-conv-1",
@@ -27,6 +27,9 @@ function w(g = {}) {
27
27
  },
28
28
  unregisterFunction: () => !0,
29
29
  getRegisteredFunctions: () => [],
30
+ getContextSnapshot: () => null,
31
+ notifyContextChanged: () => {
32
+ },
30
33
  registerNavigationFunction: () => {
31
34
  },
32
35
  on: (e, o) => {
@@ -67,5 +70,5 @@ function w(g = {}) {
67
70
  }, n;
68
71
  }
69
72
  export {
70
- w as t
73
+ C as t
71
74
  };
@@ -0,0 +1 @@
1
+ function C(g={}){const{overrides:u={}}=g,d={id:"mock-msg-1",conversationId:"mock-conv-1",content:"Mock response",role:"assistant",status:"DONE",createdAt:new Date},l={id:"mock-conv-1",title:"Mock Conversation",lastMessageDate:new Date},t=new Map,r=[],m={sendMessage:async()=>d,getConversations:async()=>[l],getConversation:async()=>l,getMessages:async()=>[d],submitToolCallForm:async()=>{},confirmToolCall:async()=>{},rejectToolCall:async()=>{},submitFeedback:async()=>{},registerFunction:()=>{},unregisterFunction:()=>!0,getRegisteredFunctions:()=>[],getContextSnapshot:()=>null,notifyContextChanged:()=>{},registerNavigationFunction:()=>{},on:(e,o)=>{t.has(e)||t.set(e,new Set),t.get(e).add(o)},off:(e,o)=>{t.get(e)?.delete(o)},once:(e,o)=>{const n=(...i)=>{t.get(e)?.delete(n),o(...i)};t.has(e)||t.set(e,new Set),t.get(e).add(n)},waitFor:(e,o)=>new Promise((n,i)=>{let a;const c=f=>{a&&clearTimeout(a),t.get(e)?.delete(c),n(f)};t.has(e)||t.set(e,new Set),t.get(e).add(c),o?.timeout&&(a=setTimeout(()=>{t.get(e)?.delete(c),i(new Error(`waitFor("${e}") timed out after ${o.timeout}ms`))},o.timeout))}),updateConfig:()=>{},destroy:()=>{t.clear()},...u},s={};for(const[e,o]of Object.entries(m))s[e]=(...n)=>(r.push({method:e,args:n}),o(...n));return s.__calls=r,s.__callsFor=e=>r.filter(o=>o.method===e),s.__emitEvent=(e,o)=>{const n=t.get(e);n&&[...n].forEach(i=>i(o))},s}Object.defineProperty(exports,"createMockClient",{enumerable:!0,get:function(){return C}});
package/dist/devtools.cjs CHANGED
@@ -1,17 +1,17 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});var s,d,u=()=>({});function p(){s&&(s.textContent=JSON.stringify(u(),null,2))}function g(t,a){if(!d)return;const e=document.createElement("div");e.className=`dev-log-line dev-log-${a}`,e.textContent=`${new Date().toLocaleTimeString()} ${t}`,d.appendChild(e),d.scrollTop=d.scrollHeight}var x="ago_dev_panel_collapsed";function r(t,a){t.classList.toggle("collapsed",a);const e=t.querySelector(".dev-toggle");e&&(e.textContent=a?"▢":"—",e.title=a?"Expand dev tools":"Collapse dev tools",e.setAttribute("aria-expanded",String(!a)));try{localStorage.setItem(x,a?"1":"0")}catch{}}function b(t){const{store:a,client:e,target:i}=t;u=a.get,m();const f=typeof i=="string"?document.querySelector(i):i instanceof Element?i:document.body,o=document.createElement("aside");o.id="ago-dev-panel",(f??document.body).appendChild(o),o.innerHTML=`
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});var c,d,v=()=>({});function i(){c&&(c.textContent=JSON.stringify(v(),null,2))}function s(l,e){if(!d)return;const o=document.createElement("div");o.className=`dev-log-line dev-log-${e}`,o.textContent=`${new Date().toLocaleTimeString()} ${l}`,d.appendChild(o),d.scrollTop=d.scrollHeight}var x="ago_dev_panel_collapsed";function r(l,e){l.classList.toggle("collapsed",e);const o=l.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(x,e?"1":"0")}catch{}}function h(l){const{client:e,target:o}=l;v=()=>e.getContextSnapshot()?.entries??{},b();const u=typeof o=="string"?document.querySelector(o):o instanceof Element?o:document.body,t=document.createElement("aside");t.id="ago-dev-panel",(u??document.body).appendChild(t),t.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(n=>n.name).join(", ")||"—"}</code></div>
8
+ <div class="dev-fns">Registered functions: <code>${(e.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
- `,s=o.querySelector("#ago-dev-state"),d=o.querySelector("#ago-dev-log"),o.querySelector(".dev-toggle")?.addEventListener("click",n=>{n.stopPropagation(),r(o,!o.classList.contains("collapsed"))}),o.addEventListener("click",()=>{o.classList.contains("collapsed")&&r(o,!1)});let c=!1;try{c=localStorage.getItem(x)==="1"}catch{}r(o,c),e.on("function:invoke",({functionName:n,arguments:l})=>{g(`→ ${n}(${JSON.stringify(l??{})})`,"invoke")}),e.on("function:result",({result:n,error:l})=>{g(l?`✗ error: ${l}`:`← ${JSON.stringify(n)}`,l?"error":"result")}),a.subscribe(p),p()}var v="ago-dev-panel-styles";function m(){if(document.getElementById(v))return;const t=document.createElement("style");t.id=v,t.textContent=h,document.head.appendChild(t)}var h=`
14
+ `,c=t.querySelector("#ago-dev-state"),d=t.querySelector("#ago-dev-log"),t.querySelector(".dev-toggle")?.addEventListener("click",a=>{a.stopPropagation(),r(t,!t.classList.contains("collapsed"))}),t.addEventListener("click",()=>{t.classList.contains("collapsed")&&r(t,!1)});let p=!1;try{p=localStorage.getItem(x)==="1"}catch{}r(t,p),e.on("function:invoke",({functionName:a,arguments:n})=>{s(`→ ${a}(${JSON.stringify(n??{})})`,"invoke")}),e.on("function:result",({result:a,error:n})=>{s(n?`✗ error: ${n}`:`← ${JSON.stringify(a)}`,n?"error":"result"),i()}),e.on("context:changed",i),e.on("conversation:loaded",a=>{const n=(a.messages??[]).flatMap(f=>f.toolCalls??[]);s(`⟳ hydrated "${a.title||a.id}" — replayed ${n.length} tool call${n.length===1?"":"s"}`,"hydrate"),i()}),i()}var g="ago-dev-panel-styles";function b(){if(document.getElementById(g))return;const l=document.createElement("style");l.id=g,l.textContent=m,document.head.appendChild(l)}var m=`
15
15
  #ago-dev-panel {
16
16
  position: fixed;
17
17
  top: 16px;
@@ -105,4 +105,5 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});var s,d,u=()=
105
105
  #ago-dev-panel .dev-log-invoke { color: #9ecbff; }
106
106
  #ago-dev-panel .dev-log-result { color: #86efac; }
107
107
  #ago-dev-panel .dev-log-error { color: #fca5a5; }
108
- `;exports.initDevPanel=b;
108
+ #ago-dev-panel .dev-log-hydrate { color: #d8b4fe; }
109
+ `;exports.initDevPanel=h;
@@ -31,6 +31,12 @@ declare class AgoClient {
31
31
  * Get a specific conversation with messages
32
32
  */
33
33
  getConversation(conversationId: string): Promise<Conversation>;
34
+ /**
35
+ * Map the backend's snake_case `tool_call_data` entries onto the camelCase
36
+ * {@link ToolCallData} shape used everywhere else in the SDK. Mirrors
37
+ * `SSEHandler.parseToolCall` so persisted and streamed tool calls match.
38
+ */
39
+ private static mapPersistedToolCalls;
34
40
  /**
35
41
  * Get messages for a conversation
36
42
  */
@@ -141,6 +147,13 @@ declare class AgoClient {
141
147
  * Remove a dynamic context provider.
142
148
  */
143
149
  removeDynamicContext(key: string): boolean;
150
+ /**
151
+ * Broadcast that the client-side context changed by emitting `context:changed`
152
+ * with the fresh snapshot. The context-mutating methods call this automatically;
153
+ * stateful helpers whose context lives in a store (e.g. `createFormCollector`)
154
+ * call it when their store updates so observers like the dev panel stay live.
155
+ */
156
+ notifyContextChanged(): void;
144
157
  /**
145
158
  * Enable automatic capture of the current browser page (URL + title).
146
159
  * Injected as a dynamic context entry named `browser-page`.
@@ -186,6 +199,14 @@ declare class AgoClient {
186
199
  * SDK Events
187
200
  */
188
201
  declare interface AgoClientEvents {
202
+ /**
203
+ * The client-side context changed: a context entry or dynamic provider was
204
+ * added/removed, or a stateful helper (e.g. `createFormCollector`) updated its
205
+ * store. Carries the fresh snapshot so observers — notably the dev panel — can
206
+ * repaint without waiting for the next message. Fires for the form collector's
207
+ * initial install too, so its missing fields show from the start of a conversation.
208
+ */
209
+ "context:changed": ContextSnapshot | null;
189
210
  "message:start": {
190
211
  conversationId: string;
191
212
  messageId: string;
@@ -201,6 +222,12 @@ declare interface AgoClientEvents {
201
222
  conversationId?: string;
202
223
  messageId?: string;
203
224
  };
225
+ /**
226
+ * A full conversation was loaded from the server (e.g. after a page reload).
227
+ * Carries the messages and their persisted tool calls so stateful helpers —
228
+ * notably `createFormCollector` — can replay them to restore their state.
229
+ */
230
+ "conversation:loaded": Conversation;
204
231
  "toolCall:received": ToolCallData;
205
232
  "toolCall:form": ToolCallData;
206
233
  "function:invoke": ClientFunctionInvocation;
@@ -350,13 +377,12 @@ declare interface Conversation {
350
377
 
351
378
  /** Options for {@link initDevPanel}. */
352
379
  export declare interface DevPanelOptions {
353
- /** The store whose state the panel renders live. */
354
- store: Store<unknown>;
355
380
  /**
356
- * Client to read registered functions from and listen to function events on.
357
- * Typed structurally so a mock client works too.
381
+ * The AGO client to debug. The panel lists its registered functions, logs its
382
+ * function events, and renders its live context snapshot
383
+ * (`client.getContextSnapshot()`)
358
384
  */
359
- client: Pick<AgoClient, "on" | "getRegisteredFunctions">;
385
+ client: Pick<AgoClient, "on" | "getRegisteredFunctions" | "getContextSnapshot">;
360
386
  /** Where to mount: a CSS selector, an Element, or `document.body` (default). */
361
387
  target?: string | Element;
362
388
  }
@@ -401,27 +427,6 @@ declare interface SendMessageOptions {
401
427
  files?: File[];
402
428
  }
403
429
 
404
- /**
405
- * A minimal observable store: one value, synchronous subscriptions, and
406
- * optional persistence via {@link PersistOptions} (localStorage by default).
407
- *
408
- * ```ts
409
- * const store = createStore({ count: 0 });
410
- * store.subscribe((state) => save(state));
411
- * store.set({ count: 1 }); // listeners fire synchronously
412
- *
413
- * const cart = createStore({ items: [] }, { key: "cart" }); // persisted
414
- * ```
415
- */
416
- declare interface Store<T> {
417
- /** Current state. */
418
- get(): T;
419
- /** Replace the state and notify subscribers synchronously. */
420
- set(next: T): void;
421
- /** Register a listener; returns a function that removes it. */
422
- subscribe(listener: (state: T) => void): () => void;
423
- }
424
-
425
430
  /**
426
431
  * Tool call data from SSE stream
427
432
  */