@smooai/chat-widget 0.12.0 → 0.13.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/dist/index.d.ts CHANGED
@@ -134,6 +134,16 @@ interface ChatWidgetConfig {
134
134
  * `require*` flags are ignored and the pre-chat form is skipped.
135
135
  */
136
136
  allowAnonymous?: boolean;
137
+ /**
138
+ * Show the agent's tool activity (grep / read_file / bash / knowledge_search…)
139
+ * as inline chips interleaved with its prose, mirroring the smooth daemon SPA.
140
+ *
141
+ * Defaults to **`false`**: for a customer-facing support widget, surfacing raw
142
+ * tool calls to an end-user is usually undesirable, so tool activity is hidden
143
+ * and only the assistant's prose renders. Enable it for internal / power-user
144
+ * surfaces where seeing what the agent did mid-turn is valuable.
145
+ */
146
+ showToolActivity?: boolean;
137
147
  /** Theme overrides. */
138
148
  theme?: ChatWidgetTheme;
139
149
  }
@@ -219,6 +229,36 @@ declare function createWidgetStore(agentId: string): StoreApi<WidgetStore>;
219
229
  */
220
230
  declare function httpBaseFromWsEndpoint(endpoint: string): string | null;
221
231
  type Role = 'user' | 'assistant';
232
+ /**
233
+ * One tool invocation within an assistant turn. Mirrors the smooth daemon SPA's
234
+ * `ToolCall` (`crates/smooth-web/web/src/operator.ts`): opens `done: false` on the
235
+ * tool call and resolves on the tool result.
236
+ */
237
+ interface ToolCall {
238
+ /** Stable id for keyed rendering (assigned when the call opens). */
239
+ id: string;
240
+ name: string;
241
+ /** Raw arguments, JSON-stringified. */
242
+ args: string;
243
+ /** Present once the tool resolves. */
244
+ result?: string;
245
+ isError?: boolean;
246
+ done: boolean;
247
+ }
248
+ /**
249
+ * One ordered segment of an assistant turn: a run of prose, or a tool call.
250
+ * Preserves the interleave order the model produced (say a bit → call a tool →
251
+ * say a bit → …) so the UI can render tool chips INLINE where the model called
252
+ * them. Mirrors the daemon SPA's `MessageBlock`. Only populated when the widget
253
+ * is configured with `showToolActivity: true`.
254
+ */
255
+ type MessageBlock = {
256
+ kind: 'text';
257
+ text: string;
258
+ } | {
259
+ kind: 'tool';
260
+ tool: ToolCall;
261
+ };
222
262
  interface ChatMessage {
223
263
  id: string;
224
264
  role: Role;
@@ -226,6 +266,12 @@ interface ChatMessage {
226
266
  text: string;
227
267
  /** True while an assistant message is still streaming. */
228
268
  streaming: boolean;
269
+ /**
270
+ * Ordered text + tool segments, interleaved as the model produced them. Present
271
+ * only on assistant messages when `showToolActivity` is enabled (absent
272
+ * otherwise — the default popover renders `text` alone, byte-for-byte unchanged).
273
+ */
274
+ blocks?: MessageBlock[];
229
275
  /**
230
276
  * Sources that grounded an assistant answer, when the terminal
231
277
  * `eventual_response` carried any. Optional + back-compatible: absent when
@@ -542,6 +588,10 @@ declare class SmoothAgentChatElement extends HTMLElement {
542
588
  /** How many chars of {@link streamTarget} are currently shown. */
543
589
  private displayedLength;
544
590
  private rafId;
591
+ /** Block structure signature of the last-rendered streaming message (tool chips
592
+ * only — text growth doesn't change it), so a chip add/resolve forces a rebuild
593
+ * while plain trailing-text growth stays on the fast reveal path. */
594
+ private prevBlockSig;
545
595
  constructor();
546
596
  connectedCallback(): void;
547
597
  disconnectedCallback(): void;
@@ -605,6 +655,14 @@ declare class SmoothAgentChatElement extends HTMLElement {
605
655
  * rebuild via {@link renderMessages}.
606
656
  */
607
657
  private handleMessages;
658
+ /** True when an assistant message's turn invoked at least one tool. */
659
+ private hasToolBlocks;
660
+ /** Signature that changes only when a tool chip is added or resolved (text growth is 'x'). */
661
+ private blockSig;
662
+ /** The live (last) text block for a tool turn, else the whole message text. */
663
+ private tailText;
664
+ /** Reveal-binding key — composite for a tool-turn tail (so a new trailing block rebinds), else msg id. */
665
+ private tailKey;
608
666
  private renderMessages;
609
667
  /**
610
668
  * Render (or clear) the mid-conversation suggested-reply chips under the
@@ -624,6 +682,19 @@ declare class SmoothAgentChatElement extends HTMLElement {
624
682
  * doesn't restart the reveal from zero), then resumes the loop.
625
683
  */
626
684
  private bindReveal;
685
+ /**
686
+ * Render a tool-activity assistant turn as an ordered strip: prose bubbles and
687
+ * inline tool chips in the order the model produced them (mirrors the daemon
688
+ * SPA's `blocks`). The live trailing text block (while streaming) binds to the
689
+ * rAF reveal; earlier/finalized text blocks render as sanitized markdown.
690
+ */
691
+ private renderAssistantBlocks;
692
+ /**
693
+ * A single tool-activity chip: icon + tool name + status (running… / done / error),
694
+ * with a truncated args preview. Tool name/args are set via `textContent` so a
695
+ * tool payload can never inject markup.
696
+ */
697
+ private buildToolChip;
627
698
  /** Start the rAF loop if it isn't already running. */
628
699
  private ensureRevealLoop;
629
700
  /**
@@ -778,5 +849,5 @@ declare function computeFingerprint(): string;
778
849
  */
779
850
  declare function getOrCreateFingerprint(get: () => string | null, set: (fp: string) => void): string;
780
851
  //#endregion
781
- export { type ChatMessage, type ChatWidgetConfig, type ChatWidgetMode, type ChatWidgetTheme, type Citation, type ConnectionStatus, type ConsentState, ConversationController, type ConversationEvents, ELEMENT_TAG, type IdentityRestore, type IdentityState, PERSIST_VERSION, type PersistedWidgetState, type RestorableConversation, type Role, SmoothAgentChatElement, type UserInfo, type WidgetStore, computeFingerprint, createWidgetStore, defineChatWidget, getOrCreateFingerprint, httpBaseFromWsEndpoint, initChatWidgetLoader, mountChatWidget, mountFullPageChat, storageKey };
852
+ export { type ChatMessage, type ChatWidgetConfig, type ChatWidgetMode, type ChatWidgetTheme, type Citation, type ConnectionStatus, type ConsentState, ConversationController, type ConversationEvents, ELEMENT_TAG, type IdentityRestore, type IdentityState, type MessageBlock, PERSIST_VERSION, type PersistedWidgetState, type RestorableConversation, type Role, SmoothAgentChatElement, type ToolCall, type UserInfo, type WidgetStore, computeFingerprint, createWidgetStore, defineChatWidget, getOrCreateFingerprint, httpBaseFromWsEndpoint, initChatWidgetLoader, mountChatWidget, mountFullPageChat, storageKey };
782
853
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/config.ts","../src/persistence.ts","../src/conversation.ts","../src/element.ts","../src/loader-core.ts","../src/fingerprint.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/config.ts","../src/persistence.ts","../src/conversation.ts","../src/element.ts","../src/loader-core.ts","../src/fingerprint.ts"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -342,6 +342,7 @@ function resolveConfig(config) {
342
342
  collectConsent: config.collectConsent ?? true,
343
343
  allowChatRestore: config.allowChatRestore ?? true,
344
344
  allowAnonymous: config.allowAnonymous ?? false,
345
+ showToolActivity: config.showToolActivity ?? false,
345
346
  theme: {
346
347
  text: theme.text ?? "#f8fafc",
347
348
  background: theme.background ?? "#040d30",
@@ -807,6 +808,59 @@ function wireMessageToChat(m, idx) {
807
808
  streaming: false
808
809
  };
809
810
  }
811
+ let toolSeq = 0;
812
+ const nextToolId = () => `tool-${++toolSeq}`;
813
+ /** Grow the trailing text block, or open a new one if the last block was a tool. */
814
+ function growTextBlock(blocks, text) {
815
+ if (!text) return;
816
+ const last = blocks[blocks.length - 1];
817
+ if (last && last.kind === "text") last.text += text;
818
+ else blocks.push({
819
+ kind: "text",
820
+ text
821
+ });
822
+ }
823
+ /**
824
+ * Fold a `stream_chunk` node-state into the ordered block list, returning `true`
825
+ * when the chunk carried tool activity.
826
+ *
827
+ * Tool activity rides `state.rawResponse.toolCall` / `state.rawResponse.toolResult`
828
+ * — **NOT** `state.toolResult`. Reading the wrong path leaves every chip stuck on
829
+ * "running…" forever (the exact bug the daemon SPA hit and this mirror avoids).
830
+ */
831
+ function applyToolChunk(blocks, state) {
832
+ const raw = state?.rawResponse;
833
+ if (!raw || typeof raw !== "object") return false;
834
+ const call = raw.toolCall;
835
+ const res = raw.toolResult;
836
+ if (call) {
837
+ const args = typeof call.arguments === "string" ? call.arguments : JSON.stringify(call.arguments ?? {});
838
+ blocks.push({
839
+ kind: "tool",
840
+ tool: {
841
+ id: nextToolId(),
842
+ name: call.name ?? "",
843
+ args,
844
+ done: false
845
+ }
846
+ });
847
+ return true;
848
+ }
849
+ if (res) {
850
+ const result = typeof res.result === "string" ? res.result : JSON.stringify(res.result ?? "");
851
+ for (let i = blocks.length - 1; i >= 0; i--) {
852
+ const b = blocks[i];
853
+ if (b && b.kind === "tool" && b.tool.name === (res.name ?? "") && !b.tool.done) {
854
+ b.tool.done = true;
855
+ b.tool.isError = !!res.isError;
856
+ b.tool.result = result;
857
+ break;
858
+ }
859
+ }
860
+ return true;
861
+ }
862
+ return false;
863
+ }
810
864
  var ConversationController = class {
811
865
  constructor(config, events, store) {
812
866
  _defineProperty(this, "config", void 0);
@@ -1153,11 +1207,13 @@ var ConversationController = class {
1153
1207
  text: trimmed,
1154
1208
  streaming: false
1155
1209
  });
1210
+ const showTools = this.config.showToolActivity === true;
1156
1211
  const assistant = {
1157
1212
  id: this.nextId("a"),
1158
1213
  role: "assistant",
1159
1214
  text: "",
1160
- streaming: true
1215
+ streaming: true,
1216
+ blocks: showTools ? [] : void 0
1161
1217
  };
1162
1218
  this.messages.push(assistant);
1163
1219
  this.emitMessages();
@@ -1172,8 +1228,11 @@ var ConversationController = class {
1172
1228
  const token = event.token ?? event.data?.token ?? "";
1173
1229
  if (token) {
1174
1230
  assistant.text += token;
1231
+ if (showTools && assistant.blocks) growTextBlock(assistant.blocks, token);
1175
1232
  this.emitMessages();
1176
1233
  }
1234
+ } else if (showTools && event.type === "stream_chunk") {
1235
+ if (assistant.blocks && applyToolChunk(assistant.blocks, event.data?.state)) this.emitMessages();
1177
1236
  } else this.handleTurnEvent(event);
1178
1237
  const inner = (await turn).data?.data;
1179
1238
  const finalText = extractFinalText(inner?.response);
@@ -1183,6 +1242,7 @@ var ConversationController = class {
1183
1242
  if (citations.length > 0) assistant.citations = citations;
1184
1243
  const suggestions = extractSuggestions(inner?.response);
1185
1244
  if (suggestions.length > 0) assistant.suggestions = suggestions;
1245
+ if (assistant.blocks && !assistant.blocks.some((b) => b.kind === "tool")) assistant.blocks = void 0;
1186
1246
  assistant.streaming = false;
1187
1247
  this.emitMessages();
1188
1248
  } catch (err) {
@@ -1494,6 +1554,7 @@ function buildStyles(theme, mode = "popover") {
1494
1554
  --sac-bg: ${theme.background};
1495
1555
  --sac-primary: ${theme.primary};
1496
1556
  --sac-primary-text: ${theme.primaryText};
1557
+ --sac-secondary: ${theme.secondary};
1497
1558
  --sac-assistant-bubble: ${theme.assistantBubble};
1498
1559
  --sac-assistant-bubble-text: ${theme.assistantBubbleText};
1499
1560
  --sac-user-bubble: ${theme.userBubble};
@@ -1806,6 +1867,47 @@ ${mode === "fullpage" ? `
1806
1867
  }
1807
1868
  @keyframes sac-blink { to { opacity: 0 } }
1808
1869
 
1870
+ /* Interleaved tool-activity strip (gated by showToolActivity): prose bubbles
1871
+ and inline tool chips stacked in the order the model produced them. */
1872
+ .blocks { display: flex; flex-direction: column; align-items: flex-start; gap: 7px; }
1873
+ .blocks .bubble { align-self: flex-start; }
1874
+ .toolchip {
1875
+ display: inline-flex;
1876
+ align-items: center;
1877
+ gap: 6px;
1878
+ max-width: 100%;
1879
+ padding: 5px 10px;
1880
+ border-radius: 99px;
1881
+ font-size: 12px;
1882
+ line-height: 1.3;
1883
+ color: color-mix(in srgb, var(--sac-text) 78%, transparent);
1884
+ background: color-mix(in srgb, var(--sac-text) 6%, transparent);
1885
+ border: 1px solid color-mix(in srgb, var(--sac-text) 12%, transparent);
1886
+ animation: sac-msg-in .3s var(--sac-ease) both;
1887
+ }
1888
+ .toolchip .ti { display: inline-flex; flex: none; opacity: .8; }
1889
+ .toolchip .ti svg { width: 13px; height: 13px; }
1890
+ .toolchip .tn { font-weight: 600; letter-spacing: .01em; }
1891
+ .toolchip .ts { opacity: .7; }
1892
+ .toolchip .ta {
1893
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
1894
+ font-size: 11px;
1895
+ opacity: .6;
1896
+ overflow: hidden;
1897
+ text-overflow: ellipsis;
1898
+ white-space: nowrap;
1899
+ min-width: 0;
1900
+ }
1901
+ .toolchip.running { border-color: color-mix(in srgb, var(--sac-primary) 45%, transparent); }
1902
+ .toolchip.running .ts { color: var(--sac-primary); opacity: 1; animation: sac-typing 1.3s ease-in-out infinite; }
1903
+ .toolchip.done .ts::before { content: '✓ '; }
1904
+ .toolchip.error {
1905
+ color: var(--sac-secondary);
1906
+ border-color: color-mix(in srgb, var(--sac-secondary) 50%, transparent);
1907
+ background: color-mix(in srgb, var(--sac-secondary) 10%, transparent);
1908
+ }
1909
+ .toolchip.error .ts::before { content: '! '; }
1910
+
1809
1911
  /* ─────────────── Rendered markdown (assistant bubbles / snippets) ─────────── */
1810
1912
  /* The renderer (markdown.ts) emits a small allowlisted set of tags; these rules
1811
1913
  keep them legible inside the tight Aurora-Glass bubble + citation card. */
@@ -2248,7 +2350,8 @@ const OBSERVED = [
2248
2350
  "greeting",
2249
2351
  "start-open",
2250
2352
  "mode",
2251
- "hide-branding"
2353
+ "hide-branding",
2354
+ "show-tool-activity"
2252
2355
  ];
2253
2356
  /**
2254
2357
  * Inline SVG icons (static, trusted strings — never interpolated with user data).
@@ -2270,7 +2373,9 @@ const ICON = {
2270
2373
  /** Identity-intake interrupt — a person. */
2271
2374
  user: `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="8.2" r="3.4" stroke="currentColor" stroke-width="1.7"/><path d="M5.5 19.5c.8-3.1 3.4-4.8 6.5-4.8s5.7 1.7 6.5 4.8" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/></svg>`,
2272
2375
  /** Tool-confirmation interrupt — a shield. */
2273
- shield: `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 3 5 6v5c0 4.4 3 7.2 7 8.5 4-1.3 7-4.1 7-8.5V6l-7-3Z" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"/><path d="m9 11.5 2 2 4-4" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg>`
2376
+ shield: `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 3 5 6v5c0 4.4 3 7.2 7 8.5 4-1.3 7-4.1 7-8.5V6l-7-3Z" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"/><path d="m9 11.5 2 2 4-4" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/></svg>`,
2377
+ /** Tool-activity chip — a wrench. */
2378
+ tool: `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M14.7 6.3a3.5 3.5 0 0 0-4.6 4.3l-5 5a1.6 1.6 0 0 0 2.3 2.3l5-5a3.5 3.5 0 0 0 4.3-4.6l-2 2-1.7-.3-.3-1.7 2-2Z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/></svg>`
2274
2379
  };
2275
2380
  /**
2276
2381
  * The identity_intake card: the fields the agent requested (pre-chat form field
@@ -2436,6 +2541,7 @@ var SmoothAgentChatElement = class extends HTMLElement {
2436
2541
  _defineProperty(this, "streamTarget", "");
2437
2542
  _defineProperty(this, "displayedLength", 0);
2438
2543
  _defineProperty(this, "rafId", 0);
2544
+ _defineProperty(this, "prevBlockSig", "");
2439
2545
  this.root = this.attachShadow({ mode: "open" });
2440
2546
  }
2441
2547
  connectedCallback() {
@@ -2507,6 +2613,7 @@ var SmoothAgentChatElement = class extends HTMLElement {
2507
2613
  collectConsent: this.overrides.collectConsent,
2508
2614
  allowChatRestore: this.overrides.allowChatRestore,
2509
2615
  allowAnonymous: this.overrides.allowAnonymous,
2616
+ showToolActivity: this.overrides.showToolActivity ?? this.hasAttribute("show-tool-activity"),
2510
2617
  theme
2511
2618
  };
2512
2619
  }
@@ -3046,15 +3153,38 @@ var SmoothAgentChatElement = class extends HTMLElement {
3046
3153
  const prev = this.messages;
3047
3154
  const last = messages[messages.length - 1];
3048
3155
  const prevLast = prev[prev.length - 1];
3049
- const structural = messages.length !== prev.length || !last || !prevLast || last.id !== prevLast.id || last.role !== prevLast.role || last.streaming !== prevLast.streaming || !!last.streaming && !prevLast.text && !!last.text;
3156
+ const structural = messages.length !== prev.length || !last || !prevLast || last.id !== prevLast.id || last.role !== prevLast.role || last.streaming !== prevLast.streaming || !!last.streaming && !prevLast.text && !!last.text || this.blockSig(last) !== this.prevBlockSig;
3050
3157
  this.messages = messages;
3051
- if (!structural && last && last.streaming && last.id === this.streamMsgId) {
3052
- this.streamTarget = last.text;
3158
+ this.prevBlockSig = this.blockSig(last);
3159
+ if (!structural && last && last.streaming && this.tailKey(last) === this.streamMsgId) {
3160
+ this.streamTarget = this.tailText(last);
3053
3161
  this.ensureRevealLoop();
3054
3162
  return;
3055
3163
  }
3056
3164
  this.renderMessages();
3057
3165
  }
3166
+ /** True when an assistant message's turn invoked at least one tool. */
3167
+ hasToolBlocks(m) {
3168
+ return !!m?.blocks?.some((b) => b.kind === "tool");
3169
+ }
3170
+ /** Signature that changes only when a tool chip is added or resolved (text growth is 'x'). */
3171
+ blockSig(m) {
3172
+ if (!m?.blocks) return "";
3173
+ return m.blocks.map((b) => b.kind === "tool" ? `t:${b.tool.id}:${b.tool.done ? 1 : 0}` : "x").join("|");
3174
+ }
3175
+ /** The live (last) text block for a tool turn, else the whole message text. */
3176
+ tailText(m) {
3177
+ if (this.hasToolBlocks(m) && m.blocks) {
3178
+ const last = m.blocks[m.blocks.length - 1];
3179
+ return last?.kind === "text" ? last.text : "";
3180
+ }
3181
+ return m.text;
3182
+ }
3183
+ /** Reveal-binding key — composite for a tool-turn tail (so a new trailing block rebinds), else msg id. */
3184
+ tailKey(m) {
3185
+ if (this.hasToolBlocks(m) && m.blocks) return `${m.id}#${m.blocks.length - 1}`;
3186
+ return m.id;
3187
+ }
3058
3188
  renderMessages() {
3059
3189
  if (!this.messagesEl) return;
3060
3190
  this.resetReveal();
@@ -3075,6 +3205,11 @@ var SmoothAgentChatElement = class extends HTMLElement {
3075
3205
  this.messagesEl.appendChild(chips);
3076
3206
  }
3077
3207
  for (const msg of this.messages) {
3208
+ if (msg.role === "assistant" && this.hasToolBlocks(msg)) {
3209
+ this.messagesEl.appendChild(this.buildRow("assistant", this.renderAssistantBlocks(msg)));
3210
+ if (!msg.streaming && msg.citations && msg.citations.length > 0) this.messagesEl.appendChild(this.renderSources(msg.citations));
3211
+ continue;
3212
+ }
3078
3213
  const bubble = document.createElement("div");
3079
3214
  bubble.className = `bubble ${msg.role}`;
3080
3215
  if (msg.role === "assistant" && msg.streaming && !msg.text) {
@@ -3132,19 +3267,77 @@ var SmoothAgentChatElement = class extends HTMLElement {
3132
3267
  * doesn't restart the reveal from zero), then resumes the loop.
3133
3268
  */
3134
3269
  bindReveal(msg, bubble) {
3135
- const carryOver = msg.id === this.streamMsgId ? Math.min(this.displayedLength, msg.text.length) : 0;
3270
+ const key = this.tailKey(msg);
3271
+ const target = this.tailText(msg);
3272
+ const carryOver = key === this.streamMsgId ? Math.min(this.displayedLength, target.length) : 0;
3136
3273
  this.streamBubbleEl = bubble;
3137
- this.streamMsgId = msg.id;
3138
- this.streamTarget = msg.text;
3274
+ this.streamMsgId = key;
3275
+ this.streamTarget = target;
3139
3276
  this.displayedLength = carryOver;
3140
3277
  if (this.prefersReducedMotion()) {
3141
- this.displayedLength = msg.text.length;
3142
- bubble.textContent = msg.text;
3278
+ this.displayedLength = target.length;
3279
+ bubble.textContent = target;
3143
3280
  return;
3144
3281
  }
3145
- bubble.textContent = msg.text.slice(0, this.displayedLength);
3282
+ bubble.textContent = target.slice(0, this.displayedLength);
3146
3283
  this.ensureRevealLoop();
3147
3284
  }
3285
+ /**
3286
+ * Render a tool-activity assistant turn as an ordered strip: prose bubbles and
3287
+ * inline tool chips in the order the model produced them (mirrors the daemon
3288
+ * SPA's `blocks`). The live trailing text block (while streaming) binds to the
3289
+ * rAF reveal; earlier/finalized text blocks render as sanitized markdown.
3290
+ */
3291
+ renderAssistantBlocks(msg) {
3292
+ const wrap = document.createElement("div");
3293
+ wrap.className = "blocks";
3294
+ const blocks = msg.blocks ?? [];
3295
+ const lastIdx = blocks.length - 1;
3296
+ blocks.forEach((block, i) => {
3297
+ if (block.kind === "tool") {
3298
+ wrap.appendChild(this.buildToolChip(block.tool));
3299
+ return;
3300
+ }
3301
+ const bubble = document.createElement("div");
3302
+ bubble.className = "bubble assistant";
3303
+ if (msg.streaming && i === lastIdx) {
3304
+ bubble.classList.add("cursor");
3305
+ this.bindReveal(msg, bubble);
3306
+ } else {
3307
+ bubble.classList.add("md");
3308
+ bubble.innerHTML = renderMarkdown(block.text);
3309
+ }
3310
+ wrap.appendChild(bubble);
3311
+ });
3312
+ return wrap;
3313
+ }
3314
+ /**
3315
+ * A single tool-activity chip: icon + tool name + status (running… / done / error),
3316
+ * with a truncated args preview. Tool name/args are set via `textContent` so a
3317
+ * tool payload can never inject markup.
3318
+ */
3319
+ buildToolChip(tool) {
3320
+ const chip = document.createElement("div");
3321
+ chip.className = `toolchip ${tool.done ? tool.isError ? "error" : "done" : "running"}`;
3322
+ chip.setAttribute("part", "tool-chip");
3323
+ const icon = document.createElement("span");
3324
+ icon.className = "ti";
3325
+ icon.innerHTML = ICON.tool;
3326
+ const name = document.createElement("span");
3327
+ name.className = "tn";
3328
+ name.textContent = tool.name || "tool";
3329
+ const status = document.createElement("span");
3330
+ status.className = "ts";
3331
+ status.textContent = tool.done ? tool.isError ? "error" : "done" : "running…";
3332
+ chip.append(icon, name, status);
3333
+ if (tool.args && tool.args !== "{}" && tool.args !== "\"\"") {
3334
+ const args = document.createElement("span");
3335
+ args.className = "ta";
3336
+ args.textContent = tool.args.length > 80 ? `${tool.args.slice(0, 80)}…` : tool.args;
3337
+ chip.appendChild(args);
3338
+ }
3339
+ return chip;
3340
+ }
3148
3341
  /** Start the rAF loop if it isn't already running. */
3149
3342
  ensureRevealLoop() {
3150
3343
  if (this.prefersReducedMotion() || typeof requestAnimationFrame !== "function") {