@artooi/ag-ui-web-component 0.23.0 → 0.24.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 +149 -36
- package/README.md +140 -9
- package/dist/ag-ui-web-component.bundle.js +113 -122
- package/dist/ag-ui-web-component.bundle.js.map +3 -3
- package/dist/constants.d.ts +71 -85
- package/dist/constants.d.ts.map +1 -1
- package/dist/core/ag_ui_chat.d.ts +105 -127
- package/dist/core/ag_ui_chat.d.ts.map +1 -1
- package/dist/core/agui_client.d.ts +24 -30
- package/dist/core/agui_client.d.ts.map +1 -1
- package/dist/core/attachment.d.ts +9 -14
- package/dist/core/attachment.d.ts.map +1 -1
- package/dist/core/conversation_store.d.ts +38 -27
- package/dist/core/conversation_store.d.ts.map +1 -1
- package/dist/core/create_http_agent.d.ts +13 -15
- package/dist/core/create_http_agent.d.ts.map +1 -1
- package/dist/core/remote_conversation_store.d.ts +10 -9
- package/dist/core/remote_conversation_store.d.ts.map +1 -1
- package/dist/core/run_index.d.ts +11 -20
- package/dist/core/run_index.d.ts.map +1 -1
- package/dist/core/transcribe_audio.d.ts +8 -8
- package/dist/core/transcribe_audio.d.ts.map +1 -1
- package/dist/core/upload_attachment.d.ts +15 -18
- package/dist/core/upload_attachment.d.ts.map +1 -1
- package/dist/core/utils.d.ts +4 -6
- package/dist/core/utils.d.ts.map +1 -1
- package/dist/dom/animations.d.ts +22 -30
- package/dist/dom/animations.d.ts.map +1 -1
- package/dist/dom/dom_driver.d.ts +7 -7
- package/dist/dom/native_setter.d.ts +2 -2
- package/dist/dom/native_setter.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +357 -383
- package/dist/index.js.map +2 -2
- package/dist/skills/fill_template.d.ts +4 -5
- package/dist/skills/fill_template.d.ts.map +1 -1
- package/dist/skills/parse_skills.d.ts.map +1 -1
- package/dist/skills/skill.d.ts +7 -8
- package/dist/skills/skill.d.ts.map +1 -1
- package/dist/tools/client_tool_registry.d.ts +2 -2
- package/dist/tools/page_action_tools.d.ts +7 -10
- package/dist/tools/page_action_tools.d.ts.map +1 -1
- package/dist/tools/page_state.d.ts +5 -8
- package/dist/tools/page_state.d.ts.map +1 -1
- package/dist/tools/route_map.d.ts +7 -10
- package/dist/tools/route_map.d.ts.map +1 -1
- package/dist/ui/approval_card.d.ts +15 -20
- package/dist/ui/approval_card.d.ts.map +1 -1
- package/dist/ui/attach_copy_buttons.d.ts +4 -10
- package/dist/ui/attach_copy_buttons.d.ts.map +1 -1
- package/dist/ui/attachment_chips.d.ts +7 -10
- package/dist/ui/attachment_chips.d.ts.map +1 -1
- package/dist/ui/attachment_tray.d.ts +6 -6
- package/dist/ui/checkpoint_menu.d.ts +7 -8
- package/dist/ui/checkpoint_menu.d.ts.map +1 -1
- package/dist/ui/confirmation_card.d.ts +10 -15
- package/dist/ui/confirmation_card.d.ts.map +1 -1
- package/dist/ui/question_card.d.ts +12 -15
- package/dist/ui/question_card.d.ts.map +1 -1
- package/dist/ui/relative_time.d.ts +5 -7
- package/dist/ui/relative_time.d.ts.map +1 -1
- package/dist/ui/render_markdown.d.ts +8 -8
- package/dist/ui/render_markdown.d.ts.map +1 -1
- package/dist/ui/resize_handle.d.ts +21 -34
- package/dist/ui/resize_handle.d.ts.map +1 -1
- package/dist/ui/run_notice.d.ts +5 -7
- package/dist/ui/run_notice.d.ts.map +1 -1
- package/dist/ui/skills_menu.d.ts +4 -5
- package/dist/ui/skills_menu.d.ts.map +1 -1
- package/dist/ui/styles.d.ts +1 -1
- package/dist/ui/styles.d.ts.map +1 -1
- package/dist/ui/thoughts_block.d.ts +9 -11
- package/dist/ui/thoughts_block.d.ts.map +1 -1
- package/dist/ui/thread_drawer.d.ts +6 -5
- package/dist/ui/thread_drawer.d.ts.map +1 -1
- package/dist/ui/tool_call_card.d.ts +17 -25
- package/dist/ui/tool_call_card.d.ts.map +1 -1
- package/dist/ui/ui_strings.d.ts +6 -12
- package/dist/ui/ui_strings.d.ts.map +1 -1
- package/dist/ui/voice_input.d.ts +10 -11
- package/dist/ui/voice_input.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/constants.ts +74 -87
- package/src/core/ag_ui_chat.ts +313 -269
- package/src/core/agui_client.ts +60 -71
- package/src/core/attachment.ts +9 -14
- package/src/core/conversation_store.ts +55 -33
- package/src/core/create_http_agent.ts +18 -22
- package/src/core/remote_conversation_store.ts +28 -15
- package/src/core/run_index.ts +14 -23
- package/src/core/transcribe_audio.ts +9 -10
- package/src/core/upload_attachment.ts +18 -21
- package/src/core/utils.ts +4 -6
- package/src/dom/animations.ts +33 -43
- package/src/dom/dom_driver.ts +7 -7
- package/src/dom/native_setter.ts +11 -12
- package/src/index.ts +3 -0
- package/src/skills/fill_template.ts +4 -5
- package/src/skills/parse_skills.ts +3 -4
- package/src/skills/skill.ts +7 -8
- package/src/tools/client_tool_registry.ts +2 -2
- package/src/tools/page_action_tools.ts +12 -15
- package/src/tools/page_state.ts +5 -8
- package/src/tools/route_map.ts +15 -19
- package/src/ui/approval_card.ts +15 -20
- package/src/ui/attach_copy_buttons.ts +9 -18
- package/src/ui/attachment_chips.ts +7 -10
- package/src/ui/attachment_tray.ts +6 -6
- package/src/ui/checkpoint_menu.ts +7 -8
- package/src/ui/confirmation_card.ts +10 -15
- package/src/ui/question_card.ts +12 -15
- package/src/ui/relative_time.ts +5 -7
- package/src/ui/render_markdown.ts +25 -51
- package/src/ui/resize_handle.ts +25 -38
- package/src/ui/run_notice.ts +9 -12
- package/src/ui/skills_menu.ts +4 -5
- package/src/ui/styles.ts +92 -101
- package/src/ui/thoughts_block.ts +11 -13
- package/src/ui/thread_drawer.ts +6 -5
- package/src/ui/tool_call_card.ts +22 -32
- package/src/ui/ui_strings.ts +6 -12
- package/src/ui/voice_input.ts +10 -11
- package/src/version.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,117 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.24.0] — 2026-08-13
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`ag-ui-run-finished`** — an event fired once per interaction, carrying the
|
|
15
|
+
tools that ran and which side ran each (`{ tools: [{ name, side }] }`, typed
|
|
16
|
+
`RunFinishedDetail` / `ToolRun`). **For hosts that render data the agent can
|
|
17
|
+
change.** A server-side tool writes without the page's knowledge, and nothing
|
|
18
|
+
the element dispatched implied "something may have moved underneath you": a
|
|
19
|
+
page that fetched its data on mount had no reason to refetch, so approving a
|
|
20
|
+
server-side write left it showing stale data with no way to notice. Shared
|
|
21
|
+
state was the only channel back, and it is not one a host can rely on, because
|
|
22
|
+
it needs the *agent* to emit `STATE_SNAPSHOT`. Fires on completion, error and
|
|
23
|
+
cancellation alike, since a partial write is still a write; a capability load
|
|
24
|
+
is not counted, since it moves nothing a host renders.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- **A server-side approval can now ask a readable question.** An AG-UI
|
|
29
|
+
interrupt's question defaults to the call spelled out —
|
|
30
|
+
`Approve create_event({"title": "Design sync", …})?` — which is accurate and
|
|
31
|
+
not something to put in front of a person, while the *client-side*
|
|
32
|
+
confirmation card has had `x-confirm` for exactly this. The approval card now
|
|
33
|
+
prefers `x-confirm` from the interrupt's `metadata`, so one key covers both
|
|
34
|
+
gates, and a server that supplies nothing keeps the generated text. Anything
|
|
35
|
+
non-string or blank under that key is ignored rather than rendered, since a
|
|
36
|
+
wire field typed `Record<string, any>` can carry an object into the one place
|
|
37
|
+
a person is being asked to allow a write.
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
|
|
41
|
+
- **Every first visit spent a request to be told `404`.** With
|
|
42
|
+
`data-threads-url` set, the element minted a thread id on mount and
|
|
43
|
+
immediately asked the server for its history — history that cannot exist,
|
|
44
|
+
because the id was three lines old. The response was correct and harmless, and
|
|
45
|
+
it put a red `404` in the console of a page where nothing had gone wrong, on
|
|
46
|
+
every first visit to every host. `ClientConversationStore` gained an optional
|
|
47
|
+
`isUnsent(threadId)`, which the session store answers from a marker it sets when
|
|
48
|
+
it mints and drops on the first save, and the remote store skips the fetch when
|
|
49
|
+
it is `true`. Deliberately narrow: *"I hold no messages for this id"* is not the
|
|
50
|
+
same claim as *"this id is new"*, and only the store that minted it can make the
|
|
51
|
+
second one — so a thread picked from the drawer, or created on another device,
|
|
52
|
+
is still fetched.
|
|
53
|
+
- **An approved call's tool card broke its own name into pieces.** The card's
|
|
54
|
+
head is a flex row in which the name is the only flexible child, so every
|
|
55
|
+
fixed badge the row gains is taken out of it. The decision badge ("approved by
|
|
56
|
+
you") appears only on the server-approval path, and in a sidebar-width panel it
|
|
57
|
+
left the name **37px** wide: `word-break: break-word` then split *Create event*
|
|
58
|
+
into "Creat / e / event" across three lines. The head wraps now and the name
|
|
59
|
+
keeps a floor instead of a zero min-width, so a badge drops to its own row
|
|
60
|
+
rather than shredding a word, while a genuinely unbreakable name still breaks
|
|
61
|
+
instead of overflowing the card. Measured in a real browser at 470px: the name
|
|
62
|
+
went 37px to 144px and three lines to one.
|
|
63
|
+
|
|
64
|
+
### Documentation
|
|
65
|
+
|
|
66
|
+
- **The four framework recipes, not just React's.** The connect-time
|
|
67
|
+
configuration boundary is reached differently by each host, and only **Vue**
|
|
68
|
+
has a hook that runs before insertion (a directive's `beforeMount`); React,
|
|
69
|
+
Svelte 5 and Angular all create the element by hand. Angular additionally needs
|
|
70
|
+
`:host { display: contents }` or its own host element breaks the page's layout.
|
|
71
|
+
- **Page actions**: a page action reports that it *fired*, not that it worked;
|
|
72
|
+
a page that saves asynchronously should say so (a `saving` flag in the page
|
|
73
|
+
map) or a verification read will outrun the save; `drag_and_drop` dispatches
|
|
74
|
+
the **native HTML5 drag sequence**, which a pointer-event drag library
|
|
75
|
+
(dnd-kit, the Angular CDK) never sees; and `scroll_to` centres vertically but
|
|
76
|
+
brings into view horizontally.
|
|
77
|
+
- **`placement="embedded"` fills the box the host gives it — so give it one.**
|
|
78
|
+
`min-height: 0` plus `overflow: hidden` on the containing element, or a growing
|
|
79
|
+
transcript pushes the composer off the bottom of the window.
|
|
80
|
+
- **The auto-injected page map may go nowhere.** It rides in
|
|
81
|
+
`RunAgentInput.context`, and pydantic-ai's AG-UI adapter does not read that
|
|
82
|
+
field, so on such a backend the injected copy is silently dropped and
|
|
83
|
+
`read_page` is the channel that works.
|
|
84
|
+
|
|
85
|
+
## [0.23.1] — 2026-08-13
|
|
86
|
+
|
|
87
|
+
### Fixed
|
|
88
|
+
|
|
89
|
+
- **A reloaded conversation lost every turn after the first plain answer.**
|
|
90
|
+
Restoring stored history iterated `message.toolCalls` behind an
|
|
91
|
+
`!== undefined` check, and an assistant turn that called no tool can arrive
|
|
92
|
+
carrying `null`: the protocol's Python models declare
|
|
93
|
+
`tool_calls: list[ToolCall] | None`, so a server dumping them without
|
|
94
|
+
`exclude_none` sends exactly that, while `@ag-ui/core` types the field
|
|
95
|
+
optional-and-not-nullable — TypeScript therefore offered no hint that the value
|
|
96
|
+
was reachable. Iterating it threw `TypeError: toolCalls is not iterable`
|
|
97
|
+
*inside* the replay, which aborted at that message and left every later turn
|
|
98
|
+
out of the transcript. Nothing surfaced: no error state, no partial-history
|
|
99
|
+
notice, just a short conversation. Measured against a server-backed store, a
|
|
100
|
+
54-message thread restored as two bubbles and one tool card.
|
|
101
|
+
|
|
102
|
+
Two things follow from where the throw happened, and both are now closed.
|
|
103
|
+
Tool calls are **narrowed rather than trusted** — the same stance
|
|
104
|
+
`messageAttachments` already takes for the neighbouring field on the same
|
|
105
|
+
message, and for the same reason. And a **shapeless entry is skipped instead of
|
|
106
|
+
ending the replay**: a stored call with no `id`, no `function.name`, or a
|
|
107
|
+
`null` where an object belongs no longer costs the rest of the conversation.
|
|
108
|
+
A call whose `arguments` are missing or not a string still renders, with empty
|
|
109
|
+
arguments, because its name is the part worth showing.
|
|
110
|
+
|
|
111
|
+
Only a **server-backed** history reaches this: the default
|
|
112
|
+
`SessionStorageStore` round-trips client-shaped objects through JSON, where an
|
|
113
|
+
absent field stays absent, so the `null` never appears. Anything reading a
|
|
114
|
+
thread index — `data-threads-url` — did, including every Django admin
|
|
115
|
+
configured with a conversation store. There the cost was worse than a short
|
|
116
|
+
transcript: each navigating tool reloads the page and the run continues only
|
|
117
|
+
because the component rehydrates and completes the pending call, so a thread
|
|
118
|
+
that had answered once abandoned its next multi-step task at the first
|
|
119
|
+
navigation.
|
|
120
|
+
|
|
10
121
|
## [0.23.0] — 2026-08-12
|
|
11
122
|
|
|
12
123
|
Attachment chips, which turned out to be the least finished corner of 0.22.
|
|
@@ -206,7 +317,7 @@ hosts that both arrange the page the way it expects.
|
|
|
206
317
|
`overflow: hidden` ancestor sharing the element's box — a card, a table cell —
|
|
207
318
|
clipped it entirely while the tool reported success. And 200 ms is below the
|
|
208
319
|
threshold at which someone who does not know where to look notices anything.
|
|
209
|
-
|
|
320
|
+
Neither our tests nor the consumer's could have found the second one:
|
|
210
321
|
headless Chromium hides the scroll race, and no automated check has an opinion
|
|
211
322
|
about whether a human sees a 200 ms ring.
|
|
212
323
|
|
|
@@ -222,7 +333,7 @@ hosts that both arrange the page the way it expects.
|
|
|
222
333
|
explicit-duration contract, and the docs now say so instead of overclaiming.
|
|
223
334
|
|
|
224
335
|
- **The tool and skill catalog fetches are deferred by one microtask**, so a React
|
|
225
|
-
`ref` assigned in the same commit as insertion is honoured.
|
|
336
|
+
`ref` assigned in the same commit as insertion is honoured. The thread-history
|
|
226
337
|
request is deliberately **not** deferred: a deferred replay can land after a
|
|
227
338
|
`sendMessage()` and duplicate the transcript. Configure before you insert, or
|
|
228
339
|
call `reload()`; the new React recipe in the README shows both.
|
|
@@ -234,7 +345,7 @@ hosts that both arrange the page the way it expects.
|
|
|
234
345
|
`data-resize-anchor="<y>-<x>"`, but the rules meant to read it were written as
|
|
235
346
|
`[data-resize-anchor~="left"]` — and `~=` matches whitespace-separated words
|
|
236
347
|
while the stamped value is a single hyphenated token, so they could never
|
|
237
|
-
match.
|
|
348
|
+
match. **The cursor rules used `=` and did match**, so the pointer followed
|
|
238
349
|
the measurement while the grip stayed where `placement` had guessed: for any
|
|
239
350
|
host that aligns the panel the other way, the cursor promised a diagonal the
|
|
240
351
|
grip was not on, and the grip sat on the corner that stays put.
|
|
@@ -260,14 +371,14 @@ hosts that both arrange the page the way it expects.
|
|
|
260
371
|
|
|
261
372
|
### Fixed
|
|
262
373
|
|
|
263
|
-
-
|
|
374
|
+
- **`placement="side"` (and `sidebar`) stopped being full height once the
|
|
264
375
|
panel had been resized.** A dragged size is written as a custom property on
|
|
265
376
|
the host, and an inline custom property **outranks a `:host([placement=…])`
|
|
266
377
|
rule setting the same property** — so a height dragged while floating capped a
|
|
267
378
|
docked sidebar that had asked for `100vh`. Since the size persists per tab,
|
|
268
379
|
one drag broke every later visit.
|
|
269
380
|
|
|
270
|
-
|
|
381
|
+
**The previous release claimed this was already handled, and the reasoning
|
|
271
382
|
was wrong.** Writing `--ag-ui-height` rather than inline `height` was supposed
|
|
272
383
|
to leave placement with the final say; it does not, because the indirection
|
|
273
384
|
changes nothing about the cascade. The fix is explicit rather than
|
|
@@ -291,12 +402,12 @@ hosts that both arrange the page the way it expects.
|
|
|
291
402
|
handle at all (a `100vw`/`100vh` layout has nothing to drag), `sidebar` /
|
|
292
403
|
`side` get width only, everything else gets both.
|
|
293
404
|
|
|
294
|
-
|
|
405
|
+
**It writes the custom properties, not inline `width` / `height`.** The
|
|
295
406
|
placement rules set those same properties, so an inline dimension would
|
|
296
407
|
outrank them — a panel dragged while floating would keep that width after
|
|
297
408
|
switching to fullscreen.
|
|
298
409
|
|
|
299
|
-
|
|
410
|
+
**Which corner the grip sits on is measured, not assumed.** A resize is
|
|
300
411
|
computed from the edge that stays still, and which edge that is belongs to the
|
|
301
412
|
*host's* layout rather than to `placement` — a floating panel is pinned
|
|
302
413
|
bottom-right, an embedded one goes wherever the page's CSS puts it. Deriving
|
|
@@ -320,7 +431,7 @@ hosts that both arrange the page the way it expects.
|
|
|
320
431
|
palette — a lone `/` — was left in the composer, which said nothing about what
|
|
321
432
|
the skill wanted or how to supply it. The hint now says what to do, too.
|
|
322
433
|
|
|
323
|
-
-
|
|
434
|
+
- **Picking a skill now sends it.** It used to write the text into the
|
|
324
435
|
composer and wait for a second click unless the skill set
|
|
325
436
|
`sendImmediately: true` — so the default behaviour of a shortcut was to not
|
|
326
437
|
take the shortcut. Set `sendImmediately: false` to keep pre-filling, which is
|
|
@@ -331,7 +442,7 @@ hosts that both arrange the page the way it expects.
|
|
|
331
442
|
sends the bare `/name` token and the agent expands it, from the harness
|
|
332
443
|
`Skills` capability or the server's own instructions.
|
|
333
444
|
|
|
334
|
-
|
|
445
|
+
**The prompt was the leak.** A catalog is either a fetched `GET` or an
|
|
335
446
|
inline `data-skills` attribute sitting in the page source, and a skill is
|
|
336
447
|
often where a project's internal workflow is written down most plainly — so
|
|
337
448
|
the client-side catalog published it to anyone who opened the page. Sending a
|
|
@@ -355,10 +466,10 @@ hosts that both arrange the page the way it expects.
|
|
|
355
466
|
`part` (`tool-card-args` / `tool-card-result`, headings via
|
|
356
467
|
`tool-card-section-label`, body via `tool-card-body`), and are pretty-printed.
|
|
357
468
|
|
|
358
|
-
|
|
469
|
+
Breaking for anyone styling `tool-card-result` as a single combined block.
|
|
359
470
|
A call with no arguments no longer renders an empty `{}` in a box of its own.
|
|
360
471
|
|
|
361
|
-
-
|
|
472
|
+
- **`data-tool-display` is now live.** Changing it restyles every card already
|
|
362
473
|
in the transcript, the way `data-answer-well` always has. The modes are pure
|
|
363
474
|
visibility over **one DOM shape**, selected by the shadow CSS from the host
|
|
364
475
|
attribute; previously each card baked its structure at construction from the
|
|
@@ -373,7 +484,7 @@ hosts that both arrange the page the way it expects.
|
|
|
373
484
|
different objects: the record is the tool card it gates, which settles to the
|
|
374
485
|
outcome and scrolls with the rest of the transcript.
|
|
375
486
|
|
|
376
|
-
-
|
|
487
|
+
- **The confirmation card was appended to the wrong parent**, and it is the
|
|
377
488
|
reason it drifted to the foot of a turn. Every other inline card — tool,
|
|
378
489
|
approval, `ask_user`, run notices — goes into the turn's answer group; this
|
|
379
490
|
one went into the message list, so it became a sibling *after* the group and
|
|
@@ -390,7 +501,7 @@ hosts that both arrange the page the way it expects.
|
|
|
390
501
|
gated, and the server-side gate left nothing at all even though it is the one
|
|
391
502
|
guarding tools that run on the backend.
|
|
392
503
|
|
|
393
|
-
|
|
504
|
+
**Session-scoped, like the "run interrupted" notice.** AG-UI carries no
|
|
394
505
|
approval message — the answer rides `resume[]` as transient run input — so a
|
|
395
506
|
reload restores the call and its result but not the note. Durable "who
|
|
396
507
|
approved what" is an audit concern, not a transcript one.
|
|
@@ -415,7 +526,7 @@ hosts that both arrange the page the way it expects.
|
|
|
415
526
|
entry is what gets persisted. The protocol has no rule to enforce and refusing
|
|
416
527
|
the event would be worse than the merge, so this warns and continues.
|
|
417
528
|
|
|
418
|
-
|
|
529
|
+
Found because the demo harness was doing exactly this, which is the
|
|
419
530
|
argument for the harness in miniature: the bug was the consumer's, the
|
|
420
531
|
invisibility was ours.
|
|
421
532
|
|
|
@@ -425,7 +536,7 @@ hosts that both arrange the page the way it expects.
|
|
|
425
536
|
of replaying one script for everything, and the page gained header-icon,
|
|
426
537
|
German-strings and reset-size controls plus a short "what to try" guide.
|
|
427
538
|
|
|
428
|
-
|
|
539
|
+
Two harness defects were making the component look broken. Its follow-up
|
|
429
540
|
detection matched **any** tool message in the thread, so once a conversation
|
|
430
541
|
had run a single tool every later turn answered "Done" to everything. And the
|
|
431
542
|
page forced `flex: 1` on the element, which silently outranks the width a
|
|
@@ -454,7 +565,7 @@ hosts that both arrange the page the way it expects.
|
|
|
454
565
|
keyboard user), styleable via the `code-copy` part, with `copyCode` /
|
|
455
566
|
`copied` / `copyFailed` in `UiStrings`.
|
|
456
567
|
|
|
457
|
-
|
|
568
|
+
**It reports failure rather than always claiming success.** The Clipboard
|
|
458
569
|
API needs a secure context and is simply absent in some embeddings; a button
|
|
459
570
|
that always says "Copied" sends the reader off to paste stale clipboard
|
|
460
571
|
content and find out somewhere else entirely.
|
|
@@ -473,7 +584,7 @@ hosts that both arrange the page the way it expects.
|
|
|
473
584
|
drawer had done this correctly all along. Focus now moves in on open, is
|
|
474
585
|
restored on close, and Tab is trapped while it is open.
|
|
475
586
|
|
|
476
|
-
|
|
587
|
+
**With no continuable runs the panel holds no controls**, so the panel
|
|
477
588
|
itself is focusable as the fallback — otherwise "move focus to the first
|
|
478
589
|
control" silently does nothing in exactly the case where the user has least to
|
|
479
590
|
go on.
|
|
@@ -495,7 +606,7 @@ hosts that both arrange the page the way it expects.
|
|
|
495
606
|
the composer, clears it, and calls this, so the two paths cannot drift.
|
|
496
607
|
|
|
497
608
|
It no-ops while a run is in flight — a second concurrent run would orphan the
|
|
498
|
-
first — and for an entirely empty message.
|
|
609
|
+
first — and for an entirely empty message. Unlike the built-in Send it does
|
|
499
610
|
**not** consult the attachment tray: what you pass is what is sent, so a host
|
|
500
611
|
composer stays in charge of its own state.
|
|
501
612
|
|
|
@@ -511,7 +622,7 @@ hosts that both arrange the page the way it expects.
|
|
|
511
622
|
a send. `detail` carries `{ attachments, pending }`: the durable refs of
|
|
512
623
|
everything settled, and how many are still in flight.
|
|
513
624
|
|
|
514
|
-
|
|
625
|
+
**This is what makes `sendMessage` usable with files at all.** The tray only
|
|
515
626
|
ever spoke to the built-in Send button, so a host composer had no way to tell a
|
|
516
627
|
settled upload from one still uploading — the same information the built-in
|
|
517
628
|
Send needs, which was simply not exposed. The tray's `onChange` hook already
|
|
@@ -527,13 +638,13 @@ hosts that both arrange the page the way it expects.
|
|
|
527
638
|
`data-strings`, `data-icon-url`.
|
|
528
639
|
|
|
529
640
|
Each is read once while connecting, to decide what chrome exists at all, and
|
|
530
|
-
no later read revisits the decision.
|
|
641
|
+
no later read revisits the decision. **The symptom is an affordance that
|
|
531
642
|
simply never appears** — which reads as a broken component rather than a
|
|
532
643
|
mis-timed assignment, and it is the common React/Vue shape: the element mounts
|
|
533
644
|
on the first render pass and the framework patches attributes in on the next.
|
|
534
645
|
|
|
535
646
|
Set them before the element enters the DOM, or remove and re-insert it to
|
|
536
|
-
apply a new value.
|
|
647
|
+
apply a new value. The attributes that genuinely *are* re-read per use —
|
|
537
648
|
`data-runs-url`, `data-page-actions`, `data-text-animation`,
|
|
538
649
|
`data-tool-display`, `endpoint`, and CSS-reactive `theme` / `collapsed` — are
|
|
539
650
|
deliberately excluded, since a late change works there and a warning would be
|
|
@@ -547,7 +658,7 @@ hosts that both arrange the page the way it expects.
|
|
|
547
658
|
So the panel ignored `theme="dark"` entirely and rendered light-on-dark unless
|
|
548
659
|
a host happened to set three variables documented nowhere. Now derived from the
|
|
549
660
|
real theme tokens, with a new theme-aware `--ag-ui-hover` defined in every
|
|
550
|
-
theme block.
|
|
661
|
+
theme block. The fallbacks are what hid it: they made an unthemed panel look
|
|
551
662
|
deliberate.
|
|
552
663
|
|
|
553
664
|
`checkpoints-title` and `checkpoint-label` also gain `part` attributes — they
|
|
@@ -592,11 +703,11 @@ hosts that both arrange the page the way it expects.
|
|
|
592
703
|
building both and diffing; only the source maps move), so consumers see no
|
|
593
704
|
change.
|
|
594
705
|
|
|
595
|
-
|
|
706
|
+
**Vitest 4 takes a provider *instance*, not the string `"playwright"`.**
|
|
596
707
|
The provider moved to its own package (`@vitest/browser-playwright`) and, with
|
|
597
708
|
v8 coverage, the old string form is a hard error rather than a deprecation.
|
|
598
709
|
|
|
599
|
-
|
|
710
|
+
**TypeScript 7 requires `rootDir` explicitly** (TS5011) instead of
|
|
600
711
|
inferring it from the common source directory. Set to the value 5.x inferred,
|
|
601
712
|
so the published layout is unchanged.
|
|
602
713
|
|
|
@@ -629,7 +740,7 @@ hosts that both arrange the page the way it expects.
|
|
|
629
740
|
transcription error body, and a restored history message with an unrecognised
|
|
630
741
|
role.
|
|
631
742
|
|
|
632
|
-
|
|
743
|
+
**One was a flaw in the test harness, not a missing test.** `makeFakeAgent`
|
|
633
744
|
ended a clean run by calling `onRunFinalized` alone, so the client's
|
|
634
745
|
`RUN_FINISHED` path could only ever be reached through `emit.interrupt()` —
|
|
635
746
|
the ordinary success outcome every real run carries was never exercised. The
|
|
@@ -648,13 +759,13 @@ hosts that both arrange the page the way it expects.
|
|
|
648
759
|
(Playwright) for the tests whose subject is sanitisation. Coverage stays
|
|
649
760
|
unified at 100% across both.
|
|
650
761
|
|
|
651
|
-
|
|
762
|
+
**A correctness requirement, not an optimisation.** DOMPurify 3.4.8+
|
|
652
763
|
silently stops sanitising under happy-dom — `<script>` and `<img>` pass
|
|
653
764
|
straight through, and ordinary markdown loses its `<p>` wrapper. A
|
|
654
765
|
happy-dom-only suite can therefore go green while this component ships no
|
|
655
766
|
sanitisation at all, which is the one failure it must never ship.
|
|
656
767
|
|
|
657
|
-
|
|
768
|
+
**The experiment settles what the pin never could**: dompurify 3.4.13
|
|
658
769
|
sanitises correctly in Chromium. The defect is happy-dom's DOM emulation, not
|
|
659
770
|
a DOMPurify regression — so **consumers were never exposed**, and the risk was
|
|
660
771
|
confined to the test environment the whole time.
|
|
@@ -680,13 +791,13 @@ hosts that both arrange the page the way it expects.
|
|
|
680
791
|
are transitive, so they are pinned through `overrides` — there is no direct
|
|
681
792
|
dependency to bump.
|
|
682
793
|
|
|
683
|
-
|
|
794
|
+
**`overrides` now live in `pnpm-workspace.yaml`**, not the `pnpm` field in
|
|
684
795
|
`package.json`; pnpm 11 ignores the latter and only warns. And an override
|
|
685
796
|
must be scoped to its major — an unbounded `brace-expansion: ">=2.1.2"`
|
|
686
797
|
resolves to 5.x, whose export shape `minimatch` cannot call, breaking `glob`
|
|
687
798
|
at runtime.
|
|
688
799
|
|
|
689
|
-
-
|
|
800
|
+
- **Four `dompurify` advisories are knowingly left open** (three LOW, one
|
|
690
801
|
MEDIUM), and the dependency is now pinned to **exactly `3.4.7`** rather than
|
|
691
802
|
`^3.4.7`.
|
|
692
803
|
|
|
@@ -695,7 +806,7 @@ hosts that both arrange the page the way it expects.
|
|
|
695
806
|
again against dompurify 3.4.13 with happy-dom 20.11.1 — moving the test DOM
|
|
696
807
|
forward does not fix it.
|
|
697
808
|
|
|
698
|
-
|
|
809
|
+
**The pin was previously a caret range**, so the hold existed only in the
|
|
699
810
|
lockfile and nowhere in the manifest, undocumented — a `pnpm update` would
|
|
700
811
|
have silently disabled sanitisation. It is now exact, explained at the import
|
|
701
812
|
site in `src/ui/render_markdown.ts`, and recorded in `CLAUDE.md`.
|
|
@@ -722,7 +833,7 @@ hosts that both arrange the page the way it expects.
|
|
|
722
833
|
client. It now renders as `Using skill <id>` **instead of** the raw tool
|
|
723
834
|
card it would otherwise produce.
|
|
724
835
|
|
|
725
|
-
|
|
836
|
+
Not to be confused with the existing `Skill` catalog — that is a *human*
|
|
726
837
|
affordance (a prompt the user launches from the chip row or `/`-palette).
|
|
727
838
|
An agent skill is chosen by the model mid-run. Only the latter emits a
|
|
728
839
|
notice.
|
|
@@ -929,7 +1040,7 @@ hosts that both arrange the page the way it expects.
|
|
|
929
1040
|
/ `onReasoningEnd`, wired from `@ag-ui/client`'s `REASONING_*` subscriber
|
|
930
1041
|
callbacks (which also cover the deprecated `THINKING_*` family).
|
|
931
1042
|
- **Voice input.** Set `data-transcribe-url` (django-ag-ui's
|
|
932
|
-
`TranscribeView`) to reveal a
|
|
1043
|
+
`TranscribeView`) to reveal a mic button in the composer (part
|
|
933
1044
|
`voice-button`): it records via `MediaRecorder`, POSTs the clip, and drops the
|
|
934
1045
|
returned transcript into the textarea. A pluggable `transcribeHandler` —
|
|
935
1046
|
`(audio: Blob) => Promise<string>` — swaps the transport (a different STT
|
|
@@ -974,7 +1085,7 @@ hosts that both arrange the page the way it expects.
|
|
|
974
1085
|
+ summary, no box chrome) with the result behind its own toggle. Every
|
|
975
1086
|
tool-call card now leads with a CSS-drawn **status icon** (part
|
|
976
1087
|
`tool-card-icon`): a spinning ring while running, then a check / cross / slash
|
|
977
|
-
on success / error / decline, replacing the hardcoded
|
|
1088
|
+
on success / error / decline, replacing the hardcoded `` glyph. Re-theme via
|
|
978
1089
|
`--ag-ui-tool-icon-done` / `--ag-ui-tool-icon-error` / `--ag-ui-tool-icon-declined`
|
|
979
1090
|
and `--ag-ui-tool-spin-duration`; the spin respects `prefers-reduced-motion`.
|
|
980
1091
|
|
|
@@ -1025,7 +1136,7 @@ hosts that both arrange the page the way it expects.
|
|
|
1025
1136
|
### Added
|
|
1026
1137
|
|
|
1027
1138
|
- **File uploads.** Set `data-attachments-url` (django-ag-ui's `AttachmentsView`)
|
|
1028
|
-
to reveal a
|
|
1139
|
+
to reveal a picker + drag-and-drop on the composer. Each file uploads
|
|
1029
1140
|
out-of-band (multipart, with the element's `headers`) into a pending tray —
|
|
1030
1141
|
one chip per file with a progress bar, settling to `ready` or `error` (with
|
|
1031
1142
|
retry / remove). On send, the ready files' refs render as read-only chips on
|
|
@@ -1041,7 +1152,7 @@ hosts that both arrange the page the way it expects.
|
|
|
1041
1152
|
`(file, onProgress) => Promise<AttachmentRef>` — swaps the built-in multipart
|
|
1042
1153
|
upload for a custom one (a resumable `tus-js-client` adapter, direct-to-S3
|
|
1043
1154
|
multipart, …) without touching the tray, chips, or AG-UI wire. When set, the
|
|
1044
|
-
|
|
1155
|
+
affordance appears even with no `data-attachments-url`. Defaults to the
|
|
1045
1156
|
built-in `uploadAttachment`.
|
|
1046
1157
|
- **New exports:** `uploadAttachment` + `UploadOptions` + `UploadHandler`, the
|
|
1047
1158
|
`AttachmentRef` type, and `messageAttachments`. `AgUiClient.send` gains an
|
|
@@ -1310,7 +1421,9 @@ hosts that both arrange the page the way it expects.
|
|
|
1310
1421
|
### Notes
|
|
1311
1422
|
- First release — exercising the automated npm OIDC publish pipeline end-to-end.
|
|
1312
1423
|
|
|
1313
|
-
[Unreleased]: https://github.com/Artui/ag-ui-web-component/compare/v0.
|
|
1424
|
+
[Unreleased]: https://github.com/Artui/ag-ui-web-component/compare/v0.24.0...HEAD
|
|
1425
|
+
[0.24.0]: https://github.com/Artui/ag-ui-web-component/compare/v0.23.1...v0.24.0
|
|
1426
|
+
[0.23.1]: https://github.com/Artui/ag-ui-web-component/compare/v0.23.0...v0.23.1
|
|
1314
1427
|
[0.23.0]: https://github.com/Artui/ag-ui-web-component/compare/v0.22.0...v0.23.0
|
|
1315
1428
|
[0.22.0]: https://github.com/Artui/ag-ui-web-component/compare/v0.21.0...v0.22.0
|
|
1316
1429
|
[0.21.0]: https://github.com/Artui/ag-ui-web-component/compare/v0.20.1...v0.21.0
|