@artooi/ag-ui-web-component 0.29.0 → 0.30.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 +199 -1
- package/README.md +196 -33
- package/dist/ag-ui-web-component.bundle.js +211 -28
- package/dist/ag-ui-web-component.bundle.js.map +4 -4
- package/dist/constants.d.ts +60 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/core/ag_ui_chat.d.ts +25 -1
- package/dist/core/ag_ui_chat.d.ts.map +1 -1
- package/dist/core/agui_client.d.ts +18 -1
- package/dist/core/agui_client.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +578 -35
- package/dist/index.js.map +4 -4
- package/dist/ui/message_actions.d.ts +14 -4
- package/dist/ui/message_actions.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/subagent_panel.d.ts +92 -0
- package/dist/ui/subagent_panel.d.ts.map +1 -0
- package/dist/ui/subagent_update.d.ts +19 -0
- package/dist/ui/subagent_update.d.ts.map +1 -0
- package/dist/ui/tool_call_card.d.ts +73 -1
- package/dist/ui/tool_call_card.d.ts.map +1 -1
- package/dist/ui/ui_strings.d.ts +10 -0
- package/dist/ui/ui_strings.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/constants.ts +63 -1
- package/src/core/ag_ui_chat.ts +180 -26
- package/src/core/agui_client.ts +26 -2
- package/src/index.ts +4 -0
- package/src/ui/message_actions.ts +20 -8
- package/src/ui/styles.ts +183 -0
- package/src/ui/subagent_panel.ts +213 -0
- package/src/ui/subagent_update.ts +80 -0
- package/src/ui/tool_call_card.ts +129 -3
- package/src/ui/ui_strings.ts +15 -0
- package/src/version.ts +1 -1
package/dist/index.js
CHANGED
|
@@ -11,6 +11,14 @@ var INVALIDATE_EVENT = "ag-ui-invalidate";
|
|
|
11
11
|
var FEEDBACK_EVENT = "ag-ui-feedback";
|
|
12
12
|
var SUGGESTIONS_ACTIVITY_TYPE = "suggestions";
|
|
13
13
|
var INVALIDATE_CUSTOM_NAME = "ag_ui.invalidate";
|
|
14
|
+
var SUBAGENT_CUSTOM_NAME = "ag_ui.subagent";
|
|
15
|
+
var SUBAGENT_PHASE = {
|
|
16
|
+
STARTED: "started",
|
|
17
|
+
TOOL_CALL: "tool_call",
|
|
18
|
+
TOOL_RESULT: "tool_result",
|
|
19
|
+
FINISHED: "finished",
|
|
20
|
+
FAILED: "failed"
|
|
21
|
+
};
|
|
14
22
|
var MESSAGE_ROLE = {
|
|
15
23
|
USER: "user",
|
|
16
24
|
ASSISTANT: "assistant"
|
|
@@ -21,6 +29,11 @@ var X_SUMMARY_KEY = "x-summary";
|
|
|
21
29
|
var X_NAVIGATES_KEY = "x-navigates";
|
|
22
30
|
var READ_PAGE_TOOL = "read_page";
|
|
23
31
|
var MAX_TOOL_ROUNDS = 10;
|
|
32
|
+
var MESSAGE_ACTIONS = {
|
|
33
|
+
COPY: "copy",
|
|
34
|
+
RETRY: "retry",
|
|
35
|
+
FEEDBACK: "feedback"
|
|
36
|
+
};
|
|
24
37
|
var TOOL_CALL_STATUS = {
|
|
25
38
|
PENDING: "pending",
|
|
26
39
|
DEFERRED: "deferred",
|
|
@@ -587,6 +600,8 @@ var DEFAULT_UI_STRINGS = {
|
|
|
587
600
|
errorLabel: "Error",
|
|
588
601
|
declinedLabel: "Declined",
|
|
589
602
|
details: "Details",
|
|
603
|
+
subAgentWorking: "Working\u2026",
|
|
604
|
+
subAgentSteps: "Steps the sub-agent took",
|
|
590
605
|
approvalEditArgs: "Edit the arguments before approving",
|
|
591
606
|
approvalArgsInvalid: "That is not valid JSON, so nothing was sent.",
|
|
592
607
|
approvalArgsNotAnObject: "Arguments have to be a JSON object.",
|
|
@@ -1744,7 +1759,10 @@ function attachMessageActions(bubble, options) {
|
|
|
1744
1759
|
return;
|
|
1745
1760
|
}
|
|
1746
1761
|
const bar = messageActionBar(bubble, options.strings);
|
|
1747
|
-
|
|
1762
|
+
const text3 = options.text;
|
|
1763
|
+
if (text3 !== void 0) {
|
|
1764
|
+
bar.appendChild(copyButton(options.strings, text3));
|
|
1765
|
+
}
|
|
1748
1766
|
if (options.onFeedback !== void 0) {
|
|
1749
1767
|
bar.append(
|
|
1750
1768
|
feedbackButton("up", options.strings.feedbackUp, options.onFeedback),
|
|
@@ -1782,11 +1800,10 @@ function messageActionButton(modifier, label, glyph) {
|
|
|
1782
1800
|
button2.appendChild(icon);
|
|
1783
1801
|
return button2;
|
|
1784
1802
|
}
|
|
1785
|
-
function copyButton(
|
|
1786
|
-
const { strings } = options;
|
|
1803
|
+
function copyButton(strings, text3) {
|
|
1787
1804
|
const button2 = messageActionButton("copy", strings.copyMessage, "\u2398");
|
|
1788
1805
|
button2.addEventListener("click", () => {
|
|
1789
|
-
void navigator.clipboard.writeText(
|
|
1806
|
+
void navigator.clipboard.writeText(text3()).then(
|
|
1790
1807
|
() => flash2(button2, strings.copied, strings.copyMessage),
|
|
1791
1808
|
// A denied clipboard permission is the common case, not an exception:
|
|
1792
1809
|
// say so on the button rather than throwing into an unhandled rejection.
|
|
@@ -6625,6 +6642,21 @@ var STYLES = `
|
|
|
6625
6642
|
color: var(--_fg);
|
|
6626
6643
|
}
|
|
6627
6644
|
|
|
6645
|
+
/* A region a host formatter took over, marked by the card. Preformatted
|
|
6646
|
+
whitespace is what makes the built-in block read as written, and it is the one
|
|
6647
|
+
thing a host cannot want: a table inherits it as mangled cell spacing, and a
|
|
6648
|
+
sentence as line breaks nobody typed.
|
|
6649
|
+
|
|
6650
|
+
Whitespace only. The card's own face, frame, padding and scroll cap stay,
|
|
6651
|
+
because the card is one visual object -- the head row and the status pill are
|
|
6652
|
+
monospaced too -- and a region that dropped the family would be the only part
|
|
6653
|
+
of it wearing a different one. A host that wants that restyles the
|
|
6654
|
+
tool-card-result part, which does not need the formatter at all. */
|
|
6655
|
+
.tool-call-args[data-formatted],
|
|
6656
|
+
.tool-call-result[data-formatted] {
|
|
6657
|
+
white-space: normal;
|
|
6658
|
+
}
|
|
6659
|
+
|
|
6628
6660
|
/* Display modes are pure visibility over one DOM shape, selected from the host
|
|
6629
6661
|
attribute rather than a value stamped on the card at build time, so flipping
|
|
6630
6662
|
data-tool-display re-styles cards already on screen. See ToolCallCard.
|
|
@@ -6689,6 +6721,174 @@ var STYLES = `
|
|
|
6689
6721
|
display: flex;
|
|
6690
6722
|
}
|
|
6691
6723
|
|
|
6724
|
+
/* A delegated sub-agent's progress, inside the card that delegated. Empty on
|
|
6725
|
+
every card that delegated nothing, so it collapses rather than adding a gap
|
|
6726
|
+
to each one -- the same shape the approval slot uses. */
|
|
6727
|
+
.tool-call-subagent:empty {
|
|
6728
|
+
display: none;
|
|
6729
|
+
}
|
|
6730
|
+
|
|
6731
|
+
.tool-call-subagent {
|
|
6732
|
+
display: flex;
|
|
6733
|
+
flex-direction: column;
|
|
6734
|
+
gap: 4px;
|
|
6735
|
+
}
|
|
6736
|
+
|
|
6737
|
+
.subagent {
|
|
6738
|
+
display: flex;
|
|
6739
|
+
flex-direction: column;
|
|
6740
|
+
gap: 4px;
|
|
6741
|
+
min-width: 0;
|
|
6742
|
+
}
|
|
6743
|
+
|
|
6744
|
+
/* The collapsed row is the status and the expander at once, which is what keeps
|
|
6745
|
+
a ten-step child one row until somebody opens it. Full width and left-aligned,
|
|
6746
|
+
because it is a line of the card rather than a button on it. */
|
|
6747
|
+
.subagent-row {
|
|
6748
|
+
display: flex;
|
|
6749
|
+
align-items: center;
|
|
6750
|
+
gap: 6px;
|
|
6751
|
+
width: 100%;
|
|
6752
|
+
box-sizing: border-box;
|
|
6753
|
+
padding: 2px 0;
|
|
6754
|
+
border: none;
|
|
6755
|
+
background: none;
|
|
6756
|
+
font: inherit;
|
|
6757
|
+
text-align: left;
|
|
6758
|
+
color: var(--_muted);
|
|
6759
|
+
cursor: pointer;
|
|
6760
|
+
}
|
|
6761
|
+
|
|
6762
|
+
/* Nothing behind the row yet -- a delegation that failed before calling
|
|
6763
|
+
anything. Drop the affordances rather than offer a control that expands onto
|
|
6764
|
+
an empty region, which is the refusal the card's own toggle already makes. */
|
|
6765
|
+
.subagent-row:disabled {
|
|
6766
|
+
cursor: default;
|
|
6767
|
+
}
|
|
6768
|
+
|
|
6769
|
+
.subagent-row::after {
|
|
6770
|
+
content: "\u25B8";
|
|
6771
|
+
flex: none;
|
|
6772
|
+
margin-left: auto;
|
|
6773
|
+
color: var(--_accent);
|
|
6774
|
+
}
|
|
6775
|
+
|
|
6776
|
+
.subagent-row[aria-expanded="true"]::after {
|
|
6777
|
+
content: "\u25BE";
|
|
6778
|
+
}
|
|
6779
|
+
|
|
6780
|
+
.subagent-row:disabled::after {
|
|
6781
|
+
display: none;
|
|
6782
|
+
}
|
|
6783
|
+
|
|
6784
|
+
/* Empty in the DOM; the glyph is drawn here from the panel's data-phase, so a
|
|
6785
|
+
host re-themes it through the same tool-icon custom properties the card uses. */
|
|
6786
|
+
.subagent-icon {
|
|
6787
|
+
flex: none;
|
|
6788
|
+
box-sizing: border-box;
|
|
6789
|
+
display: inline-flex;
|
|
6790
|
+
align-items: center;
|
|
6791
|
+
justify-content: center;
|
|
6792
|
+
width: 10px;
|
|
6793
|
+
height: 10px;
|
|
6794
|
+
font-size: 10px;
|
|
6795
|
+
line-height: 1;
|
|
6796
|
+
}
|
|
6797
|
+
|
|
6798
|
+
/* Anything that is not a terminal phase is the child still working. Selected by
|
|
6799
|
+
what it is not, so a phase this client has not heard of still spins rather
|
|
6800
|
+
than rendering as a blank. */
|
|
6801
|
+
.subagent[data-phase]:not([data-phase="finished"]):not([data-phase="failed"]) .subagent-icon {
|
|
6802
|
+
border: 2px solid var(--_muted);
|
|
6803
|
+
border-top-color: transparent;
|
|
6804
|
+
border-radius: 50%;
|
|
6805
|
+
animation: ag-ui-tool-spin var(--_tool-spin-duration) linear infinite;
|
|
6806
|
+
}
|
|
6807
|
+
|
|
6808
|
+
.subagent[data-phase="finished"] .subagent-icon::before {
|
|
6809
|
+
content: var(--_tool-icon-done);
|
|
6810
|
+
color: var(--_success);
|
|
6811
|
+
}
|
|
6812
|
+
|
|
6813
|
+
.subagent[data-phase="failed"] .subagent-icon::before {
|
|
6814
|
+
content: var(--_tool-icon-error);
|
|
6815
|
+
color: var(--_danger);
|
|
6816
|
+
}
|
|
6817
|
+
|
|
6818
|
+
@media (prefers-reduced-motion: reduce) {
|
|
6819
|
+
.subagent .subagent-icon {
|
|
6820
|
+
animation: none;
|
|
6821
|
+
}
|
|
6822
|
+
}
|
|
6823
|
+
|
|
6824
|
+
/* The server's own pre-rendered line. Shrinks and wraps rather than pushing the
|
|
6825
|
+
chevron out of the card, which is what a fixed-width sibling in a flex row
|
|
6826
|
+
does to a panel at sidebar width. */
|
|
6827
|
+
.subagent-status {
|
|
6828
|
+
flex: 1 1 auto;
|
|
6829
|
+
min-width: 0;
|
|
6830
|
+
overflow-wrap: anywhere;
|
|
6831
|
+
}
|
|
6832
|
+
|
|
6833
|
+
/* The child's own calls. Indented and ruled, so the nesting is visible without
|
|
6834
|
+
a second card frame around it. */
|
|
6835
|
+
.subagent-steps {
|
|
6836
|
+
display: flex;
|
|
6837
|
+
flex-direction: column;
|
|
6838
|
+
gap: 2px;
|
|
6839
|
+
margin-left: 4px;
|
|
6840
|
+
padding-left: 10px;
|
|
6841
|
+
border-left: 1px solid var(--_border);
|
|
6842
|
+
}
|
|
6843
|
+
|
|
6844
|
+
.subagent-steps[hidden] {
|
|
6845
|
+
display: none;
|
|
6846
|
+
}
|
|
6847
|
+
|
|
6848
|
+
.subagent-step {
|
|
6849
|
+
display: flex;
|
|
6850
|
+
align-items: center;
|
|
6851
|
+
gap: 6px;
|
|
6852
|
+
min-width: 0;
|
|
6853
|
+
color: var(--_muted);
|
|
6854
|
+
}
|
|
6855
|
+
|
|
6856
|
+
.subagent-step-icon {
|
|
6857
|
+
flex: none;
|
|
6858
|
+
box-sizing: border-box;
|
|
6859
|
+
display: inline-flex;
|
|
6860
|
+
align-items: center;
|
|
6861
|
+
justify-content: center;
|
|
6862
|
+
width: 8px;
|
|
6863
|
+
height: 8px;
|
|
6864
|
+
font-size: 9px;
|
|
6865
|
+
line-height: 1;
|
|
6866
|
+
}
|
|
6867
|
+
|
|
6868
|
+
/* No outcome yet: the wire says null while the call is in flight, and the
|
|
6869
|
+
absence of the attribute is how that arrives here. A hollow ring, not a
|
|
6870
|
+
spinner -- several can be on screen at once and the row above already spins. */
|
|
6871
|
+
.subagent-step:not([data-ok]) .subagent-step-icon {
|
|
6872
|
+
border: 1px solid var(--_muted);
|
|
6873
|
+
border-radius: 50%;
|
|
6874
|
+
}
|
|
6875
|
+
|
|
6876
|
+
.subagent-step[data-ok="true"] .subagent-step-icon::before {
|
|
6877
|
+
content: var(--_tool-icon-done);
|
|
6878
|
+
color: var(--_success);
|
|
6879
|
+
}
|
|
6880
|
+
|
|
6881
|
+
.subagent-step[data-ok="false"] .subagent-step-icon::before {
|
|
6882
|
+
content: var(--_tool-icon-error);
|
|
6883
|
+
color: var(--_danger);
|
|
6884
|
+
}
|
|
6885
|
+
|
|
6886
|
+
.subagent-step-name {
|
|
6887
|
+
flex: 1 1 auto;
|
|
6888
|
+
min-width: 0;
|
|
6889
|
+
overflow-wrap: anywhere;
|
|
6890
|
+
}
|
|
6891
|
+
|
|
6692
6892
|
.tool-call-toggle {
|
|
6693
6893
|
align-self: flex-start;
|
|
6694
6894
|
border: none;
|
|
@@ -7988,6 +8188,159 @@ var STYLES = `
|
|
|
7988
8188
|
}
|
|
7989
8189
|
`;
|
|
7990
8190
|
|
|
8191
|
+
// src/ui/subagent_panel.ts
|
|
8192
|
+
var SubAgentPanel = class {
|
|
8193
|
+
/** The panel's root; append this into the delegating card's slot. */
|
|
8194
|
+
element;
|
|
8195
|
+
/**
|
|
8196
|
+
* The collapsed row, which is the expander as well as the status.
|
|
8197
|
+
*
|
|
8198
|
+
* Disabled while the child has called nothing, so a delegation that failed
|
|
8199
|
+
* before it started offers no control that expands onto an empty region —
|
|
8200
|
+
* the same refusal the card's own Details toggle already makes.
|
|
8201
|
+
*/
|
|
8202
|
+
#row;
|
|
8203
|
+
#status;
|
|
8204
|
+
#steps;
|
|
8205
|
+
/** The child's tool calls, keyed by the child's own call id. */
|
|
8206
|
+
#stepRows = /* @__PURE__ */ new Map();
|
|
8207
|
+
constructor(strings = DEFAULT_UI_STRINGS) {
|
|
8208
|
+
this.element = document.createElement("div");
|
|
8209
|
+
this.element.className = "subagent";
|
|
8210
|
+
this.element.setAttribute("part", "subagent");
|
|
8211
|
+
this.#row = document.createElement("button");
|
|
8212
|
+
this.#row.type = "button";
|
|
8213
|
+
this.#row.className = "subagent-row";
|
|
8214
|
+
this.#row.setAttribute("part", "subagent-row");
|
|
8215
|
+
this.#row.setAttribute("aria-expanded", "false");
|
|
8216
|
+
this.#row.disabled = true;
|
|
8217
|
+
const icon = document.createElement("span");
|
|
8218
|
+
icon.className = "subagent-icon";
|
|
8219
|
+
icon.setAttribute("part", "subagent-icon");
|
|
8220
|
+
icon.setAttribute("aria-hidden", "true");
|
|
8221
|
+
this.#status = document.createElement("span");
|
|
8222
|
+
this.#status.className = "subagent-status";
|
|
8223
|
+
this.#status.setAttribute("part", "subagent-status");
|
|
8224
|
+
this.#status.textContent = strings.subAgentWorking;
|
|
8225
|
+
this.#row.append(icon, this.#status);
|
|
8226
|
+
this.#steps = document.createElement("div");
|
|
8227
|
+
this.#steps.className = "subagent-steps";
|
|
8228
|
+
this.#steps.setAttribute("part", "subagent-steps");
|
|
8229
|
+
this.#steps.setAttribute("role", "list");
|
|
8230
|
+
this.#steps.setAttribute("aria-label", strings.subAgentSteps);
|
|
8231
|
+
this.#steps.hidden = true;
|
|
8232
|
+
this.#row.addEventListener("click", () => {
|
|
8233
|
+
this.#setExpanded(this.#row.getAttribute("aria-expanded") !== "true");
|
|
8234
|
+
});
|
|
8235
|
+
this.element.append(this.#row, this.#steps);
|
|
8236
|
+
}
|
|
8237
|
+
/**
|
|
8238
|
+
* Fold one announcement in.
|
|
8239
|
+
*
|
|
8240
|
+
* Every field is applied only when the update actually carried it, so a phase
|
|
8241
|
+
* that says nothing about the agent or the status leaves both as they stand.
|
|
8242
|
+
* That is what lets `finished` be two keys wide on the wire without blanking
|
|
8243
|
+
* the row it closes.
|
|
8244
|
+
*/
|
|
8245
|
+
report(update) {
|
|
8246
|
+
this.element.setAttribute("data-phase", update.phase);
|
|
8247
|
+
if (update.agent !== null) {
|
|
8248
|
+
this.element.setAttribute("data-agent", update.agent);
|
|
8249
|
+
}
|
|
8250
|
+
if (update.status !== null) {
|
|
8251
|
+
this.#status.textContent = update.status;
|
|
8252
|
+
}
|
|
8253
|
+
if (update.tool !== null) {
|
|
8254
|
+
this.#recordStep(update.tool);
|
|
8255
|
+
}
|
|
8256
|
+
}
|
|
8257
|
+
/**
|
|
8258
|
+
* Open or settle one of the child's calls, keyed by its own id.
|
|
8259
|
+
*
|
|
8260
|
+
* The absence of `data-ok` is what "still running" looks like, mirroring the
|
|
8261
|
+
* wire's `null` rather than inventing a third value for it — so the attribute
|
|
8262
|
+
* is removed on the way in and written on the way out.
|
|
8263
|
+
*/
|
|
8264
|
+
#recordStep(tool) {
|
|
8265
|
+
const row = this.#stepRows.get(tool.toolCallId) ?? this.#createStep(tool);
|
|
8266
|
+
if (tool.ok === null) {
|
|
8267
|
+
row.removeAttribute("data-ok");
|
|
8268
|
+
return;
|
|
8269
|
+
}
|
|
8270
|
+
row.setAttribute("data-ok", String(tool.ok));
|
|
8271
|
+
}
|
|
8272
|
+
#createStep(tool) {
|
|
8273
|
+
const row = document.createElement("div");
|
|
8274
|
+
row.className = "subagent-step";
|
|
8275
|
+
row.setAttribute("part", "subagent-step");
|
|
8276
|
+
row.setAttribute("role", "listitem");
|
|
8277
|
+
row.setAttribute("data-tool-call-id", tool.toolCallId);
|
|
8278
|
+
const icon = document.createElement("span");
|
|
8279
|
+
icon.className = "subagent-step-icon";
|
|
8280
|
+
icon.setAttribute("part", "subagent-step-icon");
|
|
8281
|
+
icon.setAttribute("aria-hidden", "true");
|
|
8282
|
+
const name = document.createElement("span");
|
|
8283
|
+
name.className = "subagent-step-name";
|
|
8284
|
+
name.setAttribute("part", "subagent-step-name");
|
|
8285
|
+
name.textContent = tool.name;
|
|
8286
|
+
row.append(icon, name);
|
|
8287
|
+
this.#steps.appendChild(row);
|
|
8288
|
+
this.#stepRows.set(tool.toolCallId, row);
|
|
8289
|
+
this.#row.disabled = false;
|
|
8290
|
+
return row;
|
|
8291
|
+
}
|
|
8292
|
+
#setExpanded(expanded) {
|
|
8293
|
+
this.#steps.hidden = !expanded;
|
|
8294
|
+
this.#row.setAttribute("aria-expanded", String(expanded));
|
|
8295
|
+
}
|
|
8296
|
+
};
|
|
8297
|
+
|
|
8298
|
+
// src/ui/subagent_update.ts
|
|
8299
|
+
var PHASES = Object.values(SUBAGENT_PHASE);
|
|
8300
|
+
function asRecord(value) {
|
|
8301
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
8302
|
+
return null;
|
|
8303
|
+
}
|
|
8304
|
+
return value;
|
|
8305
|
+
}
|
|
8306
|
+
function asText(value) {
|
|
8307
|
+
return typeof value === "string" && value !== "" ? value : null;
|
|
8308
|
+
}
|
|
8309
|
+
function asTool(value) {
|
|
8310
|
+
const record = asRecord(value);
|
|
8311
|
+
if (record === null) {
|
|
8312
|
+
return null;
|
|
8313
|
+
}
|
|
8314
|
+
const toolCallId = asText(record["toolCallId"]);
|
|
8315
|
+
const name = asText(record["name"]);
|
|
8316
|
+
const ok = record["ok"];
|
|
8317
|
+
if (toolCallId === null || name === null) {
|
|
8318
|
+
return null;
|
|
8319
|
+
}
|
|
8320
|
+
if (ok !== null && typeof ok !== "boolean") {
|
|
8321
|
+
return null;
|
|
8322
|
+
}
|
|
8323
|
+
return { toolCallId, name, ok };
|
|
8324
|
+
}
|
|
8325
|
+
function subAgentUpdate(value) {
|
|
8326
|
+
const record = asRecord(value);
|
|
8327
|
+
if (record === null) {
|
|
8328
|
+
return null;
|
|
8329
|
+
}
|
|
8330
|
+
const delegationId = asText(record["delegationId"]);
|
|
8331
|
+
const phase = record["phase"];
|
|
8332
|
+
if (delegationId === null || typeof phase !== "string" || !PHASES.includes(phase)) {
|
|
8333
|
+
return null;
|
|
8334
|
+
}
|
|
8335
|
+
return {
|
|
8336
|
+
delegationId,
|
|
8337
|
+
phase,
|
|
8338
|
+
agent: asText(record["agent"]),
|
|
8339
|
+
status: asText(record["status"]),
|
|
8340
|
+
tool: asTool(record["tool"])
|
|
8341
|
+
};
|
|
8342
|
+
}
|
|
8343
|
+
|
|
7991
8344
|
// src/ui/suggestion_chips.ts
|
|
7992
8345
|
var MAX_SUGGESTIONS = 4;
|
|
7993
8346
|
var MAX_SUGGESTION_CHARS = 120;
|
|
@@ -8394,6 +8747,22 @@ var ToolCallCard = class {
|
|
|
8394
8747
|
* Empty until used, and hidden while empty by the shadow CSS.
|
|
8395
8748
|
*/
|
|
8396
8749
|
approvalSlot;
|
|
8750
|
+
/**
|
|
8751
|
+
* Where a *nested* run's progress renders — the sub-agent this call delegated
|
|
8752
|
+
* to, narrating itself while the card waits.
|
|
8753
|
+
*
|
|
8754
|
+
* A slot rather than a rendered thing, on the same reasoning as
|
|
8755
|
+
* {@link approvalSlot}: the card owns the position and something else owns the
|
|
8756
|
+
* content. What makes the position right is that the wire keys a delegation on
|
|
8757
|
+
* this card's own `toolCallId`, so the run being narrated is the one this card
|
|
8758
|
+
* already stands for.
|
|
8759
|
+
*
|
|
8760
|
+
* Placed above the Details toggle rather than inside the body, because the
|
|
8761
|
+
* body is what the display modes hide — and a progress line that only appears
|
|
8762
|
+
* in `full` mode would leave exactly the stall it exists to end. Empty on every
|
|
8763
|
+
* card that delegated nothing, and hidden while empty by the shadow CSS.
|
|
8764
|
+
*/
|
|
8765
|
+
subagentSlot;
|
|
8397
8766
|
#status;
|
|
8398
8767
|
#decision;
|
|
8399
8768
|
#toggle;
|
|
@@ -8409,10 +8778,15 @@ var ToolCallCard = class {
|
|
|
8409
8778
|
* still exist when the user is asked to approve, edit or deny the call.
|
|
8410
8779
|
*/
|
|
8411
8780
|
args;
|
|
8781
|
+
/** The raw tool name, kept for the payloads handed to {@link ToolPayloadFormatter}. */
|
|
8782
|
+
#name;
|
|
8783
|
+
#formatPayload;
|
|
8412
8784
|
#settled = false;
|
|
8413
|
-
constructor(name, args, summary, strings = DEFAULT_UI_STRINGS) {
|
|
8785
|
+
constructor(name, args, summary, strings = DEFAULT_UI_STRINGS, options = {}) {
|
|
8414
8786
|
this.#strings = strings;
|
|
8415
8787
|
this.args = args;
|
|
8788
|
+
this.#name = name;
|
|
8789
|
+
this.#formatPayload = options.formatPayload ?? null;
|
|
8416
8790
|
this.element = document.createElement("div");
|
|
8417
8791
|
this.element.className = "tool-call";
|
|
8418
8792
|
this.element.setAttribute("part", "tool-card");
|
|
@@ -8440,7 +8814,11 @@ var ToolCallCard = class {
|
|
|
8440
8814
|
this.#decision.hidden = true;
|
|
8441
8815
|
head.append(icon, label, this.#status, this.#decision);
|
|
8442
8816
|
const argsSection = this.#section("args", strings.argumentsLabel);
|
|
8443
|
-
|
|
8817
|
+
this.#renderPayload(
|
|
8818
|
+
argsSection.body,
|
|
8819
|
+
{ kind: "arguments", toolName: name, args },
|
|
8820
|
+
JSON.stringify(args, null, 2)
|
|
8821
|
+
);
|
|
8444
8822
|
argsSection.root.hidden = Object.keys(args).length === 0;
|
|
8445
8823
|
const resultSection = this.#section("result", strings.resultLabel);
|
|
8446
8824
|
this.#resultSection = resultSection.root;
|
|
@@ -8461,7 +8839,10 @@ var ToolCallCard = class {
|
|
|
8461
8839
|
this.approvalSlot = document.createElement("div");
|
|
8462
8840
|
this.approvalSlot.className = "tool-call-approval";
|
|
8463
8841
|
this.approvalSlot.setAttribute("part", "tool-card-approval");
|
|
8464
|
-
this.
|
|
8842
|
+
this.subagentSlot = document.createElement("div");
|
|
8843
|
+
this.subagentSlot.className = "tool-call-subagent";
|
|
8844
|
+
this.subagentSlot.setAttribute("part", "tool-card-subagent");
|
|
8845
|
+
this.element.append(head, this.subagentSlot, this.#toggle, body, this.approvalSlot);
|
|
8465
8846
|
}
|
|
8466
8847
|
/**
|
|
8467
8848
|
* Move between the two states that are not an outcome — `pending` (running)
|
|
@@ -8503,9 +8884,42 @@ var ToolCallCard = class {
|
|
|
8503
8884
|
this.element.setAttribute("data-status", status);
|
|
8504
8885
|
this.#status.textContent = statusLabels(this.#strings)[status];
|
|
8505
8886
|
this.#resultLabel.textContent = resultLabels(this.#strings)[status];
|
|
8506
|
-
this.#
|
|
8887
|
+
this.#renderPayload(
|
|
8888
|
+
this.#resultBody,
|
|
8889
|
+
{ kind: "result", toolName: this.#name, status, text: text3 },
|
|
8890
|
+
formatPayload(text3)
|
|
8891
|
+
);
|
|
8507
8892
|
this.#resultSection.hidden = false;
|
|
8508
8893
|
}
|
|
8894
|
+
/**
|
|
8895
|
+
* Fill one body region, offering it to the host formatter first.
|
|
8896
|
+
*
|
|
8897
|
+
* `fallback` is computed by the caller rather than here because the two
|
|
8898
|
+
* regions build it differently -- arguments are already parsed, a result is a
|
|
8899
|
+
* string that may or may not be JSON -- and because a formatter that takes
|
|
8900
|
+
* the region over should not have paid for a pretty-print nobody sees. It is
|
|
8901
|
+
* cheap either way; the point is that the built-in rendering stays written
|
|
8902
|
+
* once, beside the payload it belongs to.
|
|
8903
|
+
*
|
|
8904
|
+
* The `data-formatted` marker is what the shadow CSS reads to relax the
|
|
8905
|
+
* preformatted whitespace on a region a host owns -- a table inherits it as
|
|
8906
|
+
* mangled cell spacing, a sentence as line breaks nobody typed. Marked for a
|
|
8907
|
+
* returned string too: one rule, and a summary line wants ordinary wrapping
|
|
8908
|
+
* as much as a table does.
|
|
8909
|
+
*/
|
|
8910
|
+
#renderPayload(body, payload, fallback) {
|
|
8911
|
+
const rendered = this.#formatPayload === null ? null : this.#formatPayload(payload);
|
|
8912
|
+
if (rendered === null) {
|
|
8913
|
+
body.textContent = fallback;
|
|
8914
|
+
return;
|
|
8915
|
+
}
|
|
8916
|
+
body.setAttribute("data-formatted", "true");
|
|
8917
|
+
if (typeof rendered === "string") {
|
|
8918
|
+
body.textContent = rendered;
|
|
8919
|
+
return;
|
|
8920
|
+
}
|
|
8921
|
+
body.replaceChildren(rendered);
|
|
8922
|
+
}
|
|
8509
8923
|
/** Build one labelled region of the body: a heading plus a payload block. */
|
|
8510
8924
|
#section(kind, labelText) {
|
|
8511
8925
|
const root = document.createElement("div");
|
|
@@ -8709,6 +9123,7 @@ var AgUiClient = class {
|
|
|
8709
9123
|
*/
|
|
8710
9124
|
#closedMessageIds = /* @__PURE__ */ new Set();
|
|
8711
9125
|
#connectionLostMessage;
|
|
9126
|
+
#maxToolRounds;
|
|
8712
9127
|
// Set by cancel(); reset at the top of each #run(). Checked by the loop so
|
|
8713
9128
|
// a cancel between frontend-tool rounds doesn't start another round.
|
|
8714
9129
|
#cancelled = false;
|
|
@@ -8722,6 +9137,8 @@ var AgUiClient = class {
|
|
|
8722
9137
|
this.#onPersist = config.onPersist ?? (() => {
|
|
8723
9138
|
});
|
|
8724
9139
|
this.#connectionLostMessage = config.connectionLostMessage ?? "Connection lost";
|
|
9140
|
+
const rounds = config.maxToolRounds ?? MAX_TOOL_ROUNDS;
|
|
9141
|
+
this.#maxToolRounds = rounds >= 1 ? Math.floor(rounds) : MAX_TOOL_ROUNDS;
|
|
8725
9142
|
const onStateChanged = config.onStateChanged;
|
|
8726
9143
|
if (onStateChanged !== void 0) {
|
|
8727
9144
|
this.#agent.subscribe({
|
|
@@ -8752,7 +9169,8 @@ var AgUiClient = class {
|
|
|
8752
9169
|
*
|
|
8753
9170
|
* When the agent calls frontend tools, this executes them and re-runs the
|
|
8754
9171
|
* agent with the results, looping until the agent stops calling frontend
|
|
8755
|
-
* tools (bounded by {@link
|
|
9172
|
+
* tools (bounded by {@link AgUiClientConfig.maxToolRounds}, which defaults to
|
|
9173
|
+
* {@link MAX_TOOL_ROUNDS}).
|
|
8756
9174
|
*
|
|
8757
9175
|
* `attachments` ride on the user message as a non-standard field so the
|
|
8758
9176
|
* default store round-trips them for history replay; see
|
|
@@ -8849,7 +9267,7 @@ var AgUiClient = class {
|
|
|
8849
9267
|
}
|
|
8850
9268
|
async #runLoop() {
|
|
8851
9269
|
let resume;
|
|
8852
|
-
for (let round = 0; round <
|
|
9270
|
+
for (let round = 0; round < this.#maxToolRounds; round += 1) {
|
|
8853
9271
|
if (this.#cancelled) {
|
|
8854
9272
|
return;
|
|
8855
9273
|
}
|
|
@@ -9870,6 +10288,30 @@ var AgUiChat = class extends HTMLElement {
|
|
|
9870
10288
|
* schema never reaches the browser. Client tools should prefer `x-summary`.
|
|
9871
10289
|
*/
|
|
9872
10290
|
toolSummaries = {};
|
|
10291
|
+
/**
|
|
10292
|
+
* Optional presentation hook for the two payload regions of a tool-call card
|
|
10293
|
+
* -- the arguments and the result. Unset (the default) leaves both
|
|
10294
|
+
* pretty-printed as JSON.
|
|
10295
|
+
*
|
|
10296
|
+
* The seam exists because a wide result has no good rendering as JSON: a
|
|
10297
|
+
* thirty-field row is a wall of text where the host wanted a table, or a
|
|
10298
|
+
* sentence. `ClientTool.render` cannot answer it -- it is handed the
|
|
10299
|
+
* *arguments* only, and a server-side tool has no `ClientTool` at all, so the
|
|
10300
|
+
* result region was the one part of the transcript a host could not reach.
|
|
10301
|
+
*
|
|
10302
|
+
* **Presentation, not translation.** The card and the model already read
|
|
10303
|
+
* separate copies of a tool result: the model's is maintained by
|
|
10304
|
+
* `@ag-ui/client` from the same event and persisted with the history, and the
|
|
10305
|
+
* card has always shown that string reformatted. So a formatter changes what
|
|
10306
|
+
* the person reads and nothing the agent reads -- which makes restyling safe
|
|
10307
|
+
* and *rewording* a way to make the card disagree with the prose beside it.
|
|
10308
|
+
* Rename a value on the server, where it reaches both.
|
|
10309
|
+
*
|
|
10310
|
+
* Read at render time rather than captured, so a host that sets it from a
|
|
10311
|
+
* framework effect after the first card still formats the results that settle
|
|
10312
|
+
* afterwards. See {@link ToolPayloadFormatter}.
|
|
10313
|
+
*/
|
|
10314
|
+
formatToolPayload = null;
|
|
9873
10315
|
/**
|
|
9874
10316
|
* Localizable UI strings — a partial override merged over the English
|
|
9875
10317
|
* {@link DEFAULT_UI_STRINGS}. Resolved once on connect (so set it before the
|
|
@@ -9919,6 +10361,16 @@ var AgUiChat = class extends HTMLElement {
|
|
|
9919
10361
|
#toolRegistry = new ClientToolRegistry();
|
|
9920
10362
|
/** Tool-call cards awaiting execution, keyed by call id. */
|
|
9921
10363
|
#toolCards = /* @__PURE__ */ new Map();
|
|
10364
|
+
/**
|
|
10365
|
+
* The live delegation panels, keyed by the **parent's** `delegate_task` call
|
|
10366
|
+
* id — which is what the wire keys a sub-agent's progress on, so this map and
|
|
10367
|
+
* {@link #toolCards} answer to the same key.
|
|
10368
|
+
*
|
|
10369
|
+
* Kept beside the cards rather than on them, so a card stays a card: the tool
|
|
10370
|
+
* card holds the slot and this holds what went into it, the same division the
|
|
10371
|
+
* approval prompt already uses.
|
|
10372
|
+
*/
|
|
10373
|
+
#subagentPanels = /* @__PURE__ */ new Map();
|
|
9922
10374
|
/**
|
|
9923
10375
|
* Call ids whose card was already settled from a streamed server-side result
|
|
9924
10376
|
* (`TOOL_CALL_RESULT`), so the post-run executeTool sweep doesn't overwrite
|
|
@@ -10910,6 +11362,37 @@ ${quoted}`;
|
|
|
10910
11362
|
this.#quoteButton.hidden = true;
|
|
10911
11363
|
this.#quoting = "";
|
|
10912
11364
|
}
|
|
11365
|
+
/**
|
|
11366
|
+
* The tool-round budget from `data-max-tool-rounds`, for one send.
|
|
11367
|
+
*
|
|
11368
|
+
* Anything unparseable becomes `NaN`, which {@link AgUiClient} rejects along
|
|
11369
|
+
* with a bound below one -- so the two ways of setting this are validated in
|
|
11370
|
+
* one place rather than agreeing by coincidence.
|
|
11371
|
+
*/
|
|
11372
|
+
#maxToolRounds() {
|
|
11373
|
+
const attr = this.getAttribute("data-max-tool-rounds");
|
|
11374
|
+
return attr === null ? MAX_TOOL_ROUNDS : Number.parseInt(attr, 10);
|
|
11375
|
+
}
|
|
11376
|
+
/**
|
|
11377
|
+
* Which message actions a finished bubble offers, from
|
|
11378
|
+
* `data-message-actions`.
|
|
11379
|
+
*
|
|
11380
|
+
* Absent means all of them, so the attribute only ever subtracts: the row
|
|
11381
|
+
* shipped without an off switch and a host that never sets this must keep
|
|
11382
|
+
* exactly what it had. A value names the survivors, which makes
|
|
11383
|
+
* `data-message-actions="false"` -- the spelling its sibling
|
|
11384
|
+
* `data-quote-selection` uses -- an empty set by falling out of the same rule
|
|
11385
|
+
* rather than by a case of its own.
|
|
11386
|
+
*/
|
|
11387
|
+
#messageActions() {
|
|
11388
|
+
const attr = this.getAttribute("data-message-actions");
|
|
11389
|
+
if (attr === null) {
|
|
11390
|
+
return new Set(Object.values(MESSAGE_ACTIONS));
|
|
11391
|
+
}
|
|
11392
|
+
return new Set(
|
|
11393
|
+
attr.split(",").map((token) => token.trim()).filter((token) => token !== "")
|
|
11394
|
+
);
|
|
11395
|
+
}
|
|
10913
11396
|
/** The client-side upload size cap from `data-attachment-max-bytes`. */
|
|
10914
11397
|
#attachmentMaxBytes() {
|
|
10915
11398
|
const attr = this.getAttribute("data-attachment-max-bytes");
|
|
@@ -11455,6 +11938,7 @@ ${quoted}`;
|
|
|
11455
11938
|
this.#thoughts = null;
|
|
11456
11939
|
this.#hidePending();
|
|
11457
11940
|
this.#toolCards.clear();
|
|
11941
|
+
this.#subagentPanels.clear();
|
|
11458
11942
|
this.#serverSettled.clear();
|
|
11459
11943
|
this.#cardElements.clear();
|
|
11460
11944
|
this.#activityBlocks.clear();
|
|
@@ -12277,7 +12761,8 @@ ${quoted}`;
|
|
|
12277
12761
|
resolveInterrupts: (interrupts) => this.#resolveInterrupts(interrupts),
|
|
12278
12762
|
onPersist: (messages) => this.conversationStore.saveMessages(this.#threadId, messages),
|
|
12279
12763
|
onStateChanged: (state) => this.#onSharedStateChanged(state),
|
|
12280
|
-
connectionLostMessage: this.#strings.connectionLost
|
|
12764
|
+
connectionLostMessage: this.#strings.connectionLost,
|
|
12765
|
+
maxToolRounds: this.#maxToolRounds()
|
|
12281
12766
|
});
|
|
12282
12767
|
}
|
|
12283
12768
|
return this.#client;
|
|
@@ -12299,30 +12784,39 @@ ${quoted}`;
|
|
|
12299
12784
|
* Every finished bubble gets copy and feedback -- both are safe on a message
|
|
12300
12785
|
* of any age. Retry moves to the newest, because it is the only one where
|
|
12301
12786
|
* re-running answers the same question rather than rewriting history.
|
|
12787
|
+
*
|
|
12788
|
+
* `data-message-actions` subtracts from that. The row is built only when
|
|
12789
|
+
* something survives to go in it: an empty row still takes its margin, still
|
|
12790
|
+
* answers to the `message-actions` part, and still reads to a screen reader
|
|
12791
|
+
* as a group of actions with none in it.
|
|
12302
12792
|
*/
|
|
12303
12793
|
#attachActions(bubble, options = {}) {
|
|
12304
|
-
|
|
12305
|
-
|
|
12306
|
-
|
|
12307
|
-
|
|
12308
|
-
|
|
12309
|
-
|
|
12310
|
-
|
|
12311
|
-
|
|
12312
|
-
|
|
12313
|
-
|
|
12314
|
-
|
|
12315
|
-
|
|
12316
|
-
|
|
12317
|
-
|
|
12318
|
-
|
|
12319
|
-
|
|
12320
|
-
|
|
12321
|
-
|
|
12322
|
-
|
|
12323
|
-
|
|
12324
|
-
|
|
12325
|
-
|
|
12794
|
+
const enabled = this.#messageActions();
|
|
12795
|
+
const copyable = enabled.has(MESSAGE_ACTIONS.COPY);
|
|
12796
|
+
const rateable = options.rateable !== false && enabled.has(MESSAGE_ACTIONS.FEEDBACK);
|
|
12797
|
+
if (copyable || rateable) {
|
|
12798
|
+
attachMessageActions(bubble, {
|
|
12799
|
+
strings: this.#strings,
|
|
12800
|
+
// Read at click time, not captured: a bubble rendered from markdown
|
|
12801
|
+
// holds its text in the DOM, and that is what the user sees and means
|
|
12802
|
+
// to copy.
|
|
12803
|
+
...copyable ? { text: () => bubble.textContent } : {},
|
|
12804
|
+
...rateable ? {
|
|
12805
|
+
onFeedback: (rating) => {
|
|
12806
|
+
this.dispatchEvent(
|
|
12807
|
+
new CustomEvent(FEEDBACK_EVENT, {
|
|
12808
|
+
detail: { content: bubble.textContent, rating },
|
|
12809
|
+
bubbles: true,
|
|
12810
|
+
composed: true
|
|
12811
|
+
})
|
|
12812
|
+
);
|
|
12813
|
+
}
|
|
12814
|
+
} : {}
|
|
12815
|
+
});
|
|
12816
|
+
}
|
|
12817
|
+
if (enabled.has(MESSAGE_ACTIONS.RETRY)) {
|
|
12818
|
+
this.#moveRetryTo(messageActionBar(bubble, this.#strings));
|
|
12819
|
+
}
|
|
12326
12820
|
}
|
|
12327
12821
|
/** Move the Retry button onto `bar`, taking it off whoever held it. */
|
|
12328
12822
|
#moveRetryTo(bar) {
|
|
@@ -12559,6 +13053,10 @@ ${quoted}`;
|
|
|
12559
13053
|
this.#dispatchInvalidation(value);
|
|
12560
13054
|
return;
|
|
12561
13055
|
}
|
|
13056
|
+
if (name === SUBAGENT_CUSTOM_NAME) {
|
|
13057
|
+
this.#reportSubAgent(value);
|
|
13058
|
+
return;
|
|
13059
|
+
}
|
|
12562
13060
|
this.dispatchEvent(
|
|
12563
13061
|
new CustomEvent(CUSTOM_AGENT_EVENT, {
|
|
12564
13062
|
detail: { name, value },
|
|
@@ -12681,6 +13179,45 @@ ${quoted}`;
|
|
|
12681
13179
|
})
|
|
12682
13180
|
);
|
|
12683
13181
|
}
|
|
13182
|
+
/**
|
|
13183
|
+
* Draw one step of a delegated sub-agent's progress, on the card that
|
|
13184
|
+
* delegated.
|
|
13185
|
+
*
|
|
13186
|
+
* `delegationId` is the parent's own `delegate_task` tool-call id, so the
|
|
13187
|
+
* attachment point is a card this element already drew on `TOOL_CALL_START`.
|
|
13188
|
+
* That is the whole design: a run that hands work to a sub-agent used to read
|
|
13189
|
+
* as a stall -- the card sat at "running…" for the child's entire duration --
|
|
13190
|
+
* and the fix is to narrate *into* the thing that was already standing there,
|
|
13191
|
+
* rather than to float a second element with the same identity.
|
|
13192
|
+
*
|
|
13193
|
+
* A progress event for a call this client never drew is dropped. It has no
|
|
13194
|
+
* card to attach to, and inventing a floating one is precisely the alternative
|
|
13195
|
+
* that was rejected: parent and child interleave in the transcript with
|
|
13196
|
+
* nothing marking whose is whose, and the persisted transcript -- which never
|
|
13197
|
+
* held the progress at all -- would not match what was on screen.
|
|
13198
|
+
*
|
|
13199
|
+
* Nothing here writes to the conversation store. `CUSTOM` never enters
|
|
13200
|
+
* `agent.messages`, so a reload mid-run leaves the tool card and loses the
|
|
13201
|
+
* nested detail, which is the intended behaviour rather than a gap.
|
|
13202
|
+
*/
|
|
13203
|
+
#reportSubAgent(value) {
|
|
13204
|
+
const update = subAgentUpdate(value);
|
|
13205
|
+
if (update === null) {
|
|
13206
|
+
return;
|
|
13207
|
+
}
|
|
13208
|
+
const card = this.#toolCards.get(update.delegationId);
|
|
13209
|
+
if (card === void 0) {
|
|
13210
|
+
return;
|
|
13211
|
+
}
|
|
13212
|
+
let panel = this.#subagentPanels.get(update.delegationId);
|
|
13213
|
+
if (panel === void 0) {
|
|
13214
|
+
panel = new SubAgentPanel(this.#strings);
|
|
13215
|
+
this.#subagentPanels.set(update.delegationId, panel);
|
|
13216
|
+
card.subagentSlot.appendChild(panel.element);
|
|
13217
|
+
}
|
|
13218
|
+
panel.report(update);
|
|
13219
|
+
this.#scroller.follow();
|
|
13220
|
+
}
|
|
12684
13221
|
/** A muted "⏹ Stopped" line in the transcript (distinct from the ⚠️ error bubble). */
|
|
12685
13222
|
#appendStoppedNote() {
|
|
12686
13223
|
const note = document.createElement("div");
|
|
@@ -13017,7 +13554,12 @@ ${quoted}`;
|
|
|
13017
13554
|
}
|
|
13018
13555
|
const labelled = this.#resolveTool(call.name)?.parameters[X_SUMMARY_KEY];
|
|
13019
13556
|
const summary = typeof labelled === "string" ? labelled : this.toolSummaries[call.name] ?? this.#toolCatalog[call.name]?.summary ?? prettifyToolName(call.name);
|
|
13020
|
-
const card = new ToolCallCard(call.name, call.args, summary, this.#strings
|
|
13557
|
+
const card = new ToolCallCard(call.name, call.args, summary, this.#strings, {
|
|
13558
|
+
// A thunk over the live property, not the property itself: the card keeps
|
|
13559
|
+
// this for the life of the call, and the result region is filled when the
|
|
13560
|
+
// tool settles -- which can be long after a host set the hook.
|
|
13561
|
+
formatPayload: (payload) => this.formatToolPayload?.(payload) ?? null
|
|
13562
|
+
});
|
|
13021
13563
|
this.#toolCards.set(call.id, card);
|
|
13022
13564
|
this.#ensureGroup().appendChild(card.element);
|
|
13023
13565
|
this.#updateEmptyState();
|
|
@@ -13091,7 +13633,7 @@ function setControlValue(el2, value) {
|
|
|
13091
13633
|
}
|
|
13092
13634
|
|
|
13093
13635
|
// src/version.ts
|
|
13094
|
-
var VERSION = "0.
|
|
13636
|
+
var VERSION = "0.30.0";
|
|
13095
13637
|
export {
|
|
13096
13638
|
ATTACHMENT_EVENT,
|
|
13097
13639
|
AgUiChat,
|
|
@@ -13113,6 +13655,7 @@ export {
|
|
|
13113
13655
|
MAX_SUGGESTIONS,
|
|
13114
13656
|
MAX_SUGGESTION_CHARS,
|
|
13115
13657
|
MAX_TOOL_ROUNDS,
|
|
13658
|
+
MESSAGE_ACTIONS,
|
|
13116
13659
|
MESSAGE_ROLE,
|
|
13117
13660
|
PAGE_ACTIONS,
|
|
13118
13661
|
RUN_FINISHED_EVENT,
|