@artooi/ag-ui-web-component 0.10.0 → 0.11.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 (39) hide show
  1. package/CHANGELOG.md +44 -1
  2. package/README.md +89 -7
  3. package/dist/ag-ui-web-component.bundle.js +175 -54
  4. package/dist/ag-ui-web-component.bundle.js.map +4 -4
  5. package/dist/core/ag_ui_chat.d.ts +29 -0
  6. package/dist/core/ag_ui_chat.d.ts.map +1 -1
  7. package/dist/core/agui_client.d.ts +28 -1
  8. package/dist/core/agui_client.d.ts.map +1 -1
  9. package/dist/index.d.ts +3 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +589 -72
  12. package/dist/index.js.map +4 -4
  13. package/dist/ui/approval_card.d.ts +51 -0
  14. package/dist/ui/approval_card.d.ts.map +1 -0
  15. package/dist/ui/attachment_chips.d.ts.map +1 -1
  16. package/dist/ui/attachment_tray.d.ts.map +1 -1
  17. package/dist/ui/question_card.d.ts +52 -0
  18. package/dist/ui/question_card.d.ts.map +1 -0
  19. package/dist/ui/skills_menu.d.ts.map +1 -1
  20. package/dist/ui/styles.d.ts +1 -1
  21. package/dist/ui/styles.d.ts.map +1 -1
  22. package/dist/ui/thoughts_block.d.ts.map +1 -1
  23. package/dist/ui/thread_drawer.d.ts.map +1 -1
  24. package/dist/ui/ui_strings.d.ts +16 -0
  25. package/dist/ui/ui_strings.d.ts.map +1 -1
  26. package/package.json +4 -4
  27. package/src/core/ag_ui_chat.ts +172 -3
  28. package/src/core/agui_client.ts +86 -11
  29. package/src/index.ts +14 -0
  30. package/src/ui/approval_card.ts +119 -0
  31. package/src/ui/attachment_chips.ts +5 -0
  32. package/src/ui/attachment_tray.ts +8 -0
  33. package/src/ui/question_card.ts +216 -0
  34. package/src/ui/skills_menu.ts +6 -0
  35. package/src/ui/styles.ts +121 -0
  36. package/src/ui/thoughts_block.ts +1 -0
  37. package/src/ui/thread_drawer.ts +11 -0
  38. package/src/ui/ui_strings.ts +30 -0
  39. package/src/version.ts +1 -1
package/dist/index.js CHANGED
@@ -443,62 +443,6 @@ function createStateHookTools(hook) {
443
443
  return tools;
444
444
  }
445
445
 
446
- // src/ui/attachment_chips.ts
447
- function renderAttachmentChips(refs) {
448
- const list = document.createElement("div");
449
- list.className = "attachment-chips";
450
- for (const ref of refs) {
451
- list.appendChild(renderChip(ref));
452
- }
453
- return list;
454
- }
455
- function renderChip(ref) {
456
- const chip = document.createElement("div");
457
- chip.className = "attachment-chip attachment-chip--ready";
458
- const icon = document.createElement("span");
459
- icon.className = "attachment-chip-icon";
460
- icon.textContent = iconFor(ref.mime);
461
- icon.setAttribute("aria-hidden", "true");
462
- const name = document.createElement("span");
463
- name.className = "attachment-chip-name";
464
- name.textContent = ref.name;
465
- name.title = ref.name;
466
- const size = document.createElement("span");
467
- size.className = "attachment-chip-size";
468
- size.textContent = formatBytes(ref.size);
469
- chip.append(icon, name, size);
470
- return chip;
471
- }
472
- function iconFor(mime) {
473
- if (mime.startsWith("image/")) {
474
- return "\u{1F5BC}";
475
- }
476
- if (mime === "application/pdf") {
477
- return "\u{1F4D5}";
478
- }
479
- if (mime.startsWith("text/")) {
480
- return "\u{1F4C4}";
481
- }
482
- return "\u{1F4CE}";
483
- }
484
- function formatBytes(bytes) {
485
- if (bytes < 1024) {
486
- return `${bytes} B`;
487
- }
488
- const units = ["KB", "MB", "GB"];
489
- let value = bytes / 1024;
490
- let unit = 0;
491
- while (value >= 1024 && unit < units.length - 1) {
492
- value /= 1024;
493
- unit += 1;
494
- }
495
- const rounded = value < 10 ? Math.round(value * 10) / 10 : Math.round(value);
496
- return `${rounded} ${units[unit]}`;
497
- }
498
-
499
- // src/ui/attachment_tray.ts
500
- import { randomUUID } from "@ag-ui/client";
501
-
502
446
  // src/ui/ui_strings.ts
503
447
  var DEFAULT_UI_STRINGS = {
504
448
  title: "Assistant",
@@ -537,6 +481,14 @@ var DEFAULT_UI_STRINGS = {
537
481
  confirmRun: "Run \u201C{tool}\u201D?",
538
482
  confirm: "Confirm",
539
483
  cancel: "Cancel",
484
+ approveAction: "Approve action",
485
+ approvalPrompt: "Approve this action?",
486
+ approve: "Approve",
487
+ deny: "Deny",
488
+ askUserAction: "Question",
489
+ otherOption: "Other\u2026",
490
+ answerPlaceholder: "Type your answer\u2026",
491
+ submit: "Submit",
540
492
  chats: "Chats",
541
493
  noConversations: "No conversations yet.",
542
494
  rename: "Rename",
@@ -568,7 +520,120 @@ function mergeUiStrings(overrides) {
568
520
  return merged;
569
521
  }
570
522
 
523
+ // src/ui/approval_card.ts
524
+ function actionButton(modifier, label) {
525
+ const button = document.createElement("button");
526
+ button.type = "button";
527
+ button.className = `approval-btn approval-btn--${modifier}`;
528
+ button.setAttribute("part", `approval-button approval-${modifier}`);
529
+ button.textContent = label;
530
+ return button;
531
+ }
532
+ function requestApproval(host, request, options = {}) {
533
+ const strings = options.strings ?? DEFAULT_UI_STRINGS;
534
+ return new Promise((resolve) => {
535
+ const card = document.createElement("div");
536
+ card.className = "approval";
537
+ card.setAttribute("part", "approval");
538
+ if (request.toolName !== void 0) {
539
+ card.setAttribute("data-tool-name", request.toolName);
540
+ }
541
+ card.setAttribute("role", "group");
542
+ card.setAttribute("aria-label", strings.approveAction);
543
+ const body = document.createElement("div");
544
+ body.className = "approval-body";
545
+ body.setAttribute("part", "approval-body");
546
+ body.textContent = request.message ?? strings.approvalPrompt;
547
+ const actions = document.createElement("div");
548
+ actions.className = "approval-actions";
549
+ actions.setAttribute("part", "approval-actions");
550
+ const deny = actionButton("deny", strings.deny);
551
+ const approve = actionButton("approve", strings.approve);
552
+ let settled = false;
553
+ const close = (approved) => {
554
+ if (settled) {
555
+ return;
556
+ }
557
+ settled = true;
558
+ deny.disabled = true;
559
+ approve.disabled = true;
560
+ card.setAttribute("data-resolved", approved ? "approved" : "denied");
561
+ resolve(approved);
562
+ };
563
+ deny.addEventListener("click", () => close(false));
564
+ approve.addEventListener("click", () => close(true));
565
+ options.signal?.addEventListener("abort", () => close(false), { once: true });
566
+ actions.append(deny, approve);
567
+ card.append(body, actions);
568
+ host.appendChild(card);
569
+ if (options.signal?.aborted === true) {
570
+ close(false);
571
+ return;
572
+ }
573
+ approve.focus();
574
+ });
575
+ }
576
+
577
+ // src/ui/attachment_chips.ts
578
+ function renderAttachmentChips(refs) {
579
+ const list = document.createElement("div");
580
+ list.className = "attachment-chips";
581
+ list.setAttribute("part", "attachment-chips");
582
+ for (const ref of refs) {
583
+ list.appendChild(renderChip(ref));
584
+ }
585
+ return list;
586
+ }
587
+ function renderChip(ref) {
588
+ const chip = document.createElement("div");
589
+ chip.className = "attachment-chip attachment-chip--ready";
590
+ chip.setAttribute("part", "attachment-chip");
591
+ const icon = document.createElement("span");
592
+ icon.className = "attachment-chip-icon";
593
+ icon.setAttribute("part", "attachment-chip-icon");
594
+ icon.textContent = iconFor(ref.mime);
595
+ icon.setAttribute("aria-hidden", "true");
596
+ const name = document.createElement("span");
597
+ name.className = "attachment-chip-name";
598
+ name.setAttribute("part", "attachment-chip-name");
599
+ name.textContent = ref.name;
600
+ name.title = ref.name;
601
+ const size = document.createElement("span");
602
+ size.className = "attachment-chip-size";
603
+ size.setAttribute("part", "attachment-chip-size");
604
+ size.textContent = formatBytes(ref.size);
605
+ chip.append(icon, name, size);
606
+ return chip;
607
+ }
608
+ function iconFor(mime) {
609
+ if (mime.startsWith("image/")) {
610
+ return "\u{1F5BC}";
611
+ }
612
+ if (mime === "application/pdf") {
613
+ return "\u{1F4D5}";
614
+ }
615
+ if (mime.startsWith("text/")) {
616
+ return "\u{1F4C4}";
617
+ }
618
+ return "\u{1F4CE}";
619
+ }
620
+ function formatBytes(bytes) {
621
+ if (bytes < 1024) {
622
+ return `${bytes} B`;
623
+ }
624
+ const units = ["KB", "MB", "GB"];
625
+ let value = bytes / 1024;
626
+ let unit = 0;
627
+ while (value >= 1024 && unit < units.length - 1) {
628
+ value /= 1024;
629
+ unit += 1;
630
+ }
631
+ const rounded = value < 10 ? Math.round(value * 10) / 10 : Math.round(value);
632
+ return `${rounded} ${units[unit]}`;
633
+ }
634
+
571
635
  // src/ui/attachment_tray.ts
636
+ import { randomUUID } from "@ag-ui/client";
572
637
  var AttachmentTray = class {
573
638
  /** The tray root; append above the input row. Hidden while empty. */
574
639
  element;
@@ -708,23 +773,29 @@ var AttachmentTray = class {
708
773
  #renderChip(item) {
709
774
  const chip = document.createElement("div");
710
775
  chip.className = `attachment-chip attachment-chip--${item.status}`;
776
+ chip.setAttribute("part", "attachment-chip");
711
777
  const icon = document.createElement("span");
712
778
  icon.className = "attachment-chip-icon";
779
+ icon.setAttribute("part", "attachment-chip-icon");
713
780
  icon.textContent = iconFor(item.file.type);
714
781
  icon.setAttribute("aria-hidden", "true");
715
782
  const name = document.createElement("span");
716
783
  name.className = "attachment-chip-name";
784
+ name.setAttribute("part", "attachment-chip-name");
717
785
  name.textContent = item.file.name;
718
786
  name.title = item.file.name;
719
787
  const meta = document.createElement("span");
720
788
  meta.className = "attachment-chip-size";
789
+ meta.setAttribute("part", "attachment-chip-size");
721
790
  meta.textContent = item.status === ATTACHMENT_STATUS.ERROR ? item.error : formatBytes(item.file.size);
722
791
  chip.append(icon, name, meta);
723
792
  if (item.status === ATTACHMENT_STATUS.UPLOADING) {
724
793
  const bar = document.createElement("div");
725
794
  bar.className = "attachment-chip-bar";
795
+ bar.setAttribute("part", "attachment-chip-bar");
726
796
  const fill = document.createElement("div");
727
797
  fill.className = "attachment-chip-bar-fill";
798
+ fill.setAttribute("part", "attachment-chip-bar-fill");
728
799
  fill.style.width = `${Math.round(item.progress * 100)}%`;
729
800
  bar.appendChild(fill);
730
801
  chip.appendChild(bar);
@@ -733,6 +804,7 @@ var AttachmentTray = class {
733
804
  const retry = document.createElement("button");
734
805
  retry.type = "button";
735
806
  retry.className = "attachment-chip-retry";
807
+ retry.setAttribute("part", "attachment-chip-retry");
736
808
  retry.title = this.#strings.retry;
737
809
  retry.setAttribute("aria-label", this.#strings.retryUpload);
738
810
  retry.textContent = "\u21BB";
@@ -742,6 +814,7 @@ var AttachmentTray = class {
742
814
  const remove = document.createElement("button");
743
815
  remove.type = "button";
744
816
  remove.className = "attachment-chip-remove";
817
+ remove.setAttribute("part", "attachment-chip-remove");
745
818
  remove.title = this.#strings.remove;
746
819
  remove.setAttribute("aria-label", this.#strings.removeAttachment);
747
820
  remove.textContent = "\u2715";
@@ -769,7 +842,7 @@ function accepts(accept, file) {
769
842
  }
770
843
 
771
844
  // src/ui/confirmation_card.ts
772
- function actionButton(modifier, label) {
845
+ function actionButton2(modifier, label) {
773
846
  const button = document.createElement("button");
774
847
  button.type = "button";
775
848
  button.className = `confirm-btn confirm-btn--${modifier}`;
@@ -797,8 +870,8 @@ function requestConfirmation(host, request, options = {}) {
797
870
  const actions = document.createElement("div");
798
871
  actions.className = "confirm-actions";
799
872
  actions.setAttribute("part", "confirm-actions");
800
- const cancel = actionButton("cancel", strings.cancel);
801
- const confirm = actionButton("confirm", strings.confirm);
873
+ const cancel = actionButton2("cancel", strings.cancel);
874
+ const confirm = actionButton2("confirm", strings.confirm);
802
875
  let settled = false;
803
876
  const close = (accepted) => {
804
877
  if (settled) {
@@ -833,6 +906,149 @@ function prettifyToolName(name) {
833
906
  return spaced.charAt(0).toUpperCase() + spaced.slice(1);
834
907
  }
835
908
 
909
+ // src/ui/question_card.ts
910
+ function answerInput(placeholder) {
911
+ const input = document.createElement("input");
912
+ input.type = "text";
913
+ input.className = "question-input";
914
+ input.setAttribute("part", "question-input");
915
+ input.placeholder = placeholder;
916
+ return input;
917
+ }
918
+ function requestQuestion(host, request, options = {}) {
919
+ const strings = options.strings ?? DEFAULT_UI_STRINGS;
920
+ const choices = request.options ?? [];
921
+ const hasChoices = choices.length > 0;
922
+ const allowsText = !hasChoices || request.allowCustom === true;
923
+ return new Promise((resolve) => {
924
+ const card = document.createElement("div");
925
+ card.className = "question";
926
+ card.setAttribute("part", "question");
927
+ card.setAttribute("role", "group");
928
+ card.setAttribute("aria-label", strings.askUserAction);
929
+ const body = document.createElement("div");
930
+ body.className = "question-body";
931
+ body.setAttribute("part", "question-body");
932
+ body.textContent = request.question;
933
+ const form = document.createElement("div");
934
+ form.className = "question-options";
935
+ form.setAttribute("part", "question-options");
936
+ const group = `q-${choices.length}-${request.question.length}`;
937
+ const radios = [];
938
+ for (const choice of choices) {
939
+ const label = document.createElement("label");
940
+ label.className = "question-choice";
941
+ label.setAttribute("part", "question-choice");
942
+ const radio = document.createElement("input");
943
+ radio.type = "radio";
944
+ radio.name = group;
945
+ radio.value = choice;
946
+ radio.setAttribute("part", "question-radio");
947
+ const text2 = document.createElement("span");
948
+ text2.setAttribute("part", "question-choice-text");
949
+ text2.textContent = choice;
950
+ label.append(radio, text2);
951
+ form.appendChild(label);
952
+ radios.push(radio);
953
+ }
954
+ let otherRadio = null;
955
+ let input = null;
956
+ if (allowsText) {
957
+ input = answerInput(strings.answerPlaceholder);
958
+ if (hasChoices) {
959
+ const label = document.createElement("label");
960
+ label.className = "question-choice";
961
+ label.setAttribute("part", "question-choice");
962
+ otherRadio = document.createElement("input");
963
+ otherRadio.type = "radio";
964
+ otherRadio.name = group;
965
+ otherRadio.value = "";
966
+ otherRadio.setAttribute("part", "question-radio");
967
+ const text2 = document.createElement("span");
968
+ text2.setAttribute("part", "question-choice-text");
969
+ text2.textContent = strings.otherOption;
970
+ label.append(otherRadio, text2);
971
+ form.appendChild(label);
972
+ input.disabled = true;
973
+ }
974
+ form.appendChild(input);
975
+ }
976
+ const actions = document.createElement("div");
977
+ actions.className = "question-actions";
978
+ actions.setAttribute("part", "question-actions");
979
+ const submit = document.createElement("button");
980
+ submit.type = "button";
981
+ submit.className = "question-btn";
982
+ submit.setAttribute("part", "question-button");
983
+ submit.textContent = strings.submit;
984
+ actions.appendChild(submit);
985
+ let settled = false;
986
+ const answerFor = () => {
987
+ const picked = radios.find((r) => r.checked);
988
+ if (picked !== void 0) {
989
+ return picked.value;
990
+ }
991
+ if (input !== null && (otherRadio === null || otherRadio.checked)) {
992
+ const typed = input.value.trim();
993
+ return typed === "" ? null : typed;
994
+ }
995
+ return null;
996
+ };
997
+ const refresh = () => {
998
+ if (input !== null && otherRadio !== null) {
999
+ input.disabled = !otherRadio.checked;
1000
+ }
1001
+ submit.disabled = answerFor() === null;
1002
+ };
1003
+ const close = (answer) => {
1004
+ if (settled) {
1005
+ return;
1006
+ }
1007
+ settled = true;
1008
+ submit.disabled = true;
1009
+ for (const radio of radios) {
1010
+ radio.disabled = true;
1011
+ }
1012
+ if (otherRadio !== null) {
1013
+ otherRadio.disabled = true;
1014
+ }
1015
+ if (input !== null) {
1016
+ input.disabled = true;
1017
+ }
1018
+ card.setAttribute("data-resolved", answer === "" ? "cancelled" : "answered");
1019
+ resolve(answer);
1020
+ };
1021
+ for (const radio of [...radios, ...otherRadio !== null ? [otherRadio] : []]) {
1022
+ radio.addEventListener("change", refresh);
1023
+ }
1024
+ input?.addEventListener("input", refresh);
1025
+ input?.addEventListener("keydown", (event) => {
1026
+ if (event.key === "Enter") {
1027
+ event.preventDefault();
1028
+ const answer = answerFor();
1029
+ if (answer !== null) {
1030
+ close(answer);
1031
+ }
1032
+ }
1033
+ });
1034
+ submit.addEventListener("click", () => {
1035
+ const answer = answerFor();
1036
+ if (answer !== null) {
1037
+ close(answer);
1038
+ }
1039
+ });
1040
+ options.signal?.addEventListener("abort", () => close(""), { once: true });
1041
+ card.append(body, form, actions);
1042
+ host.appendChild(card);
1043
+ if (options.signal?.aborted === true) {
1044
+ close("");
1045
+ return;
1046
+ }
1047
+ refresh();
1048
+ (hasChoices ? radios[0] : input)?.focus();
1049
+ });
1050
+ }
1051
+
836
1052
  // node_modules/.pnpm/dompurify@3.4.7/node_modules/dompurify/dist/purify.es.mjs
837
1053
  function _arrayLikeToArray(r, a) {
838
1054
  (null == a || a > r.length) && (a = r.length);
@@ -2033,7 +2249,7 @@ function createDOMPurify() {
2033
2249
  }
2034
2250
  var purify = createDOMPurify();
2035
2251
 
2036
- // node_modules/.pnpm/marked@18.0.4/node_modules/marked/lib/marked.esm.js
2252
+ // node_modules/.pnpm/marked@18.0.5/node_modules/marked/lib/marked.esm.js
2037
2253
  function M() {
2038
2254
  return { async: false, breaks: false, extensions: null, gfm: true, hooks: null, pedantic: false, renderer: null, silent: false, tokenizer: null, walkTokens: null };
2039
2255
  }
@@ -2077,15 +2293,15 @@ var F = /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table|
2077
2293
  var $e = /^[^\n]+/;
2078
2294
  var U = /(?!\s*\])(?:\\[\s\S]|[^\[\]\\])+/;
2079
2295
  var Le = d(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n[ \t]*)?| *\n[ \t]*)(title))? *(?:\n+|$)/).replace("label", U).replace("title", /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex();
2080
- var _e = d(/^(bull)([ \t][^\n]+?)?(?:\n|$)/).replace(/bull/g, j).getRegex();
2296
+ var _e = d(/^(bull)([ \t][^\n]*?)?(?:\n|$)/).replace(/bull/g, j).getRegex();
2081
2297
  var H = "address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul";
2082
2298
  var K = /<!--(?:-?>|[\s\S]*?(?:-->|$))/;
2083
2299
  var ze = d("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|<![A-Z][\\s\\S]*?(?:>\\n*|$)|<!\\[CDATA\\[[\\s\\S]*?(?:\\]\\]>\\n*|$)|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|</(?!script|pre|style|textarea)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$))", "i").replace("comment", K).replace("tag", H).replace("attribute", / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex();
2084
- var le = d(F).replace("hr", B).replace("heading", " {0,3}#{1,6}(?:\\s|$)").replace("|lheading", "").replace("|table", "").replace("blockquote", " {0,3}>").replace("fences", " {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list", " {0,3}(?:[*+-]|1[.)])[ \\t]").replace("html", "</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag", H).getRegex();
2300
+ var le = d(F).replace("hr", B).replace("heading", " {0,3}#{1,6}(?:\\s|$)").replace("|lheading", "").replace("|table", "").replace("blockquote", " {0,3}>").replace("fences", " {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list", " {0,3}(?:[*+-]|1[.)])[ \\t]+[^ \\t\\n]").replace("html", "</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag", H).getRegex();
2085
2301
  var Me = d(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph", le).getRegex();
2086
2302
  var W = { blockquote: Me, code: we, def: Le, fences: ye, heading: Pe, hr: B, html: ze, lheading: ae, list: _e, newline: Oe, paragraph: le, table: _, text: $e };
2087
2303
  var se = d("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr", B).replace("heading", " {0,3}#{1,6}(?:\\s|$)").replace("blockquote", " {0,3}>").replace("code", "(?: {4}| {0,3} )[^\\n]").replace("fences", " {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list", " {0,3}(?:[*+-]|1[.)])[ \\t]").replace("html", "</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag", H).getRegex();
2088
- var Ee = { ...W, lheading: Se, table: se, paragraph: d(F).replace("hr", B).replace("heading", " {0,3}#{1,6}(?:\\s|$)").replace("|lheading", "").replace("table", se).replace("blockquote", " {0,3}>").replace("fences", " {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list", " {0,3}(?:[*+-]|1[.)])[ \\t]").replace("html", "</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag", H).getRegex() };
2304
+ var Ee = { ...W, lheading: Se, table: se, paragraph: d(F).replace("hr", B).replace("heading", " {0,3}#{1,6}(?:\\s|$)").replace("|lheading", "").replace("table", se).replace("blockquote", " {0,3}>").replace("fences", " {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list", " {0,3}(?:[*+-]|1[.)])[ \\t]+[^ \\t\\n]").replace("html", "</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag", H).getRegex() };
2089
2305
  var Ie = { ...W, html: d(`^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)|<tag(?:"[^"]*"|'[^']*'|\\s[^'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))`).replace("comment", K).replace(/tag/g, "(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(), def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/, heading: /^(#{1,6})(.*)(?:\n+|$)/, fences: _, lheading: /^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/, paragraph: d(F).replace("hr", B).replace("heading", ` *#{1,6} *[^
2090
2306
  ]`).replace("lheading", ae).replace("|table", "").replace("blockquote", " {0,3}>").replace("|fences", "").replace("|list", "").replace("|html", "").replace("|tag", "").getRegex() };
2091
2307
  var Ae = /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/;
@@ -3376,9 +3592,11 @@ var SkillsMenu = class {
3376
3592
  this.#onPick = onPick;
3377
3593
  this.chips = document.createElement("div");
3378
3594
  this.chips.className = "skill-chips";
3595
+ this.chips.setAttribute("part", "skill-chips");
3379
3596
  this.chips.hidden = true;
3380
3597
  this.palette = document.createElement("div");
3381
3598
  this.palette.className = "skill-palette";
3599
+ this.palette.setAttribute("part", "skill-palette");
3382
3600
  this.palette.setAttribute("role", "listbox");
3383
3601
  this.palette.hidden = true;
3384
3602
  }
@@ -3473,6 +3691,7 @@ var SkillsMenu = class {
3473
3691
  const button = document.createElement("button");
3474
3692
  button.type = "button";
3475
3693
  button.className = "skill-chip";
3694
+ button.setAttribute("part", "skill-chip");
3476
3695
  button.textContent = skill.title;
3477
3696
  button.addEventListener("click", () => this.#pick(skill));
3478
3697
  this.chips.appendChild(button);
@@ -3484,15 +3703,18 @@ var SkillsMenu = class {
3484
3703
  const item = document.createElement("button");
3485
3704
  item.type = "button";
3486
3705
  item.className = "skill-item";
3706
+ item.setAttribute("part", "skill-item");
3487
3707
  item.setAttribute("role", "option");
3488
3708
  item.setAttribute("aria-selected", index === this.#activeIndex ? "true" : "false");
3489
3709
  const title = document.createElement("span");
3490
3710
  title.className = "skill-item-title";
3711
+ title.setAttribute("part", "skill-item-title");
3491
3712
  title.textContent = skill.title;
3492
3713
  item.appendChild(title);
3493
3714
  if (skill.description !== void 0) {
3494
3715
  const desc = document.createElement("span");
3495
3716
  desc.className = "skill-item-desc";
3717
+ desc.setAttribute("part", "skill-item-desc");
3496
3718
  desc.textContent = skill.description;
3497
3719
  item.appendChild(desc);
3498
3720
  }
@@ -4529,6 +4751,127 @@ var STYLES = `
4529
4751
  color: #ffffff;
4530
4752
  }
4531
4753
 
4754
+ /* Approval card \u2014 the server-side-tool gate (approve/deny an interrupt). */
4755
+ .approval {
4756
+ align-self: stretch;
4757
+ box-sizing: border-box;
4758
+ display: flex;
4759
+ flex-direction: column;
4760
+ gap: 8px;
4761
+ padding: 12px;
4762
+ background: var(--ag-ui-bg);
4763
+ border: 1px solid var(--ag-ui-accent);
4764
+ border-radius: 10px;
4765
+ }
4766
+
4767
+ .approval[data-resolved] {
4768
+ opacity: 0.7;
4769
+ border-color: var(--ag-ui-border);
4770
+ }
4771
+
4772
+ .approval-body {
4773
+ font-weight: 600;
4774
+ }
4775
+
4776
+ .approval-actions {
4777
+ display: flex;
4778
+ gap: 8px;
4779
+ justify-content: flex-end;
4780
+ }
4781
+
4782
+ .approval-btn {
4783
+ border: 1px solid var(--ag-ui-border);
4784
+ border-radius: 8px;
4785
+ padding: 8px 14px;
4786
+ font: inherit;
4787
+ font-weight: 600;
4788
+ cursor: pointer;
4789
+ background: var(--ag-ui-bg);
4790
+ color: var(--ag-ui-fg);
4791
+ }
4792
+
4793
+ .approval-btn:disabled {
4794
+ cursor: default;
4795
+ opacity: 0.6;
4796
+ }
4797
+
4798
+ .approval-btn--approve {
4799
+ border-color: var(--ag-ui-accent);
4800
+ background: var(--ag-ui-accent);
4801
+ color: #ffffff;
4802
+ }
4803
+
4804
+ /* Question card \u2014 the built-in ask_user prompt (radios and/or free text). */
4805
+ .question {
4806
+ align-self: stretch;
4807
+ box-sizing: border-box;
4808
+ display: flex;
4809
+ flex-direction: column;
4810
+ gap: 8px;
4811
+ padding: 12px;
4812
+ background: var(--ag-ui-bg);
4813
+ border: 1px solid var(--ag-ui-accent);
4814
+ border-radius: 10px;
4815
+ }
4816
+
4817
+ .question[data-resolved] {
4818
+ opacity: 0.7;
4819
+ border-color: var(--ag-ui-border);
4820
+ }
4821
+
4822
+ .question-body {
4823
+ font-weight: 600;
4824
+ }
4825
+
4826
+ .question-options {
4827
+ display: flex;
4828
+ flex-direction: column;
4829
+ gap: 6px;
4830
+ }
4831
+
4832
+ .question-choice {
4833
+ display: flex;
4834
+ align-items: center;
4835
+ gap: 8px;
4836
+ cursor: pointer;
4837
+ }
4838
+
4839
+ .question-input {
4840
+ box-sizing: border-box;
4841
+ width: 100%;
4842
+ padding: 8px 10px;
4843
+ font: inherit;
4844
+ color: var(--ag-ui-fg);
4845
+ background: var(--ag-ui-bg);
4846
+ border: 1px solid var(--ag-ui-border);
4847
+ border-radius: 8px;
4848
+ }
4849
+
4850
+ .question-input:disabled {
4851
+ opacity: 0.6;
4852
+ }
4853
+
4854
+ .question-actions {
4855
+ display: flex;
4856
+ justify-content: flex-end;
4857
+ }
4858
+
4859
+ .question-btn {
4860
+ border: 1px solid var(--ag-ui-accent);
4861
+ border-radius: 8px;
4862
+ padding: 8px 14px;
4863
+ font: inherit;
4864
+ font-weight: 600;
4865
+ cursor: pointer;
4866
+ background: var(--ag-ui-accent);
4867
+ color: #ffffff;
4868
+ }
4869
+
4870
+ .question-btn:disabled {
4871
+ cursor: default;
4872
+ opacity: 0.6;
4873
+ }
4874
+
4532
4875
  /* Skills \u2014 chips row + the /-command palette, above the input. */
4533
4876
  .skill-chips {
4534
4877
  display: flex;
@@ -4804,6 +5147,7 @@ var ThoughtsBlock = class {
4804
5147
  this.#toggle.setAttribute("aria-expanded", "true");
4805
5148
  this.#label = document.createElement("span");
4806
5149
  this.#label.className = "thoughts-label";
5150
+ this.#label.setAttribute("part", "thoughts-label");
4807
5151
  this.#label.textContent = strings.thinking;
4808
5152
  this.#toggle.append(this.#label);
4809
5153
  this.#body = document.createElement("pre");
@@ -5012,12 +5356,15 @@ var ThreadDrawer = class {
5012
5356
  select.setAttribute("part", "drawer-row-select");
5013
5357
  const title = document.createElement("span");
5014
5358
  title.className = "drawer-row-title";
5359
+ title.setAttribute("part", "drawer-row-title");
5015
5360
  title.textContent = meta.title;
5016
5361
  const time = document.createElement("span");
5017
5362
  time.className = "drawer-row-time";
5363
+ time.setAttribute("part", "drawer-row-time");
5018
5364
  time.textContent = relativeTime(meta.updatedAt, void 0, this.#strings);
5019
5365
  const preview = document.createElement("span");
5020
5366
  preview.className = "drawer-row-preview";
5367
+ preview.setAttribute("part", "drawer-row-preview");
5021
5368
  preview.textContent = meta.preview;
5022
5369
  select.append(title, time, preview);
5023
5370
  select.addEventListener("click", () => {
@@ -5027,6 +5374,7 @@ var ThreadDrawer = class {
5027
5374
  const rename = document.createElement("button");
5028
5375
  rename.type = "button";
5029
5376
  rename.className = "drawer-row-rename";
5377
+ rename.setAttribute("part", "drawer-row-rename");
5030
5378
  rename.title = this.#strings.rename;
5031
5379
  rename.setAttribute("aria-label", this.#strings.renameConversation);
5032
5380
  rename.textContent = "\u270E";
@@ -5034,12 +5382,14 @@ var ThreadDrawer = class {
5034
5382
  const remove = document.createElement("button");
5035
5383
  remove.type = "button";
5036
5384
  remove.className = "drawer-row-delete";
5385
+ remove.setAttribute("part", "drawer-row-delete");
5037
5386
  remove.title = this.#strings.delete;
5038
5387
  remove.setAttribute("aria-label", this.#strings.deleteConversation);
5039
5388
  remove.textContent = "\u{1F5D1}";
5040
5389
  remove.addEventListener("click", () => this.#confirmDelete(row, meta));
5041
5390
  const actions = document.createElement("div");
5042
5391
  actions.className = "drawer-row-actions";
5392
+ actions.setAttribute("part", "drawer-row-actions");
5043
5393
  actions.append(rename, remove);
5044
5394
  row.append(select, actions);
5045
5395
  return row;
@@ -5049,6 +5399,7 @@ var ThreadDrawer = class {
5049
5399
  const input = document.createElement("input");
5050
5400
  input.type = "text";
5051
5401
  input.className = "drawer-rename-input";
5402
+ input.setAttribute("part", "drawer-rename-input");
5052
5403
  input.value = meta.title;
5053
5404
  let done = false;
5054
5405
  const commit = () => {
@@ -5089,17 +5440,21 @@ var ThreadDrawer = class {
5089
5440
  #confirmDelete(row, meta) {
5090
5441
  const confirm = document.createElement("div");
5091
5442
  confirm.className = "drawer-confirm";
5443
+ confirm.setAttribute("part", "drawer-confirm");
5092
5444
  const label = document.createElement("span");
5093
5445
  label.className = "drawer-confirm-label";
5446
+ label.setAttribute("part", "drawer-confirm-label");
5094
5447
  label.textContent = this.#strings.deletePrompt;
5095
5448
  const yes = document.createElement("button");
5096
5449
  yes.type = "button";
5097
5450
  yes.className = "drawer-confirm-yes";
5451
+ yes.setAttribute("part", "drawer-confirm-yes");
5098
5452
  yes.textContent = this.#strings.delete;
5099
5453
  yes.addEventListener("click", () => this.#callbacks.onDelete(meta.threadId));
5100
5454
  const no = document.createElement("button");
5101
5455
  no.type = "button";
5102
5456
  no.className = "drawer-confirm-no";
5457
+ no.setAttribute("part", "drawer-confirm-no");
5103
5458
  no.textContent = this.#strings.cancel;
5104
5459
  no.addEventListener("click", () => this.#renderList());
5105
5460
  confirm.append(label, yes, no);
@@ -5341,7 +5696,10 @@ var VoiceInput = class {
5341
5696
  };
5342
5697
 
5343
5698
  // src/core/agui_client.ts
5344
- import { randomUUID as randomUUID2 } from "@ag-ui/client";
5699
+ import {
5700
+ buildResumeArray,
5701
+ randomUUID as randomUUID2
5702
+ } from "@ag-ui/client";
5345
5703
  var ConnectionLostError = class extends Error {
5346
5704
  constructor(message) {
5347
5705
  super(message);
@@ -5354,6 +5712,7 @@ var AgUiClient = class {
5354
5712
  #getTools;
5355
5713
  #getContext;
5356
5714
  #executeTool;
5715
+ #resolveInterrupts;
5357
5716
  #onPersist;
5358
5717
  #connectionLostMessage;
5359
5718
  // Set by cancel(); reset at the top of each #run(). Checked by the loop so
@@ -5365,6 +5724,7 @@ var AgUiClient = class {
5365
5724
  this.#getTools = config.getTools ?? (() => []);
5366
5725
  this.#getContext = config.getContext ?? (() => []);
5367
5726
  this.#executeTool = config.executeTool ?? null;
5727
+ this.#resolveInterrupts = config.resolveInterrupts ?? null;
5368
5728
  this.#onPersist = config.onPersist ?? (() => {
5369
5729
  });
5370
5730
  this.#connectionLostMessage = config.connectionLostMessage ?? "Connection lost";
@@ -5444,16 +5804,22 @@ var AgUiClient = class {
5444
5804
  this.#handlers.onCancelled();
5445
5805
  }
5446
5806
  async #runLoop() {
5807
+ let resume;
5447
5808
  for (let round = 0; round < MAX_TOOL_ROUNDS; round += 1) {
5448
5809
  if (this.#cancelled) {
5449
5810
  return;
5450
5811
  }
5451
5812
  const pending = [];
5452
- const runState = { terminal: false, errored: false };
5453
- await this.#agent.runAgent(
5454
- { tools: this.#getTools(), context: this.#getContext() },
5455
- this.#buildSubscriber(pending, runState)
5456
- );
5813
+ const runState = { terminal: false, errored: false, interrupts: [] };
5814
+ const params = {
5815
+ tools: this.#getTools(),
5816
+ context: this.#getContext()
5817
+ };
5818
+ if (resume !== void 0) {
5819
+ params.resume = resume;
5820
+ }
5821
+ await this.#agent.runAgent(params, this.#buildSubscriber(pending, runState));
5822
+ resume = void 0;
5457
5823
  this.#onPersist(this.#agent.messages);
5458
5824
  if (this.#cancelled) {
5459
5825
  return;
@@ -5464,6 +5830,17 @@ var AgUiClient = class {
5464
5830
  if (runState.errored) {
5465
5831
  return;
5466
5832
  }
5833
+ if (runState.interrupts.length > 0) {
5834
+ if (this.#resolveInterrupts === null) {
5835
+ return;
5836
+ }
5837
+ const responses = await this.#resolveInterrupts(runState.interrupts);
5838
+ if (this.#cancelled) {
5839
+ return;
5840
+ }
5841
+ resume = buildResumeArray(runState.interrupts, responses);
5842
+ continue;
5843
+ }
5467
5844
  if (this.#executeTool === null || pending.length === 0) {
5468
5845
  return;
5469
5846
  }
@@ -5526,6 +5903,12 @@ var AgUiClient = class {
5526
5903
  onReasoningEndEvent() {
5527
5904
  h.onReasoningEnd();
5528
5905
  },
5906
+ onRunFinishedEvent(params) {
5907
+ runState.terminal = true;
5908
+ if (params.outcome === "interrupt") {
5909
+ runState.interrupts = params.interrupts;
5910
+ }
5911
+ },
5529
5912
  onRunErrorEvent({ event }) {
5530
5913
  runState.terminal = true;
5531
5914
  runState.errored = true;
@@ -5983,6 +6366,33 @@ var AgUiChat = class extends HTMLElement {
5983
6366
  allowImages = false;
5984
6367
  /** When true, destructive tools execute without a confirmation modal. */
5985
6368
  autoConfirm = false;
6369
+ /**
6370
+ * When true, the built-in `ask_user` frontend tool is offered to the agent:
6371
+ * calling it renders an inline question card (radio choices and/or a free-text
6372
+ * field) and returns the user's answer. Off by default — like the other
6373
+ * built-in tool groups (route / page-action), it is opt-in so it doesn't
6374
+ * change the advertised catalog until a host asks for it.
6375
+ */
6376
+ askUser = false;
6377
+ /**
6378
+ * Optional full replacement for the `ask_user` question UI. When set, calling
6379
+ * `ask_user` invokes this instead of the built-in inline card: the host
6380
+ * renders whatever it likes (a native modal, a framework component, …) and
6381
+ * resolves with the answer. Unset (default) uses the built-in
6382
+ * {@link requestQuestion} card — style that via the `strings` override and the
6383
+ * `question*` CSS `::part()`s. Requires {@link askUser} to be enabled.
6384
+ */
6385
+ askUserRenderer = null;
6386
+ /**
6387
+ * Optional full replacement for the server-side-tool approval UI. When set, an
6388
+ * approval interrupt invokes this instead of the built-in inline approval
6389
+ * card: the host renders whatever it likes and resolves `true` to approve /
6390
+ * `false` to deny. Unset (default) uses the built-in {@link requestApproval}
6391
+ * card — style that via the `strings` override and the `approval*` CSS
6392
+ * `::part()`s. The gate itself is enabled server-side; this only changes how
6393
+ * the decision is collected.
6394
+ */
6395
+ approvalRenderer = null;
5986
6396
  /**
5987
6397
  * Optional per-call confirmation predicate. When set, it is authoritative:
5988
6398
  * given a tool name + args it decides whether *this* call needs confirmation
@@ -6276,9 +6686,73 @@ var AgUiChat = class extends HTMLElement {
6276
6686
  );
6277
6687
  return createPageActionTools(enabled, (target) => this.resolvePageTarget(target));
6278
6688
  }
6279
- /** All built-in (route + page + page-action) frontend tools. */
6689
+ /** All built-in (route + page + page-action + ask_user) frontend tools. */
6280
6690
  #builtinTools() {
6281
- return [...this.#routeTools(), ...this.#pageTools(), ...this.#pageActionTools()];
6691
+ return [
6692
+ ...this.#routeTools(),
6693
+ ...this.#pageTools(),
6694
+ ...this.#pageActionTools(),
6695
+ ...this.#askUserTool()
6696
+ ];
6697
+ }
6698
+ /**
6699
+ * The built-in `ask_user` frontend tool, or `[]` when {@link askUser} is off.
6700
+ *
6701
+ * A generic "ask the user a typed question" primitive: the agent calls it, the
6702
+ * client executes it locally via the normal frontend-tool path (rendering a
6703
+ * {@link requestQuestion} card), and the chosen/typed answer flows back as the
6704
+ * tool result — no new protocol, reusing the machinery already in place.
6705
+ */
6706
+ #askUserTool() {
6707
+ if (!this.askUser) {
6708
+ return [];
6709
+ }
6710
+ return [
6711
+ {
6712
+ name: "ask_user",
6713
+ description: "Ask the user a question and wait for their answer. Provide `options` for a multiple-choice prompt; set `allow_custom` to also accept a free-text answer.",
6714
+ parameters: {
6715
+ type: "object",
6716
+ properties: {
6717
+ question: { type: "string", description: "The question to ask the user." },
6718
+ options: {
6719
+ type: "array",
6720
+ items: { type: "string" },
6721
+ description: "Preset choices offered as radio buttons."
6722
+ },
6723
+ allow_custom: {
6724
+ type: "boolean",
6725
+ description: "Allow a free-text answer in addition to any options."
6726
+ }
6727
+ },
6728
+ required: ["question"]
6729
+ },
6730
+ handler: (args) => this.#askUser(args)
6731
+ }
6732
+ ];
6733
+ }
6734
+ /** Render the `ask_user` question card and resolve with the user's answer. */
6735
+ async #askUser(args) {
6736
+ const question = typeof args["question"] === "string" ? args["question"] : "";
6737
+ const request = { question };
6738
+ const rawOptions = args["options"];
6739
+ if (Array.isArray(rawOptions)) {
6740
+ request.options = rawOptions.filter((option) => typeof option === "string");
6741
+ }
6742
+ if (args["allow_custom"] === true) {
6743
+ request.allowCustom = true;
6744
+ }
6745
+ this.#confirmAbort = new AbortController();
6746
+ const signal = this.#confirmAbort.signal;
6747
+ this.#hidePending();
6748
+ const answer = this.askUserRenderer !== null ? await this.askUserRenderer(request, { signal }) : await requestQuestion(this.#ensureGroup(), request, {
6749
+ signal,
6750
+ strings: this.#strings
6751
+ });
6752
+ this.#confirmAbort = null;
6753
+ this.#updateEmptyState();
6754
+ this.#messages.scrollTop = this.#messages.scrollHeight;
6755
+ return answer;
6282
6756
  }
6283
6757
  /** Resolve a tool by name: built-in tools first, then the registry. */
6284
6758
  #resolveTool(name) {
@@ -6916,6 +7390,7 @@ Use the read_attachment tool with an id to read a file's contents.`
6916
7390
  void this.#submit();
6917
7391
  });
6918
7392
  this.#skillHint.className = "skill-hint";
7393
+ this.#skillHint.setAttribute("part", "skill-hint");
6919
7394
  this.#skillHint.hidden = true;
6920
7395
  this.#attachButton.className = "attach-btn";
6921
7396
  this.#attachButton.type = "button";
@@ -7083,6 +7558,7 @@ Use the read_attachment tool with an id to read a file's contents.`
7083
7558
  getTools: () => this.getTools(),
7084
7559
  getContext: () => this.getContext(),
7085
7560
  executeTool: (call) => this.#executeTool(call),
7561
+ resolveInterrupts: (interrupts) => this.#resolveInterrupts(interrupts),
7086
7562
  onPersist: (messages) => this.conversationStore.saveMessages(this.#threadId, messages),
7087
7563
  connectionLostMessage: this.#strings.connectionLost
7088
7564
  });
@@ -7155,6 +7631,45 @@ Use the read_attachment tool with an id to read a file's contents.`
7155
7631
  return { content: `Error: ${message}`, error: message };
7156
7632
  }
7157
7633
  }
7634
+ /**
7635
+ * Render an approval card per server-side-tool interrupt and collect the
7636
+ * user's decisions (approve → run it, deny → decline it).
7637
+ *
7638
+ * The run is suspended on these cards; a Stop while any is open aborts the
7639
+ * shared {@link #confirmAbort} controller, resolving every still-open card as
7640
+ * denied (and the client loop then sees the cancellation and stops). An
7641
+ * approved tool runs on the follow-up (resume) run and streams its result
7642
+ * back into the same pending card; a denied one is settled here, since no
7643
+ * result will ever arrive for it.
7644
+ */
7645
+ async #resolveInterrupts(interrupts) {
7646
+ const responses = {};
7647
+ this.#confirmAbort = new AbortController();
7648
+ this.#hidePending();
7649
+ for (const interrupt of interrupts) {
7650
+ const request = {};
7651
+ if (interrupt.message !== void 0) {
7652
+ request.message = interrupt.message;
7653
+ }
7654
+ const card = interrupt.toolCallId !== void 0 ? this.#toolCards.get(interrupt.toolCallId) : void 0;
7655
+ const toolName = card?.element.getAttribute("data-tool-name");
7656
+ if (toolName !== null && toolName !== void 0) {
7657
+ request.toolName = toolName;
7658
+ }
7659
+ const signal = this.#confirmAbort.signal;
7660
+ const approved = this.approvalRenderer !== null ? await this.approvalRenderer(request, { signal }) : await requestApproval(this.#ensureGroup(), request, { signal, strings: this.#strings });
7661
+ this.#updateEmptyState();
7662
+ this.#messages.scrollTop = this.#messages.scrollHeight;
7663
+ if (approved) {
7664
+ responses[interrupt.id] = { status: "resolved", payload: { approved: true } };
7665
+ } else {
7666
+ responses[interrupt.id] = { status: "cancelled" };
7667
+ card?.settle(TOOL_CALL_STATUS.DECLINED, this.#strings.declinedAction);
7668
+ }
7669
+ }
7670
+ this.#confirmAbort = null;
7671
+ return responses;
7672
+ }
7158
7673
  #handlers() {
7159
7674
  return {
7160
7675
  onRunStart: () => {
@@ -7355,7 +7870,7 @@ function setControlValue(el, value) {
7355
7870
  }
7356
7871
 
7357
7872
  // src/version.ts
7358
- var VERSION = "0.10.0";
7873
+ var VERSION = "0.11.0";
7359
7874
  export {
7360
7875
  AgUiChat,
7361
7876
  AgUiClient,
@@ -7398,7 +7913,9 @@ export {
7398
7913
  pressThenClick,
7399
7914
  prettifyToolName,
7400
7915
  renderMarkdown,
7916
+ requestApproval,
7401
7917
  requestConfirmation,
7918
+ requestQuestion,
7402
7919
  scrollIntoCenterView,
7403
7920
  selectControl,
7404
7921
  selectOption,