@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.
Files changed (38) hide show
  1. package/CHANGELOG.md +199 -1
  2. package/README.md +196 -33
  3. package/dist/ag-ui-web-component.bundle.js +211 -28
  4. package/dist/ag-ui-web-component.bundle.js.map +4 -4
  5. package/dist/constants.d.ts +60 -1
  6. package/dist/constants.d.ts.map +1 -1
  7. package/dist/core/ag_ui_chat.d.ts +25 -1
  8. package/dist/core/ag_ui_chat.d.ts.map +1 -1
  9. package/dist/core/agui_client.d.ts +18 -1
  10. package/dist/core/agui_client.d.ts.map +1 -1
  11. package/dist/index.d.ts +2 -2
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +578 -35
  14. package/dist/index.js.map +4 -4
  15. package/dist/ui/message_actions.d.ts +14 -4
  16. package/dist/ui/message_actions.d.ts.map +1 -1
  17. package/dist/ui/styles.d.ts +1 -1
  18. package/dist/ui/styles.d.ts.map +1 -1
  19. package/dist/ui/subagent_panel.d.ts +92 -0
  20. package/dist/ui/subagent_panel.d.ts.map +1 -0
  21. package/dist/ui/subagent_update.d.ts +19 -0
  22. package/dist/ui/subagent_update.d.ts.map +1 -0
  23. package/dist/ui/tool_call_card.d.ts +73 -1
  24. package/dist/ui/tool_call_card.d.ts.map +1 -1
  25. package/dist/ui/ui_strings.d.ts +10 -0
  26. package/dist/ui/ui_strings.d.ts.map +1 -1
  27. package/package.json +1 -1
  28. package/src/constants.ts +63 -1
  29. package/src/core/ag_ui_chat.ts +180 -26
  30. package/src/core/agui_client.ts +26 -2
  31. package/src/index.ts +4 -0
  32. package/src/ui/message_actions.ts +20 -8
  33. package/src/ui/styles.ts +183 -0
  34. package/src/ui/subagent_panel.ts +213 -0
  35. package/src/ui/subagent_update.ts +80 -0
  36. package/src/ui/tool_call_card.ts +129 -3
  37. package/src/ui/ui_strings.ts +15 -0
  38. package/src/version.ts +1 -1
@@ -8,6 +8,56 @@ export type ToolDisplayMode = (typeof TOOL_DISPLAY)[keyof typeof TOOL_DISPLAY];
8
8
  export type UnsettledStatus = typeof TOOL_CALL_STATUS.PENDING | typeof TOOL_CALL_STATUS.DEFERRED;
9
9
  /** The terminal states a card settles into (everything unsettled excluded). */
10
10
  export type SettledStatus = Exclude<ToolCallStatus, UnsettledStatus>;
11
+ /**
12
+ * One region of a card's body, handed to a host {@link ToolPayloadFormatter}.
13
+ *
14
+ * A discriminated union rather than three positional parameters because the two
15
+ * halves do not carry the same thing: arguments are the parsed record the call
16
+ * was made with, and a result is the raw string the tool returned, which may not
17
+ * be JSON at all. Flattening both into one `payload` parameter would force every
18
+ * formatter to re-derive which it had before it could read it, and the
19
+ * arguments would arrive re-serialised for no reason.
20
+ *
21
+ * `toolName` is the raw tool name, not the card's `x-summary` label -- a
22
+ * formatter dispatches on identity, and the label is a display string a server
23
+ * may change.
24
+ */
25
+ export type ToolPayload = {
26
+ readonly kind: "arguments";
27
+ readonly toolName: string;
28
+ readonly args: Record<string, unknown>;
29
+ } | {
30
+ readonly kind: "result";
31
+ readonly toolName: string;
32
+ readonly status: SettledStatus;
33
+ readonly text: string;
34
+ };
35
+ /**
36
+ * Renders one region of a tool card's body, for a host that would rather show a
37
+ * table or a summary line than a wall of pretty-printed JSON.
38
+ *
39
+ * Return a `Node` to take the region over, a `string` to replace its text, or
40
+ * `null` to fall through to the built-in pretty-print -- so a formatter that
41
+ * only cares about one tool, or only about results, declines the rest rather
42
+ * than reimplementing them.
43
+ *
44
+ * **Presentation only.** The model reads the tool result from its own copy of
45
+ * the message, which this never touches, so anything said here is said to the
46
+ * person and not to the agent. Translating a value -- an enum constant into a
47
+ * friendly label, an epoch into a date -- belongs on the server, where it also
48
+ * reaches the model's prose; doing it here would make the card and the answer
49
+ * beside it disagree about what happened.
50
+ *
51
+ * A returned string is set as text, never parsed as markup: this is not a
52
+ * second HTML channel into the transcript, and a host that wants elements
53
+ * builds them itself and returns the node.
54
+ */
55
+ export type ToolPayloadFormatter = (payload: ToolPayload) => Node | string | null;
56
+ /** Optional per-card wiring beyond the name, arguments, label and strings. */
57
+ export interface ToolCallCardOptions {
58
+ /** Host presentation hook for both body regions. See {@link ToolPayloadFormatter}. */
59
+ readonly formatPayload?: ToolPayloadFormatter;
60
+ }
11
61
  /**
12
62
  * A live tool-call card for the chat transcript.
13
63
  *
@@ -27,6 +77,12 @@ export type SettledStatus = Exclude<ToolCallStatus, UnsettledStatus>;
27
77
  * `--ag-ui-tool-icon-*` custom properties or the `tool-card-icon` part without
28
78
  * the card reaching into the host stylesheet.
29
79
  *
80
+ * Either region may be drawn by the host instead: `options.formatPayload` is
81
+ * asked about each one and pretty-prints as before whenever it declines. The
82
+ * arguments are offered from the constructor and the result from {@link settle},
83
+ * because that is when each exists -- so a formatter is asked twice per card,
84
+ * potentially long apart.
85
+ *
30
86
  * Pure DOM. The host appends {@link element} into its shadow root; all visible
31
87
  * text comes from {@link UiStrings}.
32
88
  */
@@ -48,6 +104,22 @@ export declare class ToolCallCard {
48
104
  * Empty until used, and hidden while empty by the shadow CSS.
49
105
  */
50
106
  readonly approvalSlot: HTMLDivElement;
107
+ /**
108
+ * Where a *nested* run's progress renders — the sub-agent this call delegated
109
+ * to, narrating itself while the card waits.
110
+ *
111
+ * A slot rather than a rendered thing, on the same reasoning as
112
+ * {@link approvalSlot}: the card owns the position and something else owns the
113
+ * content. What makes the position right is that the wire keys a delegation on
114
+ * this card's own `toolCallId`, so the run being narrated is the one this card
115
+ * already stands for.
116
+ *
117
+ * Placed above the Details toggle rather than inside the body, because the
118
+ * body is what the display modes hide — and a progress line that only appears
119
+ * in `full` mode would leave exactly the stall it exists to end. Empty on every
120
+ * card that delegated nothing, and hidden while empty by the shadow CSS.
121
+ */
122
+ readonly subagentSlot: HTMLDivElement;
51
123
  /**
52
124
  * The arguments this call was made with.
53
125
  *
@@ -56,7 +128,7 @@ export declare class ToolCallCard {
56
128
  * still exist when the user is asked to approve, edit or deny the call.
57
129
  */
58
130
  readonly args: Record<string, unknown>;
59
- constructor(name: string, args: Record<string, unknown>, summary?: string, strings?: UiStrings);
131
+ constructor(name: string, args: Record<string, unknown>, summary?: string, strings?: UiStrings, options?: ToolCallCardOptions);
60
132
  /**
61
133
  * Move between the two states that are not an outcome — `pending` (running)
62
134
  * and `deferred` (gated, waiting on a person).
@@ -1 +1 @@
1
- {"version":3,"file":"tool_call_card.d.ts","sourceRoot":"","sources":["../../src/ui/tool_call_card.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAErE,4CAA4C;AAC5C,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,OAAO,gBAAgB,CAAC,CAAC;AAEtF,sCAAsC;AACtC,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,OAAO,YAAY,CAAC,CAAC;AAE/E,iEAAiE;AACjE,MAAM,MAAM,eAAe,GAAG,OAAO,gBAAgB,CAAC,OAAO,GAAG,OAAO,gBAAgB,CAAC,QAAQ,CAAC;AAEjG,+EAA+E;AAC/E,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC,cAAc,EAAE,eAAe,CAAC,CAAC;AA+BrE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,YAAY;;IACvB,kEAAkE;IAClE,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IAEjC;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,YAAY,EAAE,cAAc,CAAC;IAStC;;;;;;OAMG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAGvC,YACE,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,MAAM,EAChB,OAAO,GAAE,SAA8B,EAwExC;IAED;;;;;;OAMG;IACH,IAAI,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAMlC;IAED;;;;OAIG;IACH,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,GAAG,IAAI,CAKlD;IAED,gFAAgF;IAChF,IAAI,OAAO,IAAI,OAAO,CAErB;IAED;;;OAGG;IACH,MAAM,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAYhD;CAoCF"}
1
+ {"version":3,"file":"tool_call_card.d.ts","sourceRoot":"","sources":["../../src/ui/tool_call_card.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAErE,4CAA4C;AAC5C,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,OAAO,gBAAgB,CAAC,CAAC;AAEtF,sCAAsC;AACtC,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,OAAO,YAAY,CAAC,CAAC;AAE/E,iEAAiE;AACjE,MAAM,MAAM,eAAe,GAAG,OAAO,gBAAgB,CAAC,OAAO,GAAG,OAAO,gBAAgB,CAAC,QAAQ,CAAC;AAEjG,+EAA+E;AAC/E,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC,cAAc,EAAE,eAAe,CAAC,CAAC;AA+BrE;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,WAAW,GACnB;IACE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACxC,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,CAAC;AAEN;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC;AAElF,8EAA8E;AAC9E,MAAM,WAAW,mBAAmB;IAClC,sFAAsF;IACtF,QAAQ,CAAC,aAAa,CAAC,EAAE,oBAAoB,CAAC;CAC/C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,YAAY;;IACvB,kEAAkE;IAClE,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IAEjC;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,YAAY,EAAE,cAAc,CAAC;IAEtC;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,YAAY,EAAE,cAAc,CAAC;IAStC;;;;;;OAMG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAMvC,YACE,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,MAAM,EAChB,OAAO,GAAE,SAA8B,EACvC,OAAO,GAAE,mBAAwB,EAkFlC;IAED;;;;;;OAMG;IACH,IAAI,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAMlC;IAED;;;;OAIG;IACH,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,GAAG,IAAI,CAKlD;IAED,gFAAgF;IAChF,IAAI,OAAO,IAAI,OAAO,CAErB;IAED;;;OAGG;IACH,MAAM,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAgBhD;CAkEF"}
@@ -123,6 +123,16 @@ export interface UiStrings {
123
123
  declinedLabel: string;
124
124
  /** Label on the toggle that expands a tool card's body. */
125
125
  details: string;
126
+ /**
127
+ * The delegation row's text before the server's own status line lands.
128
+ *
129
+ * A fallback, not a state: every announcement carries a pre-rendered `status`,
130
+ * and this only shows if one arrives unusable. The row is the expander, so it
131
+ * must never be blank.
132
+ */
133
+ subAgentWorking: string;
134
+ /** `aria-label` of the region holding the sub-agent's own tool calls. */
135
+ subAgentSteps: string;
126
136
  /** `aria-label` of the editable arguments field on an approval card. */
127
137
  approvalEditArgs: string;
128
138
  /** Shown when the edited arguments are not valid JSON. */
@@ -1 +1 @@
1
- {"version":3,"file":"ui_strings.d.ts","sourceRoot":"","sources":["../../src/ui/ui_strings.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ui_strings.d.ts","sourceRoot":"","sources":["../../src/ui/ui_strings.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@artooi/ag-ui-web-component",
3
- "version": "0.29.0",
3
+ "version": "0.30.0",
4
4
  "description": "Framework-free <ag-ui-chat> Web Component over the AG-UI protocol. Drop-in chat sidebar with a pluggable client-side tool registry, DOM driver primitives, animations, and destructive-action confirmation modal.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/constants.ts CHANGED
@@ -119,6 +119,45 @@ export const SUGGESTIONS_ACTIVITY_TYPE = "suggestions";
119
119
  */
120
120
  export const INVALIDATE_CUSTOM_NAME = "ag_ui.invalidate";
121
121
 
122
+ /**
123
+ * The AG-UI `CUSTOM` event `name` carrying a delegated sub-agent's progress.
124
+ *
125
+ * Matched exactly, and namespaced the way {@link INVALIDATE_CUSTOM_NAME} is, so
126
+ * it cannot collide with a host's own custom events on the same open field.
127
+ *
128
+ * Routed to the delegating tool card rather than dispatched to the page: the
129
+ * payload keys on the **parent's own `delegate_task` call id**, so what it
130
+ * describes is already on screen and the progress belongs beside it. Every other
131
+ * name still reaches the host as {@link CUSTOM_AGENT_EVENT}.
132
+ *
133
+ * Deliberately on the imperative carrier and not on an activity, which means it
134
+ * is **never persisted and never replayed**. That is the right half of the
135
+ * split: a delegation that was live an hour ago is not live now, and replaying
136
+ * its progress on a thread restore would be a lie about a run that is over. On a
137
+ * reload mid-run the nested detail is gone and the tool card remains.
138
+ */
139
+ export const SUBAGENT_CUSTOM_NAME = "ag_ui.subagent";
140
+
141
+ /**
142
+ * The phases one delegation moves through, as the server spells them.
143
+ *
144
+ * Exactly one `STARTED` opens a delegation and exactly one `FINISHED` or
145
+ * `FAILED` closes it; the two tool phases repeat in between, once per call the
146
+ * child makes and once per result it gets back.
147
+ *
148
+ * `FAILED` carries **no exception text**, deliberately — the same reasoning that
149
+ * redacts a `RUN_ERROR`, since an exception's words are written for an operator.
150
+ * The detail rides the ordinary `TOOL_CALL_RESULT` for that delegation, on the
151
+ * card the progress is already attached to.
152
+ */
153
+ export const SUBAGENT_PHASE = {
154
+ STARTED: "started",
155
+ TOOL_CALL: "tool_call",
156
+ TOOL_RESULT: "tool_result",
157
+ FINISHED: "finished",
158
+ FAILED: "failed",
159
+ } as const;
160
+
122
161
  /** Roles a chat message can take. */
123
162
  export const MESSAGE_ROLE = {
124
163
  USER: "user",
@@ -158,9 +197,32 @@ export const X_NAVIGATES_KEY = "x-navigates";
158
197
  */
159
198
  export const READ_PAGE_TOOL = "read_page";
160
199
 
161
- /** Upper bound on frontend tool-call → re-run rounds within one send. */
200
+ /**
201
+ * Default upper bound on frontend tool-call → re-run rounds within one send.
202
+ * Overridable via `data-max-tool-rounds`, or `AgUiClientConfig.maxToolRounds`
203
+ * for a host driving the client directly.
204
+ */
162
205
  export const MAX_TOOL_ROUNDS = 10;
163
206
 
207
+ /**
208
+ * The actions a finished assistant message's row can carry, and the tokens
209
+ * `data-message-actions` selects them by.
210
+ *
211
+ * All three are on when the attribute is absent. Naming them individually
212
+ * rather than shipping one on/off switch is what the three differ over: feedback
213
+ * is only useful to a host listening for {@link FEEDBACK_EVENT}, retry re-runs
214
+ * the agent and a constrained surface may not permit that, and copy is the one
215
+ * nobody objects to. A single switch would make dropping either of the first two
216
+ * cost the third, and a host that only wanted one gone would rebuild the row
217
+ * from `attachMessageActions` -- reimplementing the part names, the accessible
218
+ * grouping and the retry hand-off to lose two buttons.
219
+ */
220
+ export const MESSAGE_ACTIONS = {
221
+ COPY: "copy",
222
+ RETRY: "retry",
223
+ FEEDBACK: "feedback",
224
+ } as const;
225
+
164
226
  /**
165
227
  * Lifecycle status of a rendered tool-call card. A card opens as `PENDING`
166
228
  * while the call runs, then settles to `DONE`, `ERROR`, or `DECLINED`.
@@ -15,10 +15,13 @@ import {
15
15
  INVALIDATE_CUSTOM_NAME,
16
16
  INVALIDATE_EVENT,
17
17
  LOAD_CAPABILITY_TOOL,
18
+ MAX_TOOL_ROUNDS,
19
+ MESSAGE_ACTIONS,
18
20
  MESSAGE_ROLE,
19
21
  READ_PAGE_TOOL,
20
22
  RUN_FINISHED_EVENT,
21
23
  STATE_EVENT,
24
+ SUBAGENT_CUSTOM_NAME,
22
25
  SUBMIT_EVENT,
23
26
  SUGGESTIONS_ACTIVITY_TYPE,
24
27
  TOGGLE_EVENT,
@@ -79,10 +82,16 @@ import { renderRunNotice } from "../ui/run_notice.js";
79
82
  import { SkillsMenu } from "../ui/skills_menu.js";
80
83
  import { createStickToBottom, type StickToBottom } from "../ui/stick_to_bottom.js";
81
84
  import { STYLES } from "../ui/styles.js";
85
+ import { SubAgentPanel } from "../ui/subagent_panel.js";
86
+ import { subAgentUpdate } from "../ui/subagent_update.js";
82
87
  import { renderSuggestionChips } from "../ui/suggestion_chips.js";
83
88
  import { ThoughtsBlock } from "../ui/thoughts_block.js";
84
89
  import { ThreadDrawer } from "../ui/thread_drawer.js";
85
- import { ToolCallCard, type ToolDisplayMode } from "../ui/tool_call_card.js";
90
+ import {
91
+ ToolCallCard,
92
+ type ToolDisplayMode,
93
+ type ToolPayloadFormatter,
94
+ } from "../ui/tool_call_card.js";
86
95
  import { DEFAULT_UI_STRINGS, mergeUiStrings, type UiStrings } from "../ui/ui_strings.js";
87
96
  import { VoiceInput } from "../ui/voice_input.js";
88
97
  import {
@@ -532,6 +541,31 @@ export class AgUiChat extends HTMLElement {
532
541
  */
533
542
  toolSummaries: Record<string, string> = {};
534
543
 
544
+ /**
545
+ * Optional presentation hook for the two payload regions of a tool-call card
546
+ * -- the arguments and the result. Unset (the default) leaves both
547
+ * pretty-printed as JSON.
548
+ *
549
+ * The seam exists because a wide result has no good rendering as JSON: a
550
+ * thirty-field row is a wall of text where the host wanted a table, or a
551
+ * sentence. `ClientTool.render` cannot answer it -- it is handed the
552
+ * *arguments* only, and a server-side tool has no `ClientTool` at all, so the
553
+ * result region was the one part of the transcript a host could not reach.
554
+ *
555
+ * **Presentation, not translation.** The card and the model already read
556
+ * separate copies of a tool result: the model's is maintained by
557
+ * `@ag-ui/client` from the same event and persisted with the history, and the
558
+ * card has always shown that string reformatted. So a formatter changes what
559
+ * the person reads and nothing the agent reads -- which makes restyling safe
560
+ * and *rewording* a way to make the card disagree with the prose beside it.
561
+ * Rename a value on the server, where it reaches both.
562
+ *
563
+ * Read at render time rather than captured, so a host that sets it from a
564
+ * framework effect after the first card still formats the results that settle
565
+ * afterwards. See {@link ToolPayloadFormatter}.
566
+ */
567
+ formatToolPayload: ToolPayloadFormatter | null = null;
568
+
535
569
  /**
536
570
  * Localizable UI strings — a partial override merged over the English
537
571
  * {@link DEFAULT_UI_STRINGS}. Resolved once on connect (so set it before the
@@ -586,6 +620,16 @@ export class AgUiChat extends HTMLElement {
586
620
  readonly #toolRegistry = new ClientToolRegistry();
587
621
  /** Tool-call cards awaiting execution, keyed by call id. */
588
622
  readonly #toolCards = new Map<string, ToolCallCard>();
623
+ /**
624
+ * The live delegation panels, keyed by the **parent's** `delegate_task` call
625
+ * id — which is what the wire keys a sub-agent's progress on, so this map and
626
+ * {@link #toolCards} answer to the same key.
627
+ *
628
+ * Kept beside the cards rather than on them, so a card stays a card: the tool
629
+ * card holds the slot and this holds what went into it, the same division the
630
+ * approval prompt already uses.
631
+ */
632
+ readonly #subagentPanels = new Map<string, SubAgentPanel>();
589
633
  /**
590
634
  * Call ids whose card was already settled from a streamed server-side result
591
635
  * (`TOOL_CALL_RESULT`), so the post-run executeTool sweep doesn't overwrite
@@ -1744,6 +1788,42 @@ export class AgUiChat extends HTMLElement {
1744
1788
  this.#quoting = "";
1745
1789
  }
1746
1790
 
1791
+ /**
1792
+ * The tool-round budget from `data-max-tool-rounds`, for one send.
1793
+ *
1794
+ * Anything unparseable becomes `NaN`, which {@link AgUiClient} rejects along
1795
+ * with a bound below one -- so the two ways of setting this are validated in
1796
+ * one place rather than agreeing by coincidence.
1797
+ */
1798
+ #maxToolRounds(): number {
1799
+ const attr = this.getAttribute("data-max-tool-rounds");
1800
+ return attr === null ? MAX_TOOL_ROUNDS : Number.parseInt(attr, 10);
1801
+ }
1802
+
1803
+ /**
1804
+ * Which message actions a finished bubble offers, from
1805
+ * `data-message-actions`.
1806
+ *
1807
+ * Absent means all of them, so the attribute only ever subtracts: the row
1808
+ * shipped without an off switch and a host that never sets this must keep
1809
+ * exactly what it had. A value names the survivors, which makes
1810
+ * `data-message-actions="false"` -- the spelling its sibling
1811
+ * `data-quote-selection` uses -- an empty set by falling out of the same rule
1812
+ * rather than by a case of its own.
1813
+ */
1814
+ #messageActions(): ReadonlySet<string> {
1815
+ const attr = this.getAttribute("data-message-actions");
1816
+ if (attr === null) {
1817
+ return new Set(Object.values(MESSAGE_ACTIONS));
1818
+ }
1819
+ return new Set(
1820
+ attr
1821
+ .split(",")
1822
+ .map((token) => token.trim())
1823
+ .filter((token) => token !== ""),
1824
+ );
1825
+ }
1826
+
1747
1827
  /** The client-side upload size cap from `data-attachment-max-bytes`. */
1748
1828
  #attachmentMaxBytes(): number {
1749
1829
  const attr = this.getAttribute("data-attachment-max-bytes");
@@ -2381,6 +2461,11 @@ export class AgUiChat extends HTMLElement {
2381
2461
  this.#thoughts = null;
2382
2462
  this.#hidePending();
2383
2463
  this.#toolCards.clear();
2464
+ // The panels go with the cards they hung off. Nothing restores them: the
2465
+ // progress rode the imperative carrier and was never persisted, which is
2466
+ // the correct half of that split -- a delegation that was live before this
2467
+ // transcript was wiped is not live now.
2468
+ this.#subagentPanels.clear();
2384
2469
  this.#serverSettled.clear();
2385
2470
  this.#cardElements.clear();
2386
2471
  this.#activityBlocks.clear();
@@ -3414,6 +3499,7 @@ export class AgUiChat extends HTMLElement {
3414
3499
  onPersist: (messages) => this.conversationStore.saveMessages(this.#threadId, messages),
3415
3500
  onStateChanged: (state) => this.#onSharedStateChanged(state),
3416
3501
  connectionLostMessage: this.#strings.connectionLost,
3502
+ maxToolRounds: this.#maxToolRounds(),
3417
3503
  });
3418
3504
  }
3419
3505
  return this.#client;
@@ -3437,32 +3523,45 @@ export class AgUiChat extends HTMLElement {
3437
3523
  * Every finished bubble gets copy and feedback -- both are safe on a message
3438
3524
  * of any age. Retry moves to the newest, because it is the only one where
3439
3525
  * re-running answers the same question rather than rewriting history.
3526
+ *
3527
+ * `data-message-actions` subtracts from that. The row is built only when
3528
+ * something survives to go in it: an empty row still takes its margin, still
3529
+ * answers to the `message-actions` part, and still reads to a screen reader
3530
+ * as a group of actions with none in it.
3440
3531
  */
3441
3532
  #attachActions(bubble: HTMLDivElement, options: { rateable?: boolean } = {}): void {
3442
- attachMessageActions(bubble, {
3443
- strings: this.#strings,
3444
- // Read at click time, not captured: a bubble rendered from markdown holds
3445
- // its text in the DOM, and that is what the user sees and means to copy.
3446
- text: () => bubble.textContent as string,
3447
- // A failed run is copyable -- error text is what people paste into a bug
3448
- // report -- but not rateable: a rating is a statement about an *answer*,
3449
- // and mixing "the connection dropped" into that signal makes the host's
3450
- // feedback data say less than it did before.
3451
- ...(options.rateable === false
3452
- ? {}
3453
- : {
3454
- onFeedback: (rating: "up" | "down") => {
3455
- this.dispatchEvent(
3456
- new CustomEvent<FeedbackDetail>(FEEDBACK_EVENT, {
3457
- detail: { content: bubble.textContent as string, rating },
3458
- bubbles: true,
3459
- composed: true,
3460
- }),
3461
- );
3462
- },
3463
- }),
3464
- });
3465
- this.#moveRetryTo(messageActionBar(bubble, this.#strings));
3533
+ const enabled = this.#messageActions();
3534
+ const copyable = enabled.has(MESSAGE_ACTIONS.COPY);
3535
+ // A failed run is copyable -- error text is what people paste into a bug
3536
+ // report -- but not rateable: a rating is a statement about an *answer*,
3537
+ // and mixing "the connection dropped" into that signal makes the host's
3538
+ // feedback data say less than it did before.
3539
+ const rateable = options.rateable !== false && enabled.has(MESSAGE_ACTIONS.FEEDBACK);
3540
+ if (copyable || rateable) {
3541
+ attachMessageActions(bubble, {
3542
+ strings: this.#strings,
3543
+ // Read at click time, not captured: a bubble rendered from markdown
3544
+ // holds its text in the DOM, and that is what the user sees and means
3545
+ // to copy.
3546
+ ...(copyable ? { text: () => bubble.textContent as string } : {}),
3547
+ ...(rateable
3548
+ ? {
3549
+ onFeedback: (rating: "up" | "down") => {
3550
+ this.dispatchEvent(
3551
+ new CustomEvent<FeedbackDetail>(FEEDBACK_EVENT, {
3552
+ detail: { content: bubble.textContent as string, rating },
3553
+ bubbles: true,
3554
+ composed: true,
3555
+ }),
3556
+ );
3557
+ },
3558
+ }
3559
+ : {}),
3560
+ });
3561
+ }
3562
+ if (enabled.has(MESSAGE_ACTIONS.RETRY)) {
3563
+ this.#moveRetryTo(messageActionBar(bubble, this.#strings));
3564
+ }
3466
3565
  }
3467
3566
 
3468
3567
  /** Move the Retry button onto `bar`, taking it off whoever held it. */
@@ -3822,6 +3921,10 @@ export class AgUiChat extends HTMLElement {
3822
3921
  this.#dispatchInvalidation(value);
3823
3922
  return;
3824
3923
  }
3924
+ if (name === SUBAGENT_CUSTOM_NAME) {
3925
+ this.#reportSubAgent(value);
3926
+ return;
3927
+ }
3825
3928
  // Straight out to the host page, uninterpreted. This is the imperative
3826
3929
  // carrier: whatever it means, it means it to the page, not to the
3827
3930
  // transcript -- so it is dispatched and deliberately not rendered,
@@ -4008,6 +4111,52 @@ export class AgUiChat extends HTMLElement {
4008
4111
  );
4009
4112
  }
4010
4113
 
4114
+ /**
4115
+ * Draw one step of a delegated sub-agent's progress, on the card that
4116
+ * delegated.
4117
+ *
4118
+ * `delegationId` is the parent's own `delegate_task` tool-call id, so the
4119
+ * attachment point is a card this element already drew on `TOOL_CALL_START`.
4120
+ * That is the whole design: a run that hands work to a sub-agent used to read
4121
+ * as a stall -- the card sat at "running…" for the child's entire duration --
4122
+ * and the fix is to narrate *into* the thing that was already standing there,
4123
+ * rather than to float a second element with the same identity.
4124
+ *
4125
+ * A progress event for a call this client never drew is dropped. It has no
4126
+ * card to attach to, and inventing a floating one is precisely the alternative
4127
+ * that was rejected: parent and child interleave in the transcript with
4128
+ * nothing marking whose is whose, and the persisted transcript -- which never
4129
+ * held the progress at all -- would not match what was on screen.
4130
+ *
4131
+ * Nothing here writes to the conversation store. `CUSTOM` never enters
4132
+ * `agent.messages`, so a reload mid-run leaves the tool card and loses the
4133
+ * nested detail, which is the intended behaviour rather than a gap.
4134
+ */
4135
+ #reportSubAgent(value: unknown): void {
4136
+ const update = subAgentUpdate(value);
4137
+ if (update === null) {
4138
+ return;
4139
+ }
4140
+ const card = this.#toolCards.get(update.delegationId);
4141
+ if (card === undefined) {
4142
+ return;
4143
+ }
4144
+ let panel = this.#subagentPanels.get(update.delegationId);
4145
+ if (panel === undefined) {
4146
+ // Created on whichever phase arrives first rather than only on `started`.
4147
+ // The contract says exactly one opens a delegation, and a client that
4148
+ // insisted on it would answer a server that dropped one frame by showing
4149
+ // nothing at all for the rest of the run.
4150
+ panel = new SubAgentPanel(this.#strings);
4151
+ this.#subagentPanels.set(update.delegationId, panel);
4152
+ card.subagentSlot.appendChild(panel.element);
4153
+ }
4154
+ panel.report(update);
4155
+ // The card grew, and the transcript is usually pinned to the foot while a
4156
+ // run is in flight.
4157
+ this.#scroller.follow();
4158
+ }
4159
+
4011
4160
  /** A muted "⏹ Stopped" line in the transcript (distinct from the ⚠️ error bubble). */
4012
4161
  #appendStoppedNote(): void {
4013
4162
  const note = document.createElement("div");
@@ -4404,7 +4553,12 @@ export class AgUiChat extends HTMLElement {
4404
4553
  : (this.toolSummaries[call.name] ??
4405
4554
  this.#toolCatalog[call.name]?.summary ??
4406
4555
  prettifyToolName(call.name));
4407
- const card = new ToolCallCard(call.name, call.args, summary, this.#strings);
4556
+ const card = new ToolCallCard(call.name, call.args, summary, this.#strings, {
4557
+ // A thunk over the live property, not the property itself: the card keeps
4558
+ // this for the life of the call, and the result region is filled when the
4559
+ // tool settles -- which can be long after a host set the hook.
4560
+ formatPayload: (payload) => this.formatToolPayload?.(payload) ?? null,
4561
+ });
4408
4562
  this.#toolCards.set(call.id, card);
4409
4563
  this.#ensureGroup().appendChild(card.element);
4410
4564
  this.#updateEmptyState();
@@ -182,6 +182,22 @@ export interface AgUiClientConfig extends AgUiRunInputs {
182
182
  * passes its localized string.
183
183
  */
184
184
  connectionLostMessage?: string;
185
+ /**
186
+ * Upper bound on frontend tool-call to re-run rounds within one
187
+ * {@link AgUiClient.send}. Defaults to {@link MAX_TOOL_ROUNDS}.
188
+ *
189
+ * The default suits a chat whose tools answer questions. A page-driving
190
+ * deployment reaches it legitimately -- filling a form field by field is one
191
+ * round each -- and the symptom is not an error but an answer that stops
192
+ * mid-task, which reads as the model giving up. Raise it where a turn is
193
+ * expected to take many small steps.
194
+ *
195
+ * Read once, when the client is built. The bound is a property of the
196
+ * deployment rather than of a run, so re-reading it per round would only make
197
+ * a mid-run change possible, and a mid-run change to how long the run may
198
+ * last is not a thing a host has any way to reason about.
199
+ */
200
+ maxToolRounds?: number;
185
201
  }
186
202
 
187
203
  /**
@@ -219,6 +235,7 @@ export class AgUiClient {
219
235
  */
220
236
  readonly #closedMessageIds = new Set<string>();
221
237
  readonly #connectionLostMessage: string;
238
+ readonly #maxToolRounds: number;
222
239
  // Set by cancel(); reset at the top of each #run(). Checked by the loop so
223
240
  // a cancel between frontend-tool rounds doesn't start another round.
224
241
  #cancelled = false;
@@ -232,6 +249,12 @@ export class AgUiClient {
232
249
  this.#resolveInterrupts = config.resolveInterrupts ?? null;
233
250
  this.#onPersist = config.onPersist ?? (() => {});
234
251
  this.#connectionLostMessage = config.connectionLostMessage ?? "Connection lost";
252
+ // Validated here rather than at each caller, so the element's attribute and
253
+ // a direct consumer get the same answer. A bound below one -- or a NaN from
254
+ // an unparseable attribute -- is not a smaller budget but a send that runs
255
+ // the agent zero times, which would look exactly like a broken endpoint.
256
+ const rounds = config.maxToolRounds ?? MAX_TOOL_ROUNDS;
257
+ this.#maxToolRounds = rounds >= 1 ? Math.floor(rounds) : MAX_TOOL_ROUNDS;
235
258
  const onStateChanged = config.onStateChanged;
236
259
  if (onStateChanged !== undefined) {
237
260
  // The agent applies STATE_SNAPSHOT / STATE_DELTA itself; subscribing is
@@ -270,7 +293,8 @@ export class AgUiClient {
270
293
  *
271
294
  * When the agent calls frontend tools, this executes them and re-runs the
272
295
  * agent with the results, looping until the agent stops calling frontend
273
- * tools (bounded by {@link MAX_TOOL_ROUNDS}).
296
+ * tools (bounded by {@link AgUiClientConfig.maxToolRounds}, which defaults to
297
+ * {@link MAX_TOOL_ROUNDS}).
274
298
  *
275
299
  * `attachments` ride on the user message as a non-standard field so the
276
300
  * default store round-trips them for history replay; see
@@ -389,7 +413,7 @@ export class AgUiClient {
389
413
  // continues an unfinished frontend-tool round after a page load; this stays
390
414
  // inside one #run().
391
415
  let resume: ResumeEntry[] | undefined;
392
- for (let round = 0; round < MAX_TOOL_ROUNDS; round += 1) {
416
+ for (let round = 0; round < this.#maxToolRounds; round += 1) {
393
417
  // A cancel during the previous round's tool execution lands here: the
394
418
  // running handler completed, but no further round starts.
395
419
  if (this.#cancelled) {
package/src/index.ts CHANGED
@@ -11,6 +11,7 @@ export {
11
11
  INVALIDATE_EVENT,
12
12
  LOAD_CAPABILITY_TOOL,
13
13
  MAX_TOOL_ROUNDS,
14
+ MESSAGE_ACTIONS,
14
15
  MESSAGE_ROLE,
15
16
  RUN_FINISHED_EVENT,
16
17
  STATE_EVENT,
@@ -193,8 +194,11 @@ export {
193
194
  export {
194
195
  type SettledStatus,
195
196
  ToolCallCard,
197
+ type ToolCallCardOptions,
196
198
  type ToolCallStatus,
197
199
  type ToolDisplayMode,
200
+ type ToolPayload,
201
+ type ToolPayloadFormatter,
198
202
  } from "./ui/tool_call_card.js";
199
203
  export { DEFAULT_UI_STRINGS, mergeUiStrings, type UiStrings } from "./ui/ui_strings.js";
200
204
  export { VERSION } from "./version.js";