@artooi/ag-ui-web-component 0.42.0 → 0.44.0
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 +161 -1
- package/README.md +88 -22
- package/dist/ag-ui-web-component.bundle.js +74 -74
- package/dist/ag-ui-web-component.bundle.js.map +4 -4
- package/dist/constants.d.ts +12 -4
- package/dist/constants.d.ts.map +1 -1
- package/dist/core/ag_ui_chat.d.ts +36 -8
- package/dist/core/ag_ui_chat.d.ts.map +1 -1
- package/dist/core/agui_client.d.ts +27 -2
- package/dist/core/agui_client.d.ts.map +1 -1
- package/dist/core/events/feedback_detail.d.ts +21 -0
- package/dist/core/events/feedback_detail.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +317 -139
- package/dist/index.js.map +4 -4
- package/dist/tools/tool_dispatch.d.ts +11 -1
- package/dist/tools/tool_dispatch.d.ts.map +1 -1
- package/dist/ui/composer/voice_input.d.ts +2 -2
- package/dist/ui/composer/voice_input.d.ts.map +1 -1
- package/dist/ui/history/conversation_history.d.ts +40 -24
- package/dist/ui/history/conversation_history.d.ts.map +1 -1
- package/dist/ui/transcript/answer_actions.d.ts +12 -3
- package/dist/ui/transcript/answer_actions.d.ts.map +1 -1
- package/dist/ui/ui_strings.d.ts +5 -0
- package/dist/ui/ui_strings.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/constants.ts +12 -4
- package/src/core/ag_ui_chat.ts +127 -35
- package/src/core/agui_client.ts +43 -3
- package/src/core/events/feedback_detail.ts +21 -0
- package/src/core/run_handlers.ts +2 -2
- package/src/index.ts +1 -0
- package/src/tools/tool_dispatch.ts +40 -4
- package/src/ui/composer/voice_input.ts +57 -23
- package/src/ui/history/conversation_history.ts +117 -35
- package/src/ui/transcript/answer_actions.ts +31 -3
- package/src/ui/ui_strings.ts +6 -0
- package/src/version.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,164 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.44.0] — 2026-10-20
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **`ag-ui-feedback` now says which message was rated.** Its detail carried only
|
|
15
|
+
the message's text and the rating, so a host could not tell two answers that
|
|
16
|
+
read the same apart, and could not find a rated message again after a reload.
|
|
17
|
+
The detail now also carries `messageId`, the AG-UI id of the rated assistant
|
|
18
|
+
message (the id it streamed under, or the stored message's `id` when it was
|
|
19
|
+
restored from history, including the turns a Retry keeps); `threadId`, the
|
|
20
|
+
conversation it belongs to; and `runId`, the run whose `RUN_STARTED` streamed
|
|
21
|
+
it. `runId` is present only on a message that arrived live: a stored AG-UI
|
|
22
|
+
message carries no run id, so the key is absent on a restored one rather than
|
|
23
|
+
guessed; text a sub-agent streams through the parent run carries the
|
|
24
|
+
parent's `runId`. A restored answer whose store returned no id keeps its Copy
|
|
25
|
+
but offers no rating pair, since a rating has to name a message. A restored
|
|
26
|
+
message is named by the id its store returns: the
|
|
27
|
+
built-in browser store keeps the streamed ids, and with `data-threads-url` or
|
|
28
|
+
a host's own store a rating made after a reload names the same message as one
|
|
29
|
+
made live only where the server persists the ids it streamed. Additive: `content` and
|
|
30
|
+
`rating` are unchanged. **A host has to opt in to use it**: the rating pair is
|
|
31
|
+
still off unless `data-message-actions` lists `feedback`, and a host already
|
|
32
|
+
listening reads the new fields from the same event. `AgUiClientHandlers.onTextEnd`
|
|
33
|
+
gains a second argument, `AgUiMessageIds`, naming the completed message; a
|
|
34
|
+
handler that ignores it is unaffected, and code that calls a handler table's
|
|
35
|
+
`onTextEnd` itself has to pass it.
|
|
36
|
+
- **The `data-message-actions` row of the README's attribute table said all
|
|
37
|
+
three actions are on by default.** Absent means copy and retry; the rating
|
|
38
|
+
pair has been opt-in since it was found latching with no listener. The row now
|
|
39
|
+
says so.
|
|
40
|
+
|
|
41
|
+
## [0.43.0] — 2026-10-10
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
|
|
45
|
+
- **A second click on the mic while its permission prompt is open no longer
|
|
46
|
+
leaves a recording nothing can stop.** The mic button reads idle for as long
|
|
47
|
+
as the browser's prompt is up, which is seconds the first time an origin asks
|
|
48
|
+
and a few milliseconds after that, so a double click or an impatient second
|
|
49
|
+
click started twice. Both starts recorded and held a live track, but the
|
|
50
|
+
control kept only the second: one stop click ended that one, the first kept
|
|
51
|
+
recording with the browser's recording indicator on for a button reading
|
|
52
|
+
idle, and `dispose()` could not reach it either. The first start's two-minute
|
|
53
|
+
cap timer was orphaned too, and when it fired it stopped whatever recording
|
|
54
|
+
was current, so a later, unrelated one was cut short with "Stopped at the
|
|
55
|
+
2-minute limit". If the second start's recorder then failed, the failure
|
|
56
|
+
released the second stream and hid the first. The latest click now wins: a
|
|
57
|
+
start that settles after a newer one began, or after the control was disposed,
|
|
58
|
+
releases the microphone it was granted and touches nothing the newer start
|
|
59
|
+
owns, and a refusal on such a start no longer draws an error over the newer
|
|
60
|
+
one. The button is not blocked while a prompt is open, so a prompt the user
|
|
61
|
+
dismissed without answering, which a browser may leave pending forever, does
|
|
62
|
+
not stop the next click from asking again. Nothing for a host to opt into.
|
|
63
|
+
|
|
64
|
+
- **A send made while a stored conversation loads no longer goes out without
|
|
65
|
+
it.** Every restore asks the conversation store for the conversation first:
|
|
66
|
+
on connect, on `reload()`, when a thread is picked from the drawer, and when
|
|
67
|
+
`user-key` changes from one principal to another. With `data-threads-url`
|
|
68
|
+
that is a request to the server,
|
|
69
|
+
and nothing held the composer while it was out unless the restore was about
|
|
70
|
+
to resume a run. So a send in that time went out carrying only its own turn,
|
|
71
|
+
and the agent answered without the conversation. Its save replaced the
|
|
72
|
+
stored conversation with the new exchange, and the replay then drew the
|
|
73
|
+
stored conversation under the new turn. The client the send built stayed
|
|
74
|
+
cached without the history, so every later turn on that page went out
|
|
75
|
+
without it too. A Retry in the window was refused, but it left the same
|
|
76
|
+
empty client cached, so the first send after the load lost the history the
|
|
77
|
+
same way. With the built-in store the window is one microtask, so it was
|
|
78
|
+
reached only by a send in the same task as the insertion: a script right
|
|
79
|
+
after `append`, a framework ref callback, or a layout effect.
|
|
80
|
+
Now every restore holds the composer until the conversation is drawn:
|
|
81
|
+
- A host's `sendMessage()` waits for the restore, then sends with the
|
|
82
|
+
conversation. Its bubble, its `ag-ui-submit` event and its request all move
|
|
83
|
+
to after the replay, so they arrive later than the call by the length of
|
|
84
|
+
the load. A host that listens on `ag-ui-submit` hears it that much later.
|
|
85
|
+
- If New chat, a thread switch, a change from one `user-key` to another or
|
|
86
|
+
`reload()` comes while the send waits, the send is dropped and resolves
|
|
87
|
+
`false` at once, because the conversation it was addressed to is no longer
|
|
88
|
+
on screen. If the element leaves the page, the send is dropped too, but it
|
|
89
|
+
resolves `false` only once the load answers or the element is inserted
|
|
90
|
+
again. A first `user-key` on an anonymous conversation adopts it rather than
|
|
91
|
+
replacing it, so a send waiting then still goes out. A second
|
|
92
|
+
`sendMessage()` while one waits is refused.
|
|
93
|
+
- The built-in Send queues what is typed during the load, showing it as
|
|
94
|
+
"Waiting to send", and sends it after the conversation is drawn, behind a
|
|
95
|
+
`sendMessage()` that was waiting.
|
|
96
|
+
- `retryLastTurn()` resolves `false` during the load, before it builds a
|
|
97
|
+
client.
|
|
98
|
+
- A checkpoint pick is refused during the load with a new composer hint,
|
|
99
|
+
`continueWhileLoading` ("Wait for the conversation to finish loading, then
|
|
100
|
+
pick a run to continue."). Hosts that translate the UI strings can override
|
|
101
|
+
it in `data-strings`; until they do, it shows in English. `UiStrings` gains
|
|
102
|
+
the key. The `strings` property and `data-strings` take a partial table, so
|
|
103
|
+
only code that builds a complete `UiStrings` object of its own has to add
|
|
104
|
+
it.
|
|
105
|
+
- A restore that will resume a run a navigating tool interrupted keeps
|
|
106
|
+
refusing a host's `sendMessage()`, as in 0.42.0, because that restore ends
|
|
107
|
+
by starting the run.
|
|
108
|
+
|
|
109
|
+
No host opt-in is needed: this applies to every element as soon as it is
|
|
110
|
+
upgraded. A host that sends on mount gets the conversation without changing
|
|
111
|
+
anything. The cost is that the composer queues for as long as each load
|
|
112
|
+
takes, and the load has no timeout of its own, so a request that hangs keeps
|
|
113
|
+
it queueing until the request fails. A store whose load rejects ends the wait
|
|
114
|
+
too, and what waited then goes out into the empty conversation the page is left
|
|
115
|
+
showing, so a custom store that rejects loses its stored copy to that send, as
|
|
116
|
+
a turn typed after the failure always has. The built-in remote store falls back
|
|
117
|
+
to its local cache on a failed fetch instead of rejecting.
|
|
118
|
+
|
|
119
|
+
- **Clearing the conversation while a frontend tool waits no longer leaves a
|
|
120
|
+
"thinking" indicator in the next one.** A handler cannot be aborted, and a
|
|
121
|
+
confirmation card that is open when the run stops is declined, so the call
|
|
122
|
+
resumes after the conversation has been cleared. It then showed the run
|
|
123
|
+
waiting on its next round in whatever conversation had replaced it: the
|
|
124
|
+
indicator's answer group hid the greeting and the starter chips, and nothing
|
|
125
|
+
took it down until the next run there ended. This happened after New chat,
|
|
126
|
+
picking another thread from the drawer, deleting the active thread,
|
|
127
|
+
`reload()` and re-inserting the element, each with a handler still running
|
|
128
|
+
or a confirmation card open. A `user-key` handover already drew nothing there, as of 0.42.0.
|
|
129
|
+
Now the call draws nothing once the conversation it was dispatched in has
|
|
130
|
+
been cleared. Everything else it did is unchanged:
|
|
131
|
+
- Its card still settles as done, failed or declined.
|
|
132
|
+
- A failed navigating tool still clears its checkpoint from the thread it
|
|
133
|
+
was written to.
|
|
134
|
+
- Its result still goes to the run it belonged to, as after a Stop.
|
|
135
|
+
|
|
136
|
+
No host opt-in is needed: this applies to every element as soon as it is
|
|
137
|
+
upgraded, and nothing about the tool registry or its handlers changes.
|
|
138
|
+
|
|
139
|
+
- **A microphone the browser grants but cannot record no longer fails silently.**
|
|
140
|
+
With `data-transcribe-url` set, the mic button asks for the microphone and
|
|
141
|
+
then builds a `MediaRecorder` on the stream. If the browser refuses that, as
|
|
142
|
+
Chromium does with `NotSupportedError` for a stream whose tracks have already
|
|
143
|
+
ended, the click ended in an unhandled promise rejection and the button stayed
|
|
144
|
+
idle with no message. Any track of the stream that was still live kept
|
|
145
|
+
running, so the browser's recording indicator stayed lit, and starting again
|
|
146
|
+
replaced the stream without stopping it. Now a recorder that cannot start is
|
|
147
|
+
treated as a refused microphone: the tracks are released, the button shows
|
|
148
|
+
"Transcription failed" as it does for a denied permission, and it stays idle,
|
|
149
|
+
ready to try again. The message reuses that existing string, so `UiStrings` is
|
|
150
|
+
unchanged.
|
|
151
|
+
|
|
152
|
+
No host opt-in is needed: this applies to every element as soon as it is
|
|
153
|
+
upgraded.
|
|
154
|
+
|
|
155
|
+
### Changed
|
|
156
|
+
|
|
157
|
+
- **`sendMessage()` resolves to whether it sent.** It returns
|
|
158
|
+
`Promise<boolean>` where it returned `Promise<void>`. It resolves `true` once
|
|
159
|
+
the turn was handed to the agent and the interaction settled, and `false`
|
|
160
|
+
when nothing was sent: an empty message, a run or a send already in flight, a
|
|
161
|
+
reload's resume, no `endpoint`, or a send dropped while it waited for a
|
|
162
|
+
restore. Code that ignores the result behaves as before, but type-checking
|
|
163
|
+
breaks wherever `sendMessage` is annotated, implemented, overridden or
|
|
164
|
+
replaced as returning `Promise<void>`: a wrapper's ref type declaring it, a
|
|
165
|
+
subclass override, and a test double that replaces it on the element all have
|
|
166
|
+
to return a boolean to type-check.
|
|
167
|
+
|
|
10
168
|
## [0.42.0] — 2026-10-07
|
|
11
169
|
|
|
12
170
|
### Fixed
|
|
@@ -4424,7 +4582,9 @@ hosts that both arrange the page the way it expects.
|
|
|
4424
4582
|
### Notes
|
|
4425
4583
|
- First release — exercising the automated npm OIDC publish pipeline end-to-end.
|
|
4426
4584
|
|
|
4427
|
-
[Unreleased]: https://github.com/Artui/ag-ui-web-component/compare/v0.
|
|
4585
|
+
[Unreleased]: https://github.com/Artui/ag-ui-web-component/compare/v0.44.0...HEAD
|
|
4586
|
+
[0.44.0]: https://github.com/Artui/ag-ui-web-component/compare/v0.43.0...v0.44.0
|
|
4587
|
+
[0.43.0]: https://github.com/Artui/ag-ui-web-component/compare/v0.42.0...v0.43.0
|
|
4428
4588
|
[0.42.0]: https://github.com/Artui/ag-ui-web-component/compare/v0.41.3...v0.42.0
|
|
4429
4589
|
[0.41.3]: https://github.com/Artui/ag-ui-web-component/compare/v0.41.2...v0.41.3
|
|
4430
4590
|
[0.41.2]: https://github.com/Artui/ag-ui-web-component/compare/v0.41.1...v0.41.2
|
package/README.md
CHANGED
|
@@ -199,7 +199,7 @@ another origin, add `credentials="include"` too; see
|
|
|
199
199
|
| `data-unread-badge` | — | **On by default.** `="false"` hides the launcher's unread badge; the count and the `ag-ui-unread` event keep running. See [Collapsing to the launcher](#collapsing-to-the-launcher). |
|
|
200
200
|
| `data-launcher-drag` | — | **On by default.** `="false"` leaves the widget wherever your CSS puts it. Otherwise the collapsed launcher can be dragged anywhere on screen (the panel opens into the clearest space) and the open panel can be dragged by its header. See [Moving the launcher](#moving-the-launcher) and [Moving the panel](#moving-the-panel). |
|
|
201
201
|
| `data-quote-selection` | — | **On by default.** `="false"` stops the transcript offering to quote a selection. `quote()` keeps working either way. See [Quoting a selection](#quoting-a-selection). |
|
|
202
|
-
| `data-message-actions` | — | **
|
|
202
|
+
| `data-message-actions` | — | **Copy and retry by default; feedback is opt-in.** A comma list of the actions a finished answer keeps: `copy` / `retry` / `feedback` (e.g. `"copy,retry,feedback"` to add the rating pair, which needs an `ag-ui-feedback` listener). `="false"` removes the row entirely. See [Message actions](#message-actions-copy-retry-feedback). |
|
|
203
203
|
| `data-max-tool-rounds` | — | Upper bound on frontend tool-call → re-run rounds within one send (default 10; a value below 1 is ignored). Raise it for a page-driving agent whose turn takes many small steps. See [The run loop](#the-run-loop-and-the-ag-ui-client). |
|
|
204
204
|
| `data-page-actions` | — | Opt-in built-in page-action tools: a comma list of `scroll` / `drag` / `chat` (e.g. `"scroll,drag"`). See [Page-action tools](#page-action-tools). |
|
|
205
205
|
| `data-side` | — | CSS-only, for `placement="sidebar"`: which edge it docks to — `right` (default) / `left`. |
|
|
@@ -305,23 +305,49 @@ the `copyCode` / `copied` / `copyFailed` strings.
|
|
|
305
305
|
|
|
306
306
|
`sendMessage(content, attachments?)` sends as if the user had typed it — user bubble,
|
|
307
307
|
`ag-ui-submit` event, run started. Use it for an "Ask about this order" button, a command
|
|
308
|
-
palette,
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
308
|
+
palette, a prompt fired when your page mounts the chat, or a composer of your own replacing the
|
|
309
|
+
built-in one.
|
|
310
|
+
|
|
311
|
+
It returns a `Promise<boolean>`. It resolves `true` once the turn has been handed to the agent
|
|
312
|
+
and its run has settled, frontend-tool rounds included, and `false` when nothing was sent:
|
|
313
|
+
|
|
314
|
+
- for an entirely empty message;
|
|
315
|
+
- while a run, or a checkpoint continuation picked from the panel, is in flight. A continuation
|
|
316
|
+
counts from the pick, and a send from the call rather than from when its run starts, so a
|
|
317
|
+
`sendMessage` from an `ag-ui-submit` listener, or a second call in the same task, is refused
|
|
318
|
+
rather than starting a second run;
|
|
319
|
+
- with no `endpoint` set, which the transcript also says;
|
|
320
|
+
- while a reload resumes a run a navigating tool interrupted. The resume counts from the moment
|
|
321
|
+
the element asks its conversation store for the conversation, and it ends by starting that
|
|
322
|
+
run, so a send then is refused, and the built-in Send queues behind the resumed answer;
|
|
323
|
+
- when your own code that runs on the way to the request — an `ag-ui-submit` listener, the
|
|
324
|
+
conversation store's save — stops it by starting a new chat or changing `user-key`. A
|
|
325
|
+
`getContext` or `getTools` that does the same stops it too, but after the turn was handed
|
|
326
|
+
over, so the call resolves `true`.
|
|
327
|
+
|
|
328
|
+
**While the element restores a stored conversation, a send waits for it.** Every restore —
|
|
329
|
+
when the element connects, on `reload()`, when a thread is picked from the drawer, and when
|
|
330
|
+
`user-key` changes from one principal to another — first asks the conversation store for the conversation, and with
|
|
331
|
+
`data-threads-url` that is a request to your server. A `sendMessage` made in that time waits
|
|
332
|
+
until the conversation is drawn, then sends with it: the bubble, the `ag-ui-submit` event and the
|
|
333
|
+
request all come after the replay, later than the call by the length of the load. So a prompt
|
|
334
|
+
fired on mount needs no timing of its own. If New chat, a thread switch, a change from one
|
|
335
|
+
`user-key` to another or a `reload()` comes while it waits, the conversation it was addressed
|
|
336
|
+
to is gone, so it is dropped and resolves `false` at once. The element leaving the page drops it
|
|
337
|
+
too, but it resolves `false` only once the load answers or the element is inserted again. A
|
|
338
|
+
first `user-key` on an anonymous conversation adopts that conversation rather than replacing
|
|
339
|
+
it, so a send waiting then still goes out. A second `sendMessage` while one waits is refused. The built-in Send queues what is typed during the load ("Waiting to send"),
|
|
340
|
+
and sends it after the conversation is drawn, behind a `sendMessage` that was waiting.
|
|
341
|
+
|
|
342
|
+
Unlike the built-in Send it does **not** queue, so your composer keeps what it tried to send, and
|
|
343
|
+
it does **not** consult the attachment tray: what you pass is what is sent, so your composer stays
|
|
320
344
|
in charge of its own state.
|
|
321
345
|
|
|
322
346
|
`retryLastTurn()` keeps the same rules, because it starts a run on the same conversation. It
|
|
323
|
-
returns `false` while a run, a send, another Retry
|
|
324
|
-
|
|
347
|
+
returns `false` while a run, a send, another Retry or a continuation is in flight, and while
|
|
348
|
+
any restore of the stored conversation is still loading. It does not wait for the load as a send
|
|
349
|
+
does: until the conversation is drawn there is nothing on screen to retry, and afterwards it
|
|
350
|
+
would ask the previous visit's last question again. It counts from the call as a send does: until its run starts, a `sendMessage` or a
|
|
325
351
|
second Retry is refused, the built-in Send queues, and a checkpoint pick is refused. It also
|
|
326
352
|
returns `false` when your own code stops it before its run starts: a conversation store, or an
|
|
327
353
|
activity or tool renderer it redraws through, that starts a new chat or changes `user-key`. A
|
|
@@ -499,9 +525,10 @@ function Assistant() {
|
|
|
499
525
|
|
|
500
526
|
Writing it as `<ag-ui-chat ref={...} />` in JSX and configuring in the ref callback mostly works —
|
|
501
527
|
the catalog requests are held back one microtask precisely so a ref assigned in the same commit is
|
|
502
|
-
honoured — but the thread-history request is **not** deferred
|
|
503
|
-
|
|
504
|
-
|
|
528
|
+
honoured — but the thread-history request is **not** deferred, so that one goes out with whatever
|
|
529
|
+
was configured at insertion. It starts at insertion because the restore is what a `sendMessage()`
|
|
530
|
+
from the same commit waits on: a restore that had not started yet would have nothing to hold it,
|
|
531
|
+
and the send would go out without the conversation.
|
|
505
532
|
|
|
506
533
|
If your credentials can only arrive later still — an awaited token, a passive effect — call
|
|
507
534
|
**`reload()`** once they land:
|
|
@@ -1885,13 +1912,46 @@ still takes its margin and still announces itself as a group of actions.
|
|
|
1885
1912
|
`retryLastTurn()` is public, for a host driving its own message UI.
|
|
1886
1913
|
|
|
1887
1914
|
```js
|
|
1915
|
+
await chat.retryLastTurn(); // false when there is nothing to ask again, a run is in flight, or a restore is loading
|
|
1916
|
+
```
|
|
1917
|
+
|
|
1918
|
+
### Storing a rating
|
|
1919
|
+
|
|
1920
|
+
`ag-ui-feedback` names the message that was rated, so a host can file the
|
|
1921
|
+
rating against it rather than against its text — two answers can read the same,
|
|
1922
|
+
and nothing about the text survives a reload:
|
|
1923
|
+
|
|
1924
|
+
| Field | Type | What it is |
|
|
1925
|
+
| --- | --- | --- |
|
|
1926
|
+
| `messageId` | `string` | The AG-UI id of the rated assistant message: the id it streamed under (`TEXT_MESSAGE_START` / `TEXT_MESSAGE_END`), or the stored message's `id` when it was restored from history. A restored answer whose store returned no id offers no rating pair, since a rating has to name a message. |
|
|
1927
|
+
| `threadId` | `string` | The conversation it belongs to: the element's thread when the message was drawn. |
|
|
1928
|
+
| `runId` | `string`, optional | The run that streamed it, as that run's `RUN_STARTED` named it. Text a sub-agent streams through the parent run carries the parent's `runId`. **Absent on a restored message** — an AG-UI message carries no run id, so there is none to give and the key is left out rather than guessed. |
|
|
1929
|
+
| `content` | `string` | The message's text, as rendered. |
|
|
1930
|
+
| `rating` | `"up"` \| `"down"` | Which button. |
|
|
1931
|
+
|
|
1932
|
+
```js
|
|
1933
|
+
chat.setAttribute("data-message-actions", "copy,retry,feedback");
|
|
1888
1934
|
chat.addEventListener("ag-ui-feedback", (e) => {
|
|
1889
|
-
|
|
1935
|
+
const { messageId, threadId, runId, rating } = e.detail;
|
|
1936
|
+
// Keyed by message: rating the same answer again replaces the earlier rating.
|
|
1937
|
+
fetch(`/api/threads/${threadId}/ratings/${messageId}/`, {
|
|
1938
|
+
method: "PUT",
|
|
1939
|
+
headers: { "Content-Type": "application/json" },
|
|
1940
|
+
body: JSON.stringify({ rating, ...(runId === undefined ? {} : { runId }) }),
|
|
1941
|
+
});
|
|
1890
1942
|
});
|
|
1891
|
-
|
|
1892
|
-
await chat.retryLastTurn(); // false when there is nothing to ask again, or a run is in flight
|
|
1893
1943
|
```
|
|
1894
1944
|
|
|
1945
|
+
> [!NOTE]
|
|
1946
|
+
> **A restored message is named by whatever `id` the conversation store returns
|
|
1947
|
+
> for it.** The built-in browser store saves the conversation as it streamed, so
|
|
1948
|
+
> there a message keeps its id across a reload. With `data-threads-url` or a
|
|
1949
|
+
> `conversationStore` of your own, the conversation comes back from your server,
|
|
1950
|
+
> and a message keeps its id only if the server persists the ids it streamed:
|
|
1951
|
+
> one that stores the conversation under fresh ids makes a rating made live and a
|
|
1952
|
+
> rating made after a reload name the same answer by two different ids. Live,
|
|
1953
|
+
> the id is always the one on the wire.
|
|
1954
|
+
|
|
1895
1955
|
## Quoting a selection
|
|
1896
1956
|
|
|
1897
1957
|
Select any text in the transcript and a small **Quote** offer floats beside it.
|
|
@@ -2481,6 +2541,11 @@ stop it, and what you typed stays in the composer for the pick after. The run in
|
|
|
2481
2541
|
flight is not cancelled for you, since a continuation starting over it would
|
|
2482
2542
|
leave two answers streaming into one transcript with Stop reaching only one.
|
|
2483
2543
|
|
|
2544
|
+
Picking a row while a stored conversation is still loading is refused the same
|
|
2545
|
+
way, with a hint asking you to wait for the load: a continuation sends only the
|
|
2546
|
+
new turn, so it must not start before the history it continues is on screen. The
|
|
2547
|
+
wording is the `continueWhileLoading` string.
|
|
2548
|
+
|
|
2484
2549
|
### One URL, three endpoints
|
|
2485
2550
|
|
|
2486
2551
|
`data-runs-url` is the only thing to configure. `resume/<id>/` and `fork/<id>/`
|
|
@@ -2623,6 +2688,7 @@ re-export point. Internal modules import from leaf paths.
|
|
|
2623
2688
|
| `AgUiClient` | class | Orchestration layer over an AG-UI `AbstractAgent`. |
|
|
2624
2689
|
| `AgUiClientConfig` / `AgUiClientHandlers` / `AgUiRunInputs` | type | Client config, lifecycle handlers, per-run input providers. |
|
|
2625
2690
|
| `AgUiToolCall` / `ToolExecution` / `ExecuteTool` | type | Tool-call shape, execution result, executor signature. |
|
|
2691
|
+
| `AgUiMessageIds` | type | Which message `AgUiClientHandlers.onTextEnd` completed: its `messageId`, and the `runId` its `RUN_STARTED` named. |
|
|
2626
2692
|
| `ToolOutcome` | type | The `TOOL_OUTCOME` values as a union. |
|
|
2627
2693
|
| `toolStatusFromOutcome` | function | Map a wire outcome to the card status it settles into; anything absent or unrecognised is a success. |
|
|
2628
2694
|
| `ConnectionLostError` | class | Raised (→ `onError`) when a run's stream closes with no terminal AG-UI event. |
|
|
@@ -2668,7 +2734,7 @@ re-export point. Internal modules import from leaf paths.
|
|
|
2668
2734
|
| `RunFinishedDetail` / `ToolRun` | type | `ag-ui-run-finished` detail: the tools an interaction ran, and which side ran them. |
|
|
2669
2735
|
| `CustomAgentDetail` | type | `ag-ui-custom` detail: an AG-UI `CUSTOM` event's `name` and `value`, verbatim. |
|
|
2670
2736
|
| `InvalidateDetail` | type | `ag-ui-invalidate` detail: the resource `keys` that moved, and the `reason`. |
|
|
2671
|
-
| `FeedbackDetail` | type | `ag-ui-feedback` detail: the rated message's `content` and the `rating`. |
|
|
2737
|
+
| `FeedbackDetail` | type | `ag-ui-feedback` detail: the rated message's `messageId`, its `threadId`, the `runId` that streamed it (live only), its `content` and the `rating`. See [Storing a rating](#storing-a-rating). |
|
|
2672
2738
|
| `relativeTime` | function | The built-in locale-neutral timestamp formatter (`"5m ago"`), for a host building on it. |
|
|
2673
2739
|
| `RelativeTimeFormatter` | type | What `formatRelativeTime` takes: an epoch-ms timestamp in, row text out. |
|
|
2674
2740
|
| `renderSuggestionChips` | function | Draw a `suggestions` activity as chips that send themselves; `null` when nothing survives. |
|