@artooi/ag-ui-web-component 0.22.0 → 0.23.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.
Files changed (122) hide show
  1. package/CHANGELOG.md +134 -36
  2. package/README.md +7 -7
  3. package/dist/ag-ui-web-component.bundle.js +145 -118
  4. package/dist/ag-ui-web-component.bundle.js.map +3 -3
  5. package/dist/constants.d.ts +65 -78
  6. package/dist/constants.d.ts.map +1 -1
  7. package/dist/core/ag_ui_chat.d.ts +93 -126
  8. package/dist/core/ag_ui_chat.d.ts.map +1 -1
  9. package/dist/core/agui_client.d.ts +24 -30
  10. package/dist/core/agui_client.d.ts.map +1 -1
  11. package/dist/core/attachment.d.ts +9 -14
  12. package/dist/core/attachment.d.ts.map +1 -1
  13. package/dist/core/conversation_store.d.ts +20 -27
  14. package/dist/core/conversation_store.d.ts.map +1 -1
  15. package/dist/core/create_http_agent.d.ts +13 -15
  16. package/dist/core/create_http_agent.d.ts.map +1 -1
  17. package/dist/core/remote_conversation_store.d.ts +8 -9
  18. package/dist/core/remote_conversation_store.d.ts.map +1 -1
  19. package/dist/core/run_index.d.ts +11 -20
  20. package/dist/core/run_index.d.ts.map +1 -1
  21. package/dist/core/transcribe_audio.d.ts +8 -8
  22. package/dist/core/transcribe_audio.d.ts.map +1 -1
  23. package/dist/core/upload_attachment.d.ts +15 -18
  24. package/dist/core/upload_attachment.d.ts.map +1 -1
  25. package/dist/core/utils.d.ts +4 -6
  26. package/dist/core/utils.d.ts.map +1 -1
  27. package/dist/dom/animations.d.ts +22 -30
  28. package/dist/dom/animations.d.ts.map +1 -1
  29. package/dist/dom/dom_driver.d.ts +7 -7
  30. package/dist/dom/native_setter.d.ts +2 -2
  31. package/dist/dom/native_setter.d.ts.map +1 -1
  32. package/dist/index.js +347 -401
  33. package/dist/index.js.map +2 -2
  34. package/dist/skills/fill_template.d.ts +4 -5
  35. package/dist/skills/fill_template.d.ts.map +1 -1
  36. package/dist/skills/parse_skills.d.ts.map +1 -1
  37. package/dist/skills/skill.d.ts +7 -8
  38. package/dist/skills/skill.d.ts.map +1 -1
  39. package/dist/tools/client_tool_registry.d.ts +2 -2
  40. package/dist/tools/page_action_tools.d.ts +7 -10
  41. package/dist/tools/page_action_tools.d.ts.map +1 -1
  42. package/dist/tools/page_state.d.ts +5 -8
  43. package/dist/tools/page_state.d.ts.map +1 -1
  44. package/dist/tools/route_map.d.ts +7 -10
  45. package/dist/tools/route_map.d.ts.map +1 -1
  46. package/dist/ui/approval_card.d.ts +15 -20
  47. package/dist/ui/approval_card.d.ts.map +1 -1
  48. package/dist/ui/attach_copy_buttons.d.ts +4 -10
  49. package/dist/ui/attach_copy_buttons.d.ts.map +1 -1
  50. package/dist/ui/attachment_chips.d.ts +13 -5
  51. package/dist/ui/attachment_chips.d.ts.map +1 -1
  52. package/dist/ui/attachment_tray.d.ts +6 -6
  53. package/dist/ui/attachment_tray.d.ts.map +1 -1
  54. package/dist/ui/checkpoint_menu.d.ts +7 -8
  55. package/dist/ui/checkpoint_menu.d.ts.map +1 -1
  56. package/dist/ui/confirmation_card.d.ts +10 -15
  57. package/dist/ui/confirmation_card.d.ts.map +1 -1
  58. package/dist/ui/question_card.d.ts +12 -15
  59. package/dist/ui/question_card.d.ts.map +1 -1
  60. package/dist/ui/relative_time.d.ts +5 -7
  61. package/dist/ui/relative_time.d.ts.map +1 -1
  62. package/dist/ui/render_markdown.d.ts +8 -8
  63. package/dist/ui/render_markdown.d.ts.map +1 -1
  64. package/dist/ui/resize_handle.d.ts +21 -34
  65. package/dist/ui/resize_handle.d.ts.map +1 -1
  66. package/dist/ui/run_notice.d.ts +5 -7
  67. package/dist/ui/run_notice.d.ts.map +1 -1
  68. package/dist/ui/skills_menu.d.ts +4 -5
  69. package/dist/ui/skills_menu.d.ts.map +1 -1
  70. package/dist/ui/styles.d.ts +1 -1
  71. package/dist/ui/styles.d.ts.map +1 -1
  72. package/dist/ui/thoughts_block.d.ts +9 -11
  73. package/dist/ui/thoughts_block.d.ts.map +1 -1
  74. package/dist/ui/thread_drawer.d.ts +6 -5
  75. package/dist/ui/thread_drawer.d.ts.map +1 -1
  76. package/dist/ui/tool_call_card.d.ts +17 -25
  77. package/dist/ui/tool_call_card.d.ts.map +1 -1
  78. package/dist/ui/ui_strings.d.ts +6 -12
  79. package/dist/ui/ui_strings.d.ts.map +1 -1
  80. package/dist/ui/voice_input.d.ts +10 -11
  81. package/dist/ui/voice_input.d.ts.map +1 -1
  82. package/package.json +1 -1
  83. package/src/constants.ts +71 -80
  84. package/src/core/ag_ui_chat.ts +242 -292
  85. package/src/core/agui_client.ts +60 -71
  86. package/src/core/attachment.ts +9 -14
  87. package/src/core/conversation_store.ts +25 -33
  88. package/src/core/create_http_agent.ts +18 -22
  89. package/src/core/remote_conversation_store.ts +14 -15
  90. package/src/core/run_index.ts +14 -23
  91. package/src/core/transcribe_audio.ts +9 -10
  92. package/src/core/upload_attachment.ts +18 -21
  93. package/src/core/utils.ts +4 -6
  94. package/src/dom/animations.ts +33 -43
  95. package/src/dom/dom_driver.ts +7 -7
  96. package/src/dom/native_setter.ts +11 -12
  97. package/src/skills/fill_template.ts +4 -5
  98. package/src/skills/parse_skills.ts +3 -4
  99. package/src/skills/skill.ts +7 -8
  100. package/src/tools/client_tool_registry.ts +2 -2
  101. package/src/tools/page_action_tools.ts +12 -15
  102. package/src/tools/page_state.ts +5 -8
  103. package/src/tools/route_map.ts +15 -19
  104. package/src/ui/approval_card.ts +15 -20
  105. package/src/ui/attach_copy_buttons.ts +9 -18
  106. package/src/ui/attachment_chips.ts +19 -10
  107. package/src/ui/attachment_tray.ts +8 -7
  108. package/src/ui/checkpoint_menu.ts +7 -8
  109. package/src/ui/confirmation_card.ts +10 -15
  110. package/src/ui/question_card.ts +12 -15
  111. package/src/ui/relative_time.ts +5 -7
  112. package/src/ui/render_markdown.ts +25 -51
  113. package/src/ui/resize_handle.ts +25 -38
  114. package/src/ui/run_notice.ts +9 -12
  115. package/src/ui/skills_menu.ts +4 -5
  116. package/src/ui/styles.ts +122 -93
  117. package/src/ui/thoughts_block.ts +11 -13
  118. package/src/ui/thread_drawer.ts +6 -5
  119. package/src/ui/tool_call_card.ts +22 -32
  120. package/src/ui/ui_strings.ts +6 -12
  121. package/src/ui/voice_input.ts +10 -11
  122. package/src/version.ts +1 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,102 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.23.1] — 2026-08-13
11
+
12
+ ### Fixed
13
+
14
+ - **A reloaded conversation lost every turn after the first plain answer.**
15
+ Restoring stored history iterated `message.toolCalls` behind an
16
+ `!== undefined` check, and an assistant turn that called no tool can arrive
17
+ carrying `null`: the protocol's Python models declare
18
+ `tool_calls: list[ToolCall] | None`, so a server dumping them without
19
+ `exclude_none` sends exactly that, while `@ag-ui/core` types the field
20
+ optional-and-not-nullable — TypeScript therefore offered no hint that the value
21
+ was reachable. Iterating it threw `TypeError: toolCalls is not iterable`
22
+ *inside* the replay, which aborted at that message and left every later turn
23
+ out of the transcript. Nothing surfaced: no error state, no partial-history
24
+ notice, just a short conversation. Measured against a server-backed store, a
25
+ 54-message thread restored as two bubbles and one tool card.
26
+
27
+ Two things follow from where the throw happened, and both are now closed.
28
+ Tool calls are **narrowed rather than trusted** — the same stance
29
+ `messageAttachments` already takes for the neighbouring field on the same
30
+ message, and for the same reason. And a **shapeless entry is skipped instead of
31
+ ending the replay**: a stored call with no `id`, no `function.name`, or a
32
+ `null` where an object belongs no longer costs the rest of the conversation.
33
+ A call whose `arguments` are missing or not a string still renders, with empty
34
+ arguments, because its name is the part worth showing.
35
+
36
+ Only a **server-backed** history reaches this: the default
37
+ `SessionStorageStore` round-trips client-shaped objects through JSON, where an
38
+ absent field stays absent, so the `null` never appears. Anything reading a
39
+ thread index — `data-threads-url` — did, including every Django admin
40
+ configured with a conversation store. There the cost was worse than a short
41
+ transcript: each navigating tool reloads the page and the run continues only
42
+ because the component rehydrates and completes the pending call, so a thread
43
+ that had answered once abandoned its next multi-step task at the first
44
+ navigation.
45
+
46
+ ## [0.23.0] — 2026-08-12
47
+
48
+ Attachment chips, which turned out to be the least finished corner of 0.22.
49
+
50
+ ### Fixed
51
+
52
+ - **A filename on a sent attachment chip was invisible on the stock light
53
+ theme.** `.attachment-chip` set the assistant surface as its background but no
54
+ colour, so on a user bubble it inherited `--ag-ui-user-fg` — white on
55
+ `#f1f1f6`, a contrast ratio of 1.13:1 where WCAG AA wants 4.5:1. Only the size
56
+ stayed legible, because it sets its own muted colour, which is exactly how the
57
+ bug read to a user: an icon, a blank gap, and a size. The chip now takes
58
+ `--ag-ui-text`, the same consumer-overridable token the rest of the body text
59
+ uses, so a page that themes its text themes the chip with it. The dark and
60
+ code themes were never affected, which is why this shipped.
61
+
62
+ - **The composer's attachment tray never collapsed.** The tray sets `hidden`
63
+ while it holds no chips, but its rule declared `display: flex` with no
64
+ `[hidden]` guard, and an author `display` beats the UA stylesheet's
65
+ `[hidden] { display: none }`. Every embed that wired uploads therefore carried
66
+ 8px of dead space above the composer at all times. With the tray genuinely
67
+ collapsing, its padding is also symmetric again (`8px 12px`), so a chip clears
68
+ the composer's top edge instead of sitting flush against it.
69
+
70
+ - **Filenames truncated far short of the space available.**
71
+ `.attachment-chip-name` capped itself at `14ch`, so
72
+ `LQ27552-7006-EXHIBIT-A.pdf` rendered as `LQ27552-7006 …` inside a chip with
73
+ room to spare. The cap is gone: the chip is already `max-width: 100%` with the
74
+ name ellipsising, so its container bounds it, and a genuinely long name now
75
+ ellipsises at the edge it actually reaches. The chip is `box-sizing:
76
+ border-box` with it, which stops that `100%` from overflowing its container by
77
+ the chip's own padding and border.
78
+
79
+ ### Changed
80
+
81
+ - **Attachment chip icons are inline SVG.** 0.22 moved the chrome's glyphs to
82
+ inline SVG and stopped at the chips, leaving emoji sitting beside SVG send,
83
+ attach and mic buttons — a different optical weight, varying by platform, and
84
+ taking neither `currentColor` nor a size from CSS. Four marks (image, PDF,
85
+ text, generic) now follow the same contract as the rest, painted from the
86
+ chip's own colour so an errored chip turns red glyph and all. Both the sent
87
+ bubble's chips and the composer tray's change together.
88
+
89
+ ### Removed
90
+
91
+ - **The client-side attachment manifest in `RunAgentInput.context`.**
92
+ `getContext()` appended a one-line summary of the message's attachments; the
93
+ server now derives that from the refs riding the messages, so the client's
94
+ copy only duplicated it on the turn a file was attached. Attachments still
95
+ reach the agent — through the message, which is where they already were. The
96
+ page-map half of `getContext()` is unchanged.
97
+
98
+ **This needs a server that derives the manifest itself.** `django-ag-ui`
99
+ does so from 0.42.0. Against an older server nothing replaces the client's
100
+ copy, so the agent stops being told which attachment ids exist and answers a
101
+ question about an attached file by asking for the file — which is the defect
102
+ the server-side derivation was written to fix. Upgrade the server first, or
103
+ together. A host that read the manifest out of the run context itself is
104
+ likewise affected.
105
+
10
106
  ## [0.22.0] — 2026-08-11
11
107
 
12
108
  Two complaints about how the widget *feels*, and both turned out to be structural
@@ -146,7 +242,7 @@ hosts that both arrange the page the way it expects.
146
242
  `overflow: hidden` ancestor sharing the element's box — a card, a table cell —
147
243
  clipped it entirely while the tool reported success. And 200 ms is below the
148
244
  threshold at which someone who does not know where to look notices anything.
149
- Neither our tests nor the consumer's could have found the second one:
245
+ Neither our tests nor the consumer's could have found the second one:
150
246
  headless Chromium hides the scroll race, and no automated check has an opinion
151
247
  about whether a human sees a 200 ms ring.
152
248
 
@@ -162,7 +258,7 @@ hosts that both arrange the page the way it expects.
162
258
  explicit-duration contract, and the docs now say so instead of overclaiming.
163
259
 
164
260
  - **The tool and skill catalog fetches are deferred by one microtask**, so a React
165
- `ref` assigned in the same commit as insertion is honoured. The thread-history
261
+ `ref` assigned in the same commit as insertion is honoured. The thread-history
166
262
  request is deliberately **not** deferred: a deferred replay can land after a
167
263
  `sendMessage()` and duplicate the transcript. Configure before you insert, or
168
264
  call `reload()`; the new React recipe in the README shows both.
@@ -174,7 +270,7 @@ hosts that both arrange the page the way it expects.
174
270
  `data-resize-anchor="<y>-<x>"`, but the rules meant to read it were written as
175
271
  `[data-resize-anchor~="left"]` — and `~=` matches whitespace-separated words
176
272
  while the stamped value is a single hyphenated token, so they could never
177
- match. **The cursor rules used `=` and did match**, so the pointer followed
273
+ match. **The cursor rules used `=` and did match**, so the pointer followed
178
274
  the measurement while the grip stayed where `placement` had guessed: for any
179
275
  host that aligns the panel the other way, the cursor promised a diagonal the
180
276
  grip was not on, and the grip sat on the corner that stays put.
@@ -200,14 +296,14 @@ hosts that both arrange the page the way it expects.
200
296
 
201
297
  ### Fixed
202
298
 
203
- - **`placement="side"` (and `sidebar`) stopped being full height once the
299
+ - **`placement="side"` (and `sidebar`) stopped being full height once the
204
300
  panel had been resized.** A dragged size is written as a custom property on
205
301
  the host, and an inline custom property **outranks a `:host([placement=…])`
206
302
  rule setting the same property** — so a height dragged while floating capped a
207
303
  docked sidebar that had asked for `100vh`. Since the size persists per tab,
208
304
  one drag broke every later visit.
209
305
 
210
- **The previous release claimed this was already handled, and the reasoning
306
+ **The previous release claimed this was already handled, and the reasoning
211
307
  was wrong.** Writing `--ag-ui-height` rather than inline `height` was supposed
212
308
  to leave placement with the final say; it does not, because the indirection
213
309
  changes nothing about the cascade. The fix is explicit rather than
@@ -231,12 +327,12 @@ hosts that both arrange the page the way it expects.
231
327
  handle at all (a `100vw`/`100vh` layout has nothing to drag), `sidebar` /
232
328
  `side` get width only, everything else gets both.
233
329
 
234
- **It writes the custom properties, not inline `width` / `height`.** The
330
+ **It writes the custom properties, not inline `width` / `height`.** The
235
331
  placement rules set those same properties, so an inline dimension would
236
332
  outrank them — a panel dragged while floating would keep that width after
237
333
  switching to fullscreen.
238
334
 
239
- **Which corner the grip sits on is measured, not assumed.** A resize is
335
+ **Which corner the grip sits on is measured, not assumed.** A resize is
240
336
  computed from the edge that stays still, and which edge that is belongs to the
241
337
  *host's* layout rather than to `placement` — a floating panel is pinned
242
338
  bottom-right, an embedded one goes wherever the page's CSS puts it. Deriving
@@ -260,7 +356,7 @@ hosts that both arrange the page the way it expects.
260
356
  palette — a lone `/` — was left in the composer, which said nothing about what
261
357
  the skill wanted or how to supply it. The hint now says what to do, too.
262
358
 
263
- - **Picking a skill now sends it.** It used to write the text into the
359
+ - **Picking a skill now sends it.** It used to write the text into the
264
360
  composer and wait for a second click unless the skill set
265
361
  `sendImmediately: true` — so the default behaviour of a shortcut was to not
266
362
  take the shortcut. Set `sendImmediately: false` to keep pre-filling, which is
@@ -271,7 +367,7 @@ hosts that both arrange the page the way it expects.
271
367
  sends the bare `/name` token and the agent expands it, from the harness
272
368
  `Skills` capability or the server's own instructions.
273
369
 
274
- **The prompt was the leak.** A catalog is either a fetched `GET` or an
370
+ **The prompt was the leak.** A catalog is either a fetched `GET` or an
275
371
  inline `data-skills` attribute sitting in the page source, and a skill is
276
372
  often where a project's internal workflow is written down most plainly — so
277
373
  the client-side catalog published it to anyone who opened the page. Sending a
@@ -295,10 +391,10 @@ hosts that both arrange the page the way it expects.
295
391
  `part` (`tool-card-args` / `tool-card-result`, headings via
296
392
  `tool-card-section-label`, body via `tool-card-body`), and are pretty-printed.
297
393
 
298
- Breaking for anyone styling `tool-card-result` as a single combined block.
394
+ Breaking for anyone styling `tool-card-result` as a single combined block.
299
395
  A call with no arguments no longer renders an empty `{}` in a box of its own.
300
396
 
301
- - **`data-tool-display` is now live.** Changing it restyles every card already
397
+ - **`data-tool-display` is now live.** Changing it restyles every card already
302
398
  in the transcript, the way `data-answer-well` always has. The modes are pure
303
399
  visibility over **one DOM shape**, selected by the shadow CSS from the host
304
400
  attribute; previously each card baked its structure at construction from the
@@ -313,7 +409,7 @@ hosts that both arrange the page the way it expects.
313
409
  different objects: the record is the tool card it gates, which settles to the
314
410
  outcome and scrolls with the rest of the transcript.
315
411
 
316
- - **The confirmation card was appended to the wrong parent**, and it is the
412
+ - **The confirmation card was appended to the wrong parent**, and it is the
317
413
  reason it drifted to the foot of a turn. Every other inline card — tool,
318
414
  approval, `ask_user`, run notices — goes into the turn's answer group; this
319
415
  one went into the message list, so it became a sibling *after* the group and
@@ -330,7 +426,7 @@ hosts that both arrange the page the way it expects.
330
426
  gated, and the server-side gate left nothing at all even though it is the one
331
427
  guarding tools that run on the backend.
332
428
 
333
- **Session-scoped, like the "run interrupted" notice.** AG-UI carries no
429
+ **Session-scoped, like the "run interrupted" notice.** AG-UI carries no
334
430
  approval message — the answer rides `resume[]` as transient run input — so a
335
431
  reload restores the call and its result but not the note. Durable "who
336
432
  approved what" is an audit concern, not a transcript one.
@@ -355,7 +451,7 @@ hosts that both arrange the page the way it expects.
355
451
  entry is what gets persisted. The protocol has no rule to enforce and refusing
356
452
  the event would be worse than the merge, so this warns and continues.
357
453
 
358
- Found because the demo harness was doing exactly this, which is the
454
+ Found because the demo harness was doing exactly this, which is the
359
455
  argument for the harness in miniature: the bug was the consumer's, the
360
456
  invisibility was ours.
361
457
 
@@ -365,7 +461,7 @@ hosts that both arrange the page the way it expects.
365
461
  of replaying one script for everything, and the page gained header-icon,
366
462
  German-strings and reset-size controls plus a short "what to try" guide.
367
463
 
368
- Two harness defects were making the component look broken. Its follow-up
464
+ Two harness defects were making the component look broken. Its follow-up
369
465
  detection matched **any** tool message in the thread, so once a conversation
370
466
  had run a single tool every later turn answered "Done" to everything. And the
371
467
  page forced `flex: 1` on the element, which silently outranks the width a
@@ -394,7 +490,7 @@ hosts that both arrange the page the way it expects.
394
490
  keyboard user), styleable via the `code-copy` part, with `copyCode` /
395
491
  `copied` / `copyFailed` in `UiStrings`.
396
492
 
397
- **It reports failure rather than always claiming success.** The Clipboard
493
+ **It reports failure rather than always claiming success.** The Clipboard
398
494
  API needs a secure context and is simply absent in some embeddings; a button
399
495
  that always says "Copied" sends the reader off to paste stale clipboard
400
496
  content and find out somewhere else entirely.
@@ -413,7 +509,7 @@ hosts that both arrange the page the way it expects.
413
509
  drawer had done this correctly all along. Focus now moves in on open, is
414
510
  restored on close, and Tab is trapped while it is open.
415
511
 
416
- **With no continuable runs the panel holds no controls**, so the panel
512
+ **With no continuable runs the panel holds no controls**, so the panel
417
513
  itself is focusable as the fallback — otherwise "move focus to the first
418
514
  control" silently does nothing in exactly the case where the user has least to
419
515
  go on.
@@ -435,7 +531,7 @@ hosts that both arrange the page the way it expects.
435
531
  the composer, clears it, and calls this, so the two paths cannot drift.
436
532
 
437
533
  It no-ops while a run is in flight — a second concurrent run would orphan the
438
- first — and for an entirely empty message. Unlike the built-in Send it does
534
+ first — and for an entirely empty message. Unlike the built-in Send it does
439
535
  **not** consult the attachment tray: what you pass is what is sent, so a host
440
536
  composer stays in charge of its own state.
441
537
 
@@ -451,7 +547,7 @@ hosts that both arrange the page the way it expects.
451
547
  a send. `detail` carries `{ attachments, pending }`: the durable refs of
452
548
  everything settled, and how many are still in flight.
453
549
 
454
- **This is what makes `sendMessage` usable with files at all.** The tray only
550
+ **This is what makes `sendMessage` usable with files at all.** The tray only
455
551
  ever spoke to the built-in Send button, so a host composer had no way to tell a
456
552
  settled upload from one still uploading — the same information the built-in
457
553
  Send needs, which was simply not exposed. The tray's `onChange` hook already
@@ -467,13 +563,13 @@ hosts that both arrange the page the way it expects.
467
563
  `data-strings`, `data-icon-url`.
468
564
 
469
565
  Each is read once while connecting, to decide what chrome exists at all, and
470
- no later read revisits the decision. **The symptom is an affordance that
566
+ no later read revisits the decision. **The symptom is an affordance that
471
567
  simply never appears** — which reads as a broken component rather than a
472
568
  mis-timed assignment, and it is the common React/Vue shape: the element mounts
473
569
  on the first render pass and the framework patches attributes in on the next.
474
570
 
475
571
  Set them before the element enters the DOM, or remove and re-insert it to
476
- apply a new value. The attributes that genuinely *are* re-read per use —
572
+ apply a new value. The attributes that genuinely *are* re-read per use —
477
573
  `data-runs-url`, `data-page-actions`, `data-text-animation`,
478
574
  `data-tool-display`, `endpoint`, and CSS-reactive `theme` / `collapsed` — are
479
575
  deliberately excluded, since a late change works there and a warning would be
@@ -487,7 +583,7 @@ hosts that both arrange the page the way it expects.
487
583
  So the panel ignored `theme="dark"` entirely and rendered light-on-dark unless
488
584
  a host happened to set three variables documented nowhere. Now derived from the
489
585
  real theme tokens, with a new theme-aware `--ag-ui-hover` defined in every
490
- theme block. The fallbacks are what hid it: they made an unthemed panel look
586
+ theme block. The fallbacks are what hid it: they made an unthemed panel look
491
587
  deliberate.
492
588
 
493
589
  `checkpoints-title` and `checkpoint-label` also gain `part` attributes — they
@@ -532,11 +628,11 @@ hosts that both arrange the page the way it expects.
532
628
  building both and diffing; only the source maps move), so consumers see no
533
629
  change.
534
630
 
535
- **Vitest 4 takes a provider *instance*, not the string `"playwright"`.**
631
+ **Vitest 4 takes a provider *instance*, not the string `"playwright"`.**
536
632
  The provider moved to its own package (`@vitest/browser-playwright`) and, with
537
633
  v8 coverage, the old string form is a hard error rather than a deprecation.
538
634
 
539
- **TypeScript 7 requires `rootDir` explicitly** (TS5011) instead of
635
+ **TypeScript 7 requires `rootDir` explicitly** (TS5011) instead of
540
636
  inferring it from the common source directory. Set to the value 5.x inferred,
541
637
  so the published layout is unchanged.
542
638
 
@@ -569,7 +665,7 @@ hosts that both arrange the page the way it expects.
569
665
  transcription error body, and a restored history message with an unrecognised
570
666
  role.
571
667
 
572
- **One was a flaw in the test harness, not a missing test.** `makeFakeAgent`
668
+ **One was a flaw in the test harness, not a missing test.** `makeFakeAgent`
573
669
  ended a clean run by calling `onRunFinalized` alone, so the client's
574
670
  `RUN_FINISHED` path could only ever be reached through `emit.interrupt()` —
575
671
  the ordinary success outcome every real run carries was never exercised. The
@@ -588,13 +684,13 @@ hosts that both arrange the page the way it expects.
588
684
  (Playwright) for the tests whose subject is sanitisation. Coverage stays
589
685
  unified at 100% across both.
590
686
 
591
- **A correctness requirement, not an optimisation.** DOMPurify 3.4.8+
687
+ **A correctness requirement, not an optimisation.** DOMPurify 3.4.8+
592
688
  silently stops sanitising under happy-dom — `<script>` and `<img>` pass
593
689
  straight through, and ordinary markdown loses its `<p>` wrapper. A
594
690
  happy-dom-only suite can therefore go green while this component ships no
595
691
  sanitisation at all, which is the one failure it must never ship.
596
692
 
597
- **The experiment settles what the pin never could**: dompurify 3.4.13
693
+ **The experiment settles what the pin never could**: dompurify 3.4.13
598
694
  sanitises correctly in Chromium. The defect is happy-dom's DOM emulation, not
599
695
  a DOMPurify regression — so **consumers were never exposed**, and the risk was
600
696
  confined to the test environment the whole time.
@@ -620,13 +716,13 @@ hosts that both arrange the page the way it expects.
620
716
  are transitive, so they are pinned through `overrides` — there is no direct
621
717
  dependency to bump.
622
718
 
623
- **`overrides` now live in `pnpm-workspace.yaml`**, not the `pnpm` field in
719
+ **`overrides` now live in `pnpm-workspace.yaml`**, not the `pnpm` field in
624
720
  `package.json`; pnpm 11 ignores the latter and only warns. And an override
625
721
  must be scoped to its major — an unbounded `brace-expansion: ">=2.1.2"`
626
722
  resolves to 5.x, whose export shape `minimatch` cannot call, breaking `glob`
627
723
  at runtime.
628
724
 
629
- - **Four `dompurify` advisories are knowingly left open** (three LOW, one
725
+ - **Four `dompurify` advisories are knowingly left open** (three LOW, one
630
726
  MEDIUM), and the dependency is now pinned to **exactly `3.4.7`** rather than
631
727
  `^3.4.7`.
632
728
 
@@ -635,7 +731,7 @@ hosts that both arrange the page the way it expects.
635
731
  again against dompurify 3.4.13 with happy-dom 20.11.1 — moving the test DOM
636
732
  forward does not fix it.
637
733
 
638
- **The pin was previously a caret range**, so the hold existed only in the
734
+ **The pin was previously a caret range**, so the hold existed only in the
639
735
  lockfile and nowhere in the manifest, undocumented — a `pnpm update` would
640
736
  have silently disabled sanitisation. It is now exact, explained at the import
641
737
  site in `src/ui/render_markdown.ts`, and recorded in `CLAUDE.md`.
@@ -662,7 +758,7 @@ hosts that both arrange the page the way it expects.
662
758
  client. It now renders as `Using skill <id>` **instead of** the raw tool
663
759
  card it would otherwise produce.
664
760
 
665
- Not to be confused with the existing `Skill` catalog — that is a *human*
761
+ Not to be confused with the existing `Skill` catalog — that is a *human*
666
762
  affordance (a prompt the user launches from the chip row or `/`-palette).
667
763
  An agent skill is chosen by the model mid-run. Only the latter emits a
668
764
  notice.
@@ -869,7 +965,7 @@ hosts that both arrange the page the way it expects.
869
965
  / `onReasoningEnd`, wired from `@ag-ui/client`'s `REASONING_*` subscriber
870
966
  callbacks (which also cover the deprecated `THINKING_*` family).
871
967
  - **Voice input.** Set `data-transcribe-url` (django-ag-ui's
872
- `TranscribeView`) to reveal a 🎤 mic button in the composer (part
968
+ `TranscribeView`) to reveal a mic button in the composer (part
873
969
  `voice-button`): it records via `MediaRecorder`, POSTs the clip, and drops the
874
970
  returned transcript into the textarea. A pluggable `transcribeHandler` —
875
971
  `(audio: Blob) => Promise<string>` — swaps the transport (a different STT
@@ -914,7 +1010,7 @@ hosts that both arrange the page the way it expects.
914
1010
  + summary, no box chrome) with the result behind its own toggle. Every
915
1011
  tool-call card now leads with a CSS-drawn **status icon** (part
916
1012
  `tool-card-icon`): a spinning ring while running, then a check / cross / slash
917
- on success / error / decline, replacing the hardcoded `🔧` glyph. Re-theme via
1013
+ on success / error / decline, replacing the hardcoded `` glyph. Re-theme via
918
1014
  `--ag-ui-tool-icon-done` / `--ag-ui-tool-icon-error` / `--ag-ui-tool-icon-declined`
919
1015
  and `--ag-ui-tool-spin-duration`; the spin respects `prefers-reduced-motion`.
920
1016
 
@@ -965,7 +1061,7 @@ hosts that both arrange the page the way it expects.
965
1061
  ### Added
966
1062
 
967
1063
  - **File uploads.** Set `data-attachments-url` (django-ag-ui's `AttachmentsView`)
968
- to reveal a 📎 picker + drag-and-drop on the composer. Each file uploads
1064
+ to reveal a picker + drag-and-drop on the composer. Each file uploads
969
1065
  out-of-band (multipart, with the element's `headers`) into a pending tray —
970
1066
  one chip per file with a progress bar, settling to `ready` or `error` (with
971
1067
  retry / remove). On send, the ready files' refs render as read-only chips on
@@ -981,7 +1077,7 @@ hosts that both arrange the page the way it expects.
981
1077
  `(file, onProgress) => Promise<AttachmentRef>` — swaps the built-in multipart
982
1078
  upload for a custom one (a resumable `tus-js-client` adapter, direct-to-S3
983
1079
  multipart, …) without touching the tray, chips, or AG-UI wire. When set, the
984
- 📎 affordance appears even with no `data-attachments-url`. Defaults to the
1080
+ affordance appears even with no `data-attachments-url`. Defaults to the
985
1081
  built-in `uploadAttachment`.
986
1082
  - **New exports:** `uploadAttachment` + `UploadOptions` + `UploadHandler`, the
987
1083
  `AttachmentRef` type, and `messageAttachments`. `AgUiClient.send` gains an
@@ -1250,7 +1346,9 @@ hosts that both arrange the page the way it expects.
1250
1346
  ### Notes
1251
1347
  - First release — exercising the automated npm OIDC publish pipeline end-to-end.
1252
1348
 
1253
- [Unreleased]: https://github.com/Artui/ag-ui-web-component/compare/v0.22.0...HEAD
1349
+ [Unreleased]: https://github.com/Artui/ag-ui-web-component/compare/v0.23.1...HEAD
1350
+ [0.23.1]: https://github.com/Artui/ag-ui-web-component/compare/v0.23.0...v0.23.1
1351
+ [0.23.0]: https://github.com/Artui/ag-ui-web-component/compare/v0.22.0...v0.23.0
1254
1352
  [0.22.0]: https://github.com/Artui/ag-ui-web-component/compare/v0.21.0...v0.22.0
1255
1353
  [0.21.0]: https://github.com/Artui/ag-ui-web-component/compare/v0.20.1...v0.21.0
1256
1354
  [0.20.1]: https://github.com/Artui/ag-ui-web-component/compare/v0.20.0...v0.20.1
package/README.md CHANGED
@@ -420,7 +420,7 @@ AG-UI has no server-side cancel route: cancelling **aborts the streaming request
420
420
 
421
421
  - Partial assistant text already streamed **stays in the transcript** and is persisted via
422
422
  `onPersist`, so a reload shows the truncated exchange. A muted **"⏹ Stopped"** note is appended
423
- (`.stopped-note`) — a deliberate stop is not an error, so no ⚠️ bubble.
423
+ (`.stopped-note`) — a deliberate stop is not an error, so no bubble.
424
424
  - The run loop stops: tool calls collected before the abort are **not executed**, and no further
425
425
  round starts. A frontend tool handler already running completes, but its result doesn't trigger
426
426
  a re-run.
@@ -784,7 +784,7 @@ A gated call carries the decision (`approved by you` / `declined by you`, part
784
784
  from the server-side approval interrupt alike. The prompt itself disappears once answered: a
785
785
  prompt and a record are different objects, and the record is the card.
786
786
 
787
- **The annotation is session-scoped**, like the "run interrupted" notice. AG-UI carries no
787
+ **The annotation is session-scoped**, like the "run interrupted" notice. AG-UI carries no
788
788
  approval message — the answer rides `resume[]` as transient run input — so a reload restores the
789
789
  tool call and its result but not the note that a human waved it through. If you need "who
790
790
  approved what" durably, that is an audit concern rather than a transcript one; record it
@@ -827,14 +827,14 @@ is what positions the grip.
827
827
  A drag writes `--ag-ui-width` / `--ag-ui-height` on the host as custom
828
828
  properties.
829
829
 
830
- **That alone does not leave placement in charge** — an inline custom property
830
+ **That alone does not leave placement in charge** — an inline custom property
831
831
  still outranks a `:host([placement=…])` rule setting the same property. So the
832
832
  component enforces the split directly: **a placement owns the axes it fixes**,
833
833
  and a dragged or persisted size is only ever applied to the ones it leaves free.
834
834
  Switching placement hands the owned axes back. Without that, a height dragged
835
835
  while floating capped a docked sidebar that had asked for `100vh`.
836
836
 
837
- **A host rule that sizes the element wins over both.** `ag-ui-chat { flex: 1 }`
837
+ **A host rule that sizes the element wins over both.** `ag-ui-chat { flex: 1 }`
838
838
  stretches the panel to its container and the dragged width has no visible
839
839
  effect — which reads as a broken control rather than as your stylesheet winning.
840
840
  Give the element `flex: 0 1 auto` (plus `max-width: 100%`) if it lives in a flex
@@ -1149,7 +1149,7 @@ error — a history affordance that fails is empty, not broken.
1149
1149
  ## File uploads
1150
1150
 
1151
1151
  Set **`data-attachments-url`** (django-ag-ui's `AttachmentsView`) to let the user attach files
1152
- to a message. A 📎 button and drag-and-drop appear on the composer; each picked file uploads
1152
+ to a message. A button and drag-and-drop appear on the composer; each picked file uploads
1153
1153
  out-of-band (multipart, with the element's `headers`) and shows a chip in a pending tray —
1154
1154
  `uploading` (with a progress bar) → `ready`, or `error` with a retry. On send, the ready files'
1155
1155
  **refs** ride on the user bubble as read-only chips and the agent reads their contents
@@ -1178,7 +1178,7 @@ See [Authenticating requests](#authenticating-requests).
1178
1178
  `uploadHandler`. Set your own to use a different transport — a resumable
1179
1179
  [`tus-js-client`](https://github.com/tus/tus-js-client) adapter, direct-to-S3 multipart, etc.
1180
1180
  — without touching the tray, the chips, or the AG-UI wire (refs are transport-agnostic). The
1181
- handler is `(file, onProgress) => Promise<AttachmentRef>`; when set, the 📎 affordance appears
1181
+ handler is `(file, onProgress) => Promise<AttachmentRef>`; when set, the affordance appears
1182
1182
  even with no `data-attachments-url`, and your handler owns its own endpoint and headers:
1183
1183
 
1184
1184
  ```js
@@ -1409,7 +1409,7 @@ have to hand-tune the variables:
1409
1409
  See [`src/ui/styles.ts`](src/ui/styles.ts) for the full variable + preset list. The
1410
1410
  [`demo/`](demo/) live playground (`node demo/mock-server.mjs`) flips theme, density, placement,
1411
1411
  text-animation, tool-display, and the answer well live from a single page, and demos the
1412
- streamed thoughts region, the 🎤 mic, and the header theme toggle.
1412
+ streamed thoughts region, the mic, and the header theme toggle.
1413
1413
 
1414
1414
  ### Parts and slots
1415
1415