@artooi/ag-ui-web-component 0.20.0 → 0.21.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 (41) hide show
  1. package/CHANGELOG.md +135 -1
  2. package/README.md +286 -20
  3. package/dist/ag-ui-web-component.bundle.js +407 -382
  4. package/dist/ag-ui-web-component.bundle.js.map +4 -4
  5. package/dist/core/ag_ui_chat.d.ts +82 -1
  6. package/dist/core/ag_ui_chat.d.ts.map +1 -1
  7. package/dist/core/create_http_agent.d.ts +9 -0
  8. package/dist/core/create_http_agent.d.ts.map +1 -1
  9. package/dist/core/remote_conversation_store.d.ts +8 -1
  10. package/dist/core/remote_conversation_store.d.ts.map +1 -1
  11. package/dist/core/run_index.d.ts +8 -1
  12. package/dist/core/run_index.d.ts.map +1 -1
  13. package/dist/core/transcribe_audio.d.ts +7 -0
  14. package/dist/core/transcribe_audio.d.ts.map +1 -1
  15. package/dist/core/upload_attachment.d.ts +9 -0
  16. package/dist/core/upload_attachment.d.ts.map +1 -1
  17. package/dist/core/utils.d.ts +12 -0
  18. package/dist/core/utils.d.ts.map +1 -0
  19. package/dist/dom/animations.d.ts +51 -11
  20. package/dist/dom/animations.d.ts.map +1 -1
  21. package/dist/dom/dom_driver.d.ts +12 -7
  22. package/dist/dom/dom_driver.d.ts.map +1 -1
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +766 -430
  26. package/dist/index.js.map +3 -3
  27. package/dist/ui/styles.d.ts +1 -1
  28. package/dist/ui/styles.d.ts.map +1 -1
  29. package/package.json +1 -1
  30. package/src/core/ag_ui_chat.ts +292 -31
  31. package/src/core/create_http_agent.ts +14 -3
  32. package/src/core/remote_conversation_store.ts +25 -6
  33. package/src/core/run_index.ts +27 -5
  34. package/src/core/transcribe_audio.ts +20 -5
  35. package/src/core/upload_attachment.ts +13 -0
  36. package/src/core/utils.ts +18 -0
  37. package/src/dom/animations.ts +175 -31
  38. package/src/dom/dom_driver.ts +18 -12
  39. package/src/index.ts +2 -0
  40. package/src/ui/styles.ts +377 -352
  41. package/src/version.ts +1 -1
package/dist/index.js CHANGED
@@ -134,11 +134,29 @@ function setNativeChecked(el, checked) {
134
134
 
135
135
  // src/dom/animations.ts
136
136
  var ACCENT = "#4f46e5";
137
+ var ACCENT_HALO = "rgba(79, 70, 229, 0.4)";
138
+ var ACCENT_PROPERTY = "--ag-ui-accent";
137
139
  function delay(ms) {
138
140
  return new Promise((resolve) => {
139
141
  setTimeout(resolve, ms);
140
142
  });
141
143
  }
144
+ function prefersReducedMotion() {
145
+ return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
146
+ }
147
+ function motionDelay(ms) {
148
+ if (ms <= 0 || prefersReducedMotion()) {
149
+ return Promise.resolve();
150
+ }
151
+ return delay(ms);
152
+ }
153
+ function accentColor(el, fallback) {
154
+ const themed = window.getComputedStyle(el).getPropertyValue(ACCENT_PROPERTY).trim();
155
+ return themed === "" ? fallback : themed;
156
+ }
157
+ function ringShadow(el) {
158
+ return `0 0 0 3px ${accentColor(el, ACCENT_HALO)}`;
159
+ }
142
160
  async function typeInto(el, value, options = {}) {
143
161
  const charDelayMs = options.charDelayMs ?? 35;
144
162
  setNativeValue(el, "");
@@ -156,33 +174,75 @@ async function highlightThenClick(el, options = {}) {
156
174
  const highlightMs = options.highlightMs ?? 280;
157
175
  const previousOutline = el.style.outline;
158
176
  const previousOffset = el.style.outlineOffset;
159
- el.style.outline = `2px solid ${ACCENT}`;
177
+ el.style.outline = `2px solid ${accentColor(el, ACCENT)}`;
160
178
  el.style.outlineOffset = "2px";
161
179
  await delay(highlightMs);
162
180
  el.style.outline = previousOutline;
163
181
  el.style.outlineOffset = previousOffset;
164
182
  el.click();
165
183
  }
166
- function scrollIntoCenterView(el) {
167
- el.scrollIntoView({ block: "center", inline: "nearest", behavior: "smooth" });
184
+ var SCROLL_SETTLE_MS = 600;
185
+ var SCROLL_START_MS = 100;
186
+ function scrollIntoCenterView(el, options = {}) {
187
+ const reduced = prefersReducedMotion();
188
+ el.scrollIntoView({
189
+ block: "center",
190
+ inline: "nearest",
191
+ behavior: reduced ? "auto" : "smooth"
192
+ });
193
+ if (reduced) {
194
+ return Promise.resolve();
195
+ }
196
+ return new Promise((resolve) => {
197
+ let timer;
198
+ const settle = () => {
199
+ clearTimeout(timer);
200
+ document.removeEventListener("scroll", started, true);
201
+ document.removeEventListener("scrollend", settle, true);
202
+ resolve();
203
+ };
204
+ const started = () => {
205
+ document.removeEventListener("scroll", started, true);
206
+ clearTimeout(timer);
207
+ timer = setTimeout(settle, options.settleMs ?? SCROLL_SETTLE_MS);
208
+ };
209
+ timer = setTimeout(settle, SCROLL_START_MS);
210
+ document.addEventListener("scroll", started, true);
211
+ document.addEventListener("scrollend", settle, true);
212
+ });
168
213
  }
169
- async function focusWithFlash(el, options = {}) {
170
- const flashMs = options.flashMs ?? 200;
171
- el.focus();
172
- const previousShadow = el.style.boxShadow;
173
- el.style.boxShadow = `0 0 0 3px rgba(79, 70, 229, 0.4)`;
174
- await delay(flashMs);
175
- el.style.boxShadow = previousShadow;
214
+ var FLASH_MS = 1200;
215
+ var FLASH_FADE_RATIO = 1 / 3;
216
+ async function flashRing(el, options, focusByDefault) {
217
+ if (options.focus ?? focusByDefault) {
218
+ el.focus({ preventScroll: true });
219
+ }
220
+ const flashMs = options.flashMs ?? FLASH_MS;
221
+ if (flashMs <= 0) {
222
+ return;
223
+ }
224
+ const previousOutline = el.style.outline;
225
+ const previousOffset = el.style.outlineOffset;
226
+ const previousTransition = el.style.transition;
227
+ const color = options.color ?? accentColor(el, ACCENT);
228
+ el.style.outline = `3px solid ${color}`;
229
+ el.style.outlineOffset = "2px";
230
+ const fadeMs = prefersReducedMotion() ? 0 : Math.round(flashMs * FLASH_FADE_RATIO);
231
+ await delay(flashMs - fadeMs);
232
+ if (fadeMs > 0) {
233
+ el.style.transition = `outline-color ${fadeMs}ms ease-out`;
234
+ el.style.outline = "3px solid transparent";
235
+ await delay(fadeMs);
236
+ }
237
+ el.style.outline = previousOutline;
238
+ el.style.outlineOffset = previousOffset;
239
+ el.style.transition = previousTransition;
176
240
  }
177
- var RING = "0 0 0 3px rgba(79, 70, 229, 0.4)";
178
- function prefersReducedMotion() {
179
- return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
241
+ function flash(el, options = {}) {
242
+ return flashRing(el, options, false);
180
243
  }
181
- function motionDelay(ms) {
182
- if (ms <= 0 || prefersReducedMotion()) {
183
- return Promise.resolve();
184
- }
185
- return delay(ms);
244
+ function focusWithFlash(el, options = {}) {
245
+ return flashRing(el, options, true);
186
246
  }
187
247
  async function pressThenClick(el, options = {}) {
188
248
  const pressMs = options.pressMs ?? 140;
@@ -191,7 +251,7 @@ async function pressThenClick(el, options = {}) {
191
251
  const previousShadow = el.style.boxShadow;
192
252
  el.style.transition = "transform 80ms ease";
193
253
  el.style.transform = "scale(0.96)";
194
- el.style.boxShadow = RING;
254
+ el.style.boxShadow = ringShadow(el);
195
255
  await motionDelay(pressMs);
196
256
  el.style.transform = previousTransform;
197
257
  el.style.transition = previousTransition;
@@ -214,7 +274,7 @@ async function selectOption(el, value, options = {}) {
214
274
  const highlightMs = options.highlightMs ?? 220;
215
275
  const previousOutline = el.style.outline;
216
276
  const previousOffset = el.style.outlineOffset;
217
- el.style.outline = `2px solid ${ACCENT}`;
277
+ el.style.outline = `2px solid ${accentColor(el, ACCENT)}`;
218
278
  el.style.outlineOffset = "2px";
219
279
  await motionDelay(highlightMs);
220
280
  setNativeValue(el, option.value);
@@ -226,7 +286,7 @@ async function selectOption(el, value, options = {}) {
226
286
  async function toggleControl(el, checked, options = {}) {
227
287
  const flashMs = options.flashMs ?? 200;
228
288
  const previousShadow = el.style.boxShadow;
229
- el.style.boxShadow = RING;
289
+ el.style.boxShadow = ringShadow(el);
230
290
  await motionDelay(flashMs);
231
291
  setNativeChecked(el, checked);
232
292
  el.dispatchEvent(new Event("input", { bubbles: true }));
@@ -4401,151 +4461,186 @@ var SkillsMenu = class {
4401
4461
 
4402
4462
  // src/ui/styles.ts
4403
4463
  var STYLES = `
4464
+ /* \u2500\u2500 Token defaults \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
4465
+ Every public --ag-ui-* token is read here into a private --_* alias, and
4466
+ only the alias is used by the rules below.
4467
+
4468
+ The indirection is what makes ancestor theming work. Declaring the public
4469
+ name on :host would set it *on the host element*, and a value on an element
4470
+ always beats one inherited from an ancestor \u2014 so a page that put the tokens
4471
+ on a wrapper would see no effect at all, which is the one thing the API is
4472
+ documented to support. Reading the public name with the default as a var()
4473
+ fallback leaves it undeclared on the element, so an ancestor's value is
4474
+ inherited normally while a value aimed at the element still wins over it.
4475
+
4476
+ Two invariants hold this together:
4477
+ 1. No rule outside this file's :host blocks may reference a public name
4478
+ directly. A public name read outside these blocks resolves to nothing and
4479
+ silently drops its declaration rather than erroring, so the miss shows up
4480
+ as a missing colour, not a failure.
4481
+ 2. Every --_* alias in use is declared here. Aliases inherit like any custom
4482
+ property, so an undeclared one would pick up a same-named property from
4483
+ the host page; declaring it on :host shields the shadow tree from that. */
4404
4484
  :host {
4405
4485
  /* Colors */
4406
- --ag-ui-bg: #ffffff;
4407
- --ag-ui-fg: #1a1a2e;
4408
- --ag-ui-accent: #4f46e5;
4409
- --ag-ui-user-bg: #4f46e5;
4410
- --ag-ui-user-fg: #ffffff;
4411
- --ag-ui-assistant-bg: #f1f1f6;
4412
- --ag-ui-hover: #e7e7ee;
4413
- --ag-ui-input-bg: var(--ag-ui-bg);
4414
- --ag-ui-tool-bg: var(--ag-ui-assistant-bg);
4415
- --ag-ui-tool-fg: var(--ag-ui-accent);
4416
- --ag-ui-header-bg: var(--ag-ui-accent);
4417
- --ag-ui-header-fg: #ffffff;
4418
- --ag-ui-border: #e2e2ec;
4419
- --ag-ui-radius: 12px;
4486
+ --_bg: var(--ag-ui-bg, #ffffff);
4487
+ --_fg: var(--ag-ui-fg, #1a1a2e);
4488
+ --_accent: var(--ag-ui-accent, #4f46e5);
4489
+ --_user-bg: var(--ag-ui-user-bg, #4f46e5);
4490
+ --_user-fg: var(--ag-ui-user-fg, #ffffff);
4491
+ --_assistant-bg: var(--ag-ui-assistant-bg, #f1f1f6);
4492
+ --_hover: var(--ag-ui-hover, #e7e7ee);
4493
+ --_input-bg: var(--ag-ui-input-bg, var(--_bg));
4494
+ --_tool-bg: var(--ag-ui-tool-bg, var(--_assistant-bg));
4495
+ --_tool-fg: var(--ag-ui-tool-fg, var(--_accent));
4496
+ --_header-bg: var(--ag-ui-header-bg, var(--_accent));
4497
+ --_header-fg: var(--ag-ui-header-fg, #ffffff);
4498
+ --_border: var(--ag-ui-border, #e2e2ec);
4499
+ --_radius: var(--ag-ui-radius, 12px);
4500
+
4501
+ /* Body text and raised chrome. Both are referenced by the code-block copy
4502
+ button; neither had a default before, so the declarations reading them
4503
+ were dropped and the button fell back to the inherited colour over a
4504
+ transparent box. The defaults below restate exactly that, so this is a
4505
+ rename with no repaint \u2014 see the note on .code-copy. */
4506
+ --_text: var(--ag-ui-text, var(--_fg));
4507
+ --_surface: var(--ag-ui-surface, transparent);
4420
4508
 
4421
4509
  /* Status accents for tool-call cards. */
4422
- --ag-ui-success: #15803d;
4423
- --ag-ui-danger: #b91c1c;
4424
- --ag-ui-muted: #6b7280;
4510
+ --_success: var(--ag-ui-success, #15803d);
4511
+ --_danger: var(--ag-ui-danger, #b91c1c);
4512
+ --_muted: var(--ag-ui-muted, #6b7280);
4425
4513
 
4426
4514
  /* Tool-call status icon glyphs (override to re-theme) + spinner speed.
4427
4515
  The pending state is the animated ring; the settled states use these. */
4428
- --ag-ui-tool-icon-done: "\u2713";
4429
- --ag-ui-tool-icon-error: "\u2715";
4430
- --ag-ui-tool-icon-declined: "\u2298";
4431
- --ag-ui-tool-spin-duration: 0.7s;
4516
+ --_tool-icon-done: var(--ag-ui-tool-icon-done, "\u2713");
4517
+ --_tool-icon-error: var(--ag-ui-tool-icon-error, "\u2715");
4518
+ --_tool-icon-declined: var(--ag-ui-tool-icon-declined, "\u2298");
4519
+ --_tool-spin-duration: var(--ag-ui-tool-spin-duration, 0.7s);
4432
4520
 
4433
4521
  /* Answer well (opt-in via data-answer-well) \u2014 boxes a whole assistant turn. */
4434
- --ag-ui-well-bg: transparent;
4435
- --ag-ui-well-border: var(--ag-ui-border);
4522
+ --_well-bg: var(--ag-ui-well-bg, transparent);
4523
+ --_well-border: var(--ag-ui-well-border, var(--_border));
4436
4524
 
4437
4525
  /* Surface \u2014 set --ag-ui-shadow: none for a flush, embedded panel. */
4438
- --ag-ui-shadow: 0 12px 32px rgba(20, 20, 50, 0.18);
4439
- --ag-ui-font: inherit;
4440
- --ag-ui-font-size: 14px;
4441
- --ag-ui-code-font: ui-monospace, "SF Mono", Menlo, monospace;
4526
+ --_shadow: var(--ag-ui-shadow, 0 12px 32px rgba(20, 20, 50, 0.18));
4527
+ --_font: var(--ag-ui-font, inherit);
4528
+ --_font-size: var(--ag-ui-font-size, 14px);
4529
+ --_code-font: var(--ag-ui-code-font, ui-monospace, "SF Mono", Menlo, monospace);
4530
+
4531
+ /* Header / launcher icon box. */
4532
+ --_icon-size: var(--ag-ui-icon-size, 22px);
4533
+ --_icon-radius: var(--ag-ui-icon-radius, 4px);
4442
4534
 
4443
4535
  /* Spacing \u2014 the density preset overrides these. */
4444
- --ag-ui-space: 10px;
4445
- --ag-ui-pad: 16px;
4446
- --ag-ui-msg-pad: 8px 12px;
4447
- --ag-ui-msg-radius: 14px;
4536
+ --_space: var(--ag-ui-space, 10px);
4537
+ --_pad: var(--ag-ui-pad, 16px);
4538
+ --_msg-pad: var(--ag-ui-msg-pad, 8px 12px);
4539
+ --_msg-radius: var(--ag-ui-msg-radius, 14px);
4448
4540
 
4449
4541
  /* Layout \u2014 override from outside to dock the widget anywhere.
4450
4542
  Set --ag-ui-position: static (and place this element in your own
4451
4543
  grid/flex layout) to embed it in the page flow instead of floating. */
4452
- --ag-ui-position: fixed;
4453
- --ag-ui-z-index: 2147483000;
4454
- --ag-ui-width: 380px;
4455
- --ag-ui-height: 560px;
4456
- --ag-ui-inset: auto 24px 24px auto;
4457
- --ag-ui-max-width: calc(100vw - 48px);
4458
- --ag-ui-max-height: calc(100vh - 48px);
4544
+ --_position: var(--ag-ui-position, fixed);
4545
+ --_z-index: var(--ag-ui-z-index, 2147483000);
4546
+ --_width: var(--ag-ui-width, 380px);
4547
+ --_height: var(--ag-ui-height, 560px);
4548
+ --_inset: var(--ag-ui-inset, auto 24px 24px auto);
4549
+ --_max-width: var(--ag-ui-max-width, calc(100vw - 48px));
4550
+ --_max-height: var(--ag-ui-max-height, calc(100vh - 48px));
4459
4551
  /* Reading-column width for placement="page" (full-bleed, centred content). */
4460
- --ag-ui-content-max-width: 820px;
4461
-
4462
- position: var(--ag-ui-position);
4463
- inset: var(--ag-ui-inset);
4464
- z-index: var(--ag-ui-z-index);
4465
- width: var(--ag-ui-width);
4466
- max-width: var(--ag-ui-max-width);
4467
- height: var(--ag-ui-height);
4468
- max-height: var(--ag-ui-max-height);
4552
+ --_content-max-width: var(--ag-ui-content-max-width, 820px);
4553
+ /* Slim rail the sidebar placement collapses to. Only that placement reads
4554
+ it, but it is declared here so invariant 2 holds for every alias. */
4555
+ --_rail-width: var(--ag-ui-rail-width, 52px);
4556
+
4557
+ position: var(--_position);
4558
+ inset: var(--_inset);
4559
+ z-index: var(--_z-index);
4560
+ width: var(--_width);
4561
+ max-width: var(--_max-width);
4562
+ height: var(--_height);
4563
+ max-height: var(--_max-height);
4469
4564
  display: flex;
4470
- font-family: var(--ag-ui-font);
4471
- font-size: var(--ag-ui-font-size);
4472
- color: var(--ag-ui-fg);
4565
+ font-family: var(--_font);
4566
+ font-size: var(--_font-size);
4567
+ color: var(--_fg);
4473
4568
  }
4474
4569
 
4475
4570
  /* \u2500\u2500 Themes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
4476
4571
  Themes only re-set the colour variables; layout/spacing are unaffected.
4477
4572
  theme="auto" follows the OS via prefers-color-scheme. */
4478
4573
  :host([theme="dark"]) {
4479
- --ag-ui-bg: #15151f;
4480
- --ag-ui-fg: #e8e8f2;
4481
- --ag-ui-assistant-bg: #25253a;
4482
- --ag-ui-hover: #303049;
4483
- --ag-ui-header-bg: #1f1f30;
4484
- --ag-ui-header-fg: #e8e8f2;
4485
- --ag-ui-border: #33334a;
4486
- --ag-ui-muted: #9aa0b4;
4487
- --ag-ui-shadow: 0 12px 32px rgba(0, 0, 0, 0.5);
4574
+ --_bg: var(--ag-ui-bg, #15151f);
4575
+ --_fg: var(--ag-ui-fg, #e8e8f2);
4576
+ --_assistant-bg: var(--ag-ui-assistant-bg, #25253a);
4577
+ --_hover: var(--ag-ui-hover, #303049);
4578
+ --_header-bg: var(--ag-ui-header-bg, #1f1f30);
4579
+ --_header-fg: var(--ag-ui-header-fg, #e8e8f2);
4580
+ --_border: var(--ag-ui-border, #33334a);
4581
+ --_muted: var(--ag-ui-muted, #9aa0b4);
4582
+ --_shadow: var(--ag-ui-shadow, 0 12px 32px rgba(0, 0, 0, 0.5));
4488
4583
  }
4489
4584
 
4490
4585
  @media (prefers-color-scheme: dark) {
4491
4586
  :host([theme="auto"]) {
4492
- --ag-ui-bg: #15151f;
4493
- --ag-ui-fg: #e8e8f2;
4494
- --ag-ui-assistant-bg: #25253a;
4495
- --ag-ui-hover: #303049;
4496
- --ag-ui-header-bg: #1f1f30;
4497
- --ag-ui-header-fg: #e8e8f2;
4498
- --ag-ui-border: #33334a;
4499
- --ag-ui-muted: #9aa0b4;
4500
- --ag-ui-shadow: 0 12px 32px rgba(0, 0, 0, 0.5);
4587
+ --_bg: var(--ag-ui-bg, #15151f);
4588
+ --_fg: var(--ag-ui-fg, #e8e8f2);
4589
+ --_assistant-bg: var(--ag-ui-assistant-bg, #25253a);
4590
+ --_hover: var(--ag-ui-hover, #303049);
4591
+ --_header-bg: var(--ag-ui-header-bg, #1f1f30);
4592
+ --_header-fg: var(--ag-ui-header-fg, #e8e8f2);
4593
+ --_border: var(--ag-ui-border, #33334a);
4594
+ --_muted: var(--ag-ui-muted, #9aa0b4);
4595
+ --_shadow: var(--ag-ui-shadow, 0 12px 32px rgba(0, 0, 0, 0.5));
4501
4596
  }
4502
4597
  }
4503
4598
 
4504
4599
  /* A terminal-flavoured "code" theme: dark, monospace, green accent. */
4505
4600
  :host([theme="code"]) {
4506
- --ag-ui-bg: #0d1117;
4507
- --ag-ui-fg: #c9d1d9;
4508
- --ag-ui-accent: #3fb950;
4509
- --ag-ui-user-bg: #238636;
4510
- --ag-ui-assistant-bg: #161b22;
4511
- --ag-ui-hover: #21262d;
4512
- --ag-ui-header-bg: #010409;
4513
- --ag-ui-header-fg: #c9d1d9;
4514
- --ag-ui-border: #30363d;
4515
- --ag-ui-muted: #8b949e;
4516
- --ag-ui-font: var(--ag-ui-code-font);
4517
- --ag-ui-shadow: 0 12px 32px rgba(0, 0, 0, 0.6);
4601
+ --_bg: var(--ag-ui-bg, #0d1117);
4602
+ --_fg: var(--ag-ui-fg, #c9d1d9);
4603
+ --_accent: var(--ag-ui-accent, #3fb950);
4604
+ --_user-bg: var(--ag-ui-user-bg, #238636);
4605
+ --_assistant-bg: var(--ag-ui-assistant-bg, #161b22);
4606
+ --_hover: var(--ag-ui-hover, #21262d);
4607
+ --_header-bg: var(--ag-ui-header-bg, #010409);
4608
+ --_header-fg: var(--ag-ui-header-fg, #c9d1d9);
4609
+ --_border: var(--ag-ui-border, #30363d);
4610
+ --_muted: var(--ag-ui-muted, #8b949e);
4611
+ --_font: var(--ag-ui-font, var(--_code-font));
4612
+ --_shadow: var(--ag-ui-shadow, 0 12px 32px rgba(0, 0, 0, 0.6));
4518
4613
  }
4519
4614
 
4520
4615
  /* \u2500\u2500 Density \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
4521
4616
  :host([density="compact"]) {
4522
- --ag-ui-font-size: 13px;
4523
- --ag-ui-space: 6px;
4524
- --ag-ui-pad: 10px;
4525
- --ag-ui-msg-pad: 5px 9px;
4526
- --ag-ui-msg-radius: 10px;
4617
+ --_font-size: var(--ag-ui-font-size, 13px);
4618
+ --_space: var(--ag-ui-space, 6px);
4619
+ --_pad: var(--ag-ui-pad, 10px);
4620
+ --_msg-pad: var(--ag-ui-msg-pad, 5px 9px);
4621
+ --_msg-radius: var(--ag-ui-msg-radius, 10px);
4527
4622
  }
4528
4623
 
4529
4624
  /* \u2500\u2500 Placement presets \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
4530
4625
  :host([placement="bottom-left"]) {
4531
- --ag-ui-inset: auto auto 24px 24px;
4626
+ --_inset: var(--ag-ui-inset, auto auto 24px 24px);
4532
4627
  }
4533
4628
 
4534
4629
  :host([placement="side"]) {
4535
- --ag-ui-inset: 0 0 0 auto;
4536
- --ag-ui-width: 420px;
4537
- --ag-ui-height: 100vh;
4538
- --ag-ui-max-height: 100vh;
4539
- --ag-ui-radius: 0;
4630
+ --_inset: var(--ag-ui-inset, 0 0 0 auto);
4631
+ --_width: var(--ag-ui-width, 420px);
4632
+ --_height: var(--ag-ui-height, 100vh);
4633
+ --_max-height: var(--ag-ui-max-height, 100vh);
4634
+ --_radius: var(--ag-ui-radius, 0);
4540
4635
  }
4541
4636
 
4542
4637
  :host([placement="full"]) {
4543
- --ag-ui-inset: 0;
4544
- --ag-ui-width: 100vw;
4545
- --ag-ui-height: 100vh;
4546
- --ag-ui-max-width: 100vw;
4547
- --ag-ui-max-height: 100vh;
4548
- --ag-ui-radius: 0;
4638
+ --_inset: var(--ag-ui-inset, 0);
4639
+ --_width: var(--ag-ui-width, 100vw);
4640
+ --_height: var(--ag-ui-height, 100vh);
4641
+ --_max-width: var(--ag-ui-max-width, 100vw);
4642
+ --_max-height: var(--ag-ui-max-height, 100vh);
4643
+ --_radius: var(--ag-ui-radius, 0);
4549
4644
  }
4550
4645
 
4551
4646
  /* Page: full-bleed background with a centred reading column. Unlike
@@ -4554,85 +4649,20 @@ var STYLES = `
4554
4649
  padding on the scroll area + composer (no per-row wrapper), so user pills
4555
4650
  still right-align and the assistant well spans the column. */
4556
4651
  :host([placement="page"]) {
4557
- --ag-ui-inset: 0;
4558
- --ag-ui-width: 100vw;
4559
- --ag-ui-height: 100vh;
4560
- --ag-ui-max-width: 100vw;
4561
- --ag-ui-max-height: 100vh;
4562
- --ag-ui-radius: 0;
4652
+ --_inset: var(--ag-ui-inset, 0);
4653
+ --_width: var(--ag-ui-width, 100vw);
4654
+ --_height: var(--ag-ui-height, 100vh);
4655
+ --_max-width: var(--ag-ui-max-width, 100vw);
4656
+ --_max-height: var(--ag-ui-max-height, 100vh);
4657
+ --_radius: var(--ag-ui-radius, 0);
4563
4658
  }
4564
4659
 
4565
4660
  :host([placement="page"]) .messages {
4566
- padding-inline: max(var(--ag-ui-pad), calc((100% - var(--ag-ui-content-max-width)) / 2));
4661
+ padding-inline: max(var(--_pad), calc((100% - var(--_content-max-width)) / 2));
4567
4662
  }
4568
4663
 
4569
- :host([placement="page"]) /* Resize handle: a corner grip on a floating panel, an edge grip on a docked
4570
- one. Absent entirely where the placement is full-bleed, since there is
4571
- nothing to drag. */
4572
- .resize-handle {
4573
- position: absolute;
4574
- z-index: 2;
4575
- background: transparent;
4576
- touch-action: none;
4577
- }
4578
-
4579
- .resize-handle:focus-visible {
4580
- outline: 2px solid var(--ag-ui-accent);
4581
- outline-offset: -2px;
4582
- }
4583
-
4584
- /* The grip sits at the corner the panel grows toward. Which corner that is
4585
- depends on the host's layout, not on placement, so the element measures it
4586
- and stamps data-resize-anchor with the edges that stay put.
4587
-
4588
- Default (bottom-right pinned, the floating case) puts the grip top-left. */
4589
- .resize-handle {
4590
- top: 0;
4591
- left: 0;
4592
- width: 14px;
4593
- height: 14px;
4594
- cursor: nwse-resize;
4595
- }
4596
-
4597
- :host([data-resize-anchor~="left"]) .resize-handle {
4598
- left: auto;
4599
- right: 0;
4600
- }
4601
-
4602
- :host([data-resize-anchor~="top"]) .resize-handle {
4603
- top: auto;
4604
- bottom: 0;
4605
- }
4606
-
4607
- /* One axis flipped means the drag runs along the other diagonal. */
4608
- :host([data-resize-anchor="top-right"]) .resize-handle,
4609
- :host([data-resize-anchor="bottom-left"]) .resize-handle {
4610
- cursor: nesw-resize;
4611
- }
4612
-
4613
- /* Docked: the placement owns the height, so only the inner edge is the user's. */
4614
- :host([placement="sidebar"]) .resize-handle,
4615
- :host([placement="side"]) .resize-handle {
4616
- top: 0;
4617
- bottom: auto;
4618
- width: 8px;
4619
- height: 100%;
4620
- cursor: ew-resize;
4621
- }
4622
-
4623
- /* Full-bleed: nothing to drag. */
4624
- :host([placement="full"]) .resize-handle,
4625
- :host([placement="page"]) .resize-handle {
4626
- display: none;
4627
- }
4628
-
4629
- .resize-handle[data-dragging] {
4630
- background: var(--ag-ui-accent);
4631
- opacity: 0.35;
4632
- }
4633
-
4634
- .input-row {
4635
- padding-inline: max(12px, calc((100% - var(--ag-ui-content-max-width)) / 2));
4664
+ :host([placement="page"]) .input-row {
4665
+ padding-inline: max(12px, calc((100% - var(--_content-max-width)) / 2));
4636
4666
  }
4637
4667
 
4638
4668
  /* The rows between the message list and the composer (skill chips, the
@@ -4641,12 +4671,12 @@ var STYLES = `
4641
4671
  margin-based, so each gets its own inline axis nudged by the same gutter. */
4642
4672
  :host([placement="page"]) .skill-chips,
4643
4673
  :host([placement="page"]) .attachment-tray {
4644
- padding-inline: max(12px, calc((100% - var(--ag-ui-content-max-width)) / 2));
4674
+ padding-inline: max(12px, calc((100% - var(--_content-max-width)) / 2));
4645
4675
  }
4646
4676
 
4647
4677
  :host([placement="page"]) .skill-palette,
4648
4678
  :host([placement="page"]) .skill-hint {
4649
- margin-inline: max(12px, calc((100% - var(--ag-ui-content-max-width)) / 2));
4679
+ margin-inline: max(12px, calc((100% - var(--_content-max-width)) / 2));
4650
4680
  }
4651
4681
 
4652
4682
  /* In the reading column the assistant well uses the full width; the user
@@ -4661,17 +4691,16 @@ var STYLES = `
4661
4691
  --ag-ui-position: static (and place this element in your own layout) for a
4662
4692
  host-managed push instead. */
4663
4693
  :host([placement="sidebar"]) {
4664
- --ag-ui-inset: 0 0 0 auto;
4665
- --ag-ui-width: 420px;
4666
- --ag-ui-height: 100vh;
4667
- --ag-ui-max-height: 100vh;
4668
- --ag-ui-radius: 0;
4669
- --ag-ui-rail-width: 52px;
4694
+ --_inset: var(--ag-ui-inset, 0 0 0 auto);
4695
+ --_width: var(--ag-ui-width, 420px);
4696
+ --_height: var(--ag-ui-height, 100vh);
4697
+ --_max-height: var(--ag-ui-max-height, 100vh);
4698
+ --_radius: var(--ag-ui-radius, 0);
4670
4699
  transition: width 0.28s ease;
4671
4700
  }
4672
4701
 
4673
4702
  :host([placement="sidebar"][data-side="left"]) {
4674
- --ag-ui-inset: 0 auto 0 0;
4703
+ --_inset: var(--ag-ui-inset, 0 auto 0 0);
4675
4704
  }
4676
4705
 
4677
4706
  :host([placement="sidebar"]) .chat {
@@ -4682,7 +4711,7 @@ var STYLES = `
4682
4711
  reveal the rail. Higher specificity than the generic collapse rules, so it
4683
4712
  wins regardless of source order. */
4684
4713
  :host([placement="sidebar"][collapsed]) {
4685
- width: var(--ag-ui-rail-width);
4714
+ width: var(--_rail-width);
4686
4715
  height: 100vh;
4687
4716
  max-height: 100vh;
4688
4717
  bottom: 0;
@@ -4707,9 +4736,9 @@ var STYLES = `
4707
4736
  align-items: flex-start;
4708
4737
  justify-content: center;
4709
4738
  padding-top: 16px;
4710
- border: 1px solid var(--ag-ui-border);
4711
- background: var(--ag-ui-header-bg);
4712
- color: var(--ag-ui-header-fg);
4739
+ border: 1px solid var(--_border);
4740
+ background: var(--_header-bg);
4741
+ color: var(--_header-fg);
4713
4742
  cursor: pointer;
4714
4743
  }
4715
4744
 
@@ -4723,13 +4752,13 @@ var STYLES = `
4723
4752
  /* Embedded: drop the floating chrome and the high z-index stacking context so
4724
4753
  the widget lives in the host's own layout (fixes overlay/z-index clashes). */
4725
4754
  :host([placement="embedded"]) {
4726
- --ag-ui-position: static;
4727
- --ag-ui-width: 100%;
4728
- --ag-ui-height: 100%;
4729
- --ag-ui-max-width: 100%;
4730
- --ag-ui-max-height: 100%;
4731
- --ag-ui-shadow: none;
4732
- --ag-ui-z-index: auto;
4755
+ --_position: var(--ag-ui-position, static);
4756
+ --_width: var(--ag-ui-width, 100%);
4757
+ --_height: var(--ag-ui-height, 100%);
4758
+ --_max-width: var(--ag-ui-max-width, 100%);
4759
+ --_max-height: var(--ag-ui-max-height, 100%);
4760
+ --_shadow: var(--ag-ui-shadow, none);
4761
+ --_z-index: var(--ag-ui-z-index, auto);
4733
4762
  }
4734
4763
 
4735
4764
  .chat {
@@ -4738,10 +4767,10 @@ var STYLES = `
4738
4767
  flex-direction: column;
4739
4768
  flex: 1;
4740
4769
  min-height: 0;
4741
- background: var(--ag-ui-bg);
4742
- border: 1px solid var(--ag-ui-border);
4743
- border-radius: var(--ag-ui-radius);
4744
- box-shadow: var(--ag-ui-shadow);
4770
+ background: var(--_bg);
4771
+ border: 1px solid var(--_border);
4772
+ border-radius: var(--_radius);
4773
+ box-shadow: var(--_shadow);
4745
4774
  overflow: hidden;
4746
4775
  }
4747
4776
 
@@ -4751,9 +4780,9 @@ var STYLES = `
4751
4780
  justify-content: space-between;
4752
4781
  gap: 8px;
4753
4782
  padding: 10px 14px;
4754
- border-bottom: 1px solid var(--ag-ui-border);
4755
- background: var(--ag-ui-header-bg);
4756
- color: var(--ag-ui-header-fg);
4783
+ border-bottom: 1px solid var(--_border);
4784
+ background: var(--_header-bg);
4785
+ color: var(--_header-fg);
4757
4786
  }
4758
4787
 
4759
4788
  .header-title {
@@ -4772,8 +4801,8 @@ var STYLES = `
4772
4801
  align-items: center;
4773
4802
  justify-content: center;
4774
4803
  flex: none;
4775
- width: var(--ag-ui-icon-size, 22px);
4776
- height: var(--ag-ui-icon-size, 22px);
4804
+ width: var(--_icon-size);
4805
+ height: var(--_icon-size);
4777
4806
  line-height: 1;
4778
4807
  }
4779
4808
 
@@ -4781,7 +4810,7 @@ var STYLES = `
4781
4810
  width: 100%;
4782
4811
  height: 100%;
4783
4812
  object-fit: contain;
4784
- border-radius: var(--ag-ui-icon-radius, 4px);
4813
+ border-radius: var(--_icon-radius);
4785
4814
  }
4786
4815
 
4787
4816
  .header-controls {
@@ -4833,10 +4862,10 @@ var STYLES = `
4833
4862
  .messages {
4834
4863
  flex: 1;
4835
4864
  overflow-y: auto;
4836
- padding: var(--ag-ui-pad);
4865
+ padding: var(--_pad);
4837
4866
  display: flex;
4838
4867
  flex-direction: column;
4839
- gap: var(--ag-ui-space);
4868
+ gap: var(--_space);
4840
4869
  }
4841
4870
 
4842
4871
  /* Empty-state region (slot): centred while it's the only thing in the
@@ -4844,7 +4873,7 @@ var STYLES = `
4844
4873
  .empty {
4845
4874
  margin: auto;
4846
4875
  text-align: center;
4847
- color: var(--ag-ui-muted);
4876
+ color: var(--_muted);
4848
4877
  }
4849
4878
 
4850
4879
  .empty[hidden] {
@@ -4860,22 +4889,22 @@ var STYLES = `
4860
4889
  .answer {
4861
4890
  display: flex;
4862
4891
  flex-direction: column;
4863
- gap: var(--ag-ui-space);
4892
+ gap: var(--_space);
4864
4893
  align-self: stretch;
4865
4894
  min-width: 0;
4866
4895
  }
4867
4896
 
4868
4897
  :host([data-answer-well]) .answer {
4869
- padding: var(--ag-ui-pad);
4870
- background: var(--ag-ui-well-bg);
4871
- border: 1px solid var(--ag-ui-well-border);
4872
- border-radius: var(--ag-ui-radius);
4898
+ padding: var(--_pad);
4899
+ background: var(--_well-bg);
4900
+ border: 1px solid var(--_well-border);
4901
+ border-radius: var(--_radius);
4873
4902
  }
4874
4903
 
4875
4904
  .message {
4876
4905
  max-width: 80%;
4877
- padding: var(--ag-ui-msg-pad);
4878
- border-radius: var(--ag-ui-msg-radius);
4906
+ padding: var(--_msg-pad);
4907
+ border-radius: var(--_msg-radius);
4879
4908
  line-height: 1.4;
4880
4909
  white-space: pre-wrap;
4881
4910
  word-break: break-word;
@@ -4913,14 +4942,14 @@ var STYLES = `
4913
4942
 
4914
4943
  .message--user {
4915
4944
  align-self: flex-end;
4916
- background: var(--ag-ui-user-bg);
4917
- color: var(--ag-ui-user-fg);
4945
+ background: var(--_user-bg);
4946
+ color: var(--_user-fg);
4918
4947
  border-bottom-right-radius: 4px;
4919
4948
  }
4920
4949
 
4921
4950
  .message--assistant {
4922
4951
  align-self: flex-start;
4923
- background: var(--ag-ui-assistant-bg);
4952
+ background: var(--_assistant-bg);
4924
4953
  border-bottom-left-radius: 4px;
4925
4954
  /* Assistant bubbles hold rendered markdown/HTML, so collapse the source
4926
4955
  whitespace the renderer leaves between block tags. */
@@ -4950,7 +4979,7 @@ var STYLES = `
4950
4979
  }
4951
4980
 
4952
4981
  .message--assistant a {
4953
- color: var(--ag-ui-accent);
4982
+ color: var(--_accent);
4954
4983
  text-decoration: underline;
4955
4984
  }
4956
4985
 
@@ -4965,8 +4994,8 @@ var STYLES = `
4965
4994
  .message--assistant pre {
4966
4995
  padding: 8px 10px;
4967
4996
  overflow: auto;
4968
- background: var(--ag-ui-bg);
4969
- border: 1px solid var(--ag-ui-border);
4997
+ background: var(--_bg);
4998
+ border: 1px solid var(--_border);
4970
4999
  border-radius: 6px;
4971
5000
  }
4972
5001
 
@@ -4982,6 +5011,13 @@ var STYLES = `
4982
5011
  position: relative;
4983
5012
  }
4984
5013
 
5014
+ /* This button is the only reader of --ag-ui-surface and --ag-ui-text, and
5015
+ neither had a default until the alias layer gave them one. Both references
5016
+ used to resolve to nothing, dropping the declaration: the background fell
5017
+ back to the initial transparent, and the hover/copied colour to the
5018
+ inherited body colour. The defaults chosen upstream (transparent, and the
5019
+ body foreground) reproduce that, so the rename repaints nothing. A raised
5020
+ surface behind the button is a separate design question. */
4985
5021
  .code-copy {
4986
5022
  position: absolute;
4987
5023
  top: 4px;
@@ -4990,9 +5026,9 @@ var STYLES = `
4990
5026
  font: inherit;
4991
5027
  font-size: 0.75em;
4992
5028
  line-height: 1.6;
4993
- color: var(--ag-ui-muted);
4994
- background: var(--ag-ui-surface);
4995
- border: 1px solid var(--ag-ui-border);
5029
+ color: var(--_muted);
5030
+ background: var(--_surface);
5031
+ border: 1px solid var(--_border);
4996
5032
  border-radius: 4px;
4997
5033
  cursor: pointer;
4998
5034
  opacity: 0;
@@ -5007,24 +5043,24 @@ var STYLES = `
5007
5043
  }
5008
5044
 
5009
5045
  .code-copy:hover {
5010
- color: var(--ag-ui-text);
5011
- background: var(--ag-ui-hover);
5046
+ color: var(--_text);
5047
+ background: var(--_hover);
5012
5048
  }
5013
5049
 
5014
5050
  .code-copy[data-state="copied"] {
5015
5051
  opacity: 1;
5016
- color: var(--ag-ui-text);
5052
+ color: var(--_text);
5017
5053
  }
5018
5054
 
5019
5055
  .code-copy[data-state="failed"] {
5020
5056
  opacity: 1;
5021
- color: var(--ag-ui-danger, var(--ag-ui-text));
5057
+ color: var(--_danger, var(--_text));
5022
5058
  }
5023
5059
 
5024
5060
  .message--assistant blockquote {
5025
5061
  padding-left: 10px;
5026
- border-left: 3px solid var(--ag-ui-border);
5027
- color: var(--ag-ui-muted);
5062
+ border-left: 3px solid var(--_border);
5063
+ color: var(--_muted);
5028
5064
  }
5029
5065
 
5030
5066
  /* Markdown tables. table/thead/tbody/tr/th/td are all in
@@ -5045,13 +5081,13 @@ var STYLES = `
5045
5081
  .message--assistant th,
5046
5082
  .message--assistant td {
5047
5083
  padding: 6px 10px;
5048
- border: 1px solid var(--ag-ui-border);
5084
+ border: 1px solid var(--_border);
5049
5085
  text-align: left;
5050
5086
  vertical-align: top;
5051
5087
  }
5052
5088
 
5053
5089
  .message--assistant th {
5054
- background: var(--ag-ui-hover);
5090
+ background: var(--_hover);
5055
5091
  font-weight: 600;
5056
5092
  }
5057
5093
 
@@ -5061,7 +5097,7 @@ var STYLES = `
5061
5097
  gap: 4px;
5062
5098
  align-items: center;
5063
5099
  padding: 12px 14px;
5064
- background: var(--ag-ui-assistant-bg);
5100
+ background: var(--_assistant-bg);
5065
5101
  border-radius: 14px;
5066
5102
  border-bottom-left-radius: 4px;
5067
5103
  }
@@ -5070,7 +5106,7 @@ var STYLES = `
5070
5106
  width: 6px;
5071
5107
  height: 6px;
5072
5108
  border-radius: 50%;
5073
- background: var(--ag-ui-muted);
5109
+ background: var(--_muted);
5074
5110
  animation: ag-ui-pending 1.2s infinite ease-in-out both;
5075
5111
  }
5076
5112
 
@@ -5103,7 +5139,7 @@ var STYLES = `
5103
5139
  flex-direction: column;
5104
5140
  gap: 4px;
5105
5141
  font-size: 12px;
5106
- color: var(--ag-ui-muted);
5142
+ color: var(--_muted);
5107
5143
  }
5108
5144
 
5109
5145
  .thoughts-toggle {
@@ -5114,7 +5150,7 @@ var STYLES = `
5114
5150
  font: inherit;
5115
5151
  font-size: 12px;
5116
5152
  font-weight: 600;
5117
- color: var(--ag-ui-muted);
5153
+ color: var(--_muted);
5118
5154
  cursor: pointer;
5119
5155
  }
5120
5156
 
@@ -5139,7 +5175,7 @@ var STYLES = `
5139
5175
  .thoughts-body {
5140
5176
  margin: 0;
5141
5177
  padding: 4px 0 4px 10px;
5142
- border-left: 2px solid var(--ag-ui-border);
5178
+ border-left: 2px solid var(--_border);
5143
5179
  max-height: 220px;
5144
5180
  overflow: auto;
5145
5181
  white-space: pre-wrap;
@@ -5168,9 +5204,9 @@ var STYLES = `
5168
5204
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
5169
5205
  padding: 8px 10px;
5170
5206
  border-radius: 8px;
5171
- background: var(--ag-ui-tool-bg);
5172
- border: 1px solid var(--ag-ui-border);
5173
- color: var(--ag-ui-tool-fg);
5207
+ background: var(--_tool-bg);
5208
+ border: 1px solid var(--_border);
5209
+ color: var(--_tool-fg);
5174
5210
  }
5175
5211
 
5176
5212
  .tool-call-head {
@@ -5203,10 +5239,10 @@ var STYLES = `
5203
5239
 
5204
5240
  /* Pending: a real spinning ring. Speed is tunable; reduced motion stops it. */
5205
5241
  .tool-call[data-status="pending"] .tool-call-icon {
5206
- border: 2px solid var(--ag-ui-muted);
5242
+ border: 2px solid var(--_muted);
5207
5243
  border-top-color: transparent;
5208
5244
  border-radius: 50%;
5209
- animation: ag-ui-tool-spin var(--ag-ui-tool-spin-duration) linear infinite;
5245
+ animation: ag-ui-tool-spin var(--_tool-spin-duration) linear infinite;
5210
5246
  }
5211
5247
 
5212
5248
  @keyframes ag-ui-tool-spin {
@@ -5215,18 +5251,18 @@ var STYLES = `
5215
5251
 
5216
5252
  /* Settled: a themeable glyph coloured by outcome. */
5217
5253
  .tool-call[data-status="done"] .tool-call-icon::before {
5218
- content: var(--ag-ui-tool-icon-done);
5219
- color: var(--ag-ui-success);
5254
+ content: var(--_tool-icon-done);
5255
+ color: var(--_success);
5220
5256
  }
5221
5257
 
5222
5258
  .tool-call[data-status="error"] .tool-call-icon::before {
5223
- content: var(--ag-ui-tool-icon-error);
5224
- color: var(--ag-ui-danger);
5259
+ content: var(--_tool-icon-error);
5260
+ color: var(--_danger);
5225
5261
  }
5226
5262
 
5227
5263
  .tool-call[data-status="declined"] .tool-call-icon::before {
5228
- content: var(--ag-ui-tool-icon-declined);
5229
- color: var(--ag-ui-muted);
5264
+ content: var(--_tool-icon-declined);
5265
+ color: var(--_muted);
5230
5266
  }
5231
5267
 
5232
5268
  @media (prefers-reduced-motion: reduce) {
@@ -5252,19 +5288,19 @@ var STYLES = `
5252
5288
  font-size: 11px;
5253
5289
  font-weight: 600;
5254
5290
  background: rgba(127, 127, 127, 0.16);
5255
- color: var(--ag-ui-muted);
5291
+ color: var(--_muted);
5256
5292
  }
5257
5293
 
5258
5294
  .tool-call[data-status="done"] .tool-call-status {
5259
- color: var(--ag-ui-success);
5295
+ color: var(--_success);
5260
5296
  }
5261
5297
 
5262
5298
  .tool-call[data-status="error"] .tool-call-status {
5263
- color: var(--ag-ui-danger);
5299
+ color: var(--_danger);
5264
5300
  }
5265
5301
 
5266
5302
  .tool-call[data-status="declined"] .tool-call-status {
5267
- color: var(--ag-ui-muted);
5303
+ color: var(--_muted);
5268
5304
  }
5269
5305
 
5270
5306
  .tool-call-body {
@@ -5286,7 +5322,7 @@ var STYLES = `
5286
5322
  font-weight: 700;
5287
5323
  letter-spacing: 0.06em;
5288
5324
  text-transform: uppercase;
5289
- color: var(--ag-ui-muted);
5325
+ color: var(--_muted);
5290
5326
  }
5291
5327
 
5292
5328
  /* The record of a human decision on a gated call. An approved call used to
@@ -5294,7 +5330,7 @@ var STYLES = `
5294
5330
  .skill-item-token {
5295
5331
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
5296
5332
  font-size: 0.92em;
5297
- color: var(--ag-ui-accent);
5333
+ color: var(--_accent);
5298
5334
  margin-right: 6px;
5299
5335
  }
5300
5336
 
@@ -5302,7 +5338,7 @@ var STYLES = `
5302
5338
  flex: none;
5303
5339
  font-size: 11px;
5304
5340
  font-style: italic;
5305
- color: var(--ag-ui-muted);
5341
+ color: var(--_muted);
5306
5342
  }
5307
5343
 
5308
5344
  .tool-call-args,
@@ -5311,12 +5347,12 @@ var STYLES = `
5311
5347
  padding: 6px 8px;
5312
5348
  max-height: 160px;
5313
5349
  overflow: auto;
5314
- background: var(--ag-ui-bg);
5315
- border: 1px solid var(--ag-ui-border);
5350
+ background: var(--_bg);
5351
+ border: 1px solid var(--_border);
5316
5352
  border-radius: 6px;
5317
5353
  white-space: pre-wrap;
5318
5354
  word-break: break-word;
5319
- color: var(--ag-ui-fg);
5355
+ color: var(--_fg);
5320
5356
  }
5321
5357
 
5322
5358
  /* Display modes are pure visibility over one DOM shape, selected from the host
@@ -5363,7 +5399,7 @@ var STYLES = `
5363
5399
  background: none;
5364
5400
  font: inherit;
5365
5401
  font-weight: 600;
5366
- color: var(--ag-ui-accent);
5402
+ color: var(--_accent);
5367
5403
  cursor: pointer;
5368
5404
  }
5369
5405
 
@@ -5386,13 +5422,12 @@ var STYLES = `
5386
5422
  }
5387
5423
 
5388
5424
  .resize-handle:focus-visible {
5389
- outline: 2px solid var(--ag-ui-accent);
5425
+ outline: 2px solid var(--_accent);
5390
5426
  outline-offset: -2px;
5391
5427
  }
5392
5428
 
5393
5429
  /* The grip sits on the corner opposite the panel's anchor, because a resize
5394
- measures from whichever edge is not moving. Selected from the host attribute,
5395
- so switching placement moves the grip immediately.
5430
+ measures from whichever edge is not moving.
5396
5431
 
5397
5432
  Default is floating: pinned bottom-right, so the grip is top-left. */
5398
5433
  .resize-handle {
@@ -5403,7 +5438,9 @@ var STYLES = `
5403
5438
  cursor: nwse-resize;
5404
5439
  }
5405
5440
 
5406
- /* Embedded sits in normal flow, pinned top-left, so it grows bottom-right. */
5441
+ /* Placement is only the opening guess, and these two rules are all it is good
5442
+ for. Embedded sits in normal flow, pinned top-left, so it grows bottom-right;
5443
+ bottom-left is pinned there, so its free corner is top-right. */
5407
5444
  :host([placement="embedded"]) .resize-handle {
5408
5445
  top: auto;
5409
5446
  left: auto;
@@ -5412,16 +5449,64 @@ var STYLES = `
5412
5449
  cursor: nwse-resize;
5413
5450
  }
5414
5451
 
5415
- /* Pinned bottom-left, so the free corner is top-right. */
5416
5452
  :host([placement="bottom-left"]) .resize-handle {
5417
5453
  left: auto;
5418
5454
  right: 0;
5419
5455
  cursor: nesw-resize;
5420
5456
  }
5421
5457
 
5422
- /* Docked: the placement owns the height, so only the inner edge is the user's. */
5458
+ /* Then the measurement corrects it. Which edges are held still belongs to the
5459
+ host's layout rather than to placement -- a floating panel a host right-aligns
5460
+ is anchored bottom-left, not bottom-right -- so the element measures the edges
5461
+ that stay put and stamps them here as "<y>-<x>". Equal specificity to the
5462
+ rules above, so source order is what lets the measured value win.
5463
+
5464
+ These must not be written as [data-resize-anchor~="left"]: the stamped value
5465
+ is one hyphenated token, and ~= matches whitespace-separated words, so such a
5466
+ selector cannot ever match. It did not, which left the grip drawn at the
5467
+ corner that moves while the cursor pointed along the right diagonal.
5468
+
5469
+ Each rule also sets both sides of its axis rather than only the side it
5470
+ moves. One that flipped a single way could not undo a placement guess that
5471
+ had flipped the other, which put the grip on the anchored corner for an
5472
+ embedded panel its host right-aligns. */
5473
+ :host([data-resize-anchor$="-left"]) .resize-handle {
5474
+ left: auto;
5475
+ right: 0;
5476
+ }
5477
+
5478
+ :host([data-resize-anchor$="-right"]) .resize-handle {
5479
+ left: 0;
5480
+ right: auto;
5481
+ }
5482
+
5483
+ :host([data-resize-anchor^="top-"]) .resize-handle {
5484
+ top: auto;
5485
+ bottom: 0;
5486
+ }
5487
+
5488
+ :host([data-resize-anchor^="bottom-"]) .resize-handle {
5489
+ top: 0;
5490
+ bottom: auto;
5491
+ }
5492
+
5493
+ /* One axis flipped means the drag runs along the other diagonal. */
5494
+ :host([data-resize-anchor="top-left"]) .resize-handle,
5495
+ :host([data-resize-anchor="bottom-right"]) .resize-handle {
5496
+ cursor: nwse-resize;
5497
+ }
5498
+
5499
+ :host([data-resize-anchor="top-right"]) .resize-handle,
5500
+ :host([data-resize-anchor="bottom-left"]) .resize-handle {
5501
+ cursor: nesw-resize;
5502
+ }
5503
+
5504
+ /* Docked: the placement owns the height, so only the inner edge is the user's --
5505
+ an edge, not a corner, so this outranks the measured anchor by coming after. */
5423
5506
  :host([placement="sidebar"]) .resize-handle,
5424
5507
  :host([placement="side"]) .resize-handle {
5508
+ top: 0;
5509
+ bottom: auto;
5425
5510
  width: 8px;
5426
5511
  height: 100%;
5427
5512
  cursor: ew-resize;
@@ -5440,7 +5525,7 @@ var STYLES = `
5440
5525
  }
5441
5526
 
5442
5527
  .resize-handle[data-dragging] {
5443
- background: var(--ag-ui-accent);
5528
+ background: var(--_accent);
5444
5529
  opacity: 0.35;
5445
5530
  }
5446
5531
 
@@ -5448,14 +5533,14 @@ var STYLES = `
5448
5533
  display: flex;
5449
5534
  gap: 8px;
5450
5535
  padding: 12px;
5451
- border-top: 1px solid var(--ag-ui-border);
5536
+ border-top: 1px solid var(--_border);
5452
5537
  }
5453
5538
 
5454
5539
  .input {
5455
5540
  flex: 1;
5456
5541
  resize: none;
5457
- background: var(--ag-ui-input-bg);
5458
- border: 1px solid var(--ag-ui-border);
5542
+ background: var(--_input-bg);
5543
+ border: 1px solid var(--_border);
5459
5544
  border-radius: 8px;
5460
5545
  padding: 8px 10px;
5461
5546
  font: inherit;
@@ -5464,14 +5549,14 @@ var STYLES = `
5464
5549
  }
5465
5550
 
5466
5551
  .input:focus {
5467
- border-color: var(--ag-ui-accent);
5552
+ border-color: var(--_accent);
5468
5553
  }
5469
5554
 
5470
5555
  .send {
5471
5556
  border: none;
5472
5557
  border-radius: 8px;
5473
5558
  padding: 0 16px;
5474
- background: var(--ag-ui-accent);
5559
+ background: var(--_accent);
5475
5560
  color: #ffffff;
5476
5561
  font: inherit;
5477
5562
  font-weight: 600;
@@ -5485,23 +5570,23 @@ var STYLES = `
5485
5570
 
5486
5571
  /* The composer button doubles as the Stop control while a run is in flight. */
5487
5572
  .send[data-state="running"] {
5488
- background: var(--ag-ui-muted);
5573
+ background: var(--_muted);
5489
5574
  }
5490
5575
 
5491
5576
  /* \u2500\u2500 File attachments \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
5492
5577
  /* The \u{1F4CE} picker button sits left of the input; hidden until data-attachments-url. */
5493
5578
  .attach-btn {
5494
- border: 1px solid var(--ag-ui-border);
5579
+ border: 1px solid var(--_border);
5495
5580
  border-radius: 8px;
5496
5581
  padding: 0 10px;
5497
- background: var(--ag-ui-input-bg);
5582
+ background: var(--_input-bg);
5498
5583
  color: inherit;
5499
5584
  font: inherit;
5500
5585
  cursor: pointer;
5501
5586
  }
5502
5587
 
5503
5588
  .attach-btn:hover {
5504
- border-color: var(--ag-ui-accent);
5589
+ border-color: var(--_accent);
5505
5590
  }
5506
5591
 
5507
5592
  .attach-input {
@@ -5514,17 +5599,17 @@ var STYLES = `
5514
5599
  }
5515
5600
 
5516
5601
  .voice-btn {
5517
- border: 1px solid var(--ag-ui-border);
5602
+ border: 1px solid var(--_border);
5518
5603
  border-radius: 8px;
5519
5604
  padding: 0 10px;
5520
- background: var(--ag-ui-input-bg);
5605
+ background: var(--_input-bg);
5521
5606
  color: inherit;
5522
5607
  font: inherit;
5523
5608
  cursor: pointer;
5524
5609
  }
5525
5610
 
5526
5611
  .voice-btn:hover {
5527
- border-color: var(--ag-ui-accent);
5612
+ border-color: var(--_accent);
5528
5613
  }
5529
5614
 
5530
5615
  .voice-btn:disabled {
@@ -5534,8 +5619,8 @@ var STYLES = `
5534
5619
 
5535
5620
  /* Recording: a red tint + a gentle pulse so it's clearly "live". */
5536
5621
  .voice-btn[data-state="recording"] {
5537
- border-color: var(--ag-ui-danger);
5538
- background: var(--ag-ui-danger);
5622
+ border-color: var(--_danger);
5623
+ background: var(--_danger);
5539
5624
  color: #ffffff;
5540
5625
  animation: ag-ui-voice-pulse 1.2s ease-in-out infinite;
5541
5626
  }
@@ -5578,10 +5663,10 @@ var STYLES = `
5578
5663
  max-width: 100%;
5579
5664
  margin: 2px 0;
5580
5665
  padding: 3px 10px;
5581
- border: 1px dashed var(--ag-ui-border);
5666
+ border: 1px dashed var(--_border);
5582
5667
  border-radius: 999px;
5583
5668
  background: transparent;
5584
- color: var(--ag-ui-muted);
5669
+ color: var(--_muted);
5585
5670
  font-size: 0.8em;
5586
5671
  line-height: 1.4;
5587
5672
  }
@@ -5602,16 +5687,16 @@ var STYLES = `
5602
5687
  gap: 6px;
5603
5688
  max-width: 100%;
5604
5689
  padding: 4px 8px;
5605
- border: 1px solid var(--ag-ui-border);
5690
+ border: 1px solid var(--_border);
5606
5691
  border-radius: 999px;
5607
- background: var(--ag-ui-assistant-bg);
5692
+ background: var(--_assistant-bg);
5608
5693
  font-size: 0.85em;
5609
5694
  position: relative;
5610
5695
  }
5611
5696
 
5612
5697
  .attachment-chip--error {
5613
- border-color: var(--ag-ui-danger);
5614
- color: var(--ag-ui-danger);
5698
+ border-color: var(--_danger);
5699
+ color: var(--_danger);
5615
5700
  }
5616
5701
 
5617
5702
  .attachment-chip-name {
@@ -5622,12 +5707,12 @@ var STYLES = `
5622
5707
  }
5623
5708
 
5624
5709
  .attachment-chip-size {
5625
- color: var(--ag-ui-muted);
5710
+ color: var(--_muted);
5626
5711
  white-space: nowrap;
5627
5712
  }
5628
5713
 
5629
5714
  .attachment-chip--error .attachment-chip-size {
5630
- color: var(--ag-ui-danger);
5715
+ color: var(--_danger);
5631
5716
  }
5632
5717
 
5633
5718
  /* The progress bar fills as the file uploads. */
@@ -5635,13 +5720,13 @@ var STYLES = `
5635
5720
  flex-basis: 100%;
5636
5721
  height: 3px;
5637
5722
  border-radius: 2px;
5638
- background: var(--ag-ui-border);
5723
+ background: var(--_border);
5639
5724
  overflow: hidden;
5640
5725
  }
5641
5726
 
5642
5727
  .attachment-chip-bar-fill {
5643
5728
  height: 100%;
5644
- background: var(--ag-ui-accent);
5729
+ background: var(--_accent);
5645
5730
  transition: width 0.15s ease;
5646
5731
  }
5647
5732
 
@@ -5663,14 +5748,14 @@ var STYLES = `
5663
5748
 
5664
5749
  /* A subtle outline while a file is dragged over the shell. */
5665
5750
  .chat--dragover {
5666
- outline: 2px dashed var(--ag-ui-accent);
5751
+ outline: 2px dashed var(--_accent);
5667
5752
  outline-offset: -4px;
5668
5753
  }
5669
5754
 
5670
5755
  /* Muted "\u23F9 Stopped" line after a cancelled run \u2014 a note, not an error bubble. */
5671
5756
  .stopped-note {
5672
5757
  align-self: flex-start;
5673
- color: var(--ag-ui-muted);
5758
+ color: var(--_muted);
5674
5759
  font-size: 12px;
5675
5760
  padding: 2px 4px;
5676
5761
  }
@@ -5683,14 +5768,14 @@ var STYLES = `
5683
5768
  flex-direction: column;
5684
5769
  gap: 8px;
5685
5770
  padding: 12px;
5686
- background: var(--ag-ui-bg);
5687
- border: 1px solid var(--ag-ui-accent);
5771
+ background: var(--_bg);
5772
+ border: 1px solid var(--_accent);
5688
5773
  border-radius: 10px;
5689
5774
  }
5690
5775
 
5691
5776
  .confirm[data-resolved] {
5692
5777
  opacity: 0.7;
5693
- border-color: var(--ag-ui-border);
5778
+ border-color: var(--_border);
5694
5779
  }
5695
5780
 
5696
5781
  .confirm-body {
@@ -5704,7 +5789,7 @@ var STYLES = `
5704
5789
  overflow: auto;
5705
5790
  font-size: 12px;
5706
5791
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
5707
- background: var(--ag-ui-assistant-bg);
5792
+ background: var(--_assistant-bg);
5708
5793
  border-radius: 8px;
5709
5794
  white-space: pre-wrap;
5710
5795
  word-break: break-word;
@@ -5717,14 +5802,14 @@ var STYLES = `
5717
5802
  }
5718
5803
 
5719
5804
  .confirm-btn {
5720
- border: 1px solid var(--ag-ui-border);
5805
+ border: 1px solid var(--_border);
5721
5806
  border-radius: 8px;
5722
5807
  padding: 8px 14px;
5723
5808
  font: inherit;
5724
5809
  font-weight: 600;
5725
5810
  cursor: pointer;
5726
- background: var(--ag-ui-bg);
5727
- color: var(--ag-ui-fg);
5811
+ background: var(--_bg);
5812
+ color: var(--_fg);
5728
5813
  }
5729
5814
 
5730
5815
  .confirm-btn:disabled {
@@ -5733,8 +5818,8 @@ var STYLES = `
5733
5818
  }
5734
5819
 
5735
5820
  .confirm-btn--confirm {
5736
- border-color: var(--ag-ui-accent);
5737
- background: var(--ag-ui-accent);
5821
+ border-color: var(--_accent);
5822
+ background: var(--_accent);
5738
5823
  color: #ffffff;
5739
5824
  }
5740
5825
 
@@ -5746,14 +5831,14 @@ var STYLES = `
5746
5831
  flex-direction: column;
5747
5832
  gap: 8px;
5748
5833
  padding: 12px;
5749
- background: var(--ag-ui-bg);
5750
- border: 1px solid var(--ag-ui-accent);
5834
+ background: var(--_bg);
5835
+ border: 1px solid var(--_accent);
5751
5836
  border-radius: 10px;
5752
5837
  }
5753
5838
 
5754
5839
  .approval[data-resolved] {
5755
5840
  opacity: 0.7;
5756
- border-color: var(--ag-ui-border);
5841
+ border-color: var(--_border);
5757
5842
  }
5758
5843
 
5759
5844
  .approval-body {
@@ -5767,14 +5852,14 @@ var STYLES = `
5767
5852
  }
5768
5853
 
5769
5854
  .approval-btn {
5770
- border: 1px solid var(--ag-ui-border);
5855
+ border: 1px solid var(--_border);
5771
5856
  border-radius: 8px;
5772
5857
  padding: 8px 14px;
5773
5858
  font: inherit;
5774
5859
  font-weight: 600;
5775
5860
  cursor: pointer;
5776
- background: var(--ag-ui-bg);
5777
- color: var(--ag-ui-fg);
5861
+ background: var(--_bg);
5862
+ color: var(--_fg);
5778
5863
  }
5779
5864
 
5780
5865
  .approval-btn:disabled {
@@ -5783,8 +5868,8 @@ var STYLES = `
5783
5868
  }
5784
5869
 
5785
5870
  .approval-btn--approve {
5786
- border-color: var(--ag-ui-accent);
5787
- background: var(--ag-ui-accent);
5871
+ border-color: var(--_accent);
5872
+ background: var(--_accent);
5788
5873
  color: #ffffff;
5789
5874
  }
5790
5875
 
@@ -5796,14 +5881,14 @@ var STYLES = `
5796
5881
  flex-direction: column;
5797
5882
  gap: 8px;
5798
5883
  padding: 12px;
5799
- background: var(--ag-ui-bg);
5800
- border: 1px solid var(--ag-ui-accent);
5884
+ background: var(--_bg);
5885
+ border: 1px solid var(--_accent);
5801
5886
  border-radius: 10px;
5802
5887
  }
5803
5888
 
5804
5889
  .question[data-resolved] {
5805
5890
  opacity: 0.7;
5806
- border-color: var(--ag-ui-border);
5891
+ border-color: var(--_border);
5807
5892
  }
5808
5893
 
5809
5894
  .question-body {
@@ -5828,9 +5913,9 @@ var STYLES = `
5828
5913
  width: 100%;
5829
5914
  padding: 8px 10px;
5830
5915
  font: inherit;
5831
- color: var(--ag-ui-fg);
5832
- background: var(--ag-ui-bg);
5833
- border: 1px solid var(--ag-ui-border);
5916
+ color: var(--_fg);
5917
+ background: var(--_bg);
5918
+ border: 1px solid var(--_border);
5834
5919
  border-radius: 8px;
5835
5920
  }
5836
5921
 
@@ -5844,13 +5929,13 @@ var STYLES = `
5844
5929
  }
5845
5930
 
5846
5931
  .question-btn {
5847
- border: 1px solid var(--ag-ui-accent);
5932
+ border: 1px solid var(--_accent);
5848
5933
  border-radius: 8px;
5849
5934
  padding: 8px 14px;
5850
5935
  font: inherit;
5851
5936
  font-weight: 600;
5852
5937
  cursor: pointer;
5853
- background: var(--ag-ui-accent);
5938
+ background: var(--_accent);
5854
5939
  color: #ffffff;
5855
5940
  }
5856
5941
 
@@ -5868,18 +5953,18 @@ var STYLES = `
5868
5953
  }
5869
5954
 
5870
5955
  .skill-chip {
5871
- border: 1px solid var(--ag-ui-border);
5956
+ border: 1px solid var(--_border);
5872
5957
  border-radius: 999px;
5873
5958
  padding: 4px 12px;
5874
5959
  font: inherit;
5875
5960
  font-size: 0.9em;
5876
5961
  cursor: pointer;
5877
- background: var(--ag-ui-assistant-bg);
5878
- color: var(--ag-ui-fg);
5962
+ background: var(--_assistant-bg);
5963
+ color: var(--_fg);
5879
5964
  }
5880
5965
 
5881
5966
  .skill-chip:hover {
5882
- border-color: var(--ag-ui-accent);
5967
+ border-color: var(--_accent);
5883
5968
  }
5884
5969
 
5885
5970
  .skill-palette {
@@ -5888,8 +5973,8 @@ var STYLES = `
5888
5973
  flex-direction: column;
5889
5974
  max-height: 220px;
5890
5975
  overflow: auto;
5891
- background: var(--ag-ui-bg);
5892
- border: 1px solid var(--ag-ui-border);
5976
+ background: var(--_bg);
5977
+ border: 1px solid var(--_border);
5893
5978
  border-radius: 8px;
5894
5979
  box-shadow: 0 8px 24px rgba(20, 20, 50, 0.16);
5895
5980
  }
@@ -5905,11 +5990,11 @@ var STYLES = `
5905
5990
  font: inherit;
5906
5991
  text-align: left;
5907
5992
  cursor: pointer;
5908
- color: var(--ag-ui-fg);
5993
+ color: var(--_fg);
5909
5994
  }
5910
5995
 
5911
5996
  .skill-item[aria-selected="true"] {
5912
- background: var(--ag-ui-assistant-bg);
5997
+ background: var(--_assistant-bg);
5913
5998
  }
5914
5999
 
5915
6000
  .skill-item-title {
@@ -5918,7 +6003,7 @@ var STYLES = `
5918
6003
 
5919
6004
  .skill-item-desc {
5920
6005
  font-size: 0.85em;
5921
- color: var(--ag-ui-muted);
6006
+ color: var(--_muted);
5922
6007
  }
5923
6008
 
5924
6009
  /* The hint sits directly above the composer's top border, so a zero bottom
@@ -5927,7 +6012,7 @@ var STYLES = `
5927
6012
  margin: 8px 12px;
5928
6013
  font-size: 0.85em;
5929
6014
  line-height: 1.4;
5930
- color: var(--ag-ui-danger);
6015
+ color: var(--_danger);
5931
6016
  }
5932
6017
 
5933
6018
  /* Chat-history drawer \u2014 a slide-over within the chat panel. */
@@ -5951,9 +6036,9 @@ var STYLES = `
5951
6036
  flex-direction: column;
5952
6037
  gap: 0.25rem;
5953
6038
  padding: 0.5rem;
5954
- border: 1px solid var(--ag-ui-border);
6039
+ border: 1px solid var(--_border);
5955
6040
  border-radius: 0.5rem;
5956
- background: var(--ag-ui-assistant-bg);
6041
+ background: var(--_assistant-bg);
5957
6042
  box-shadow: 0 6px 24px rgb(0 0 0 / 12%);
5958
6043
  max-height: 60%;
5959
6044
  overflow-y: auto;
@@ -5984,7 +6069,7 @@ var STYLES = `
5984
6069
  }
5985
6070
 
5986
6071
  .checkpoint-row:hover {
5987
- background: var(--ag-ui-hover);
6072
+ background: var(--_hover);
5988
6073
  }
5989
6074
 
5990
6075
  .checkpoint-label {
@@ -5999,7 +6084,7 @@ var STYLES = `
5999
6084
  font-size: 0.6875rem;
6000
6085
  padding: 0 0.375rem;
6001
6086
  border-radius: 999px;
6002
- background: var(--ag-ui-hover);
6087
+ background: var(--_hover);
6003
6088
  opacity: 0.8;
6004
6089
  }
6005
6090
 
@@ -6008,14 +6093,14 @@ var STYLES = `
6008
6093
  font-size: 0.75rem;
6009
6094
  cursor: pointer;
6010
6095
  padding: 0.125rem 0.5rem;
6011
- border: 1px solid var(--ag-ui-border);
6096
+ border: 1px solid var(--_border);
6012
6097
  border-radius: 0.375rem;
6013
6098
  background: transparent;
6014
6099
  color: inherit;
6015
6100
  }
6016
6101
 
6017
6102
  .checkpoint-action:hover {
6018
- background: var(--ag-ui-hover);
6103
+ background: var(--_hover);
6019
6104
  }
6020
6105
 
6021
6106
  .drawer-backdrop {
@@ -6030,9 +6115,9 @@ var STYLES = `
6030
6115
  flex-direction: column;
6031
6116
  width: min(300px, 85%);
6032
6117
  height: 100%;
6033
- background: var(--ag-ui-bg);
6034
- border-right: 1px solid var(--ag-ui-border);
6035
- box-shadow: var(--ag-ui-shadow);
6118
+ background: var(--_bg);
6119
+ border-right: 1px solid var(--_border);
6120
+ box-shadow: var(--_shadow);
6036
6121
  overflow: hidden;
6037
6122
  }
6038
6123
 
@@ -6040,9 +6125,9 @@ var STYLES = `
6040
6125
  display: flex;
6041
6126
  align-items: center;
6042
6127
  justify-content: space-between;
6043
- gap: var(--ag-ui-space);
6044
- padding: var(--ag-ui-pad);
6045
- border-bottom: 1px solid var(--ag-ui-border);
6128
+ gap: var(--_space);
6129
+ padding: var(--_pad);
6130
+ border-bottom: 1px solid var(--_border);
6046
6131
  }
6047
6132
 
6048
6133
  .drawer-title {
@@ -6050,10 +6135,10 @@ var STYLES = `
6050
6135
  }
6051
6136
 
6052
6137
  .drawer-new {
6053
- border: 1px solid var(--ag-ui-border);
6054
- border-radius: var(--ag-ui-radius);
6055
- background: var(--ag-ui-bg);
6056
- color: var(--ag-ui-accent);
6138
+ border: 1px solid var(--_border);
6139
+ border-radius: var(--_radius);
6140
+ background: var(--_bg);
6141
+ color: var(--_accent);
6057
6142
  padding: 4px 10px;
6058
6143
  font: inherit;
6059
6144
  font-size: 0.85em;
@@ -6067,19 +6152,19 @@ var STYLES = `
6067
6152
  }
6068
6153
 
6069
6154
  .drawer-empty {
6070
- padding: var(--ag-ui-pad);
6155
+ padding: var(--_pad);
6071
6156
  font-size: 0.9em;
6072
- color: var(--ag-ui-muted);
6157
+ color: var(--_muted);
6073
6158
  }
6074
6159
 
6075
6160
  .drawer-row {
6076
6161
  display: flex;
6077
6162
  align-items: stretch;
6078
- border-bottom: 1px solid var(--ag-ui-border);
6163
+ border-bottom: 1px solid var(--_border);
6079
6164
  }
6080
6165
 
6081
6166
  .drawer-row--active {
6082
- background: var(--ag-ui-assistant-bg);
6167
+ background: var(--_assistant-bg);
6083
6168
  }
6084
6169
 
6085
6170
  .drawer-row-select {
@@ -6106,12 +6191,12 @@ var STYLES = `
6106
6191
 
6107
6192
  .drawer-row-time {
6108
6193
  font-size: 0.72em;
6109
- color: var(--ag-ui-muted);
6194
+ color: var(--_muted);
6110
6195
  }
6111
6196
 
6112
6197
  .drawer-row-preview {
6113
6198
  font-size: 0.8em;
6114
- color: var(--ag-ui-muted);
6199
+ color: var(--_muted);
6115
6200
  overflow: hidden;
6116
6201
  white-space: nowrap;
6117
6202
  text-overflow: ellipsis;
@@ -6128,7 +6213,7 @@ var STYLES = `
6128
6213
  .drawer-row-delete {
6129
6214
  border: none;
6130
6215
  background: none;
6131
- color: var(--ag-ui-muted);
6216
+ color: var(--_muted);
6132
6217
  font-size: 0.9em;
6133
6218
  padding: 4px;
6134
6219
  cursor: pointer;
@@ -6139,10 +6224,10 @@ var STYLES = `
6139
6224
  min-width: 0;
6140
6225
  margin: 6px 10px;
6141
6226
  padding: 4px 8px;
6142
- border: 1px solid var(--ag-ui-accent);
6227
+ border: 1px solid var(--_accent);
6143
6228
  border-radius: 6px;
6144
- background: var(--ag-ui-input-bg);
6145
- color: var(--ag-ui-fg);
6229
+ background: var(--_input-bg);
6230
+ color: var(--_fg);
6146
6231
  font: inherit;
6147
6232
  }
6148
6233
 
@@ -6155,13 +6240,13 @@ var STYLES = `
6155
6240
  }
6156
6241
 
6157
6242
  .drawer-confirm-label {
6158
- color: var(--ag-ui-danger);
6243
+ color: var(--_danger);
6159
6244
  }
6160
6245
 
6161
6246
  .drawer-confirm-yes {
6162
6247
  border: none;
6163
6248
  border-radius: 6px;
6164
- background: var(--ag-ui-danger);
6249
+ background: var(--_danger);
6165
6250
  color: #ffffff;
6166
6251
  padding: 3px 10px;
6167
6252
  font: inherit;
@@ -6169,7 +6254,7 @@ var STYLES = `
6169
6254
  }
6170
6255
 
6171
6256
  .drawer-confirm-no {
6172
- border: 1px solid var(--ag-ui-border);
6257
+ border: 1px solid var(--_border);
6173
6258
  border-radius: 6px;
6174
6259
  background: none;
6175
6260
  color: inherit;
@@ -7232,6 +7317,13 @@ function truncate(text2, limit) {
7232
7317
 
7233
7318
  // src/core/create_http_agent.ts
7234
7319
  import { HttpAgent } from "@ag-ui/client";
7320
+
7321
+ // src/core/utils.ts
7322
+ function withCredentials(init, credentials) {
7323
+ return credentials === void 0 ? init : { ...init, credentials };
7324
+ }
7325
+
7326
+ // src/core/create_http_agent.ts
7235
7327
  function createHttpAgent(options) {
7236
7328
  return new HttpAgent({
7237
7329
  url: options.endpoint,
@@ -7242,17 +7334,18 @@ function createHttpAgent(options) {
7242
7334
  // "Illegal invocation" in browsers. Wrap it so `fetch` is always called as
7243
7335
  // a free function with the correct receiver. The wrapper also overlays
7244
7336
  // `getHeaders()` per request, so header rotation (CSRF, short-lived JWT)
7245
- // reaches the stream even though the agent instance is cached.
7337
+ // reaches the stream even though the agent instance is cached, and applies
7338
+ // the configured cookie policy — the agent's own config has no seam for it.
7246
7339
  fetch: (url, init) => {
7247
7340
  const fresh = options.getHeaders?.();
7248
7341
  if (fresh === void 0) {
7249
- return fetch(url, init);
7342
+ return fetch(url, withCredentials(init, options.credentials));
7250
7343
  }
7251
7344
  const headers = new Headers(init?.headers);
7252
7345
  for (const [name, value] of Object.entries(fresh)) {
7253
7346
  headers.set(name, value);
7254
7347
  }
7255
- return fetch(url, { ...init, headers });
7348
+ return fetch(url, withCredentials({ ...init, headers }, options.credentials));
7256
7349
  },
7257
7350
  // Spread conditionally: under `exactOptionalPropertyTypes` an explicit
7258
7351
  // `undefined` is not assignable to these optional config fields.
@@ -7266,12 +7359,14 @@ var RemoteConversationStore = class {
7266
7359
  #url;
7267
7360
  #headers;
7268
7361
  #local;
7362
+ #credentials;
7269
7363
  #dropped = /* @__PURE__ */ new Set();
7270
7364
  #renamed = /* @__PURE__ */ new Map();
7271
- constructor(url, headers = () => ({}), local = new SessionStorageStore()) {
7365
+ constructor(url, headers = () => ({}), local = new SessionStorageStore(), credentials = () => void 0) {
7272
7366
  this.#url = url.endsWith("/") ? url : `${url}/`;
7273
7367
  this.#headers = headers;
7274
7368
  this.#local = local;
7369
+ this.#credentials = credentials;
7275
7370
  }
7276
7371
  threadId() {
7277
7372
  return this.#local.threadId();
@@ -7349,7 +7444,7 @@ var RemoteConversationStore = class {
7349
7444
  /** GET that resolves to the `Response`, or `null` on a network error. */
7350
7445
  async #get(url) {
7351
7446
  try {
7352
- return await fetch(url, { headers: this.#headers() });
7447
+ return await fetch(url, withCredentials({ headers: this.#headers() }, this.#credentials()));
7353
7448
  } catch {
7354
7449
  return null;
7355
7450
  }
@@ -7358,11 +7453,17 @@ var RemoteConversationStore = class {
7358
7453
  async #mutate(threadId, method, body) {
7359
7454
  const headers = this.#headers();
7360
7455
  try {
7361
- await fetch(`${this.#url}${encodeURIComponent(threadId)}/`, {
7362
- method,
7363
- headers: body === void 0 ? headers : { ...headers, "content-type": "application/json" },
7364
- body: body === void 0 ? null : JSON.stringify(body)
7365
- });
7456
+ await fetch(
7457
+ `${this.#url}${encodeURIComponent(threadId)}/`,
7458
+ withCredentials(
7459
+ {
7460
+ method,
7461
+ headers: body === void 0 ? headers : { ...headers, "content-type": "application/json" },
7462
+ body: body === void 0 ? null : JSON.stringify(body)
7463
+ },
7464
+ this.#credentials()
7465
+ )
7466
+ );
7366
7467
  } catch {
7367
7468
  }
7368
7469
  }
@@ -7372,9 +7473,11 @@ var RemoteConversationStore = class {
7372
7473
  var RunIndex = class {
7373
7474
  #url;
7374
7475
  #headers;
7375
- constructor(url, headers = () => ({})) {
7476
+ #credentials;
7477
+ constructor(url, headers = () => ({}), credentials = () => void 0) {
7376
7478
  this.#url = url.endsWith("/") ? url : `${url}/`;
7377
7479
  this.#headers = headers;
7480
+ this.#credentials = credentials;
7378
7481
  }
7379
7482
  /**
7380
7483
  * The user's runs, or `[]` when the endpoint is unreachable or answers with
@@ -7384,10 +7487,16 @@ var RunIndex = class {
7384
7487
  */
7385
7488
  async list() {
7386
7489
  try {
7387
- const response = await fetch(this.#url, {
7388
- method: "GET",
7389
- headers: { Accept: "application/json", ...this.#headers() }
7390
- });
7490
+ const response = await fetch(
7491
+ this.#url,
7492
+ withCredentials(
7493
+ {
7494
+ method: "GET",
7495
+ headers: { Accept: "application/json", ...this.#headers() }
7496
+ },
7497
+ this.#credentials()
7498
+ )
7499
+ );
7391
7500
  if (!response.ok) {
7392
7501
  return [];
7393
7502
  }
@@ -7426,11 +7535,17 @@ var RunIndex = class {
7426
7535
  async function transcribeAudio(audio, options) {
7427
7536
  const form = new FormData();
7428
7537
  form.append("audio", audio, "recording.webm");
7429
- const response = await fetch(options.url, {
7430
- method: "POST",
7431
- headers: { ...options.headers ?? {} },
7432
- body: form
7433
- });
7538
+ const response = await fetch(
7539
+ options.url,
7540
+ withCredentials(
7541
+ {
7542
+ method: "POST",
7543
+ headers: { ...options.headers ?? {} },
7544
+ body: form
7545
+ },
7546
+ options.credentials
7547
+ )
7548
+ );
7434
7549
  if (!response.ok) {
7435
7550
  throw new Error(await errorMessage(response));
7436
7551
  }
@@ -7458,6 +7573,7 @@ function uploadAttachment(file, options) {
7458
7573
  form.append("file", file);
7459
7574
  const xhr = new XMLHttpRequest();
7460
7575
  xhr.open("POST", options.url);
7576
+ xhr.withCredentials = options.credentials === "include";
7461
7577
  for (const [key, value] of Object.entries(options.headers ?? {})) {
7462
7578
  xhr.setRequestHeader(key, value);
7463
7579
  }
@@ -7531,14 +7647,42 @@ var CONNECT_TIME_ATTRIBUTES = [
7531
7647
  "data-strings",
7532
7648
  "data-icon-url"
7533
7649
  ];
7650
+ var CREDENTIALS_MODES = ["omit", "same-origin", "include"];
7651
+ function isCredentialsMode(value) {
7652
+ return CREDENTIALS_MODES.includes(value);
7653
+ }
7534
7654
  var COLLAPSED_KEY = "ag-ui-chat:collapsed";
7535
7655
  var SIZE_KEY = "ag-ui-chat:size";
7536
7656
  var THEME_KEY = "ag-ui-chat:theme";
7537
7657
  var AgUiChat = class extends HTMLElement {
7538
7658
  /** Agent factory; override to inject a custom or fake agent (tests). */
7539
7659
  agentFactory = createHttpAgent;
7540
- /** Extra HTTP headers for the AG-UI endpoint (e.g. CSRF). */
7660
+ /**
7661
+ * Static extra HTTP headers, sent with **every** request this element makes —
7662
+ * the agent run, the thread index and its messages, the tool and skill
7663
+ * catalogs, the run index, uploads and transcription.
7664
+ *
7665
+ * Right for values fixed for the element's lifetime. A credential that
7666
+ * rotates (a short-lived JWT, a re-issued CSRF token) belongs in
7667
+ * {@link getHeaders} instead: this is read at request time, but only a
7668
+ * re-assignment updates it, so a token captured here is pinned until the host
7669
+ * remembers to assign again.
7670
+ */
7541
7671
  headers = {};
7672
+ /**
7673
+ * Live header source, consulted immediately before every request — the way to
7674
+ * supply rotating credentials.
7675
+ *
7676
+ * Set it to a function and each request calls it afresh: a token refreshed by
7677
+ * the host between two requests reaches the second one, with nothing to
7678
+ * re-assign and nothing to keep in sync.
7679
+ *
7680
+ * Composes with {@link headers} rather than replacing it: the two are merged
7681
+ * per key with `getHeaders()` winning, so a static `X-Client` and a rotating
7682
+ * `Authorization` can be configured independently and neither silently drops
7683
+ * the other.
7684
+ */
7685
+ getHeaders = null;
7542
7686
  /**
7543
7687
  * Permit `<img>` in rendered assistant markdown. **Off by default**: a
7544
7688
  * model-controlled image URL is fetched with no user interaction, which
@@ -7825,7 +7969,11 @@ var AgUiChat = class extends HTMLElement {
7825
7969
  return null;
7826
7970
  }
7827
7971
  if (this.#runIndex === null) {
7828
- this.#runIndex = new RunIndex(url, () => this.headers);
7972
+ this.#runIndex = new RunIndex(
7973
+ url,
7974
+ () => this.#requestHeaders(),
7975
+ () => this.#requestCredentials()
7976
+ );
7829
7977
  }
7830
7978
  return this.#runIndex;
7831
7979
  }
@@ -7857,8 +8005,9 @@ var AgUiChat = class extends HTMLElement {
7857
8005
  const endpoint = verb === "resume" ? index.resumeUrl(runId) : index.forkUrl(runId);
7858
8006
  const agent = this.agentFactory({
7859
8007
  endpoint,
7860
- headers: this.headers,
7861
- getHeaders: () => this.headers,
8008
+ headers: this.#requestHeaders(),
8009
+ getHeaders: () => this.#requestHeaders(),
8010
+ ...this.#credentialsOption(),
7862
8011
  threadId: this.#threadId,
7863
8012
  // The seed the endpoints assume: nothing. The snapshot is the history.
7864
8013
  initialMessages: []
@@ -7881,10 +8030,19 @@ var AgUiChat = class extends HTMLElement {
7881
8030
  }
7882
8031
  /** Attributes the element reacts to after it has been connected. */
7883
8032
  static get observedAttributes() {
7884
- return ["title-text", "placement", ...CONNECT_TIME_ATTRIBUTES];
8033
+ return ["title-text", "placement", "credentials", ...CONNECT_TIME_ATTRIBUTES];
7885
8034
  }
7886
8035
  attributeChangedCallback(name, previous, value) {
8036
+ if (name === "credentials") {
8037
+ if (value !== null && !isCredentialsMode(value)) {
8038
+ console.error(
8039
+ `<ag-ui-chat>: credentials="${value}" is not a fetch credentials mode (${CREDENTIALS_MODES.join(" / ")}) \u2014 it is being ignored, so requests use the browser default and cross-origin cookies will not be sent.`
8040
+ );
8041
+ }
8042
+ return;
8043
+ }
7887
8044
  if (name === "placement") {
8045
+ this.#releaseOwnedAxes();
7888
8046
  requestAnimationFrame(() => this.#syncResizeAnchor());
7889
8047
  return;
7890
8048
  }
@@ -8073,6 +8231,72 @@ var AgUiChat = class extends HTMLElement {
8073
8231
  set endpoint(value) {
8074
8232
  this.setAttribute("endpoint", value);
8075
8233
  }
8234
+ /**
8235
+ * Cookie policy for **every** request this element makes, as `fetch`'s own
8236
+ * `credentials` mode (`"omit"` / `"same-origin"` / `"include"`). Mirrored to
8237
+ * the `credentials` attribute, so markup embeds can set it without script.
8238
+ *
8239
+ * `null` (the default) leaves the browser's default of `same-origin` in
8240
+ * place. That default sends **no cookies at all** when the endpoints live on
8241
+ * a different origin from the page — app.example.com calling
8242
+ * api.example.com is cross-origin — and the request goes out anonymously
8243
+ * rather than failing, so the symptom is a 401 from a server that looks
8244
+ * correctly configured. A cookie-authenticated cross-origin deployment wants
8245
+ * `"include"`, plus `Access-Control-Allow-Credentials: true` and a concrete
8246
+ * (non-wildcard) `Access-Control-Allow-Origin` on the server.
8247
+ *
8248
+ * Read per request, so a late assignment applies to everything after it.
8249
+ * `"omit"` cannot be honoured by the built-in **upload** transport, which is
8250
+ * an `XMLHttpRequest` and only has a two-state cookie switch; every other
8251
+ * endpoint honours all three modes.
8252
+ */
8253
+ get credentials() {
8254
+ const attr = this.getAttribute("credentials");
8255
+ return attr !== null && isCredentialsMode(attr) ? attr : null;
8256
+ }
8257
+ set credentials(value) {
8258
+ if (value === null) {
8259
+ this.removeAttribute("credentials");
8260
+ return;
8261
+ }
8262
+ if (!isCredentialsMode(value)) {
8263
+ throw new TypeError(
8264
+ `<ag-ui-chat>: credentials must be one of ${CREDENTIALS_MODES.map((mode) => `"${mode}"`).join(", ")} (got ${JSON.stringify(value)}).`
8265
+ );
8266
+ }
8267
+ this.setAttribute("credentials", value);
8268
+ }
8269
+ /**
8270
+ * The headers for the request about to go out: the static {@link headers}
8271
+ * with {@link getHeaders}'s live values overlaid, per key.
8272
+ *
8273
+ * Every request site goes through here, so "how this element authenticates"
8274
+ * is one answer rather than one per endpoint.
8275
+ */
8276
+ #requestHeaders() {
8277
+ return { ...this.headers, ...this.getHeaders?.() };
8278
+ }
8279
+ /** The configured cookie policy as `fetch` spells it; `undefined` when unset. */
8280
+ #requestCredentials() {
8281
+ return this.credentials ?? void 0;
8282
+ }
8283
+ /**
8284
+ * The `credentials` entry for an {@link AgentFactory} call, or nothing at all.
8285
+ *
8286
+ * Spread rather than assigned: `exactOptionalPropertyTypes` rejects an
8287
+ * explicit `credentials: undefined`, and a factory should see the field
8288
+ * absent — not present-and-empty — when no policy is configured. The agent
8289
+ * reads it when it is built (first send, thread switch, continuation), by
8290
+ * which time any host configuration has landed.
8291
+ */
8292
+ #credentialsOption() {
8293
+ const credentials = this.#requestCredentials();
8294
+ return credentials === void 0 ? {} : { credentials };
8295
+ }
8296
+ /** The `fetch` init for the element's own plain GETs (catalogs). */
8297
+ #fetchInit() {
8298
+ return withCredentials({ headers: this.#requestHeaders() }, this.#requestCredentials());
8299
+ }
8076
8300
  /**
8077
8301
  * How much detail tool-call cards show, from the `data-tool-display`
8078
8302
  * attribute (`minimal` / `inline` / `compact` / `full`). Defaults to `full`.
@@ -8110,7 +8334,6 @@ var AgUiChat = class extends HTMLElement {
8110
8334
  }
8111
8335
  this.#syncRail();
8112
8336
  this.#initSkills();
8113
- void this.#fetchToolCatalog();
8114
8337
  if (this.#storageNs !== "" && this.conversationStore instanceof SessionStorageStore) {
8115
8338
  this.conversationStore = new SessionStorageStore(this.#storageNs);
8116
8339
  }
@@ -8118,9 +8341,62 @@ var AgUiChat = class extends HTMLElement {
8118
8341
  this.#wireAttachments();
8119
8342
  this.#wireVoice();
8120
8343
  this.#threadId = this.conversationStore.threadId();
8344
+ queueMicrotask(() => this.#startup());
8121
8345
  void this.#rehydrate();
8122
8346
  this.#connected = true;
8123
8347
  }
8348
+ /**
8349
+ * The catalog requests the element issues on startup: the tool labels
8350
+ * (`data-tools-url`) and the backend skills (`data-skills-url`).
8351
+ *
8352
+ * Deliberately one microtask behind `connectedCallback`. A host that
8353
+ * configures the element through a framework ref necessarily does so *after*
8354
+ * inserting the node — React attaches refs and runs layout effects in the
8355
+ * same commit as the insertion, but strictly afterwards — so a request issued
8356
+ * from `connectedCallback` itself goes out before `headers`,
8357
+ * {@link getHeaders} or {@link credentials} exist, and comes back 401 in a
8358
+ * way that reads as a server fault rather than a mis-timed assignment. A
8359
+ * microtask lands after that commit and still before paint.
8360
+ *
8361
+ * Two things it is **not**. It is not a fix for configuration that arrives
8362
+ * later than the commit (a passive `useEffect`, an awaited token fetch):
8363
+ * configure before insertion (`createElement` → configure → `append`) or call
8364
+ * {@link reload} once configured, because a longer timer would hide that race
8365
+ * rather than close it. And it deliberately excludes the *history* replay,
8366
+ * which stays in `connectedCallback`: the replay renders into the transcript,
8367
+ * so deferring it lets a `sendMessage()` issued in the same task land first
8368
+ * and the replay then duplicate it. The thread history is therefore the one
8369
+ * request that can still go out before a ref is attached — {@link reload}
8370
+ * covers it.
8371
+ */
8372
+ #startup() {
8373
+ if (!this.#connected) {
8374
+ return;
8375
+ }
8376
+ void this.#fetchToolCatalog();
8377
+ void this.#fetchSkills();
8378
+ }
8379
+ /**
8380
+ * Re-run everything the element loads on startup — the tool-label catalog,
8381
+ * the backend skill catalog and the thread's history — with the transport
8382
+ * configuration as it stands now.
8383
+ *
8384
+ * This is the answer for a host that can only configure the element after the
8385
+ * fact (a token fetched in a passive effect, an async auth handshake): the
8386
+ * startup requests already went out with whatever was set then, and this says
8387
+ * "try again, properly authenticated" without removing and re-inserting the
8388
+ * node.
8389
+ *
8390
+ * A reload, not a merge — the in-flight run is cancelled and the transcript
8391
+ * is rebuilt from the persisted history, so anything streamed since is
8392
+ * dropped. Call it once, when configuration lands; not between turns.
8393
+ */
8394
+ async reload() {
8395
+ this.#cancelRun();
8396
+ this.#resetState();
8397
+ this.#setRunning(false);
8398
+ await Promise.all([this.#fetchToolCatalog(), this.#fetchSkills(), this.#rehydrate()]);
8399
+ }
8124
8400
  /**
8125
8401
  * Tear down live resources when the element leaves the DOM (a removed node, a
8126
8402
  * client-side route swap): cancel the in-flight run so its SSE stream closes,
@@ -8202,7 +8478,13 @@ var AgUiChat = class extends HTMLElement {
8202
8478
  if (url === null) {
8203
8479
  return null;
8204
8480
  }
8205
- return (file, onProgress, signal) => uploadAttachment(file, { url, headers: this.headers, onProgress, signal });
8481
+ return (file, onProgress, signal) => uploadAttachment(file, {
8482
+ url,
8483
+ headers: this.#requestHeaders(),
8484
+ ...this.#credentialsOption(),
8485
+ onProgress,
8486
+ signal
8487
+ });
8206
8488
  }
8207
8489
  /**
8208
8490
  * Reveal the composer's 🎤 mic button when transcription is possible — either
@@ -8229,7 +8511,11 @@ var AgUiChat = class extends HTMLElement {
8229
8511
  if (url === null) {
8230
8512
  return null;
8231
8513
  }
8232
- return (audio) => transcribeAudio(audio, { url, headers: this.headers });
8514
+ return (audio) => transcribeAudio(audio, {
8515
+ url,
8516
+ headers: this.#requestHeaders(),
8517
+ ...this.#credentialsOption()
8518
+ });
8233
8519
  }
8234
8520
  /** Drop a voice transcript into the composer (appended to any typed text). */
8235
8521
  #insertVoiceText(text2) {
@@ -8304,8 +8590,9 @@ Use the read_attachment tool with an id to read a file's contents.`
8304
8590
  if (url !== null) {
8305
8591
  this.conversationStore = new RemoteConversationStore(
8306
8592
  url,
8307
- () => this.headers,
8308
- this.conversationStore
8593
+ () => this.#requestHeaders(),
8594
+ this.conversationStore,
8595
+ () => this.#requestCredentials()
8309
8596
  );
8310
8597
  }
8311
8598
  }
@@ -8316,7 +8603,7 @@ Use the read_attachment tool with an id to read a file's contents.`
8316
8603
  return;
8317
8604
  }
8318
8605
  try {
8319
- const response = await fetch(url, { headers: this.headers });
8606
+ const response = await fetch(url, this.#fetchInit());
8320
8607
  this.#toolCatalog = parseToolCatalog(await response.json());
8321
8608
  } catch {
8322
8609
  }
@@ -8329,13 +8616,16 @@ Use the read_attachment tool with an id to read a file's contents.`
8329
8616
  this.#clientSkills = skills;
8330
8617
  this.#recomputeSkills();
8331
8618
  }
8332
- /** Wire the skill surfaces: opt-in flags, embedded catalog, optional fetch. */
8619
+ /**
8620
+ * Wire the skill surfaces: opt-in flags and the embedded catalog. The backend
8621
+ * catalog is fetched from `#startup`, a microtask later, so it carries the
8622
+ * host's transport configuration.
8623
+ */
8333
8624
  #initSkills() {
8334
8625
  this.#skillsMenu.enableChips(this.#flag("data-prompt-chips"));
8335
8626
  this.#skillsMenu.enableSlash(this.#flag("data-slash-commands"));
8336
8627
  this.#embedSkills = this.#readEmbeddedSkills();
8337
8628
  this.#recomputeSkills();
8338
- void this.#fetchSkills();
8339
8629
  }
8340
8630
  /** Parse the inline `data-skills` JSON catalog (empty when absent/malformed). */
8341
8631
  #readEmbeddedSkills() {
@@ -8356,7 +8646,7 @@ Use the read_attachment tool with an id to read a file's contents.`
8356
8646
  return;
8357
8647
  }
8358
8648
  try {
8359
- const response = await fetch(url, { headers: this.headers });
8649
+ const response = await fetch(url, this.#fetchInit());
8360
8650
  this.#backendSkills = parseSkills(await response.json());
8361
8651
  this.#recomputeSkills();
8362
8652
  } catch {
@@ -8531,21 +8821,45 @@ Use the read_attachment tool with an id to read a file's contents.`
8531
8821
  this.style.setProperty(name, value);
8532
8822
  }
8533
8823
  /**
8534
- * Write a dragged size onto the host as custom properties.
8824
+ * Write a dragged size onto the host, on the axes this placement leaves free.
8535
8825
  *
8536
- * Properties rather than inline `width` / `height`: the placement rules set
8537
- * those same properties, so an inline dimension would outrank them and a
8538
- * panel dragged while floating would keep that width after switching to
8539
- * fullscreen.
8826
+ * Writing the custom property rather than inline `width` / `height` does
8827
+ * **not** by itself leave placement in charge an inline custom property
8828
+ * still outranks a `:host([placement=…])` rule setting the same property, so
8829
+ * a height dragged while floating capped a docked sidebar that had asked for
8830
+ * `100vh`. The cascade cannot arbitrate this; the axis check has to.
8831
+ *
8832
+ * So the rule is explicit: a placement owns the axes it fixes, and a
8833
+ * persisted size is only ever applied to the ones it does not.
8540
8834
  */
8541
8835
  #applySize(size) {
8836
+ const axis = this.#resizeAxis();
8837
+ if (axis === "none") {
8838
+ return;
8839
+ }
8542
8840
  if (size.width !== void 0) {
8543
8841
  this.style.setProperty("--ag-ui-width", `${size.width}px`);
8544
8842
  }
8545
- if (size.height !== void 0) {
8843
+ if (size.height !== void 0 && axis === "both") {
8546
8844
  this.style.setProperty("--ag-ui-height", `${size.height}px`);
8547
8845
  }
8548
8846
  }
8847
+ /**
8848
+ * Drop any dragged size the new placement has taken ownership of.
8849
+ *
8850
+ * Without this a size survives the switch as an inline property and silently
8851
+ * overrides the placement it moved to — the panel keeps a floating height
8852
+ * while docked, and reads as a component that cannot do full height.
8853
+ */
8854
+ #releaseOwnedAxes() {
8855
+ const axis = this.#resizeAxis();
8856
+ if (axis !== "both") {
8857
+ this.style.removeProperty("--ag-ui-height");
8858
+ }
8859
+ if (axis === "none") {
8860
+ this.style.removeProperty("--ag-ui-width");
8861
+ }
8862
+ }
8549
8863
  /** Persist a dragged size per tab, alongside the collapsed/theme preferences. */
8550
8864
  #persistSize(size) {
8551
8865
  const stored = { ...this.#readSize(), ...size };
@@ -8585,6 +8899,32 @@ Use the read_attachment tool with an id to read a file's contents.`
8585
8899
  const dark = this.getAttribute("theme") === "dark";
8586
8900
  this.#themeToggle.textContent = dark ? "\u2600\uFE0F" : "\u{1F319}";
8587
8901
  }
8902
+ /**
8903
+ * Open the thread-history drawer: the imperative route to the control that
8904
+ * renders as `::part(history-button)`.
8905
+ *
8906
+ * A host that hides `::part(header)` to render its own title bar hides the
8907
+ * history, new-chat and collapse buttons with it — and thread switching then
8908
+ * has no route at all, because those controls live inside the header. Each of
8909
+ * them has a method, so a host chrome can rebuild the set: this one,
8910
+ * {@link openCheckpoints}, {@link newChat}, {@link toggleCollapsed} and
8911
+ * {@link toggleTheme}.
8912
+ */
8913
+ openThreads() {
8914
+ void this.#refreshDrawer();
8915
+ this.#drawer.open();
8916
+ }
8917
+ /**
8918
+ * Open the checkpoints panel (the `::part(checkpoints-button)` route).
8919
+ *
8920
+ * It lists the runs the `data-runs-url` server reports as continuable;
8921
+ * without that attribute the built-in button is never rendered and this opens
8922
+ * an empty panel.
8923
+ */
8924
+ openCheckpoints() {
8925
+ void this.#refreshCheckpoints();
8926
+ this.#checkpoints.open();
8927
+ }
8588
8928
  /**
8589
8929
  * Start a fresh conversation: forget the persisted history, drop the
8590
8930
  * in-memory run state, clear the transcript, and mint a new thread id.
@@ -8855,15 +9195,9 @@ Use the read_attachment tool with an id to read a file's contents.`
8855
9195
  controls.className = "header-controls";
8856
9196
  controls.setAttribute("part", "header-controls");
8857
9197
  const history = this.#headerButton("history", this.#strings.chatHistory, "\u2630");
8858
- history.addEventListener("click", () => {
8859
- void this.#refreshDrawer();
8860
- this.#drawer.open();
8861
- });
9198
+ history.addEventListener("click", () => this.openThreads());
8862
9199
  const checkpoints = this.#headerButton("checkpoints", this.#strings.checkpoints, "\u2B6F");
8863
- checkpoints.addEventListener("click", () => {
8864
- void this.#refreshCheckpoints();
8865
- this.#checkpoints.open();
8866
- });
9200
+ checkpoints.addEventListener("click", () => this.openCheckpoints());
8867
9201
  const newChat = this.#headerButton("new", this.#strings.newChat, "\u271A");
8868
9202
  newChat.addEventListener("click", () => this.newChat());
8869
9203
  const collapse = this.#headerButton("collapse", this.#strings.collapse, "\u2014");
@@ -9164,11 +9498,12 @@ Use the read_attachment tool with an id to read a file's contents.`
9164
9498
  if (this.#client === null) {
9165
9499
  const agent = this.agentFactory({
9166
9500
  endpoint: this.endpoint,
9167
- headers: this.headers,
9501
+ headers: this.#requestHeaders(),
9168
9502
  // Live getter: the client is built once and cached, but a rotated
9169
9503
  // token must still reach every request — the factory's fetch wrapper
9170
9504
  // re-reads this on each call.
9171
- getHeaders: () => this.headers,
9505
+ getHeaders: () => this.#requestHeaders(),
9506
+ ...this.#credentialsOption(),
9172
9507
  threadId: this.#threadId,
9173
9508
  initialMessages: this.#initialMessages,
9174
9509
  initialState: this.#sharedState
@@ -9538,24 +9873,24 @@ function defineAgUiChat() {
9538
9873
 
9539
9874
  // src/dom/dom_driver.ts
9540
9875
  async function fillField(el, value, options = {}) {
9541
- scrollIntoCenterView(el);
9542
- await focusWithFlash(el, { flashMs: options.flashMs ?? 0 });
9876
+ await scrollIntoCenterView(el);
9877
+ await focusWithFlash(el, { ...options, flashMs: options.flashMs ?? 0 });
9543
9878
  await typeInto(el, value, options);
9544
9879
  }
9545
9880
  async function clickElement(el, options = {}) {
9546
- scrollIntoCenterView(el);
9881
+ await scrollIntoCenterView(el);
9547
9882
  await highlightThenClick(el, options);
9548
9883
  }
9549
9884
  async function pressButton(el, options = {}) {
9550
- scrollIntoCenterView(el);
9885
+ await scrollIntoCenterView(el);
9551
9886
  await pressThenClick(el, options);
9552
9887
  }
9553
9888
  async function selectControl(el, value, options = {}) {
9554
- scrollIntoCenterView(el);
9889
+ await scrollIntoCenterView(el);
9555
9890
  await selectOption(el, value, options);
9556
9891
  }
9557
9892
  async function toggleCheckbox(el, checked, options = {}) {
9558
- scrollIntoCenterView(el);
9893
+ await scrollIntoCenterView(el);
9559
9894
  await toggleControl(el, checked, options);
9560
9895
  }
9561
9896
  function setControlValue(el, value) {
@@ -9569,7 +9904,7 @@ function setControlValue(el, value) {
9569
9904
  }
9570
9905
 
9571
9906
  // src/version.ts
9572
- var VERSION = "0.20.0";
9907
+ var VERSION = "0.21.0";
9573
9908
  export {
9574
9909
  ATTACHMENT_EVENT,
9575
9910
  AgUiChat,
@@ -9607,6 +9942,7 @@ export {
9607
9942
  createStateHookTools,
9608
9943
  defineAgUiChat,
9609
9944
  fillField,
9945
+ flash,
9610
9946
  focusWithFlash,
9611
9947
  highlightThenClick,
9612
9948
  isDestructive,