@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.
Files changed (124) hide show
  1. package/CHANGELOG.md +149 -36
  2. package/README.md +140 -9
  3. package/dist/ag-ui-web-component.bundle.js +113 -122
  4. package/dist/ag-ui-web-component.bundle.js.map +3 -3
  5. package/dist/constants.d.ts +71 -85
  6. package/dist/constants.d.ts.map +1 -1
  7. package/dist/core/ag_ui_chat.d.ts +105 -127
  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 +38 -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 +10 -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.d.ts +2 -2
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +357 -383
  35. package/dist/index.js.map +2 -2
  36. package/dist/skills/fill_template.d.ts +4 -5
  37. package/dist/skills/fill_template.d.ts.map +1 -1
  38. package/dist/skills/parse_skills.d.ts.map +1 -1
  39. package/dist/skills/skill.d.ts +7 -8
  40. package/dist/skills/skill.d.ts.map +1 -1
  41. package/dist/tools/client_tool_registry.d.ts +2 -2
  42. package/dist/tools/page_action_tools.d.ts +7 -10
  43. package/dist/tools/page_action_tools.d.ts.map +1 -1
  44. package/dist/tools/page_state.d.ts +5 -8
  45. package/dist/tools/page_state.d.ts.map +1 -1
  46. package/dist/tools/route_map.d.ts +7 -10
  47. package/dist/tools/route_map.d.ts.map +1 -1
  48. package/dist/ui/approval_card.d.ts +15 -20
  49. package/dist/ui/approval_card.d.ts.map +1 -1
  50. package/dist/ui/attach_copy_buttons.d.ts +4 -10
  51. package/dist/ui/attach_copy_buttons.d.ts.map +1 -1
  52. package/dist/ui/attachment_chips.d.ts +7 -10
  53. package/dist/ui/attachment_chips.d.ts.map +1 -1
  54. package/dist/ui/attachment_tray.d.ts +6 -6
  55. package/dist/ui/checkpoint_menu.d.ts +7 -8
  56. package/dist/ui/checkpoint_menu.d.ts.map +1 -1
  57. package/dist/ui/confirmation_card.d.ts +10 -15
  58. package/dist/ui/confirmation_card.d.ts.map +1 -1
  59. package/dist/ui/question_card.d.ts +12 -15
  60. package/dist/ui/question_card.d.ts.map +1 -1
  61. package/dist/ui/relative_time.d.ts +5 -7
  62. package/dist/ui/relative_time.d.ts.map +1 -1
  63. package/dist/ui/render_markdown.d.ts +8 -8
  64. package/dist/ui/render_markdown.d.ts.map +1 -1
  65. package/dist/ui/resize_handle.d.ts +21 -34
  66. package/dist/ui/resize_handle.d.ts.map +1 -1
  67. package/dist/ui/run_notice.d.ts +5 -7
  68. package/dist/ui/run_notice.d.ts.map +1 -1
  69. package/dist/ui/skills_menu.d.ts +4 -5
  70. package/dist/ui/skills_menu.d.ts.map +1 -1
  71. package/dist/ui/styles.d.ts +1 -1
  72. package/dist/ui/styles.d.ts.map +1 -1
  73. package/dist/ui/thoughts_block.d.ts +9 -11
  74. package/dist/ui/thoughts_block.d.ts.map +1 -1
  75. package/dist/ui/thread_drawer.d.ts +6 -5
  76. package/dist/ui/thread_drawer.d.ts.map +1 -1
  77. package/dist/ui/tool_call_card.d.ts +17 -25
  78. package/dist/ui/tool_call_card.d.ts.map +1 -1
  79. package/dist/ui/ui_strings.d.ts +6 -12
  80. package/dist/ui/ui_strings.d.ts.map +1 -1
  81. package/dist/ui/voice_input.d.ts +10 -11
  82. package/dist/ui/voice_input.d.ts.map +1 -1
  83. package/package.json +1 -1
  84. package/src/constants.ts +74 -87
  85. package/src/core/ag_ui_chat.ts +313 -269
  86. package/src/core/agui_client.ts +60 -71
  87. package/src/core/attachment.ts +9 -14
  88. package/src/core/conversation_store.ts +55 -33
  89. package/src/core/create_http_agent.ts +18 -22
  90. package/src/core/remote_conversation_store.ts +28 -15
  91. package/src/core/run_index.ts +14 -23
  92. package/src/core/transcribe_audio.ts +9 -10
  93. package/src/core/upload_attachment.ts +18 -21
  94. package/src/core/utils.ts +4 -6
  95. package/src/dom/animations.ts +33 -43
  96. package/src/dom/dom_driver.ts +7 -7
  97. package/src/dom/native_setter.ts +11 -12
  98. package/src/index.ts +3 -0
  99. package/src/skills/fill_template.ts +4 -5
  100. package/src/skills/parse_skills.ts +3 -4
  101. package/src/skills/skill.ts +7 -8
  102. package/src/tools/client_tool_registry.ts +2 -2
  103. package/src/tools/page_action_tools.ts +12 -15
  104. package/src/tools/page_state.ts +5 -8
  105. package/src/tools/route_map.ts +15 -19
  106. package/src/ui/approval_card.ts +15 -20
  107. package/src/ui/attach_copy_buttons.ts +9 -18
  108. package/src/ui/attachment_chips.ts +7 -10
  109. package/src/ui/attachment_tray.ts +6 -6
  110. package/src/ui/checkpoint_menu.ts +7 -8
  111. package/src/ui/confirmation_card.ts +10 -15
  112. package/src/ui/question_card.ts +12 -15
  113. package/src/ui/relative_time.ts +5 -7
  114. package/src/ui/render_markdown.ts +25 -51
  115. package/src/ui/resize_handle.ts +25 -38
  116. package/src/ui/run_notice.ts +9 -12
  117. package/src/ui/skills_menu.ts +4 -5
  118. package/src/ui/styles.ts +92 -101
  119. package/src/ui/thoughts_block.ts +11 -13
  120. package/src/ui/thread_drawer.ts +6 -5
  121. package/src/ui/tool_call_card.ts +22 -32
  122. package/src/ui/ui_strings.ts +6 -12
  123. package/src/ui/voice_input.ts +10 -11
  124. 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
- Neither our tests nor the consumer's could have found the second one:
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. The thread-history
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. **The cursor rules used `=` and did match**, so the pointer followed
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
- - **`placement="side"` (and `sidebar`) stopped being full height once the
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
- **The previous release claimed this was already handled, and the reasoning
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
- **It writes the custom properties, not inline `width` / `height`.** The
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
- **Which corner the grip sits on is measured, not assumed.** A resize is
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
- - **Picking a skill now sends it.** It used to write the text into the
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
- **The prompt was the leak.** A catalog is either a fetched `GET` or an
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
- Breaking for anyone styling `tool-card-result` as a single combined block.
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
- - **`data-tool-display` is now live.** Changing it restyles every card already
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
- - **The confirmation card was appended to the wrong parent**, and it is the
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
- **Session-scoped, like the "run interrupted" notice.** AG-UI carries no
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
- Found because the demo harness was doing exactly this, which is the
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
- Two harness defects were making the component look broken. Its follow-up
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
- **It reports failure rather than always claiming success.** The Clipboard
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
- **With no continuable runs the panel holds no controls**, so the panel
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. Unlike the built-in Send it does
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
- **This is what makes `sendMessage` usable with files at all.** The tray only
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. **The symptom is an affordance that
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. The attributes that genuinely *are* re-read per use —
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. The fallbacks are what hid it: they made an unthemed panel look
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
- **Vitest 4 takes a provider *instance*, not the string `"playwright"`.**
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
- **TypeScript 7 requires `rootDir` explicitly** (TS5011) instead of
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
- **One was a flaw in the test harness, not a missing test.** `makeFakeAgent`
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
- **A correctness requirement, not an optimisation.** DOMPurify 3.4.8+
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
- **The experiment settles what the pin never could**: dompurify 3.4.13
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
- **`overrides` now live in `pnpm-workspace.yaml`**, not the `pnpm` field in
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
- - **Four `dompurify` advisories are knowingly left open** (three LOW, one
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
- **The pin was previously a caret range**, so the hold existed only in the
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
- Not to be confused with the existing `Skill` catalog — that is a *human*
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 🎤 mic button in the composer (part
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 `🔧` glyph. Re-theme via
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 📎 picker + drag-and-drop on the composer. Each file uploads
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
- 📎 affordance appears even with no `data-attachments-url`. Defaults to the
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.23.0...HEAD
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