@useago/sdk 1.5.6 → 1.7.1
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 +233 -0
- package/README.md +59 -0
- package/dist/{AgoClient-B_R_qi2s.js → AgoClient-BJjLTOw4.js} +658 -388
- package/dist/AgoClient-B_u_d-XS.cjs +9 -0
- package/dist/angular.cjs +1 -1
- package/dist/angular.d.cts +230 -5
- package/dist/angular.d.ts +230 -5
- package/dist/angular.js +10 -1
- package/dist/{createConversationSession-Dqh7NXK4.cjs → createConversationSession-BCdwAhLF.cjs} +1 -1
- package/dist/{createConversationSession-CdtI4vrf.js → createConversationSession-Bgvx753t.js} +1 -1
- package/dist/{createFormCollector-YwaZ78wU.js → createFormCollector-CM3qjGcM.js} +1 -1
- package/dist/{createFormCollector-BjO3oB63.cjs → createFormCollector-ssBP8u0p.cjs} +1 -1
- package/dist/{createMockClient-DcuNlufr.js → createMockClient-BDLW6tVc.js} +5 -2
- package/dist/createMockClient-BSNuN3ys.cjs +1 -0
- package/dist/devtools.d.cts +220 -5
- package/dist/devtools.d.ts +220 -5
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +238 -6
- package/dist/index.d.ts +238 -6
- package/dist/index.js +111 -109
- package/dist/react.cjs +40 -3
- package/dist/react.d.cts +419 -7
- package/dist/react.d.ts +419 -7
- package/dist/react.js +1195 -743
- package/dist/sheetController-CBUVOroo.js +205 -0
- package/dist/sheetController-DY-sC13T.cjs +1 -0
- package/dist/testing.cjs +1 -1
- package/dist/testing.d.cts +220 -5
- package/dist/testing.d.ts +220 -5
- package/dist/testing.js +1 -1
- package/dist/vue.cjs +1 -1
- package/dist/vue.d.cts +235 -5
- package/dist/vue.d.ts +235 -5
- package/dist/vue.js +121 -100
- package/dist/widget.cjs +4 -4
- package/dist/widget.d.cts +328 -5
- package/dist/widget.d.ts +328 -5
- package/dist/widget.js +721 -629
- package/llms-full.txt +202 -14
- package/package.json +1 -1
- package/dist/AgoClient-DLB9icvv.cjs +0 -8
- package/dist/createMockClient-Dw-cyISN.cjs +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,239 @@ All notable changes to `@useago/sdk` are documented here.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.7.1] - 2026-08-10
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- The `setPageState` function synthesized by `registerPageStateFunction` /
|
|
13
|
+
`useAgoPageState` now sends an explicit `required: []` in its parameter
|
|
14
|
+
schema. Backends that treat a missing `required` array as "every property is
|
|
15
|
+
required" forced the agent to set all controls on every call; controls are
|
|
16
|
+
optional, and the agent sets only the ones the user asked for.
|
|
17
|
+
|
|
18
|
+
## [1.7.0] - 2026-08-07
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- `registerPageStateFunction` / `useAgoPageState` accept a `data` source, so the
|
|
23
|
+
agent gets back **what the page displays** as the result of its own
|
|
24
|
+
`setPageState` call instead of only as context on the next message. An agent
|
|
25
|
+
that filters a list can now say what it found.
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
useAgoPageState(controls, {
|
|
29
|
+
data: {
|
|
30
|
+
description: "The users matching the current filters.",
|
|
31
|
+
get: () => users ?? [],
|
|
32
|
+
isLoading: () => isFetching, // isFetching, not isLoading
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`setPageState` then returns `{ success, applied, data }`. Declaring `data`
|
|
38
|
+
also registers a read-only `readPageData` companion (no parameters, changes
|
|
39
|
+
nothing, returns `{ data }`) for "what's on screen?" questions; it is removed
|
|
40
|
+
by `unregisterPageStateFunction`.
|
|
41
|
+
- The SDK waits for the work a control change triggered before reading the
|
|
42
|
+
snapshot. Two ways to tell it when that work is done:
|
|
43
|
+
- **`get()` returns a promise** (preferred): awaiting it *is* the completion
|
|
44
|
+
signal, so the SDK is exact. `get: () => queryClient.ensureQueryData({...})`
|
|
45
|
+
shares the single in-flight request with the UI.
|
|
46
|
+
- **`isLoading` is declared**: the SDK polls it every 50 ms and treats two
|
|
47
|
+
consecutive idle readings as settled. A heuristic, and a flag raised more
|
|
48
|
+
than ~100 ms after the change (a debounced fetch) is missed, so the agent
|
|
49
|
+
reads the previous rows.
|
|
50
|
+
|
|
51
|
+
Both are capped by `settleTimeoutMs` (10 s by default); a promise that never
|
|
52
|
+
settles no longer holds the turn open, and a rejected `get()` is reported to
|
|
53
|
+
the agent as a failed read instead of an empty page.
|
|
54
|
+
- Oversized snapshots are truncated **by item**: the first whole items that fit
|
|
55
|
+
the budget (`data.maxResultBytes`, defaulting to `maxFunctionResultBytes`) plus
|
|
56
|
+
a `truncation` field carrying `{ truncated, returnedItems, totalItems, hint }`.
|
|
57
|
+
The counts are nested so they cannot overwrite a same-named field of your own
|
|
58
|
+
snapshot. `success` and `applied` are never dropped, so a filter that worked is
|
|
59
|
+
never reported as an unusable blob. When the snapshot's other fields overflow
|
|
60
|
+
on their own, it falls back to a size-bounded preview rather than returning
|
|
61
|
+
something over the ceiling.
|
|
62
|
+
- A `readPageData` companion is only removed by the SDK if the SDK registered it,
|
|
63
|
+
so a host app that already has a function by that name keeps it, and
|
|
64
|
+
re-registering a page-state function without `data` drops the stale companion
|
|
65
|
+
instead of letting it answer with the previous page's rows.
|
|
66
|
+
- `AgoPageDataSource` is exported from the root, `/react`, `/vue` and `/angular`
|
|
67
|
+
entries.
|
|
68
|
+
- `<ChatWidget>` now says **what the agent is doing** while it works, instead of
|
|
69
|
+
three dots. While a client function is running it shows a status row naming it
|
|
70
|
+
("Updating the page", "Looking up prices"), which matters most during a
|
|
71
|
+
pause/resume loop: the per-message dots disappear as soon as the first token
|
|
72
|
+
lands, so a tool pause after some text had been written showed nothing at all.
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
<ChatWidget functionLabels={{ lookupFlavorPrices: "Je regarde les prix" }} />
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The row shows the latest step of the current turn and **keeps it until
|
|
79
|
+
something replaces it** (another call, a reasoning step) or the turn ends.
|
|
80
|
+
Clearing it when the call returned made a fast function flash for a few frames
|
|
81
|
+
and read as nothing at all. The agent's `reasoning` steps are included by
|
|
82
|
+
default (`showReasoning={false}` to leave them out), so the row does not go
|
|
83
|
+
blank between two calls.
|
|
84
|
+
|
|
85
|
+
`functionLabels` takes a map, or a function `(name, args) => string` for full
|
|
86
|
+
control. Unmapped names fall back to a prettified function name, so the row
|
|
87
|
+
works with no configuration. Pass `false` to keep the plain indicator. The row
|
|
88
|
+
is a `role="status"` live region and is driven by the existing
|
|
89
|
+
`useAgoActivity` hook, so it covers server-side tool calls and approval holds
|
|
90
|
+
too. It is scoped to the assistant message being written, so a new turn never
|
|
91
|
+
opens showing the previous one's last step.
|
|
92
|
+
|
|
93
|
+
The label itself is animated: a slow sheen keeps travelling across the text,
|
|
94
|
+
so a step that sits for several seconds (a fetch, a settle wait) reads as
|
|
95
|
+
still working rather than stuck, and each new step fades in as its own node so
|
|
96
|
+
one visibly gives way to the next instead of the characters swapping in place.
|
|
97
|
+
Colours go through `--ago-activity-color` / `--ago-activity-sheen` /
|
|
98
|
+
`--ago-activity-bg`; the sheen is behind `@supports (background-clip: text)`
|
|
99
|
+
and stops under `prefers-reduced-motion`.
|
|
100
|
+
|
|
101
|
+
The label carries no animated dots, and the answer bubble above it drops its
|
|
102
|
+
own while the row is up: the text already reads as progress, and the two
|
|
103
|
+
together were just noise. `<Message>` takes a `showStreamingDots` prop for
|
|
104
|
+
the same reason in a custom UI (`false` removes the empty bubble entirely
|
|
105
|
+
rather than leaving a blank box). It renders simple inline Markdown
|
|
106
|
+
(`**bold**`, `*italic*`, `` `code` ``) so a backend reasoning step keeps its
|
|
107
|
+
emphasis; block Markdown is deliberately not supported there, since a
|
|
108
|
+
paragraph or a table would break a one-line row.
|
|
109
|
+
|
|
110
|
+
Omit `data` and the result stays exactly `{ success, applied }`, byte for byte.
|
|
111
|
+
The snapshot stays out of the `page-state:` context entry, so it travels only
|
|
112
|
+
when the agent asks for it. The intra-turn round trip requires
|
|
113
|
+
`clientFunctionsMode: "pause"` (the default); the SDK warns once if `data` is
|
|
114
|
+
declared in `"placeholder"` mode.
|
|
115
|
+
|
|
116
|
+
### Fixed
|
|
117
|
+
|
|
118
|
+
- Client functions executed on the resume path (after a reload, via
|
|
119
|
+
`resumePendingClientFunctions`) now emit `function:invoke` and
|
|
120
|
+
`function:result` like the live stream does. A UI driven by those events used
|
|
121
|
+
to sit on a spinner for those calls.
|
|
122
|
+
- An agent calling the same client function twice in one turn no longer strands
|
|
123
|
+
the turn. The per-stream dedupe (which exists because the backend emits one
|
|
124
|
+
call under two SSE shapes) keyed on function name + arguments alone, so a
|
|
125
|
+
second call with identical arguments was swallowed: its result was never
|
|
126
|
+
submitted, the backend never saw every result, and the paused turn stayed in
|
|
127
|
+
`WAITING_CLIENT` forever. It now uses the invocation id to tell a repeat call
|
|
128
|
+
apart from a repeat *emission* of the same call. Zero-argument functions (such
|
|
129
|
+
as the new `readPageData`) hit this every time.
|
|
130
|
+
- Vue's `useAgoPageState` no longer drops `requiresApproval` (and now forwards
|
|
131
|
+
every option, matching React and Angular).
|
|
132
|
+
|
|
133
|
+
## [1.6.2] - 2026-08-06
|
|
134
|
+
|
|
135
|
+
### Fixed
|
|
136
|
+
|
|
137
|
+
- Side-placement panels (`placement: "left" | "right"`) now get the mobile
|
|
138
|
+
treatment the inline card already had. On a small viewport the open panel
|
|
139
|
+
tracks the visible viewport, so the on-screen keyboard no longer covers the
|
|
140
|
+
composer, and it is exposed as a real dialog (`role="dialog"`, `aria-modal`,
|
|
141
|
+
Tab trap, Escape to close). Opening it moves focus into the panel rather than
|
|
142
|
+
the text field, so the keyboard no longer opens unprompted.
|
|
143
|
+
- A desktop side panel is explicitly **not** modal: it no longer traps Tab or
|
|
144
|
+
locks the page behind it, so the host page stays usable beside it.
|
|
145
|
+
- The background scroll lock now restores the host page's own inline `<body>` /
|
|
146
|
+
`<html>` styles instead of deleting them. A host that had already pinned its
|
|
147
|
+
own body (for its own modal) is left exactly as it was found.
|
|
148
|
+
- The scroll lock is tracked per owner instead of by a counter, so overlapping
|
|
149
|
+
open / close / destroy paths can no longer release a lock they don't hold,
|
|
150
|
+
and `destroy()` is idempotent.
|
|
151
|
+
- `destroy()` during an opening animation no longer re-pins the page after
|
|
152
|
+
teardown, which could leave `<body>` fixed with no owner left to release it.
|
|
153
|
+
- `destroy()` now drops its keydown and `visualViewport` listeners for every
|
|
154
|
+
placement; side-placement widgets previously leaked them.
|
|
155
|
+
- A phone in landscape (short viewport, e.g. 844x390) now counts as mobile, so
|
|
156
|
+
it keeps the full-screen sheet and the keyboard handling instead of falling
|
|
157
|
+
back to the desktop layout mid-conversation.
|
|
158
|
+
- A streamed answer no longer rebuilds the whole thread on every token. Screen
|
|
159
|
+
readers announced the entire conversation per chunk, and the rebuild collapsed
|
|
160
|
+
the pane's scroll height, which is what made a "follow the bottom" check latch
|
|
161
|
+
off after the first token.
|
|
162
|
+
- The message pane now follows new content only while you are already at the
|
|
163
|
+
bottom. Scroll up to re-read something and the stream stops yanking you back;
|
|
164
|
+
a "jump to latest" button returns you. Sending always re-attaches.
|
|
165
|
+
- On a compact viewport the composer is no longer re-focused after every answer,
|
|
166
|
+
which was re-opening the on-screen keyboard over the reply you were reading.
|
|
167
|
+
- A failed send no longer destroys the message: the text (and any attachments)
|
|
168
|
+
go back into the composer instead of being lost.
|
|
169
|
+
- Touch targets are at least 44px across both the vanilla widget and the React
|
|
170
|
+
components: send, stop, attach, remove-attachment, suggested replies, and both
|
|
171
|
+
close buttons.
|
|
172
|
+
|
|
173
|
+
### Added
|
|
174
|
+
|
|
175
|
+
- Bottom-sheet presentation for compact viewports, opt-in via the new `sheet`
|
|
176
|
+
option on the React `<ChatWidget>`. It rests as a `peek` bar pinned to the
|
|
177
|
+
bottom edge (named header, two-line preview of the last reply, composer) and
|
|
178
|
+
expands to a `full` screen with `role="dialog"`, a background scroll lock and
|
|
179
|
+
Escape to collapse. `peek` is deliberately never modal: the page behind it
|
|
180
|
+
stays scrollable and keyboard-reachable.
|
|
181
|
+
- `createSheetController` (`@useago/sdk/widget`): the framework-agnostic state
|
|
182
|
+
machine behind it. It returns a **props bag** rather than touching the DOM, so
|
|
183
|
+
the React binding, the vanilla widget and any Vue or Angular binding drive the
|
|
184
|
+
same implementation.
|
|
185
|
+
- `useSheet` (`@useago/sdk/react`), which reads that controller through
|
|
186
|
+
`useSyncExternalStore` with a non-compact server snapshot, so `matchMedia`
|
|
187
|
+
never runs during render and hydration matches.
|
|
188
|
+
- `<ChatWidget>` now forwards a ref exposing `expand()`, `collapse()`,
|
|
189
|
+
`toggle()` and the current `state`.
|
|
190
|
+
- `sheet.bottomOffset` leaves room for a host page that already owns the bottom
|
|
191
|
+
edge (a bottom nav, a sticky checkout bar). `sheet: false` (the default) keeps
|
|
192
|
+
the widget out of the host's fixed layers entirely.
|
|
193
|
+
- `thinkingLabel` on `<ChatWidget>`: what the collapsed sheet says next to the
|
|
194
|
+
animated dots while the agent is working and has produced no text yet.
|
|
195
|
+
- `--ago-sheet-height`: the collapsed sheet measures itself and publishes its
|
|
196
|
+
footprint as a custom property on the host document. A fixed bar covers the end
|
|
197
|
+
of the page, so hosts need to reserve that space, and a constant in their CSS
|
|
198
|
+
would drift from the real height (which depends on their own composer styling).
|
|
199
|
+
Use it as `padding-bottom: var(--ago-sheet-height, 0px)`.
|
|
200
|
+
|
|
201
|
+
### Changed
|
|
202
|
+
|
|
203
|
+
- The streaming dots now paint in `currentColor` instead of a fixed grey, so
|
|
204
|
+
they stay legible on a dark surface as well as a light one.
|
|
205
|
+
- React `<ChatInput>`'s `onSend` may now return a boolean (or a promise of one).
|
|
206
|
+
Returning `false` tells the composer the send failed so it can restore the
|
|
207
|
+
draft. Existing handlers that return nothing keep working unchanged.
|
|
208
|
+
|
|
209
|
+
## [1.6.1] - 2026-08-05
|
|
210
|
+
|
|
211
|
+
### Added
|
|
212
|
+
|
|
213
|
+
- The React `<ChatInput>` textarea now exposes an
|
|
214
|
+
`ago-chat-input__field` class for stable custom styling.
|
|
215
|
+
|
|
216
|
+
### Fixed
|
|
217
|
+
|
|
218
|
+
- React `<ChatWidget>` auto-scroll now stays inside the message pane instead of
|
|
219
|
+
moving ancestor containers or the whole page, and preserves the reader's
|
|
220
|
+
position while they review earlier messages.
|
|
221
|
+
|
|
222
|
+
## [1.6.0] - 2026-08-04
|
|
223
|
+
|
|
224
|
+
### Added
|
|
225
|
+
|
|
226
|
+
- Stop generation. `client.stop()` interrupts the turn being generated: it
|
|
227
|
+
closes the stream and calls the new `POST /api/sdk/v1/messages/{id}/stop`, so
|
|
228
|
+
the agent really stops instead of finishing in the background. The text
|
|
229
|
+
produced so far is kept and the message ends as `CANCELED`; the in-flight
|
|
230
|
+
`sendMessage` resolves with it rather than rejecting.
|
|
231
|
+
- `message:stopped` event, fired once the stopped turn has unwound.
|
|
232
|
+
- `client.stopMessage(messageId)` to stop a turn by id (e.g. one still running
|
|
233
|
+
after a page reload), and `client.isGenerating()` to know whether there is
|
|
234
|
+
anything to stop.
|
|
235
|
+
- `stop()` on the React `useMessages` / `useChat` hooks, the Vue `useMessages` /
|
|
236
|
+
`useChat` composables, the Angular `AgoService`, and the vanilla widget handle.
|
|
237
|
+
- The React `<ChatWidget>` and the vanilla widget now show a Stop button in
|
|
238
|
+
place of the send button while the agent answers. Opt out with
|
|
239
|
+
`allowStop: false`.
|
|
240
|
+
|
|
8
241
|
## [1.5.6] - 2026-07-29
|
|
9
242
|
|
|
10
243
|
### Fixed
|
package/README.md
CHANGED
|
@@ -145,6 +145,30 @@ so the agent changes only what the user asked for. The current value from each
|
|
|
145
145
|
`get()` is sent with every message, so the agent knows the state before it
|
|
146
146
|
changes it. Vue and Angular have the same helper.
|
|
147
147
|
|
|
148
|
+
Add a `data` source and the agent also gets back **what the page now shows**, as
|
|
149
|
+
the result of its own call, so it can answer "did you find dupont?" in the same
|
|
150
|
+
turn:
|
|
151
|
+
|
|
152
|
+
```tsx
|
|
153
|
+
const { data: invoices, isFetching } = useQuery({
|
|
154
|
+
queryKey: ["invoices", status, sort],
|
|
155
|
+
queryFn: fetchInvoices,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
useAgoPageState(controls, {
|
|
159
|
+
data: {
|
|
160
|
+
description: "The invoices matching the current filters.",
|
|
161
|
+
get: () => invoices ?? [],
|
|
162
|
+
isLoading: () => isFetching, // isFetching, not isLoading
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
The SDK waits for `isLoading` to go false before reading, so the agent never
|
|
168
|
+
receives the previous search's rows, and it truncates a long list by whole rows
|
|
169
|
+
rather than sending a huge payload. Details in
|
|
170
|
+
[functions and context](docs/general/functions-and-context.md#return-what-the-page-displays).
|
|
171
|
+
|
|
148
172
|
## Turn a JavaScript function into an agent tool
|
|
149
173
|
|
|
150
174
|
Keep your application logic as a regular JavaScript function, then describe its
|
|
@@ -198,6 +222,41 @@ Whenever the agent calls the tool, the SDK runs your handler in the browser
|
|
|
198
222
|
with the generated arguments and sends its return value back to the agent. Keep
|
|
199
223
|
the return value small and structured so the agent can use it reliably.
|
|
200
224
|
|
|
225
|
+
## Let the user stop a long answer
|
|
226
|
+
|
|
227
|
+
`<ChatWidget>` already does this: while the agent answers, its send button turns
|
|
228
|
+
into a **Stop** button (pass `allowStop={false}` to opt out). In a custom UI, wire
|
|
229
|
+
the `stop` returned by `useChat` / `useMessages`:
|
|
230
|
+
|
|
231
|
+
```tsx
|
|
232
|
+
import { useChat } from "@useago/sdk/react";
|
|
233
|
+
|
|
234
|
+
function Chat() {
|
|
235
|
+
const { messages, sendMessage, stop, isLoading } = useChat();
|
|
236
|
+
|
|
237
|
+
return (
|
|
238
|
+
<div>
|
|
239
|
+
{messages.map((m) => (
|
|
240
|
+
<p key={m.id}><b>{m.role}:</b> {m.content}</p>
|
|
241
|
+
))}
|
|
242
|
+
{isLoading ? (
|
|
243
|
+
<button onClick={() => void stop()}>Stop</button>
|
|
244
|
+
) : (
|
|
245
|
+
<button onClick={() => sendMessage("Write me a long report")}>Send</button>
|
|
246
|
+
)}
|
|
247
|
+
</div>
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Stopping closes the stream **and** tells the backend to stop generating. Closing
|
|
253
|
+
the stream alone would not stop the agent: it would keep running and the full
|
|
254
|
+
answer would reappear on the next load. The text already produced stays on
|
|
255
|
+
screen, the message ends as `CANCELED`, and the pending `sendMessage` resolves
|
|
256
|
+
with that partial message instead of rejecting. Vue and Angular have the same
|
|
257
|
+
`stop`, and the vanilla `mountChatWidget` handle gained `stop()`. Details in
|
|
258
|
+
[Stop the answer](docs/general/core.md#stop-the-answer).
|
|
259
|
+
|
|
201
260
|
## Show the source docs the agent retrieved
|
|
202
261
|
|
|
203
262
|
Each assistant message carries the knowledge sources it used in `m.sources`
|