@artooi/ag-ui-web-component 0.19.0 → 0.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,94 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.20.1] — 2026-08-11
11
+
12
+ ### Fixed
13
+
14
+ - ⛔ **`placement="side"` (and `sidebar`) stopped being full height once the
15
+ panel had been resized.** A dragged size is written as a custom property on
16
+ the host, and an inline custom property **outranks a `:host([placement=…])`
17
+ rule setting the same property** — so a height dragged while floating capped a
18
+ docked sidebar that had asked for `100vh`. Since the size persists per tab,
19
+ one drag broke every later visit.
20
+
21
+ ⚠ **The previous release claimed this was already handled, and the reasoning
22
+ was wrong.** Writing `--ag-ui-height` rather than inline `height` was supposed
23
+ to leave placement with the final say; it does not, because the indirection
24
+ changes nothing about the cascade. The fix is explicit rather than
25
+ cascade-dependent: **a placement owns the axes it fixes**, a persisted size is
26
+ applied only to the axes it leaves free, and switching placement hands the
27
+ owned axes back.
28
+
29
+ ⇒ *"I used the more specific-looking mechanism" is not a substitute for
30
+ checking which declaration actually wins.*
31
+
32
+ ## [0.20.0] — 2026-08-11
33
+
34
+ ### Added
35
+
36
+ - **The panel is resizable.** A drag handle on the corner the layout grows
37
+ toward (or the inner edge when docked), with the size persisted per tab and
38
+ restored before the first paint. Arrow keys resize from the keyboard, since a pointer-only control
39
+ has no equivalent elsewhere in the UI; style it via the `resize-handle` part.
40
+
41
+ Which axes are draggable is the placement's call: `full` and `page` get no
42
+ handle at all (a `100vw`/`100vh` layout has nothing to drag), `sidebar` /
43
+ `side` get width only, everything else gets both.
44
+
45
+ ⚠ **It writes the custom properties, not inline `width` / `height`.** The
46
+ placement rules set those same properties, so an inline dimension would
47
+ outrank them — a panel dragged while floating would keep that width after
48
+ switching to fullscreen.
49
+
50
+ ⭐ **Which corner the grip sits on is measured, not assumed.** A resize is
51
+ computed from the edge that stays still, and which edge that is belongs to the
52
+ *host's* layout rather than to `placement` — a floating panel is pinned
53
+ bottom-right, an embedded one goes wherever the page's CSS puts it. Deriving
54
+ it from `placement` was wrong for any host that right-aligns the element, and
55
+ the symptom was bad enough to read as a broken control: the panel shrank when
56
+ dragged outward and travelled by its opposite corner. The element now probes
57
+ its own geometry and reflects the result as `data-resize-anchor`.
58
+
59
+ ### Changed
60
+
61
+ - **The `/` palette leads with the command.** Each row now reads
62
+ `/fill-article Fill the article` rather than the label alone (part
63
+ `skill-item-token`), and a chip's tooltip names the token it stands for. A
64
+ palette that shows only labels cannot teach its own vocabulary — a user who
65
+ never sees `/fill-article` has no way to learn to type it.
66
+
67
+ - **A skill blocked on an unfilled `{placeholder}` now hands back the
68
+ template.** The partially-filled prompt goes into the composer with the first
69
+ unresolved placeholder selected, so the next keystroke replaces it. Previously
70
+ the pick was refused with a hint and whatever the user had typed to open the
71
+ palette — a lone `/` — was left in the composer, which said nothing about what
72
+ the skill wanted or how to supply it. The hint now says what to do, too.
73
+
74
+ - ⚠ **Picking a skill now sends it.** It used to write the text into the
75
+ composer and wait for a second click unless the skill set
76
+ `sendImmediately: true` — so the default behaviour of a shortcut was to not
77
+ take the shortcut. Set `sendImmediately: false` to keep pre-filling, which is
78
+ worth doing where the user is expected to edit before sending.
79
+
80
+ - **`Skill.prompt` is now optional, and omitting it is the better default for
81
+ anything internal.** A skill with no prompt is **server-resolved**: picking it
82
+ sends the bare `/name` token and the agent expands it, from the harness
83
+ `Skills` capability or the server's own instructions.
84
+
85
+ ⛔ **The prompt was the leak.** A catalog is either a fetched `GET` or an
86
+ inline `data-skills` attribute sitting in the page source, and a skill is
87
+ often where a project's internal workflow is written down most plainly — so
88
+ the client-side catalog published it to anyone who opened the page. Sending a
89
+ token instead keeps the wording on the server entirely, which is what
90
+ "trigger a `/command` without exposing the prompt" actually requires; hiding
91
+ the text behind a chip label would only have moved it off screen.
92
+
93
+ `parseSkills` accepts a catalog entry with no `prompt` rather than dropping
94
+ it — requiring the field would have silently discarded exactly the skills
95
+ whose wording was kept off the browser. Pairs with `django-ag-ui`'s
96
+ `SkillSpec.prompt` becoming optional.
97
+
10
98
  ## [0.19.0] — 2026-08-11
11
99
 
12
100
  ### Changed
@@ -45,10 +133,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
45
133
 
46
134
  ### Added
47
135
 
48
- - **A gated call records the decision.** The tool card carries `approved by you`
49
- / `declined by you` (part `tool-card-decision`, attribute `data-decision`).
50
- Previously only a *refusal* left a trace an approved call simply ran, making
51
- a gated call's transcript identical to one that was never gated.
136
+ - **A gated call records the decision**, from the client-side confirmation card
137
+ **and** the server-side approval interrupt. The tool card carries
138
+ `approved by you` / `declined by you` (part `tool-card-decision`, attribute
139
+ `data-decision`). Previously only a *refusal* left a trace an approved call
140
+ simply ran, making a gated call's transcript identical to one that was never
141
+ gated, and the server-side gate left nothing at all even though it is the one
142
+ guarding tools that run on the backend.
143
+
144
+ ⚠ **Session-scoped, like the "run interrupted" notice.** AG-UI carries no
145
+ approval message — the answer rides `resume[]` as transient run input — so a
146
+ reload restores the call and its result but not the note. Durable "who
147
+ approved what" is an audit concern, not a transcript one.
52
148
 
53
149
  - **Each header control takes its own icon slot** — `icon-history`,
54
150
  `icon-checkpoints`, `icon-new`, `icon-collapse` — with the built-in glyph as
@@ -64,6 +160,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
64
160
 
65
161
  ### Fixed
66
162
 
163
+ - **A server that reuses a message id now gets a warning.** `@ag-ui/client`
164
+ appends to a message id already in its history rather than starting a new one,
165
+ so two answers merge into one transcript entry — silently, and the merged
166
+ entry is what gets persisted. The protocol has no rule to enforce and refusing
167
+ the event would be worse than the merge, so this warns and continues.
168
+
169
+ ⭐ Found because the demo harness was doing exactly this, which is the
170
+ argument for the harness in miniature: the bug was the consumer's, the
171
+ invisibility was ours.
172
+
173
+ - **The demo playground covers the surface it is meant to demonstrate.** The
174
+ scripted agent now dispatches on the latest turn — a server-resolved skill, a
175
+ tool that throws, an `ask_user` question, or the form-filling script — instead
176
+ of replaying one script for everything, and the page gained header-icon,
177
+ German-strings and reset-size controls plus a short "what to try" guide.
178
+
179
+ ⚠ Two harness defects were making the component look broken. Its follow-up
180
+ detection matched **any** tool message in the thread, so once a conversation
181
+ had run a single tool every later turn answered "Done" to everything. And the
182
+ page forced `flex: 1` on the element, which silently outranks the width a
183
+ resize writes — the drag worked and nothing moved.
184
+
67
185
  - **The demo harness reused message ids**, which produced three symptoms that
68
186
  all read as component bugs and were none of them. It streamed every follow-up
69
187
  answer under a hardcoded id, and `@ag-ui/client` appends to a message id
@@ -943,7 +1061,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
943
1061
  ### Notes
944
1062
  - First release — exercising the automated npm OIDC publish pipeline end-to-end.
945
1063
 
946
- [Unreleased]: https://github.com/Artui/ag-ui-web-component/compare/v0.19.0...HEAD
1064
+ [Unreleased]: https://github.com/Artui/ag-ui-web-component/compare/v0.20.1...HEAD
1065
+ [0.20.1]: https://github.com/Artui/ag-ui-web-component/compare/v0.20.0...v0.20.1
1066
+ [0.20.0]: https://github.com/Artui/ag-ui-web-component/compare/v0.19.0...v0.20.0
947
1067
  [0.19.0]: https://github.com/Artui/ag-ui-web-component/compare/v0.18.0...v0.19.0
948
1068
  [0.18.0]: https://github.com/Artui/ag-ui-web-component/compare/v0.17.0...v0.18.0
949
1069
  [0.17.0]: https://github.com/Artui/ag-ui-web-component/compare/v0.16.0...v0.17.0
package/README.md CHANGED
@@ -529,10 +529,16 @@ visible. Style them via the `tool-card-args` / `tool-card-result` parts, their h
529
529
  transcript, the way `data-answer-well` does — the modes are pure visibility over one DOM shape,
530
530
  selected by the shadow CSS from the host attribute.
531
531
 
532
- A call gated behind the confirmation card also carries the decision (`approved by you` /
533
- `declined by you`, part `tool-card-decision`, attribute `data-decision`). The prompt itself
534
- disappears once answered: a prompt and a record are different objects, and the record is the
535
- card.
532
+ A gated call carries the decision (`approved by you` / `declined by you`, part
533
+ `tool-card-decision`, attribute `data-decision`) from the client-side confirmation card and
534
+ from the server-side approval interrupt alike. The prompt itself disappears once answered: a
535
+ prompt and a record are different objects, and the record is the card.
536
+
537
+ ⚠ **The annotation is session-scoped**, like the "run interrupted" notice. AG-UI carries no
538
+ approval message — the answer rides `resume[]` as transient run input — so a reload restores the
539
+ tool call and its result but not the note that a human waved it through. If you need "who
540
+ approved what" durably, that is an audit concern rather than a transcript one; record it
541
+ server-side.
536
542
 
537
543
  If a tool's schema carries an `x-summary` string (use `X_SUMMARY_KEY`), the card shows it on the
538
544
  label instead of the raw tool name.
@@ -549,6 +555,48 @@ speed; the spin respects `prefers-reduced-motion`).
549
555
 
550
556
  ---
551
557
 
558
+ ## Resizing the panel
559
+
560
+ The panel carries a drag handle on its leading corner (or leading edge, docked),
561
+ so a reader can widen it without the host having to re-theme anything.
562
+
563
+ - `placement="full"` / `placement="page"` get **no handle** — a full-bleed layout
564
+ is `100vw`/`100vh` by definition, so there is nothing to drag.
565
+ - `placement="sidebar"` / `placement="side"` get **width only**; the placement
566
+ owns the height.
567
+ - Everything else resizes on both axes.
568
+
569
+ **The grip sits at the corner your layout grows toward, and the component
570
+ measures which one that is.** A resize has to be computed from the edge that
571
+ stays still, and that belongs to *your* CSS rather than to `placement` — a
572
+ floating panel is pinned bottom-right, an embedded one goes wherever the page
573
+ puts it. The element probes its own geometry and reflects the result as
574
+ `data-resize-anchor` (e.g. `bottom-right` means those two edges are fixed), which
575
+ is what positions the grip.
576
+
577
+ A drag writes `--ag-ui-width` / `--ag-ui-height` on the host as custom
578
+ properties.
579
+
580
+ ⚠ **That alone does not leave placement in charge** — an inline custom property
581
+ still outranks a `:host([placement=…])` rule setting the same property. So the
582
+ component enforces the split directly: **a placement owns the axes it fixes**,
583
+ and a dragged or persisted size is only ever applied to the ones it leaves free.
584
+ Switching placement hands the owned axes back. Without that, a height dragged
585
+ while floating capped a docked sidebar that had asked for `100vh`.
586
+
587
+ ⚠ **A host rule that sizes the element wins over both.** `ag-ui-chat { flex: 1 }`
588
+ stretches the panel to its container and the dragged width has no visible
589
+ effect — which reads as a broken control rather than as your stylesheet winning.
590
+ Give the element `flex: 0 1 auto` (plus `max-width: 100%`) if it lives in a flex
591
+ container.
592
+
593
+ The size persists per tab (`sessionStorage`, namespaced per element like the
594
+ collapsed and theme preferences) and is restored before the first paint.
595
+ Arrow keys resize from the keyboard (`Shift` for a larger step); style the grip
596
+ via the `resize-handle` part.
597
+
598
+ ---
599
+
552
600
  ## Markdown rendering
553
601
 
554
602
  Assistant bubbles render sanitized markdown/HTML via [`marked`](https://www.npmjs.com/package/marked)
@@ -614,7 +662,7 @@ opt-in via two attributes:
614
662
  <ag-ui-chat endpoint="/agent/" data-prompt-chips="true" data-slash-commands="true"></ag-ui-chat>
615
663
  ```
616
664
 
617
- A `Skill` is `{ name, title, description?, prompt, sendImmediately?, chip? }`. Skills are merged
665
+ A `Skill` is `{ name, title, description?, prompt?, sendImmediately?, chip? }`. Skills are merged
618
666
  from three sources — **backend → embed → client** (later wins by `name`):
619
667
 
620
668
  - `data-skills-url` — a JSON endpoint, fetched with the element's `headers`.
@@ -623,11 +671,21 @@ from three sources — **backend → embed → client** (later wins by `name`):
623
671
 
624
672
  ```js
625
673
  chat.setSkills([
674
+ // Server-resolved: no prompt here, so picking it sends the bare "/triage"
675
+ // token and the agent decides what it means.
676
+ { name: "triage", title: "Triage this", chip: true },
677
+ // Client-side: the page owns the wording and fills the placeholders.
626
678
  { name: "summarize", title: "Summarize page", prompt: "Summarize {title}.", chip: true },
627
679
  ]);
628
680
  ```
629
681
 
630
- A skill `prompt` may contain `{placeholder}` tokens; the `skillContext` property
682
+ **Prefer omitting `prompt` for anything internal.** A skill is often where a project's workflow is
683
+ written down most plainly, and a catalog is either a plain `GET` or sits in the page source — so
684
+ shipping the wording to the browser publishes it. Without a `prompt` the component sends `/name`
685
+ and the agent expands it (from the harness `Skills` capability, or your own instructions); the text
686
+ never leaves the server. `django-ag-ui`'s `SkillRegistry` supports this by leaving `prompt` unset.
687
+
688
+ A skill that *does* carry a `prompt` may use `{placeholder}` tokens; the `skillContext` property
631
689
  (`() => Record<string, unknown>`) supplies the values, filled in before send. A missing placeholder
632
690
  blocks the send and shows a hint instead.
633
691
 
@@ -635,6 +693,10 @@ blocks the send and shows a hint instead.
635
693
  chat.skillContext = () => ({ title: document.title });
636
694
  ```
637
695
 
696
+ **Picking a skill sends it.** A chip that needs a second click to do anything is a two-step
697
+ shortcut. Set `sendImmediately: false` on a prompt-carrying skill to pre-fill the composer instead —
698
+ useful when the user is expected to edit before sending. A server-resolved skill always sends.
699
+
638
700
  ---
639
701
 
640
702
  ## MPA durability: surviving full page reloads