@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.
- package/CHANGELOG.md +44 -1
- package/README.md +89 -7
- package/dist/ag-ui-web-component.bundle.js +175 -54
- package/dist/ag-ui-web-component.bundle.js.map +4 -4
- package/dist/core/ag_ui_chat.d.ts +29 -0
- package/dist/core/ag_ui_chat.d.ts.map +1 -1
- package/dist/core/agui_client.d.ts +28 -1
- package/dist/core/agui_client.d.ts.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +589 -72
- package/dist/index.js.map +4 -4
- package/dist/ui/approval_card.d.ts +51 -0
- package/dist/ui/approval_card.d.ts.map +1 -0
- package/dist/ui/attachment_chips.d.ts.map +1 -1
- package/dist/ui/attachment_tray.d.ts.map +1 -1
- package/dist/ui/question_card.d.ts +52 -0
- package/dist/ui/question_card.d.ts.map +1 -0
- package/dist/ui/skills_menu.d.ts.map +1 -1
- package/dist/ui/styles.d.ts +1 -1
- package/dist/ui/styles.d.ts.map +1 -1
- package/dist/ui/thoughts_block.d.ts.map +1 -1
- package/dist/ui/thread_drawer.d.ts.map +1 -1
- package/dist/ui/ui_strings.d.ts +16 -0
- package/dist/ui/ui_strings.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/core/ag_ui_chat.ts +172 -3
- package/src/core/agui_client.ts +86 -11
- package/src/index.ts +14 -0
- package/src/ui/approval_card.ts +119 -0
- package/src/ui/attachment_chips.ts +5 -0
- package/src/ui/attachment_tray.ts +8 -0
- package/src/ui/question_card.ts +216 -0
- package/src/ui/skills_menu.ts +6 -0
- package/src/ui/styles.ts +121 -0
- package/src/ui/thoughts_block.ts +1 -0
- package/src/ui/thread_drawer.ts +11 -0
- package/src/ui/ui_strings.ts +30 -0
- 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
|
|
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 =
|
|
801
|
-
const 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.
|
|
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]
|
|
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 {
|
|
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
|
-
|
|
5454
|
-
|
|
5455
|
-
this.#
|
|
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 [
|
|
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.
|
|
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,
|