@hachej/boring-agent 0.1.93 → 0.1.94

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.
@@ -68,7 +68,7 @@ function rawWorkspaceFileUrl(path, opts) {
68
68
  }
69
69
 
70
70
  // src/front/chat/PiChatPanel.tsx
71
- import { lazy, Suspense, useCallback as useCallback18, useEffect as useEffect21, useMemo as useMemo14, useRef as useRef18, useState as useState20 } from "react";
71
+ import { lazy, Suspense, useCallback as useCallback18, useEffect as useEffect21, useMemo as useMemo16, useRef as useRef18, useState as useState20 } from "react";
72
72
 
73
73
  // src/front/ArtifactOpenContext.tsx
74
74
  import { createContext, useContext } from "react";
@@ -346,7 +346,7 @@ var lineClass = {
346
346
  " ": ""
347
347
  };
348
348
  var prefixMap = { "+": "+", "-": "-", " ": " " };
349
- function DiffView({ oldString, newString, path, replaceAll }) {
349
+ function DiffView({ oldString, newString, path, replaceAll, actions }) {
350
350
  const [expanded, setExpanded] = useState(false);
351
351
  if (oldString === newString) {
352
352
  return /* @__PURE__ */ jsx3("div", { "data-testid": "diff-no-change", className: "py-2 italic opacity-60", children: "No changes" });
@@ -356,9 +356,12 @@ function DiffView({ oldString, newString, path, replaceAll }) {
356
356
  const collapsed = !expanded && lines.length > COLLAPSE_THRESHOLD;
357
357
  const visible = collapsed ? lines.slice(0, COLLAPSE_THRESHOLD) : lines;
358
358
  return /* @__PURE__ */ jsxs2("div", { "data-testid": "diff-view", children: [
359
- /* @__PURE__ */ jsxs2("div", { className: "px-3 py-1.5 font-[family-name:var(--boring-agent-font-mono,monospace)] text-xs opacity-70", children: [
360
- path,
361
- replaceAll ? " (replace all)" : ""
359
+ /* @__PURE__ */ jsxs2("div", { className: "flex min-h-7 items-center justify-between gap-2 px-2 py-0.5 font-[family-name:var(--boring-agent-font-mono,monospace)] text-xs opacity-70", children: [
360
+ /* @__PURE__ */ jsxs2("span", { className: "min-w-0 truncate", children: [
361
+ path,
362
+ replaceAll ? " (replace all)" : ""
363
+ ] }),
364
+ actions ? /* @__PURE__ */ jsx3("span", { className: "flex shrink-0 items-center gap-0.5", children: actions }) : null
362
365
  ] }),
363
366
  /* @__PURE__ */ jsx3("pre", { className: "m-0 overflow-auto py-2 font-[family-name:var(--boring-agent-font-mono,monospace)] text-[0.8125rem] leading-relaxed", children: visible.map((line, i) => /* @__PURE__ */ jsxs2("div", { className: cn("px-3", lineClass[line.type]), children: [
364
367
  /* @__PURE__ */ jsx3("span", { className: "inline-block w-[1.5ch] select-none opacity-50", children: prefixMap[line.type] }),
@@ -1264,9 +1267,9 @@ var statusIcons = {
1264
1267
  "output-error": /* @__PURE__ */ jsx9(XCircleIcon, { className: "size-4 text-destructive" }),
1265
1268
  aborted: /* @__PURE__ */ jsx9(XCircleIcon, { className: "size-4 text-muted-foreground" })
1266
1269
  };
1267
- var getStatusBadge = (status) => /* @__PURE__ */ jsxs8(Badge, { className: "shrink-0 gap-1.5 rounded-full text-xs", variant: "secondary", children: [
1270
+ var getStatusBadge = (status, label = statusLabels[status]) => /* @__PURE__ */ jsxs8(Badge, { className: "shrink-0 gap-1.5 rounded-full text-xs", variant: "secondary", children: [
1268
1271
  statusIcons[status],
1269
- statusLabels[status]
1272
+ label
1270
1273
  ] });
1271
1274
  var ToolHeader = ({
1272
1275
  className,
@@ -1275,6 +1278,7 @@ var ToolHeader = ({
1275
1278
  state,
1276
1279
  icon,
1277
1280
  toolName,
1281
+ statusLabel,
1278
1282
  ...props
1279
1283
  }) => {
1280
1284
  const derivedName = type === "dynamic-tool" || type === "tool-call" ? toolName : type.split("-").slice(1).join("-");
@@ -1291,7 +1295,7 @@ var ToolHeader = ({
1291
1295
  /* @__PURE__ */ jsxs8("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
1292
1296
  icon ?? /* @__PURE__ */ jsx9(WrenchIcon, { className: "size-4 shrink-0 text-muted-foreground" }),
1293
1297
  /* @__PURE__ */ jsx9("span", { "data-boring-agent-part": "tool-title", className: "min-w-0 flex-1 truncate text-sm font-medium", children: title ?? derivedName }),
1294
- getStatusBadge(state)
1298
+ getStatusBadge(state, statusLabel)
1295
1299
  ] }),
1296
1300
  /* @__PURE__ */ jsx9(
1297
1301
  ChevronDownIcon,
@@ -1357,7 +1361,7 @@ var ToolOutput = ({
1357
1361
 
1358
1362
  // src/front/toolRenderers.tsx
1359
1363
  import { CollapsibleTrigger as CollapsibleTrigger2 } from "@hachej/boring-ui-kit";
1360
- import { ChevronDownIcon as ChevronDownIcon2, ExternalLinkIcon, FileDiffIcon, FilePlus2Icon, FileTextIcon, SearchIcon, SquareTerminalIcon, ZapIcon } from "lucide-react";
1364
+ import { ChevronDownIcon as ChevronDownIcon2, CopyIcon as CopyIcon2, DownloadIcon, ExternalLinkIcon, FileDiffIcon, FilePlus2Icon, FileTextIcon, SearchIcon, SquareTerminalIcon, ZapIcon } from "lucide-react";
1361
1365
 
1362
1366
  // src/front/primitives/artifact.tsx
1363
1367
  import { Button as Button6 } from "@hachej/boring-ui-kit";
@@ -1369,44 +1373,6 @@ import {
1369
1373
  } from "@hachej/boring-ui-kit";
1370
1374
  import { XIcon } from "lucide-react";
1371
1375
  import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
1372
- var Artifact = ({ className, ...props }) => /* @__PURE__ */ jsx10(
1373
- "div",
1374
- {
1375
- className: cn(
1376
- "flex flex-col overflow-hidden rounded-lg border bg-background shadow-sm",
1377
- className
1378
- ),
1379
- ...props
1380
- }
1381
- );
1382
- var ArtifactHeader = ({
1383
- className,
1384
- ...props
1385
- }) => /* @__PURE__ */ jsx10(
1386
- "div",
1387
- {
1388
- className: cn(
1389
- "flex items-center justify-between border-b bg-muted/50 px-4 py-3",
1390
- className
1391
- ),
1392
- ...props
1393
- }
1394
- );
1395
- var ArtifactTitle = ({ className, ...props }) => /* @__PURE__ */ jsx10(
1396
- "p",
1397
- {
1398
- className: cn("font-medium text-foreground text-sm", className),
1399
- ...props
1400
- }
1401
- );
1402
- var ArtifactDescription = ({
1403
- className,
1404
- ...props
1405
- }) => /* @__PURE__ */ jsx10("p", { className: cn("text-muted-foreground text-sm", className), ...props });
1406
- var ArtifactActions = ({
1407
- className,
1408
- ...props
1409
- }) => /* @__PURE__ */ jsx10("div", { className: cn("flex items-center gap-1", className), ...props });
1410
1376
  var ArtifactAction = ({
1411
1377
  tooltip,
1412
1378
  label,
@@ -1442,13 +1408,6 @@ var ArtifactAction = ({
1442
1408
  }
1443
1409
  return button;
1444
1410
  };
1445
- var ArtifactContent = ({
1446
- className,
1447
- ...props
1448
- }) => /* @__PURE__ */ jsx10("div", { className: cn("flex-1 overflow-auto p-4", className), ...props });
1449
-
1450
- // src/front/toolRenderers.tsx
1451
- import { CopyIcon as CopyIcon2, DownloadIcon } from "lucide-react";
1452
1411
 
1453
1412
  // src/front/runtimeReadinessStatus.ts
1454
1413
  var COPY = {
@@ -1514,7 +1473,7 @@ function getWorkspaceNotReadyStatus(output) {
1514
1473
  }
1515
1474
 
1516
1475
  // src/front/toolRenderers.tsx
1517
- import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
1476
+ import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
1518
1477
  function asRecord2(value) {
1519
1478
  return typeof value === "object" && value !== null ? value : {};
1520
1479
  }
@@ -1672,59 +1631,44 @@ function renderWrite2(part) {
1672
1631
  const path = typeof input.path === "string" ? input.path : "";
1673
1632
  const filesystem = typeof input.filesystem === "string" ? input.filesystem : void 0;
1674
1633
  const content = typeof input.content === "string" ? input.content : "";
1675
- const bytes = content.length;
1676
- const lang = langFromPath(path);
1677
- return /* @__PURE__ */ jsxs10(Tool2, { children: [
1678
- /* @__PURE__ */ jsx11(ToolHeader, { icon: /* @__PURE__ */ jsx11(FilePlus2Icon, { className: "size-4 text-muted-foreground" }), title: pathTitle("write", path, filesystem), ...toHeaderProps(part) }),
1679
- /* @__PURE__ */ jsx11(ToolContent, { children: /* @__PURE__ */ jsxs10(Artifact, { className: "rounded-none border-0 bg-transparent shadow-none", children: [
1680
- /* @__PURE__ */ jsxs10(ArtifactHeader, { className: "border-0 px-0 pt-0 pb-2", children: [
1681
- /* @__PURE__ */ jsxs10("div", { children: [
1682
- /* @__PURE__ */ jsx11(ArtifactTitle, { children: path || "untitled" }),
1683
- /* @__PURE__ */ jsxs10(ArtifactDescription, { children: [
1684
- bytes.toLocaleString(),
1685
- " ",
1686
- bytes === 1 ? "byte" : "bytes",
1687
- lang ? ` \xB7 ${lang}` : ""
1688
- ] })
1689
- ] }),
1690
- /* @__PURE__ */ jsxs10(ArtifactActions, { children: [
1691
- /* @__PURE__ */ jsx11(
1692
- ArtifactAction,
1693
- {
1694
- icon: CopyIcon2,
1695
- tooltip: "Copy contents",
1696
- label: "Copy",
1697
- onClick: () => {
1698
- void copyTextToClipboard(content);
1699
- }
1700
- }
1701
- ),
1702
- /* @__PURE__ */ jsx11(
1703
- ArtifactAction,
1704
- {
1705
- icon: DownloadIcon,
1706
- tooltip: "Download file",
1707
- label: "Download",
1708
- onClick: () => {
1709
- try {
1634
+ return /* @__PURE__ */ jsxs10(Tool2, { defaultOpen: part.state === "output-available", children: [
1635
+ /* @__PURE__ */ jsx11(ToolHeader, { className: "p-2", icon: /* @__PURE__ */ jsx11(FilePlus2Icon, { className: "size-4 shrink-0 text-muted-foreground" }), title: pathTitle("write", path, filesystem), ...toHeaderProps(part) }),
1636
+ /* @__PURE__ */ jsxs10(ToolContent, { className: "p-1.5", children: [
1637
+ /* @__PURE__ */ jsx11("div", { className: "max-h-80 overflow-auto overscroll-contain rounded-sm", children: content ? /* @__PURE__ */ jsx11(
1638
+ DiffView,
1639
+ {
1640
+ oldString: "",
1641
+ newString: content,
1642
+ path,
1643
+ actions: /* @__PURE__ */ jsxs10(Fragment3, { children: [
1644
+ /* @__PURE__ */ jsx11(ArtifactAction, { className: "size-6", icon: CopyIcon2, tooltip: "Copy contents", label: "Copy", onClick: () => {
1645
+ void copyTextToClipboard(content);
1646
+ } }),
1647
+ /* @__PURE__ */ jsx11(
1648
+ ArtifactAction,
1649
+ {
1650
+ className: "size-6",
1651
+ icon: DownloadIcon,
1652
+ tooltip: "Download file",
1653
+ label: "Download",
1654
+ onClick: () => {
1710
1655
  const blob = new Blob([content], { type: "text/plain" });
1711
1656
  const url = URL.createObjectURL(blob);
1712
- const a = document.createElement("a");
1713
- a.href = url;
1714
- a.download = path.split("/").pop() || "artifact.txt";
1715
- document.body.appendChild(a);
1716
- a.click();
1717
- a.remove();
1657
+ const anchor = document.createElement("a");
1658
+ anchor.href = url;
1659
+ anchor.download = path.split("/").pop() || "artifact.txt";
1660
+ document.body.appendChild(anchor);
1661
+ anchor.click();
1662
+ anchor.remove();
1718
1663
  URL.revokeObjectURL(url);
1719
- } catch {
1720
1664
  }
1721
1665
  }
1722
- }
1723
- )
1724
- ] })
1725
- ] }),
1726
- content && /* @__PURE__ */ jsx11(ArtifactContent, { className: "p-0", children: /* @__PURE__ */ jsx11(CodeBlock2, { code: content, language: lang ?? "text", showLineNumbers: true }) })
1727
- ] }) })
1666
+ )
1667
+ ] })
1668
+ }
1669
+ ) : /* @__PURE__ */ jsx11("p", { className: "px-2 py-1 text-[12px] text-muted-foreground", children: "No file content was included with this tool call." }) }),
1670
+ part.errorText || part.state === "output-error" || part.state === "output-denied" ? /* @__PURE__ */ jsx11(ToolOutput, { output: part.output, errorText: part.errorText }) : null
1671
+ ] })
1728
1672
  ] });
1729
1673
  }
1730
1674
  function renderEdit2(part) {
@@ -1733,25 +1677,26 @@ function renderEdit2(part) {
1733
1677
  const input = asRecord2(part.input);
1734
1678
  const path = typeof input.path === "string" ? input.path : "";
1735
1679
  const filesystem = typeof input.filesystem === "string" ? input.filesystem : void 0;
1736
- const oldString = typeof input.oldString === "string" ? input.oldString : "";
1737
- const newString = typeof input.newString === "string" ? input.newString : "";
1738
- return /* @__PURE__ */ jsxs10(Tool2, { children: [
1739
- /* @__PURE__ */ jsx11(ToolHeader, { icon: /* @__PURE__ */ jsx11(FileDiffIcon, { className: "size-4 text-muted-foreground" }), title: pathTitle("edit", path, filesystem), ...toHeaderProps(part) }),
1740
- /* @__PURE__ */ jsxs10(ToolContent, { children: [
1741
- /* @__PURE__ */ jsxs10("section", { className: "space-y-2", children: [
1742
- /* @__PURE__ */ jsx11("h4", { className: "font-medium text-muted-foreground text-xs uppercase tracking-wide", children: "Diff" }),
1743
- /* @__PURE__ */ jsxs10("div", { className: "overflow-hidden rounded-sm border border-input/60 bg-muted/30 font-mono text-[13px] leading-relaxed", children: [
1744
- oldString && /* @__PURE__ */ jsxs10("div", { className: "grid grid-cols-[auto_1fr] gap-2 border-b border-input/40 bg-destructive/5 px-3 py-2", children: [
1745
- /* @__PURE__ */ jsx11("span", { className: "select-none text-destructive/70", children: "-" }),
1746
- /* @__PURE__ */ jsx11("span", { className: cn("whitespace-pre-wrap break-all text-destructive/90"), children: oldString })
1747
- ] }),
1748
- newString && /* @__PURE__ */ jsxs10("div", { className: "grid grid-cols-[auto_1fr] gap-2 bg-accent/5 px-3 py-2", children: [
1749
- /* @__PURE__ */ jsx11("span", { className: "select-none text-accent/80", children: "+" }),
1750
- /* @__PURE__ */ jsx11("span", { className: "whitespace-pre-wrap break-all text-accent", children: newString })
1751
- ] })
1752
- ] })
1753
- ] }),
1754
- /* @__PURE__ */ jsx11(ToolOutput, { output: part.output, errorText: part.errorText })
1680
+ const edits = Array.isArray(input.edits) ? input.edits.flatMap((value) => {
1681
+ const edit = asRecord2(value);
1682
+ const oldText = typeof edit.oldText === "string" ? edit.oldText : "";
1683
+ const newText = typeof edit.newText === "string" ? edit.newText : "";
1684
+ return oldText || newText ? [{ oldText, newText }] : [];
1685
+ }) : [];
1686
+ const legacyOldText = typeof input.oldString === "string" ? input.oldString : typeof input.oldText === "string" ? input.oldText : "";
1687
+ const legacyNewText = typeof input.newString === "string" ? input.newString : typeof input.newText === "string" ? input.newText : "";
1688
+ const visibleEdits = edits.length > 0 ? edits : legacyOldText || legacyNewText ? [{ oldText: legacyOldText, newText: legacyNewText }] : [];
1689
+ return /* @__PURE__ */ jsxs10(Tool2, { defaultOpen: part.state === "output-available", children: [
1690
+ /* @__PURE__ */ jsx11(ToolHeader, { className: "p-2", icon: /* @__PURE__ */ jsx11(FileDiffIcon, { className: "size-4 shrink-0 text-muted-foreground" }), title: pathTitle("edit", path, filesystem), ...toHeaderProps(part) }),
1691
+ /* @__PURE__ */ jsxs10(ToolContent, { className: "space-y-2 p-2", children: [
1692
+ /* @__PURE__ */ jsx11("div", { className: "max-h-80 space-y-2 overflow-auto overscroll-contain rounded-sm", children: visibleEdits.length > 0 ? visibleEdits.map((edit, index) => /* @__PURE__ */ jsxs10("div", { children: [
1693
+ visibleEdits.length > 1 ? /* @__PURE__ */ jsxs10("div", { className: "mb-1 px-1 text-[11px] text-muted-foreground", children: [
1694
+ "Change ",
1695
+ index + 1
1696
+ ] }) : null,
1697
+ /* @__PURE__ */ jsx11(DiffView, { oldString: edit.oldText, newString: edit.newText, path })
1698
+ ] }, `${index}:${edit.oldText.length}:${edit.newText.length}`)) : /* @__PURE__ */ jsx11("p", { className: "px-2 py-1 text-[12px] text-muted-foreground", children: "Diff details were not included with this tool call." }) }),
1699
+ part.errorText || part.state === "output-error" || part.state === "output-denied" ? /* @__PURE__ */ jsx11(ToolOutput, { output: part.output, errorText: part.errorText }) : null
1755
1700
  ] })
1756
1701
  ] });
1757
1702
  }
@@ -2027,7 +1972,7 @@ function usePickerKeyboard({
2027
1972
  }
2028
1973
 
2029
1974
  // src/front/primitives/mention-picker.tsx
2030
- import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
1975
+ import { Fragment as Fragment4, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
2031
1976
  var mentionSearchGlob = toFileSearchGlob;
2032
1977
  function highlight(text, query) {
2033
1978
  if (!query) return null;
@@ -2090,7 +2035,7 @@ function MentionPicker({
2090
2035
  onSelect(path);
2091
2036
  },
2092
2037
  children: [
2093
- /* @__PURE__ */ jsx12("span", { className: "font-medium", children: hl ? /* @__PURE__ */ jsxs11(Fragment3, { children: [
2038
+ /* @__PURE__ */ jsx12("span", { className: "font-medium", children: hl ? /* @__PURE__ */ jsxs11(Fragment4, { children: [
2094
2039
  hl.before,
2095
2040
  /* @__PURE__ */ jsx12("mark", { className: "bg-transparent text-[color:var(--accent)]", children: hl.match }),
2096
2041
  hl.after
@@ -2496,10 +2441,11 @@ var PiComposerPolicyController = class {
2496
2441
  }));
2497
2442
  }
2498
2443
  async runLocalCommand(commandName, args) {
2499
- if (isPiChatBusy(this.options.session.getState().status)) {
2444
+ const command = this.options.registry.get(commandName);
2445
+ if (isPiChatBusy(this.options.session.getState().status) && command?.allowWhileBusy?.(args) !== true) {
2500
2446
  return this.block("busy-slash-command", "Slash commands are not queued while the agent is responding.");
2501
2447
  }
2502
- const result = await Promise.resolve(this.options.registry.get(commandName)?.handler(args, this.options.slashContext));
2448
+ const result = await Promise.resolve(command?.handler(args, this.options.slashContext));
2503
2449
  const message = typeof result === "string" ? result : result && typeof result === "object" ? result.message : void 0;
2504
2450
  const preserveDraft = Boolean(result && typeof result === "object" && result.preserveDraft === true);
2505
2451
  if (message) this.options.onCommandResult?.(message);
@@ -2912,6 +2858,25 @@ function composerNoticeForRuntimeDependencies(status) {
2912
2858
  return null;
2913
2859
  }
2914
2860
 
2861
+ // src/front/chat/composerContributions.tsx
2862
+ import { createContext as createContext3, useContext as useContext3, useMemo as useMemo3 } from "react";
2863
+ import { jsx as jsx13 } from "react/jsx-runtime";
2864
+ var ComposerContributionContext = createContext3([]);
2865
+ function ComposerContributionProvider({
2866
+ contribution,
2867
+ children
2868
+ }) {
2869
+ const parent = useContext3(ComposerContributionContext);
2870
+ const contributions = useMemo3(
2871
+ () => [contribution, ...parent.filter((entry) => entry.id !== contribution.id)],
2872
+ [contribution, parent]
2873
+ );
2874
+ return /* @__PURE__ */ jsx13(ComposerContributionContext.Provider, { value: contributions, children });
2875
+ }
2876
+ function useComposerContributions() {
2877
+ return useContext3(ComposerContributionContext);
2878
+ }
2879
+
2915
2880
  // src/front/chat/pi/piChatMessageMetadata.ts
2916
2881
  function earliestCreatedAt(left, right) {
2917
2882
  if (!left) return right;
@@ -5159,7 +5124,7 @@ function resolveStorage2(storage) {
5159
5124
  }
5160
5125
 
5161
5126
  // src/front/chat/session/usePiSessions.ts
5162
- import { useCallback as useCallback7, useEffect as useEffect8, useMemo as useMemo3, useRef as useRef8, useState as useState10 } from "react";
5127
+ import { useCallback as useCallback7, useEffect as useEffect8, useMemo as useMemo4, useRef as useRef8, useState as useState10 } from "react";
5163
5128
  var DEFAULT_SESSIONS_API_PATH = "/api/v1/agent/pi-chat/sessions";
5164
5129
  var SESSION_PAGE_SIZE = 50;
5165
5130
  var DEFAULT_MAX_RETRIES = 60;
@@ -5180,16 +5145,16 @@ function usePiSessions(options = {}) {
5180
5145
  const addressed = typeof options.agentTypeId === "string" && options.agentTypeId.length > 0;
5181
5146
  const sessionsApiPath = options.sessionsApiPath ?? (addressed ? `/api/v1/agents/${encodeURIComponent(options.agentTypeId)}/sessions` : DEFAULT_SESSIONS_API_PATH);
5182
5147
  const storageScope = options.storageScope ?? "default";
5183
- const fetchImpl = useMemo3(() => options.fetch ?? globalThis.fetch.bind(globalThis), [options.fetch]);
5148
+ const fetchImpl = useMemo4(() => options.fetch ?? globalThis.fetch.bind(globalThis), [options.fetch]);
5184
5149
  const createRemoteSession = options.createRemoteSession ?? createRemotePiSession;
5185
5150
  const connectActiveSession = options.connectActiveSession ?? true;
5186
5151
  const retryMaxRetries = options.retry?.maxRetries ?? DEFAULT_MAX_RETRIES;
5187
5152
  const retryBaseMs = options.retry?.baseMs ?? DEFAULT_RETRY_BASE_MS;
5188
5153
  const retryMaxMs = options.retry?.maxMs ?? DEFAULT_RETRY_MAX_MS;
5189
- const headersKey = useMemo3(() => headersScopeKey(options.requestHeaders, storageScope), [options.requestHeaders, storageScope]);
5190
- const normalizedHeaders = useMemo3(() => buildRequestHeaders(options.requestHeaders, storageScope), [headersKey, storageScope]);
5191
- const requestScopeKey = useMemo3(() => requestScopeIdentity(apiBaseUrl, sessionsApiPath, storageScope, headersKey), [apiBaseUrl, headersKey, sessionsApiPath, storageScope]);
5192
- const dataSourceKey = useMemo3(() => dataSourceIdentity(apiBaseUrl, sessionsApiPath, storageScope), [apiBaseUrl, sessionsApiPath, storageScope]);
5154
+ const headersKey = useMemo4(() => headersScopeKey(options.requestHeaders, storageScope), [options.requestHeaders, storageScope]);
5155
+ const normalizedHeaders = useMemo4(() => buildRequestHeaders(options.requestHeaders, storageScope), [headersKey, storageScope]);
5156
+ const requestScopeKey = useMemo4(() => requestScopeIdentity(apiBaseUrl, sessionsApiPath, storageScope, headersKey), [apiBaseUrl, headersKey, sessionsApiPath, storageScope]);
5157
+ const dataSourceKey = useMemo4(() => dataSourceIdentity(apiBaseUrl, sessionsApiPath, storageScope), [apiBaseUrl, sessionsApiPath, storageScope]);
5193
5158
  const [sessions, setSessions] = useState10([]);
5194
5159
  const [dataStorageScope, setDataStorageScope] = useState10(storageScope);
5195
5160
  const [activeSessionId, setActiveSessionId] = useState10(() => options.initialActiveSessionId ?? readActiveSessionId({ storageScope, storage: options.storage }));
@@ -5216,7 +5181,7 @@ function usePiSessions(options = {}) {
5216
5181
  requestScopeRef.current = requestScopeKey;
5217
5182
  const remoteSessionOptionsRef = useRef8(options.remoteSessionOptions);
5218
5183
  remoteSessionOptionsRef.current = options.remoteSessionOptions;
5219
- const remoteSessionOptionsKey = useMemo3(
5184
+ const remoteSessionOptionsKey = useMemo4(
5220
5185
  () => remoteSessionOptionsIdentity(options.remoteSessionOptions),
5221
5186
  [options.remoteSessionOptions]
5222
5187
  );
@@ -5658,10 +5623,10 @@ function clearRetryTimer(ref) {
5658
5623
  }
5659
5624
 
5660
5625
  // src/front/chat/session/SessionList.tsx
5661
- import { useMemo as useMemo4 } from "react";
5626
+ import { useMemo as useMemo5 } from "react";
5662
5627
  import { ChevronLeftIcon, PlusIcon, Trash2Icon } from "lucide-react";
5663
5628
  import { IconButton } from "@hachej/boring-ui-kit";
5664
- import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
5629
+ import { Fragment as Fragment5, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
5665
5630
  var DAY_MS = 24 * 60 * 60 * 1e3;
5666
5631
  function SessionList({
5667
5632
  sessions,
@@ -5676,7 +5641,7 @@ function SessionList({
5676
5641
  onClose,
5677
5642
  className
5678
5643
  }) {
5679
- const groups = useMemo4(() => groupSessions(sessions), [sessions]);
5644
+ const groups = useMemo5(() => groupSessions(sessions), [sessions]);
5680
5645
  return /* @__PURE__ */ jsxs12(
5681
5646
  "div",
5682
5647
  {
@@ -5687,26 +5652,26 @@ function SessionList({
5687
5652
  "aria-busy": loading ? "true" : void 0,
5688
5653
  children: [
5689
5654
  /* @__PURE__ */ jsxs12("div", { className: "flex h-11 items-center justify-between border-b border-border/60 px-3.5", children: [
5690
- /* @__PURE__ */ jsx13("span", { className: "text-[12px] font-medium tracking-tight text-foreground/70", children: "Sessions" }),
5655
+ /* @__PURE__ */ jsx14("span", { className: "text-[12px] font-medium tracking-tight text-foreground/70", children: "Sessions" }),
5691
5656
  /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-0.5", children: [
5692
- onCreate ? /* @__PURE__ */ jsx13(IconButton, { type: "button", variant: "ghost", size: "icon-xs", onClick: onCreate, "aria-label": "New session", title: "New chat", children: /* @__PURE__ */ jsx13(PlusIcon, { className: "h-3.5 w-3.5", strokeWidth: 1.75 }) }) : null,
5693
- onClose ? /* @__PURE__ */ jsx13(IconButton, { type: "button", variant: "ghost", size: "icon-xs", onClick: onClose, "aria-label": "Close sessions", title: "Close sessions", children: /* @__PURE__ */ jsx13(ChevronLeftIcon, { className: "h-4 w-4", strokeWidth: 1.75 }) }) : null
5657
+ onCreate ? /* @__PURE__ */ jsx14(IconButton, { type: "button", variant: "ghost", size: "icon-xs", onClick: onCreate, "aria-label": "New session", title: "New chat", children: /* @__PURE__ */ jsx14(PlusIcon, { className: "h-3.5 w-3.5", strokeWidth: 1.75 }) }) : null,
5658
+ onClose ? /* @__PURE__ */ jsx14(IconButton, { type: "button", variant: "ghost", size: "icon-xs", onClick: onClose, "aria-label": "Close sessions", title: "Close sessions", children: /* @__PURE__ */ jsx14(ChevronLeftIcon, { className: "h-4 w-4", strokeWidth: 1.75 }) }) : null
5694
5659
  ] })
5695
5660
  ] }),
5696
5661
  /* @__PURE__ */ jsxs12("div", { className: "flex-1 overflow-y-auto py-2.5", children: [
5697
5662
  sessions.length === 0 ? /* @__PURE__ */ jsxs12("div", { "data-boring-agent-part": "session-list-empty", className: "px-3 py-8 text-center text-[13px] text-muted-foreground", children: [
5698
5663
  loading ? "Loading sessions\u2026" : "No sessions yet.",
5699
- !loading ? /* @__PURE__ */ jsxs12(Fragment4, { children: [
5700
- /* @__PURE__ */ jsx13("br", {}),
5664
+ !loading ? /* @__PURE__ */ jsxs12(Fragment5, { children: [
5665
+ /* @__PURE__ */ jsx14("br", {}),
5701
5666
  "Start a new chat to begin."
5702
5667
  ] }) : null
5703
5668
  ] }) : null,
5704
5669
  groups.map((group, index) => /* @__PURE__ */ jsxs12("section", { className: cn(index > 0 && "mt-4"), children: [
5705
5670
  /* @__PURE__ */ jsxs12("div", { className: "flex items-baseline justify-between gap-2 px-3.5 pb-2 pt-2 text-[11px] font-medium tracking-tight text-muted-foreground/75", children: [
5706
- /* @__PURE__ */ jsx13("span", { children: group.label }),
5707
- /* @__PURE__ */ jsx13("span", { "aria-hidden": "true", className: "text-[10.5px] tabular-nums text-muted-foreground/40", children: group.items.length })
5671
+ /* @__PURE__ */ jsx14("span", { children: group.label }),
5672
+ /* @__PURE__ */ jsx14("span", { "aria-hidden": "true", className: "text-[10.5px] tabular-nums text-muted-foreground/40", children: group.items.length })
5708
5673
  ] }),
5709
- /* @__PURE__ */ jsx13("ul", { role: "list", className: "flex flex-col", children: group.items.map((session) => /* @__PURE__ */ jsx13(
5674
+ /* @__PURE__ */ jsx14("ul", { role: "list", className: "flex flex-col", children: group.items.map((session) => /* @__PURE__ */ jsx14(
5710
5675
  SessionRow,
5711
5676
  {
5712
5677
  session,
@@ -5717,7 +5682,7 @@ function SessionList({
5717
5682
  session.id
5718
5683
  )) })
5719
5684
  ] }, group.key)),
5720
- hasMore && onLoadMore ? /* @__PURE__ */ jsx13("div", { className: "px-3 py-3", children: /* @__PURE__ */ jsx13(
5685
+ hasMore && onLoadMore ? /* @__PURE__ */ jsx14("div", { className: "px-3 py-3", children: /* @__PURE__ */ jsx14(
5721
5686
  "button",
5722
5687
  {
5723
5688
  type: "button",
@@ -5749,10 +5714,10 @@ function SessionRow({ session, active, onSwitch, onDelete }) {
5749
5714
  onClick: () => onSwitch?.(session.id),
5750
5715
  children: [
5751
5716
  /* @__PURE__ */ jsxs12("span", { className: "min-w-0 flex-1 truncate leading-5", title: session.title, children: [
5752
- /* @__PURE__ */ jsx13("span", { className: cn(active ? "font-medium text-foreground" : "text-foreground/90"), children: session.title || "Untitled" }),
5753
- time ? /* @__PURE__ */ jsx13("span", { className: cn("ml-1.5 tabular-nums text-[11px]", active ? "text-[color:var(--accent)]" : "text-muted-foreground/60"), children: time }) : null
5717
+ /* @__PURE__ */ jsx14("span", { className: cn(active ? "font-medium text-foreground" : "text-foreground/90"), children: session.title || "Untitled" }),
5718
+ time ? /* @__PURE__ */ jsx14("span", { className: cn("ml-1.5 tabular-nums text-[11px]", active ? "text-[color:var(--accent)]" : "text-muted-foreground/60"), children: time }) : null
5754
5719
  ] }),
5755
- onDelete ? /* @__PURE__ */ jsx13(
5720
+ onDelete ? /* @__PURE__ */ jsx14(
5756
5721
  IconButton,
5757
5722
  {
5758
5723
  type: "button",
@@ -5764,7 +5729,7 @@ function SessionRow({ session, active, onSwitch, onDelete }) {
5764
5729
  onDelete(session.id);
5765
5730
  },
5766
5731
  "aria-label": `Delete ${session.title || "session"}`,
5767
- children: /* @__PURE__ */ jsx13(Trash2Icon, { className: "h-3 w-3" })
5732
+ children: /* @__PURE__ */ jsx14(Trash2Icon, { className: "h-3 w-3" })
5768
5733
  }
5769
5734
  ) : null
5770
5735
  ]
@@ -6019,7 +5984,7 @@ import { Button as Button7 } from "@hachej/boring-ui-kit";
6019
5984
  import { ArrowDownIcon, DownloadIcon as DownloadIcon2 } from "lucide-react";
6020
5985
  import { useCallback as useCallback8, useEffect as useEffect9 } from "react";
6021
5986
  import { StickToBottom, useStickToBottomContext } from "use-stick-to-bottom";
6022
- import { Fragment as Fragment5, jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
5987
+ import { Fragment as Fragment6, jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
6023
5988
  var ConversationScrollController = ({
6024
5989
  onScrollToBottomReady
6025
5990
  }) => {
@@ -6045,7 +6010,7 @@ var Conversation = ({
6045
6010
  role: "log",
6046
6011
  ...props,
6047
6012
  children: [
6048
- /* @__PURE__ */ jsx14(ConversationScrollController, { onScrollToBottomReady }),
6013
+ /* @__PURE__ */ jsx15(ConversationScrollController, { onScrollToBottomReady }),
6049
6014
  children
6050
6015
  ]
6051
6016
  }
@@ -6053,7 +6018,7 @@ var Conversation = ({
6053
6018
  var ConversationContent = ({
6054
6019
  className,
6055
6020
  ...props
6056
- }) => /* @__PURE__ */ jsx14(
6021
+ }) => /* @__PURE__ */ jsx15(
6057
6022
  StickToBottom.Content,
6058
6023
  {
6059
6024
  className: cn("flex flex-col gap-8 p-4", className),
@@ -6068,7 +6033,7 @@ var ConversationScrollButton = ({
6068
6033
  const handleScrollToBottom = useCallback8(() => {
6069
6034
  scrollToBottom();
6070
6035
  }, [scrollToBottom]);
6071
- return !isAtBottom && /* @__PURE__ */ jsx14(
6036
+ return !isAtBottom && /* @__PURE__ */ jsx15(
6072
6037
  Button7,
6073
6038
  {
6074
6039
  className: cn(
@@ -6081,7 +6046,7 @@ var ConversationScrollButton = ({
6081
6046
  variant: "outline",
6082
6047
  "aria-label": "Scroll to latest message",
6083
6048
  ...props,
6084
- children: /* @__PURE__ */ jsx14(ArrowDownIcon, { className: "size-4" })
6049
+ children: /* @__PURE__ */ jsx15(ArrowDownIcon, { className: "size-4" })
6085
6050
  }
6086
6051
  );
6087
6052
  };
@@ -6117,16 +6082,16 @@ function noticeTextClass(className) {
6117
6082
  import { memo as memo2 } from "react";
6118
6083
  import { AlertTriangleIcon, InfoIcon, Loader2Icon, PlugZapIcon, RefreshCwIcon, XIcon as XIcon2 } from "lucide-react";
6119
6084
  import { Button as Button8 } from "@hachej/boring-ui-kit";
6120
- import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
6085
+ import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
6121
6086
  var RuntimeNotices = memo2(({ notices, onDismiss, onAction, renderAction, className, ...props }) => {
6122
6087
  if (notices.length === 0) return null;
6123
- return /* @__PURE__ */ jsx15(
6088
+ return /* @__PURE__ */ jsx16(
6124
6089
  "div",
6125
6090
  {
6126
6091
  "data-boring-agent-part": "runtime-notices",
6127
6092
  className: cn("flex flex-col gap-2 px-4 py-2", className),
6128
6093
  ...props,
6129
- children: notices.map((notice) => /* @__PURE__ */ jsx15(RuntimeNoticeRow, { notice, onDismiss, onAction, renderAction }, notice.id))
6094
+ children: notices.map((notice) => /* @__PURE__ */ jsx16(RuntimeNoticeRow, { notice, onDismiss, onAction, renderAction }, notice.id))
6130
6095
  }
6131
6096
  );
6132
6097
  });
@@ -6150,11 +6115,11 @@ function RuntimeNoticeRow({ notice, onDismiss, onAction, renderAction }) {
6150
6115
  "flex items-start gap-2.5 motion-reduce:transition-none"
6151
6116
  ),
6152
6117
  children: [
6153
- /* @__PURE__ */ jsx15(Icon, { className: cn(noticeIconClass(notice.level), kind === "retry" && "animate-spin motion-reduce:animate-none"), "aria-hidden": "true" }),
6154
- /* @__PURE__ */ jsx15("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx15("p", { className: noticeTextClass(), children: notice.text }) }),
6118
+ /* @__PURE__ */ jsx16(Icon, { className: cn(noticeIconClass(notice.level), kind === "retry" && "animate-spin motion-reduce:animate-none"), "aria-hidden": "true" }),
6119
+ /* @__PURE__ */ jsx16("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx16("p", { className: noticeTextClass(), children: notice.text }) }),
6155
6120
  hostAction ?? null,
6156
- actionLabel && onAction ? /* @__PURE__ */ jsx15(Button8, { type: "button", variant: "ghost", size: "sm", onClick: () => onAction(notice.id), children: actionLabel }) : null,
6157
- notice.dismissible && onDismiss ? /* @__PURE__ */ jsx15(
6121
+ actionLabel && onAction ? /* @__PURE__ */ jsx16(Button8, { type: "button", variant: "ghost", size: "sm", onClick: () => onAction(notice.id), children: actionLabel }) : null,
6122
+ notice.dismissible && onDismiss ? /* @__PURE__ */ jsx16(
6158
6123
  Button8,
6159
6124
  {
6160
6125
  type: "button",
@@ -6163,7 +6128,7 @@ function RuntimeNoticeRow({ notice, onDismiss, onAction, renderAction }) {
6163
6128
  "aria-label": "Dismiss notice",
6164
6129
  onClick: () => onDismiss(notice.id),
6165
6130
  className: "-mr-1 -mt-1 shrink-0 text-muted-foreground/70 hover:bg-muted hover:text-foreground",
6166
- children: /* @__PURE__ */ jsx15(XIcon2, { className: "size-3" })
6131
+ children: /* @__PURE__ */ jsx16(XIcon2, { className: "size-3" })
6167
6132
  }
6168
6133
  ) : null
6169
6134
  ]
@@ -6192,7 +6157,7 @@ function defaultActionLabel(kind) {
6192
6157
  }
6193
6158
 
6194
6159
  // src/front/chat/components/ChatNotices.tsx
6195
- import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
6160
+ import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
6196
6161
  function RuntimeNoticeMessages({
6197
6162
  notices,
6198
6163
  onDismiss,
@@ -6202,7 +6167,7 @@ function RuntimeNoticeMessages({
6202
6167
  (notice) => notice.level === "error" || notice.level === "warning" || notice.id === "connection-reconnecting" || notice.id === "auto-retry" || notice.id === "large-state-warning" || notice.id.startsWith("command:") || notice.id.startsWith("composer-warning:")
6203
6168
  );
6204
6169
  if (visible.length === 0) return null;
6205
- return /* @__PURE__ */ jsx16(
6170
+ return /* @__PURE__ */ jsx17(
6206
6171
  RuntimeNotices,
6207
6172
  {
6208
6173
  notices: visible,
@@ -6214,7 +6179,7 @@ function RuntimeNoticeMessages({
6214
6179
  }
6215
6180
  function ComposerRuntimeNotice({ notice }) {
6216
6181
  const level = notice.code === ErrorCode.enum.AGENT_RUNTIME_NOT_READY ? "info" : "error";
6217
- return /* @__PURE__ */ jsx16(
6182
+ return /* @__PURE__ */ jsx17(
6218
6183
  "div",
6219
6184
  {
6220
6185
  "data-testid": "chat-composer-runtime-notice",
@@ -6222,17 +6187,17 @@ function ComposerRuntimeNotice({ notice }) {
6222
6187
  "aria-live": "polite",
6223
6188
  className: noticeSurfaceClass(level, "mx-auto mb-2 w-full max-w-3xl text-xs"),
6224
6189
  children: /* @__PURE__ */ jsxs15("div", { className: "flex items-start gap-2", children: [
6225
- notice.code === ErrorCode.enum.AGENT_RUNTIME_NOT_READY ? /* @__PURE__ */ jsx16(Loader2, { "aria-hidden": "true", className: "mt-0.5 size-3.5 shrink-0 animate-spin text-muted-foreground" }) : /* @__PURE__ */ jsx16(AlertCircleIcon, { "aria-hidden": "true", className: "mt-0.5 size-3.5 shrink-0 text-destructive" }),
6190
+ notice.code === ErrorCode.enum.AGENT_RUNTIME_NOT_READY ? /* @__PURE__ */ jsx17(Loader2, { "aria-hidden": "true", className: "mt-0.5 size-3.5 shrink-0 animate-spin text-muted-foreground" }) : /* @__PURE__ */ jsx17(AlertCircleIcon, { "aria-hidden": "true", className: "mt-0.5 size-3.5 shrink-0 text-destructive" }),
6226
6191
  /* @__PURE__ */ jsxs15("div", { className: "min-w-0", children: [
6227
- /* @__PURE__ */ jsx16("div", { className: "font-medium", children: notice.title }),
6228
- notice.detail ? /* @__PURE__ */ jsx16(NoticeText, { className: "mt-0.5 text-muted-foreground", children: notice.detail }) : null
6192
+ /* @__PURE__ */ jsx17("div", { className: "font-medium", children: notice.title }),
6193
+ notice.detail ? /* @__PURE__ */ jsx17(NoticeText, { className: "mt-0.5 text-muted-foreground", children: notice.detail }) : null
6229
6194
  ] })
6230
6195
  ] })
6231
6196
  }
6232
6197
  );
6233
6198
  }
6234
6199
  function NoticeText({ className, children }) {
6235
- return /* @__PURE__ */ jsx16("div", { className: noticeTextClass(className), children });
6200
+ return /* @__PURE__ */ jsx17("div", { className: noticeTextClass(className), children });
6236
6201
  }
6237
6202
  function ComposerBlockerNotice({
6238
6203
  blocker,
@@ -6246,10 +6211,10 @@ function ComposerBlockerNotice({
6246
6211
  "aria-live": "polite",
6247
6212
  className: noticeSurfaceClass("info", "relative z-20 mx-auto mb-2 w-full max-w-3xl text-xs"),
6248
6213
  children: [
6249
- /* @__PURE__ */ jsx16("span", { children: label }),
6214
+ /* @__PURE__ */ jsx17("span", { children: label }),
6250
6215
  blocker?.actions?.map((action) => {
6251
6216
  const icon = composerBlockerActionIcon(action.id);
6252
- return icon ? /* @__PURE__ */ jsx16(
6217
+ return icon ? /* @__PURE__ */ jsx17(
6253
6218
  IconButton2,
6254
6219
  {
6255
6220
  type: "button",
@@ -6262,7 +6227,7 @@ function ComposerBlockerNotice({
6262
6227
  children: icon
6263
6228
  },
6264
6229
  action.id
6265
- ) : /* @__PURE__ */ jsx16(
6230
+ ) : /* @__PURE__ */ jsx17(
6266
6231
  "button",
6267
6232
  {
6268
6233
  type: "button",
@@ -6278,8 +6243,8 @@ function ComposerBlockerNotice({
6278
6243
  );
6279
6244
  }
6280
6245
  function composerBlockerActionIcon(actionId) {
6281
- if (actionId === "open") return /* @__PURE__ */ jsx16(ExternalLinkIcon2, { className: "size-3.5", "aria-hidden": "true" });
6282
- if (actionId === "cancel") return /* @__PURE__ */ jsx16(XIcon3, { className: "size-3.5", "aria-hidden": "true" });
6246
+ if (actionId === "open") return /* @__PURE__ */ jsx17(ExternalLinkIcon2, { className: "size-3.5", "aria-hidden": "true" });
6247
+ if (actionId === "cancel") return /* @__PURE__ */ jsx17(XIcon3, { className: "size-3.5", "aria-hidden": "true" });
6283
6248
  return null;
6284
6249
  }
6285
6250
  function QueuedComposerNotice({ followUps, onEdit }) {
@@ -6298,9 +6263,9 @@ function QueuedComposerNotice({ followUps, onEdit }) {
6298
6263
  " queued follow-up",
6299
6264
  followUps.length === 1 ? "" : "s"
6300
6265
  ] }),
6301
- /* @__PURE__ */ jsx16("div", { className: "truncate text-[color:var(--muted-foreground)]", "data-boring-agent-part": "composer-queue-preview-text", children: followUps.map((followUp) => followUp.displayText).join(" - ") })
6266
+ /* @__PURE__ */ jsx17("div", { className: "truncate text-[color:var(--muted-foreground)]", "data-boring-agent-part": "composer-queue-preview-text", children: followUps.map((followUp) => followUp.displayText).join(" - ") })
6302
6267
  ] }),
6303
- /* @__PURE__ */ jsx16(
6268
+ /* @__PURE__ */ jsx17(
6304
6269
  IconButton2,
6305
6270
  {
6306
6271
  type: "button",
@@ -6310,7 +6275,7 @@ function QueuedComposerNotice({ followUps, onEdit }) {
6310
6275
  className: "shrink-0 text-[color:var(--muted-foreground)] hover:bg-[color:var(--muted)] hover:text-[color:var(--foreground)]",
6311
6276
  "aria-label": "Edit queued follow-ups",
6312
6277
  title: "Edit queued follow-ups",
6313
- children: /* @__PURE__ */ jsx16(ListRestartIcon, { className: "size-3.5", "aria-hidden": "true" })
6278
+ children: /* @__PURE__ */ jsx17(ListRestartIcon, { className: "size-3.5", "aria-hidden": "true" })
6314
6279
  }
6315
6280
  )
6316
6281
  ]
@@ -6319,7 +6284,7 @@ function QueuedComposerNotice({ followUps, onEdit }) {
6319
6284
  }
6320
6285
 
6321
6286
  // src/front/chat/components/PiTimelineMessage.tsx
6322
- import { useCallback as useCallback13, useEffect as useEffect13, useMemo as useMemo10, useRef as useRef10, useState as useState14 } from "react";
6287
+ import { useCallback as useCallback13, useEffect as useEffect13, useMemo as useMemo12, useRef as useRef10, useState as useState14 } from "react";
6323
6288
  import { CheckIcon as CheckIcon2, CopyIcon as CopyIcon3 } from "lucide-react";
6324
6289
  import { Button as Button11 } from "@hachej/boring-ui-kit";
6325
6290
 
@@ -6334,8 +6299,8 @@ import {
6334
6299
  VideoIcon,
6335
6300
  XIcon as XIcon4
6336
6301
  } from "lucide-react";
6337
- import { createContext as createContext3, useCallback as useCallback9, useContext as useContext3, useMemo as useMemo5 } from "react";
6338
- import { jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
6302
+ import { createContext as createContext4, useCallback as useCallback9, useContext as useContext4, useMemo as useMemo6 } from "react";
6303
+ import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
6339
6304
  var mediaCategoryIcons = {
6340
6305
  audio: Music2Icon,
6341
6306
  document: FileTextIcon2,
@@ -6370,7 +6335,7 @@ var getAttachmentLabel = (data) => {
6370
6335
  const category = getMediaCategory(data);
6371
6336
  return data.filename || (category === "image" ? "Image" : "Attachment");
6372
6337
  };
6373
- var renderAttachmentImage = (url, filename, isGrid) => isGrid ? /* @__PURE__ */ jsx17(
6338
+ var renderAttachmentImage = (url, filename, isGrid) => isGrid ? /* @__PURE__ */ jsx18(
6374
6339
  "img",
6375
6340
  {
6376
6341
  alt: filename || "Image",
@@ -6379,7 +6344,7 @@ var renderAttachmentImage = (url, filename, isGrid) => isGrid ? /* @__PURE__ */
6379
6344
  src: url,
6380
6345
  width: 96
6381
6346
  }
6382
- ) : /* @__PURE__ */ jsx17(
6347
+ ) : /* @__PURE__ */ jsx18(
6383
6348
  "img",
6384
6349
  {
6385
6350
  alt: filename || "Image",
@@ -6389,11 +6354,11 @@ var renderAttachmentImage = (url, filename, isGrid) => isGrid ? /* @__PURE__ */
6389
6354
  width: 20
6390
6355
  }
6391
6356
  );
6392
- var AttachmentsContext = createContext3(null);
6393
- var AttachmentContext = createContext3(null);
6394
- var useAttachmentsContext = () => useContext3(AttachmentsContext) ?? { variant: "grid" };
6357
+ var AttachmentsContext = createContext4(null);
6358
+ var AttachmentContext = createContext4(null);
6359
+ var useAttachmentsContext = () => useContext4(AttachmentsContext) ?? { variant: "grid" };
6395
6360
  var useAttachmentContext = () => {
6396
- const ctx = useContext3(AttachmentContext);
6361
+ const ctx = useContext4(AttachmentContext);
6397
6362
  if (!ctx) {
6398
6363
  throw new Error("Attachment components must be used within <Attachment>");
6399
6364
  }
@@ -6405,8 +6370,8 @@ var Attachments = ({
6405
6370
  children,
6406
6371
  ...props
6407
6372
  }) => {
6408
- const contextValue = useMemo5(() => ({ variant }), [variant]);
6409
- return /* @__PURE__ */ jsx17(AttachmentsContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx17(
6373
+ const contextValue = useMemo6(() => ({ variant }), [variant]);
6374
+ return /* @__PURE__ */ jsx18(AttachmentsContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx18(
6410
6375
  "div",
6411
6376
  {
6412
6377
  className: cn(
@@ -6429,11 +6394,11 @@ var Attachment = ({
6429
6394
  }) => {
6430
6395
  const { variant } = useAttachmentsContext();
6431
6396
  const mediaCategory = getMediaCategory(data);
6432
- const contextValue = useMemo5(
6397
+ const contextValue = useMemo6(
6433
6398
  () => ({ data, mediaCategory, onRemove, variant }),
6434
6399
  [data, mediaCategory, onRemove, variant]
6435
6400
  );
6436
- return /* @__PURE__ */ jsx17(AttachmentContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx17(
6401
+ return /* @__PURE__ */ jsx18(AttachmentContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx18(
6437
6402
  "div",
6438
6403
  {
6439
6404
  className: cn(
@@ -6463,18 +6428,18 @@ var AttachmentPreview = ({
6463
6428
  }) => {
6464
6429
  const { data, mediaCategory, variant } = useAttachmentContext();
6465
6430
  const iconSize = variant === "inline" ? "size-3" : "size-4";
6466
- const renderIcon = (Icon) => /* @__PURE__ */ jsx17(Icon, { className: cn(iconSize, "text-muted-foreground") });
6431
+ const renderIcon = (Icon) => /* @__PURE__ */ jsx18(Icon, { className: cn(iconSize, "text-muted-foreground") });
6467
6432
  const renderContent = () => {
6468
6433
  if (mediaCategory === "image" && data.type === "file" && data.url) {
6469
6434
  return renderAttachmentImage(data.url, data.filename, variant === "grid");
6470
6435
  }
6471
6436
  if (mediaCategory === "video" && data.type === "file" && data.url) {
6472
- return /* @__PURE__ */ jsx17("video", { className: "size-full object-cover", muted: true, src: data.url });
6437
+ return /* @__PURE__ */ jsx18("video", { className: "size-full object-cover", muted: true, src: data.url });
6473
6438
  }
6474
6439
  const Icon = mediaCategoryIcons[mediaCategory];
6475
6440
  return fallbackIcon ?? renderIcon(Icon);
6476
6441
  };
6477
- return /* @__PURE__ */ jsx17(
6442
+ return /* @__PURE__ */ jsx18(
6478
6443
  "div",
6479
6444
  {
6480
6445
  className: cn(
@@ -6500,8 +6465,8 @@ var AttachmentInfo = ({
6500
6465
  return null;
6501
6466
  }
6502
6467
  return /* @__PURE__ */ jsxs16("div", { className: cn("min-w-0 flex-1", className), ...props, children: [
6503
- /* @__PURE__ */ jsx17("span", { className: "block truncate", children: label }),
6504
- showMediaType && data.mediaType && /* @__PURE__ */ jsx17("span", { className: "block truncate text-muted-foreground text-xs", children: data.mediaType })
6468
+ /* @__PURE__ */ jsx18("span", { className: "block truncate", children: label }),
6469
+ showMediaType && data.mediaType && /* @__PURE__ */ jsx18("span", { className: "block truncate text-muted-foreground text-xs", children: data.mediaType })
6505
6470
  ] });
6506
6471
  };
6507
6472
  var AttachmentRemove = ({
@@ -6546,8 +6511,8 @@ var AttachmentRemove = ({
6546
6511
  variant: "ghost",
6547
6512
  ...props,
6548
6513
  children: [
6549
- children ?? /* @__PURE__ */ jsx17(XIcon4, {}),
6550
- /* @__PURE__ */ jsx17("span", { className: "sr-only", children: label })
6514
+ children ?? /* @__PURE__ */ jsx18(XIcon4, {}),
6515
+ /* @__PURE__ */ jsx18("span", { className: "sr-only", children: label })
6551
6516
  ]
6552
6517
  }
6553
6518
  );
@@ -6572,22 +6537,22 @@ import { math } from "@streamdown/math";
6572
6537
  import { mermaid } from "@streamdown/mermaid";
6573
6538
  import { ChevronLeftIcon as ChevronLeftIcon2, ChevronRightIcon } from "lucide-react";
6574
6539
  import {
6575
- createContext as createContext4,
6540
+ createContext as createContext5,
6576
6541
  memo as memo3,
6577
6542
  useCallback as useCallback10,
6578
- useContext as useContext4,
6543
+ useContext as useContext5,
6579
6544
  useEffect as useEffect10,
6580
- useMemo as useMemo6,
6545
+ useMemo as useMemo7,
6581
6546
  useState as useState11
6582
6547
  } from "react";
6583
6548
  import { Streamdown } from "streamdown";
6584
- import { jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
6585
- var Message = ({ className, from, ...props }) => /* @__PURE__ */ jsx18(
6549
+ import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
6550
+ var Message = ({ className, from, ...props }) => /* @__PURE__ */ jsx19(
6586
6551
  "div",
6587
6552
  {
6588
6553
  "data-boring-agent-message-role": from,
6589
6554
  className: cn(
6590
- "group flex w-full max-w-[95%] flex-col gap-2",
6555
+ "group flex w-full max-w-full flex-col gap-1.5",
6591
6556
  from === "user" ? "is-user ml-auto justify-end" : "is-assistant",
6592
6557
  className
6593
6558
  ),
@@ -6598,13 +6563,13 @@ var MessageContent = ({
6598
6563
  children,
6599
6564
  className,
6600
6565
  ...props
6601
- }) => /* @__PURE__ */ jsx18(
6566
+ }) => /* @__PURE__ */ jsx19(
6602
6567
  "div",
6603
6568
  {
6604
6569
  className: cn(
6605
- "is-user:dark flex w-fit min-w-0 max-w-full flex-col gap-2 overflow-hidden text-sm",
6606
- "group-[.is-user]:ml-auto group-[.is-user]:rounded-lg group-[.is-user]:bg-secondary group-[.is-user]:px-4 group-[.is-user]:py-3 group-[.is-user]:text-foreground",
6607
- "group-[.is-assistant]:text-foreground",
6570
+ "is-user:dark flex w-fit min-w-0 max-w-full flex-col gap-2 overflow-visible text-[13px] leading-5",
6571
+ "group-[.is-user]:ml-auto group-[.is-user]:max-w-[80%] group-[.is-user]:rounded-[var(--radius-lg)] group-[.is-user]:bg-secondary group-[.is-user]:px-4 group-[.is-user]:py-2.5 group-[.is-user]:text-foreground",
6572
+ "group-[.is-assistant]:w-full group-[.is-assistant]:max-w-[40.5rem] group-[.is-assistant]:bg-transparent group-[.is-assistant]:p-0 group-[.is-assistant]:text-foreground",
6608
6573
  className
6609
6574
  ),
6610
6575
  ...props,
@@ -6615,7 +6580,7 @@ var MessageActions = ({
6615
6580
  className,
6616
6581
  children,
6617
6582
  ...props
6618
- }) => /* @__PURE__ */ jsx18("div", { className: cn("flex items-center gap-1", className), ...props, children });
6583
+ }) => /* @__PURE__ */ jsx19("div", { className: cn("flex items-center gap-1", className), ...props, children });
6619
6584
  var MessageAction = ({
6620
6585
  tooltip,
6621
6586
  children,
@@ -6626,17 +6591,17 @@ var MessageAction = ({
6626
6591
  }) => {
6627
6592
  const button = /* @__PURE__ */ jsxs17(Button10, { size, type: "button", variant, ...props, children: [
6628
6593
  children,
6629
- /* @__PURE__ */ jsx18("span", { className: "sr-only", children: label || tooltip })
6594
+ /* @__PURE__ */ jsx19("span", { className: "sr-only", children: label || tooltip })
6630
6595
  ] });
6631
6596
  if (tooltip) {
6632
- return /* @__PURE__ */ jsx18(TooltipProvider2, { children: /* @__PURE__ */ jsxs17(Tooltip2, { children: [
6633
- /* @__PURE__ */ jsx18(TooltipTrigger2, { asChild: true, children: button }),
6634
- /* @__PURE__ */ jsx18(TooltipContent2, { children: /* @__PURE__ */ jsx18("p", { children: tooltip }) })
6597
+ return /* @__PURE__ */ jsx19(TooltipProvider2, { children: /* @__PURE__ */ jsxs17(Tooltip2, { children: [
6598
+ /* @__PURE__ */ jsx19(TooltipTrigger2, { asChild: true, children: button }),
6599
+ /* @__PURE__ */ jsx19(TooltipContent2, { children: /* @__PURE__ */ jsx19("p", { children: tooltip }) })
6635
6600
  ] }) });
6636
6601
  }
6637
6602
  return button;
6638
6603
  };
6639
- var MessageBranchContext = createContext4(
6604
+ var MessageBranchContext = createContext5(
6640
6605
  null
6641
6606
  );
6642
6607
  var streamdownPlugins = { cjk, code, math, mermaid };
@@ -6672,9 +6637,9 @@ function extractCodeBlockText(children) {
6672
6637
  var MarkdownPre = ({ children }) => {
6673
6638
  const code3 = extractCodeBlockText(children).replace(/\n$/, "");
6674
6639
  const language = extractCodeBlockLanguage(children);
6675
- return /* @__PURE__ */ jsx18(CodeBlock2, { code: code3, language: language ?? "text", className: "my-3", children: /* @__PURE__ */ jsxs17(CodeBlockHeader, { children: [
6676
- /* @__PURE__ */ jsx18(CodeBlockTitle, { children: /* @__PURE__ */ jsx18(CodeBlockFilename, { children: language ?? "text" }) }),
6677
- /* @__PURE__ */ jsx18(CodeBlockCopyButton, {})
6640
+ return /* @__PURE__ */ jsx19(CodeBlock2, { code: code3, language: language ?? "text", className: "my-3", children: /* @__PURE__ */ jsxs17(CodeBlockHeader, { children: [
6641
+ /* @__PURE__ */ jsx19(CodeBlockTitle, { children: /* @__PURE__ */ jsx19(CodeBlockFilename, { children: language ?? "text" }) }),
6642
+ /* @__PURE__ */ jsx19(CodeBlockCopyButton, {})
6678
6643
  ] }) });
6679
6644
  };
6680
6645
  var MarkdownCode = ({
@@ -6684,9 +6649,9 @@ var MarkdownCode = ({
6684
6649
  ...props
6685
6650
  }) => {
6686
6651
  if (inline === false) {
6687
- return /* @__PURE__ */ jsx18("code", { className, ...props, children });
6652
+ return /* @__PURE__ */ jsx19("code", { className, ...props, children });
6688
6653
  }
6689
- return /* @__PURE__ */ jsx18(
6654
+ return /* @__PURE__ */ jsx19(
6690
6655
  "code",
6691
6656
  {
6692
6657
  className: cn(
@@ -6703,7 +6668,7 @@ var markdownComponents = {
6703
6668
  code: MarkdownCode
6704
6669
  };
6705
6670
  var MessageResponse = memo3(
6706
- ({ className, shikiTheme, components, ...props }) => /* @__PURE__ */ jsx18(
6671
+ ({ className, shikiTheme, components, ...props }) => /* @__PURE__ */ jsx19(
6707
6672
  Streamdown,
6708
6673
  {
6709
6674
  className: cn(
@@ -6733,12 +6698,12 @@ import { math as math2 } from "@streamdown/math";
6733
6698
  import { mermaid as mermaid2 } from "@streamdown/mermaid";
6734
6699
  import { BrainIcon, ChevronDownIcon as ChevronDownIcon3 } from "lucide-react";
6735
6700
  import {
6736
- createContext as createContext5,
6701
+ createContext as createContext6,
6737
6702
  memo as memo5,
6738
6703
  useCallback as useCallback11,
6739
- useContext as useContext5,
6704
+ useContext as useContext6,
6740
6705
  useEffect as useEffect11,
6741
- useMemo as useMemo8,
6706
+ useMemo as useMemo9,
6742
6707
  useRef as useRef9,
6743
6708
  useState as useState12
6744
6709
  } from "react";
@@ -6746,8 +6711,8 @@ import { Streamdown as Streamdown2 } from "streamdown";
6746
6711
 
6747
6712
  // src/front/primitives/shimmer.tsx
6748
6713
  import { motion } from "motion/react";
6749
- import { memo as memo4, useMemo as useMemo7 } from "react";
6750
- import { jsx as jsx19 } from "react/jsx-runtime";
6714
+ import { memo as memo4, useMemo as useMemo8 } from "react";
6715
+ import { jsx as jsx20 } from "react/jsx-runtime";
6751
6716
  var motionComponentCache = /* @__PURE__ */ new Map();
6752
6717
  var getMotionComponent = (element) => {
6753
6718
  let component = motionComponentCache.get(element);
@@ -6767,11 +6732,11 @@ var ShimmerComponent = ({
6767
6732
  const MotionComponent = getMotionComponent(
6768
6733
  Component
6769
6734
  );
6770
- const dynamicSpread = useMemo7(
6735
+ const dynamicSpread = useMemo8(
6771
6736
  () => (children?.length ?? 0) * spread,
6772
6737
  [children, spread]
6773
6738
  );
6774
- return /* @__PURE__ */ jsx19(
6739
+ return /* @__PURE__ */ jsx20(
6775
6740
  MotionComponent,
6776
6741
  {
6777
6742
  animate: { backgroundPosition: "0% center" },
@@ -6797,10 +6762,10 @@ var ShimmerComponent = ({
6797
6762
  var Shimmer = memo4(ShimmerComponent);
6798
6763
 
6799
6764
  // src/front/primitives/reasoning.tsx
6800
- import { Fragment as Fragment6, jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
6801
- var ReasoningContext = createContext5(null);
6765
+ import { Fragment as Fragment7, jsx as jsx21, jsxs as jsxs18 } from "react/jsx-runtime";
6766
+ var ReasoningContext = createContext6(null);
6802
6767
  var useReasoning = () => {
6803
- const context = useContext5(ReasoningContext);
6768
+ const context = useContext6(ReasoningContext);
6804
6769
  if (!context) {
6805
6770
  throw new Error("Reasoning components must be used within Reasoning");
6806
6771
  }
@@ -6865,11 +6830,11 @@ var Reasoning = memo5(
6865
6830
  },
6866
6831
  [setIsOpen]
6867
6832
  );
6868
- const contextValue = useMemo8(
6833
+ const contextValue = useMemo9(
6869
6834
  () => ({ duration, isOpen, isStreaming, setIsOpen }),
6870
6835
  [duration, isOpen, isStreaming, setIsOpen]
6871
6836
  );
6872
- return /* @__PURE__ */ jsx20(ReasoningContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx20(
6837
+ return /* @__PURE__ */ jsx21(ReasoningContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx21(
6873
6838
  Collapsible2,
6874
6839
  {
6875
6840
  className: cn("not-prose mb-2", className),
@@ -6883,10 +6848,10 @@ var Reasoning = memo5(
6883
6848
  );
6884
6849
  var defaultGetThinkingMessage = (isStreaming, duration) => {
6885
6850
  if (isStreaming || duration === 0) {
6886
- return /* @__PURE__ */ jsx20(Shimmer, { duration: 1, children: "thinking" });
6851
+ return /* @__PURE__ */ jsx21(Shimmer, { duration: 1, children: "thinking" });
6887
6852
  }
6888
6853
  if (duration === void 0) {
6889
- return /* @__PURE__ */ jsx20("span", { children: "thoughts" });
6854
+ return /* @__PURE__ */ jsx21("span", { children: "thoughts" });
6890
6855
  }
6891
6856
  return /* @__PURE__ */ jsxs18("span", { children: [
6892
6857
  "thoughts \xB7 ",
@@ -6902,7 +6867,7 @@ var ReasoningTrigger = memo5(
6902
6867
  ...props
6903
6868
  }) => {
6904
6869
  const { isStreaming, isOpen, duration } = useReasoning();
6905
- return /* @__PURE__ */ jsx20(
6870
+ return /* @__PURE__ */ jsx21(
6906
6871
  CollapsibleTrigger3,
6907
6872
  {
6908
6873
  className: cn(
@@ -6910,10 +6875,10 @@ var ReasoningTrigger = memo5(
6910
6875
  className
6911
6876
  ),
6912
6877
  ...props,
6913
- children: children ?? /* @__PURE__ */ jsxs18(Fragment6, { children: [
6914
- /* @__PURE__ */ jsx20(BrainIcon, { className: "size-3" }),
6878
+ children: children ?? /* @__PURE__ */ jsxs18(Fragment7, { children: [
6879
+ /* @__PURE__ */ jsx21(BrainIcon, { className: "size-3" }),
6915
6880
  getThinkingMessage(isStreaming, duration),
6916
- /* @__PURE__ */ jsx20(
6881
+ /* @__PURE__ */ jsx21(
6917
6882
  ChevronDownIcon3,
6918
6883
  {
6919
6884
  className: cn(
@@ -6929,7 +6894,7 @@ var ReasoningTrigger = memo5(
6929
6894
  );
6930
6895
  var streamdownPlugins2 = { cjk: cjk2, code: code2, math: math2, mermaid: mermaid2 };
6931
6896
  var ReasoningContent = memo5(
6932
- ({ className, children, ...props }) => /* @__PURE__ */ jsx20(
6897
+ ({ className, children, ...props }) => /* @__PURE__ */ jsx21(
6933
6898
  CollapsibleContent2,
6934
6899
  {
6935
6900
  className: cn(
@@ -6938,7 +6903,7 @@ var ReasoningContent = memo5(
6938
6903
  className
6939
6904
  ),
6940
6905
  ...props,
6941
- children: /* @__PURE__ */ jsx20(Streamdown2, { plugins: streamdownPlugins2, children })
6906
+ children: /* @__PURE__ */ jsx21(Streamdown2, { plugins: streamdownPlugins2, children })
6942
6907
  }
6943
6908
  )
6944
6909
  );
@@ -6948,7 +6913,7 @@ ReasoningContent.displayName = "ReasoningContent";
6948
6913
 
6949
6914
  // src/front/primitives/tool-call-group.tsx
6950
6915
  import { ChevronDownIcon as ChevronDownIcon4 } from "lucide-react";
6951
- import { memo as memo6, useCallback as useCallback12, useEffect as useEffect12, useMemo as useMemo9, useState as useState13 } from "react";
6916
+ import { memo as memo6, useCallback as useCallback12, useEffect as useEffect12, useMemo as useMemo10, useState as useState13 } from "react";
6952
6917
  import { Collapsible as Collapsible3, CollapsibleContent as CollapsibleContent3, CollapsibleTrigger as CollapsibleTrigger4 } from "@hachej/boring-ui-kit";
6953
6918
 
6954
6919
  // src/front/primitives/tool-call-group-state.ts
@@ -6987,10 +6952,7 @@ var TOOL_GROUP_VISUAL_PRESENTATION = {
6987
6952
  };
6988
6953
 
6989
6954
  // src/front/primitives/tool-call-group.tsx
6990
- import { jsx as jsx21, jsxs as jsxs19 } from "react/jsx-runtime";
6991
- function isSettledState(state) {
6992
- return state === "output-available" || state === "output-error" || state === "output-denied" || state === "approval-responded" || state === "aborted";
6993
- }
6955
+ import { jsx as jsx22, jsxs as jsxs19 } from "react/jsx-runtime";
6994
6956
  function isInputRunningState(state) {
6995
6957
  return state === "input-streaming" || state === "input-available";
6996
6958
  }
@@ -7040,11 +7002,10 @@ function resolveVisualState({
7040
7002
  return "settled";
7041
7003
  }
7042
7004
  var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
7043
- const toolParts = useMemo9(() => tools.map(({ part, key }) => {
7005
+ const toolParts = useMemo10(() => tools.map(({ part, key }) => {
7044
7006
  const toolPart = toToolPart(part);
7045
7007
  return toolPart ? { part: toolPart, key } : null;
7046
7008
  }).filter((entry) => Boolean(entry)), [tools]);
7047
- const isSettled = toolParts.every(({ part }) => isSettledState(part.state));
7048
7009
  const workspaceNotReady = toolParts.map(({ part }) => getWorkspaceNotReadyStatus(part.output)).find(Boolean);
7049
7010
  const hasError = toolParts.some(({ part }) => part.state === "output-error") && !workspaceNotReady;
7050
7011
  const hasAborted = toolParts.some(({ part }) => part.state === "aborted") && !workspaceNotReady;
@@ -7061,7 +7022,7 @@ var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
7061
7022
  });
7062
7023
  const isRunning = visualState === RUNNING_TOOL_GROUP_VISUAL_STATE;
7063
7024
  const [elapsedSeconds, setElapsedSeconds] = useState13(0);
7064
- const title = useMemo9(() => buildTitle(toolParts.map(({ part }) => part), visualState), [toolParts, visualState]);
7025
+ const title = useMemo10(() => buildTitle(toolParts.map(({ part }) => part), visualState), [toolParts, visualState]);
7065
7026
  const elapsedLabel = isRunning ? `Running ${formatElapsed(elapsedSeconds)}` : null;
7066
7027
  useEffect12(() => {
7067
7028
  if (!isRunning) {
@@ -7089,50 +7050,48 @@ var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
7089
7050
  {
7090
7051
  "aria-label": `Tool calls: ${title}`,
7091
7052
  className: cn(
7092
- "group/trigger flex w-fit items-center gap-1.5 rounded-md px-2.5 py-1 text-[12px] font-medium transition-colors motion-reduce:transition-none",
7093
- "border border-border/40 bg-card/40 text-muted-foreground/70",
7094
- "hover:border-border/70 hover:bg-card/70 hover:text-muted-foreground",
7053
+ "group/trigger flex min-h-10 w-full items-center gap-2 rounded-[var(--radius-md)] px-2 py-1 text-[13px] font-normal",
7054
+ "border border-border/60 bg-muted/45 text-muted-foreground",
7055
+ "transition-colors hover:bg-muted/70 hover:text-foreground motion-reduce:transition-none",
7056
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
7095
7057
  TOOL_GROUP_VISUAL_PRESENTATION[visualState].triggerClassName
7096
7058
  ),
7097
7059
  children: [
7098
- /* @__PURE__ */ jsx21(
7060
+ /* @__PURE__ */ jsx22("span", { className: "flex size-4 shrink-0 items-center justify-center", "aria-hidden": "true", children: /* @__PURE__ */ jsx22(
7099
7061
  "span",
7100
7062
  {
7101
7063
  "data-boring-agent-part": "tool-group-state-dot",
7102
7064
  className: cn(
7103
- "size-1.5 shrink-0 rounded-full",
7065
+ "size-1.5 rounded-full",
7104
7066
  TOOL_GROUP_VISUAL_PRESENTATION[visualState].dotClassName
7105
7067
  )
7106
7068
  }
7107
- ),
7108
- /* @__PURE__ */ jsx21(
7069
+ ) }),
7070
+ /* @__PURE__ */ jsx22("span", { "data-boring-agent-part": "tool-group-title", children: workspaceNotReady ? workspaceNotReady.message : title }),
7071
+ elapsedLabel ? /* @__PURE__ */ jsx22("span", { className: "ml-1 shrink-0 tabular-nums text-[12px] text-muted-foreground/70", children: elapsedLabel }) : null,
7072
+ /* @__PURE__ */ jsx22("span", { className: "ml-1 shrink-0 tabular-nums text-[12px] text-muted-foreground/70", children: toolParts.length }),
7073
+ /* @__PURE__ */ jsx22(
7109
7074
  ChevronDownIcon4,
7110
7075
  {
7111
7076
  className: cn(
7112
- "size-3 shrink-0 transition-transform duration-150",
7077
+ "ml-auto size-4 shrink-0 transition-transform duration-150 motion-reduce:transition-none",
7113
7078
  isOpen && "rotate-180"
7114
7079
  )
7115
7080
  }
7116
- ),
7117
- workspaceNotReady ? /* @__PURE__ */ jsx21("span", { "data-boring-agent-part": "tool-group-title", children: workspaceNotReady.message }) : !isSettled ? /* @__PURE__ */ jsx21("span", { "data-boring-agent-part": "tool-group-title", children: /* @__PURE__ */ jsx21(Shimmer, { as: "span", duration: 1.5, children: title }) }) : /* @__PURE__ */ jsx21("span", { "data-boring-agent-part": "tool-group-title", children: title }),
7118
- elapsedLabel ? /* @__PURE__ */ jsx21("span", { className: "ml-1 shrink-0 tabular-nums text-[10px] text-muted-foreground/50", children: elapsedLabel }) : null,
7119
- /* @__PURE__ */ jsx21("span", { className: cn(
7120
- "ml-1 shrink-0 rounded-sm border border-border/40 px-1 tabular-nums",
7121
- "text-[10px] text-muted-foreground/50"
7122
- ), children: toolParts.length })
7081
+ )
7123
7082
  ]
7124
7083
  }
7125
7084
  ),
7126
- /* @__PURE__ */ jsx21(CollapsibleContent3, { className: "overflow-hidden data-[state=closed]:animate-[boring-collapse-close_200ms_ease] data-[state=open]:animate-[boring-collapse-open_200ms_ease]", children: /* @__PURE__ */ jsxs19(
7085
+ /* @__PURE__ */ jsx22(CollapsibleContent3, { className: "overflow-hidden data-[state=closed]:animate-[boring-collapse-close_200ms_ease] data-[state=open]:animate-[boring-collapse-open_200ms_ease]", children: /* @__PURE__ */ jsxs19(
7127
7086
  "div",
7128
7087
  {
7129
7088
  "data-boring-agent-part": "tool-group-details",
7130
7089
  className: "relative mt-1.5 w-full max-w-2xl pl-[18px]",
7131
7090
  children: [
7132
- /* @__PURE__ */ jsx21("div", { className: "absolute left-[5px] top-0 bottom-2 w-px bg-border/35" }),
7133
- /* @__PURE__ */ jsx21("div", { className: "flex min-w-0 flex-col gap-1.5 [&_[data-boring-agent-part=tool-card]]:!my-0", children: toolParts.map(({ part, key }) => {
7091
+ /* @__PURE__ */ jsx22("div", { className: "absolute left-[5px] top-0 bottom-2 w-px bg-border/35" }),
7092
+ /* @__PURE__ */ jsx22("div", { className: "flex min-w-0 flex-col gap-1.5 [&_[data-boring-agent-part=tool-card]]:!my-0", children: toolParts.map(({ part, key }) => {
7134
7093
  const { renderer, part: resolvedPart, resolution } = resolveToolRendererForPart(part, mergedToolRenderers);
7135
- return /* @__PURE__ */ jsx21(
7094
+ return /* @__PURE__ */ jsx22(
7136
7095
  "div",
7137
7096
  {
7138
7097
  className: "min-w-0",
@@ -7153,9 +7112,37 @@ var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
7153
7112
  });
7154
7113
  ToolCallGroup.displayName = "ToolCallGroup";
7155
7114
 
7115
+ // src/front/chat/messageContributions.tsx
7116
+ import { createContext as createContext7, createElement, useContext as useContext7, useMemo as useMemo11 } from "react";
7117
+ import { jsx as jsx23 } from "react/jsx-runtime";
7118
+ var ChatMessageContributionContext = createContext7([]);
7119
+ function ChatMessageContributionProvider({
7120
+ contribution,
7121
+ children
7122
+ }) {
7123
+ const parent = useContext7(ChatMessageContributionContext);
7124
+ const contributions = useMemo11(
7125
+ () => [contribution, ...parent.filter((entry) => entry.id !== contribution.id)],
7126
+ [contribution, parent]
7127
+ );
7128
+ return /* @__PURE__ */ jsx23(ChatMessageContributionContext.Provider, { value: contributions, children });
7129
+ }
7130
+ function useCustomChatMessage(message) {
7131
+ const contributions = useContext7(ChatMessageContributionContext);
7132
+ const contribution = contributions.find((entry) => entry.matches(message));
7133
+ return contribution ? createElement(contribution.Component, { message }) : void 0;
7134
+ }
7135
+ function ChatMessageContributionBoundary({
7136
+ message,
7137
+ children
7138
+ }) {
7139
+ const customMessage = useCustomChatMessage(message);
7140
+ return customMessage !== void 0 ? customMessage : children;
7141
+ }
7142
+
7156
7143
  // src/front/chat/components/MessageMentions.tsx
7157
7144
  import { Children, cloneElement, isValidElement as isValidElement2 } from "react";
7158
- import { Fragment as Fragment7, jsx as jsx22 } from "react/jsx-runtime";
7145
+ import { Fragment as Fragment8, jsx as jsx24 } from "react/jsx-runtime";
7159
7146
  var TERMINAL_BOUNDARY = String.raw`(?=$|\s|[.,;!?)}\]"'”’]+(?=$|\s))`;
7160
7147
  var PREFIX_BOUNDARY = String.raw`(^|[\s(\[{"'“‘])`;
7161
7148
  function escapeRegExp2(value) {
@@ -7264,7 +7251,7 @@ function MessageMentions({
7264
7251
  }).map((segment, segmentIndex) => {
7265
7252
  if (!segment.mention) return segment.text;
7266
7253
  const actionLabel = mentionActionLabel(segment.mention);
7267
- return /* @__PURE__ */ jsx22(
7254
+ return /* @__PURE__ */ jsx24(
7268
7255
  "button",
7269
7256
  {
7270
7257
  type: "button",
@@ -7290,7 +7277,7 @@ function MessageMentions({
7290
7277
  return cloneElement(
7291
7278
  element,
7292
7279
  void 0,
7293
- /* @__PURE__ */ jsx22(
7280
+ /* @__PURE__ */ jsx24(
7294
7281
  MessageMentions,
7295
7282
  {
7296
7283
  catalog,
@@ -7308,19 +7295,19 @@ function MessageMentions({
7308
7295
  return nodes.map(decorate);
7309
7296
  }
7310
7297
  function createMessageMentionMarkdownComponents(catalog, onActivate) {
7311
- const decorate = (children) => /* @__PURE__ */ jsx22(MessageMentions, { catalog, onActivate, children });
7298
+ const decorate = (children) => /* @__PURE__ */ jsx24(MessageMentions, { catalog, onActivate, children });
7312
7299
  const paragraph = ({ node: _node, children, ...props }) => {
7313
7300
  const content = Children.toArray(children).filter((child) => child !== null && child !== "");
7314
7301
  if (content.length === 1 && isValidElement2(content[0])) {
7315
7302
  const child = content[0];
7316
- if (child.props.node?.tagName === "img") return /* @__PURE__ */ jsx22(Fragment7, { children });
7317
- if (child.props.node?.tagName === "code" && "data-block" in child.props) return /* @__PURE__ */ jsx22(Fragment7, { children });
7303
+ if (child.props.node?.tagName === "img") return /* @__PURE__ */ jsx24(Fragment8, { children });
7304
+ if (child.props.node?.tagName === "code" && "data-block" in child.props) return /* @__PURE__ */ jsx24(Fragment8, { children });
7318
7305
  }
7319
- return /* @__PURE__ */ jsx22("p", { ...props, children: decorate(children) });
7306
+ return /* @__PURE__ */ jsx24("p", { ...props, children: decorate(children) });
7320
7307
  };
7321
- const tableHeader = ({ node: _node, children, className, ...props }) => /* @__PURE__ */ jsx22("th", { ...props, "data-streamdown": "table-header-cell", className: cn("whitespace-nowrap px-4 py-2 text-left font-semibold text-sm", className), children: decorate(children) });
7322
- const tableCell = ({ node: _node, children, className, ...props }) => /* @__PURE__ */ jsx22("td", { ...props, "data-streamdown": "table-cell", className: cn("px-4 py-2 text-sm", className), children: decorate(children) });
7323
- const listItem = ({ node: _node, children, className, ...props }) => /* @__PURE__ */ jsx22("li", { ...props, "data-streamdown": "list-item", className: cn("py-1 [&>p]:inline", className), children: decorate(children) });
7308
+ const tableHeader = ({ node: _node, children, className, ...props }) => /* @__PURE__ */ jsx24("th", { ...props, "data-streamdown": "table-header-cell", className: cn("whitespace-nowrap px-4 py-2 text-left font-semibold text-sm", className), children: decorate(children) });
7309
+ const tableCell = ({ node: _node, children, className, ...props }) => /* @__PURE__ */ jsx24("td", { ...props, "data-streamdown": "table-cell", className: cn("px-4 py-2 text-sm", className), children: decorate(children) });
7310
+ const listItem = ({ node: _node, children, className, ...props }) => /* @__PURE__ */ jsx24("li", { ...props, "data-streamdown": "list-item", className: cn("py-1 [&>p]:inline", className), children: decorate(children) });
7324
7311
  const headingClasses = {
7325
7312
  h1: "text-3xl",
7326
7313
  h2: "text-2xl",
@@ -7329,8 +7316,8 @@ function createMessageMentionMarkdownComponents(catalog, onActivate) {
7329
7316
  h5: "text-base",
7330
7317
  h6: "text-sm"
7331
7318
  };
7332
- const heading = (Tag) => ({ node: _node, children, className, ...props }) => /* @__PURE__ */ jsx22(Tag, { ...props, "data-streamdown": `heading-${Tag.slice(1)}`, className: cn("mt-6 mb-2 font-semibold", headingClasses[Tag], className), children: decorate(children) });
7333
- const inlineCode = ({ node: _node, children, className, ...props }) => /* @__PURE__ */ jsx22(
7319
+ const heading = (Tag) => ({ node: _node, children, className, ...props }) => /* @__PURE__ */ jsx24(Tag, { ...props, "data-streamdown": `heading-${Tag.slice(1)}`, className: cn("mt-6 mb-2 font-semibold", headingClasses[Tag], className), children: decorate(children) });
7320
+ const inlineCode = ({ node: _node, children, className, ...props }) => /* @__PURE__ */ jsx24(
7334
7321
  "code",
7335
7322
  {
7336
7323
  ...props,
@@ -7354,7 +7341,7 @@ function createMessageMentionMarkdownComponents(catalog, onActivate) {
7354
7341
  }
7355
7342
 
7356
7343
  // src/front/chat/components/PiTimelineMessage.tsx
7357
- import { jsx as jsx23, jsxs as jsxs20 } from "react/jsx-runtime";
7344
+ import { jsx as jsx25, jsxs as jsxs20 } from "react/jsx-runtime";
7358
7345
  var EMPTY_MENTION_CATALOG = { commands: [], skills: [], files: false };
7359
7346
  var COLLAPSIBLE_TOOL_NAMES = /* @__PURE__ */ new Set([
7360
7347
  "read",
@@ -7369,7 +7356,10 @@ var COLLAPSIBLE_TOOL_NAMES = /* @__PURE__ */ new Set([
7369
7356
  function isCollapsibleTool(part) {
7370
7357
  return part.type === "tool-call" && COLLAPSIBLE_TOOL_NAMES.has(part.toolName);
7371
7358
  }
7372
- function PiTimelineMessage({ message, isLast, isStreaming, showThoughts, toolRenderers, mentionCatalog = EMPTY_MENTION_CATALOG, onMentionActivate }) {
7359
+ function PiTimelineMessage(props) {
7360
+ return /* @__PURE__ */ jsx25(ChatMessageContributionBoundary, { message: props.message, children: /* @__PURE__ */ jsx25(DefaultPiTimelineMessage, { ...props }) });
7361
+ }
7362
+ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts, toolRenderers, mentionCatalog = EMPTY_MENTION_CATALOG, onMentionActivate }) {
7373
7363
  const role = message.role;
7374
7364
  const isAssistant = role === "assistant";
7375
7365
  const textParts = message.parts.filter((part) => part.type === "text");
@@ -7377,7 +7367,7 @@ function PiTimelineMessage({ message, isLast, isStreaming, showThoughts, toolRen
7377
7367
  const finalParts = groupRenderableParts(message);
7378
7368
  const attachmentSummaryPaths = role === "user" ? attachmentPathsFromTextParts(textParts) : [];
7379
7369
  const openArtifact = useOpenArtifact();
7380
- const effectiveMentionCatalog = useMemo10(() => ({
7370
+ const effectiveMentionCatalog = useMemo12(() => ({
7381
7371
  commands: mentionCatalog.commands,
7382
7372
  skills: mentionCatalog.skills,
7383
7373
  files: mentionCatalog.files && Boolean(openArtifact)
@@ -7398,7 +7388,7 @@ function PiTimelineMessage({ message, isLast, isStreaming, showThoughts, toolRen
7398
7388
  }, [openArtifact]);
7399
7389
  const hasMentions = effectiveMentionCatalog.files || effectiveMentionCatalog.commands.length > 0 || effectiveMentionCatalog.skills.length > 0;
7400
7390
  const mentionsEnabled = Boolean(isAssistant && !(isStreaming && isLast) && hasMentions);
7401
- const mentionMarkdownComponents = useMemo10(
7391
+ const mentionMarkdownComponents = useMemo12(
7402
7392
  () => mentionsEnabled ? createMessageMentionMarkdownComponents(effectiveMentionCatalog, activateCurrentMention) : void 0,
7403
7393
  [activateCurrentMention, effectiveMentionCatalog, mentionSignature, mentionsEnabled]
7404
7394
  );
@@ -7411,120 +7401,93 @@ function PiTimelineMessage({ message, isLast, isStreaming, showThoughts, toolRen
7411
7401
  "data-boring-agent-message-id": message.id,
7412
7402
  "data-boring-agent-message-role": role,
7413
7403
  "data-boring-agent-message-status": message.status,
7414
- className: "!max-w-full !gap-1.5",
7415
7404
  children: [
7416
- /* @__PURE__ */ jsxs20(
7417
- MessageContent,
7418
- {
7419
- className: cn(
7420
- "!overflow-visible text-[13px] leading-relaxed text-foreground",
7421
- role === "user" ? cn(
7422
- "!ml-auto !max-w-[80%] !rounded-[var(--radius-lg)]",
7423
- "!px-4 !py-2.5",
7424
- "!bg-secondary !text-secondary-foreground"
7425
- ) : "!w-full !bg-transparent !p-0"
7426
- ),
7427
- children: [
7428
- fileParts.length > 0 ? /* @__PURE__ */ jsx23(
7429
- Attachments,
7430
- {
7431
- variant: role === "user" ? "inline" : "list",
7432
- className: cn(
7433
- role === "user" ? "mb-2 w-full gap-2" : "gap-1.5",
7434
- role === "user" ? "[&>div]:max-w-full [&>div]:border-input/60 [&>div]:bg-background/35 [&>div]:px-1.5 [&>div]:text-secondary-foreground [&>div:hover]:bg-background/45" : void 0
7435
- ),
7436
- children: fileParts.map((file, index) => {
7437
- const openPath = file.path ?? attachmentSummaryPaths[index];
7438
- const openUrl = file.url && !openPath ? file.url : void 0;
7439
- const canOpen = Boolean(openArtifact && openPath || openUrl);
7440
- const openAttachment = () => {
7441
- if (openPath) {
7442
- if (file.filesystem) openArtifact?.(openPath, { filesystem: file.filesystem });
7443
- else openArtifact?.(openPath);
7444
- return;
7445
- }
7446
- if (openUrl) window.open(openUrl, "_blank", "noopener,noreferrer");
7447
- };
7448
- const openAttachmentFromKeyboard = (event) => {
7449
- if (event.key !== "Enter" && event.key !== " ") return;
7450
- event.preventDefault();
7451
- openAttachment();
7452
- };
7453
- return /* @__PURE__ */ jsxs20(
7454
- Attachment,
7455
- {
7456
- data: toAttachmentData(file, `file-${message.id}-${index}`),
7457
- ...canOpen ? {
7458
- role: "button",
7459
- tabIndex: 0,
7460
- title: openPath ? `Open ${openPath} in workspace` : `Open ${file.filename ?? "attachment"}`,
7461
- "aria-label": openPath ? `Open ${file.filename ?? openPath} in workspace` : `Open ${file.filename ?? "attachment"}`,
7462
- ...openPath ? { "data-workspace-path": openPath } : {},
7463
- onClick: openAttachment,
7464
- onKeyDown: openAttachmentFromKeyboard
7465
- } : void 0,
7466
- children: [
7467
- /* @__PURE__ */ jsx23(AttachmentPreview, { className: role === "user" ? "!size-5 shrink-0 rounded-[var(--radius-sm)]" : "size-10 shrink-0 rounded-[var(--radius-md)]" }),
7468
- /* @__PURE__ */ jsx23(AttachmentInfo, { className: role === "user" ? "min-w-0 max-w-[220px] flex-1 text-[12px]" : "min-w-0 flex-1" })
7469
- ]
7470
- },
7471
- `file-${message.id}-${index}`
7472
- );
7473
- })
7474
- }
7475
- ) : null,
7476
- finalParts.map((item) => {
7477
- if (item.kind === "reasoning") {
7478
- return /* @__PURE__ */ jsx23(
7479
- TimelineReasoningPart,
7480
- {
7481
- item,
7482
- showThoughts
7483
- },
7484
- item.key
7485
- );
7486
- }
7487
- if (item.kind === "tool-group") {
7488
- return /* @__PURE__ */ jsx23("div", { "data-boring-agent-part": "message-tools", children: /* @__PURE__ */ jsx23(ToolCallGroup, { tools: item.tools, mergedToolRenderers: toolRenderers }) }, item.key);
7489
- }
7490
- if (item.kind === "tool-plain") {
7491
- return /* @__PURE__ */ jsx23("div", { "data-boring-agent-part": "message-tools", children: /* @__PURE__ */ jsx23(PlainToolCard, { part: item.part, renderers: toolRenderers }) }, item.key);
7492
- }
7493
- if (item.part.type === "text") {
7494
- const text = textForMessageDisplay(item.part.text, role);
7495
- if (!text) return null;
7496
- return /* @__PURE__ */ jsx23("div", { "data-boring-agent-part": "message-text", children: /* @__PURE__ */ jsx23(
7497
- MessageResponse,
7498
- {
7499
- components: mentionMarkdownComponents,
7500
- className: cn(
7501
- "max-w-none",
7502
- "prose prose-invert prose-neutral",
7503
- "prose-p:my-3 prose-p:leading-[1.7] prose-p:text-[13px]",
7504
- "prose-headings:mt-5 prose-headings:mb-2 prose-headings:font-semibold prose-headings:tracking-[-0.01em]",
7505
- "prose-ul:my-3 prose-ul:pl-6 prose-ol:my-3 prose-ol:pl-6",
7506
- "prose-li:my-1.5 prose-li:leading-[1.7] prose-li:pl-1 prose-li:marker:text-muted-foreground/70",
7507
- "prose-strong:font-semibold prose-strong:text-foreground",
7508
- "prose-em:text-foreground/90",
7509
- "prose-a:text-[color:var(--accent)] prose-a:underline-offset-4 hover:prose-a:underline",
7510
- "prose-code:before:content-none prose-code:after:content-none",
7511
- "prose-pre:my-0 prose-pre:rounded-none prose-pre:border-0",
7512
- "prose-pre:bg-transparent prose-pre:p-0"
7513
- ),
7514
- children: text
7515
- },
7516
- `${mentionSignature || "no-message-mentions"}:${mentionsEnabled ? "enabled" : "static"}`
7517
- ) }, item.key);
7518
- }
7519
- if (item.part.type === "notice") {
7520
- return /* @__PURE__ */ jsx23(NoticeBubble, { level: item.part.level, text: item.part.text }, item.key);
7521
- }
7522
- return null;
7405
+ /* @__PURE__ */ jsxs20(MessageContent, { children: [
7406
+ fileParts.length > 0 ? /* @__PURE__ */ jsx25(
7407
+ Attachments,
7408
+ {
7409
+ variant: role === "user" ? "inline" : "list",
7410
+ className: cn(
7411
+ role === "user" ? "mb-2 w-full gap-2" : "gap-1.5",
7412
+ role === "user" ? "[&>div]:max-w-full [&>div]:border-input/60 [&>div]:bg-background/35 [&>div]:px-1.5 [&>div]:text-secondary-foreground [&>div:hover]:bg-background/45" : void 0
7413
+ ),
7414
+ children: fileParts.map((file, index) => {
7415
+ const openPath = file.path ?? attachmentSummaryPaths[index];
7416
+ const openUrl = file.url && !openPath ? file.url : void 0;
7417
+ const canOpen = Boolean(openArtifact && openPath || openUrl);
7418
+ const openAttachment = () => {
7419
+ if (openPath) {
7420
+ if (file.filesystem) openArtifact?.(openPath, { filesystem: file.filesystem });
7421
+ else openArtifact?.(openPath);
7422
+ return;
7423
+ }
7424
+ if (openUrl) window.open(openUrl, "_blank", "noopener,noreferrer");
7425
+ };
7426
+ const openAttachmentFromKeyboard = (event) => {
7427
+ if (event.key !== "Enter" && event.key !== " ") return;
7428
+ event.preventDefault();
7429
+ openAttachment();
7430
+ };
7431
+ return /* @__PURE__ */ jsxs20(
7432
+ Attachment,
7433
+ {
7434
+ data: toAttachmentData(file, `file-${message.id}-${index}`),
7435
+ ...canOpen ? {
7436
+ role: "button",
7437
+ tabIndex: 0,
7438
+ title: openPath ? `Open ${openPath} in workspace` : `Open ${file.filename ?? "attachment"}`,
7439
+ "aria-label": openPath ? `Open ${file.filename ?? openPath} in workspace` : `Open ${file.filename ?? "attachment"}`,
7440
+ ...openPath ? { "data-workspace-path": openPath } : {},
7441
+ onClick: openAttachment,
7442
+ onKeyDown: openAttachmentFromKeyboard
7443
+ } : void 0,
7444
+ children: [
7445
+ /* @__PURE__ */ jsx25(AttachmentPreview, { className: role === "user" ? "!size-5 shrink-0 rounded-[var(--radius-sm)]" : "size-10 shrink-0 rounded-[var(--radius-md)]" }),
7446
+ /* @__PURE__ */ jsx25(AttachmentInfo, { className: role === "user" ? "min-w-0 max-w-[220px] flex-1 text-[12px]" : "min-w-0 flex-1" })
7447
+ ]
7448
+ },
7449
+ `file-${message.id}-${index}`
7450
+ );
7523
7451
  })
7524
- ]
7525
- }
7526
- ),
7527
- isAssistant && (textParts.length > 0 || shouldReserveStreamingActions) ? /* @__PURE__ */ jsx23(
7452
+ }
7453
+ ) : null,
7454
+ finalParts.map((item) => {
7455
+ if (item.kind === "reasoning") {
7456
+ return /* @__PURE__ */ jsx25(
7457
+ TimelineReasoningPart,
7458
+ {
7459
+ item,
7460
+ showThoughts
7461
+ },
7462
+ item.key
7463
+ );
7464
+ }
7465
+ if (item.kind === "tool-group") {
7466
+ return /* @__PURE__ */ jsx25("div", { "data-boring-agent-part": "message-tools", children: /* @__PURE__ */ jsx25(ToolCallGroup, { tools: item.tools, mergedToolRenderers: toolRenderers }) }, item.key);
7467
+ }
7468
+ if (item.kind === "tool-plain") {
7469
+ return /* @__PURE__ */ jsx25("div", { "data-boring-agent-part": "message-tools", children: /* @__PURE__ */ jsx25(PlainToolCard, { part: item.part, renderers: toolRenderers }) }, item.key);
7470
+ }
7471
+ if (item.part.type === "text") {
7472
+ const text = textForMessageDisplay(item.part.text, role);
7473
+ if (!text) return null;
7474
+ return /* @__PURE__ */ jsx25("div", { "data-boring-agent-part": "message-text", children: /* @__PURE__ */ jsx25(
7475
+ MessageResponse,
7476
+ {
7477
+ components: mentionMarkdownComponents,
7478
+ className: "boring-agent-markdown max-w-none",
7479
+ children: text
7480
+ },
7481
+ `${mentionSignature || "no-message-mentions"}:${mentionsEnabled ? "enabled" : "static"}`
7482
+ ) }, item.key);
7483
+ }
7484
+ if (item.part.type === "notice") {
7485
+ return /* @__PURE__ */ jsx25(NoticeBubble, { level: item.part.level, text: item.part.text }, item.key);
7486
+ }
7487
+ return null;
7488
+ })
7489
+ ] }),
7490
+ isAssistant && (textParts.length > 0 || shouldReserveStreamingActions) ? /* @__PURE__ */ jsx25(
7528
7491
  MessageActionsBar,
7529
7492
  {
7530
7493
  text: textParts.map((part) => part.text).join("\n\n"),
@@ -7559,15 +7522,15 @@ function TimelineReasoningPart({ item, showThoughts }) {
7559
7522
  onOpenChange: handleOpenChange,
7560
7523
  autoClose: false,
7561
7524
  children: [
7562
- /* @__PURE__ */ jsx23(
7525
+ /* @__PURE__ */ jsx25(
7563
7526
  ReasoningTrigger,
7564
7527
  {
7565
7528
  className: "mb-1 w-fit rounded-[var(--radius-sm)] px-0 py-0 !text-xs !font-normal !text-muted-foreground/75 hover:bg-transparent hover:!text-muted-foreground/75 [&_svg]:!text-muted-foreground/75",
7566
- getThinkingMessage: (streaming) => /* @__PURE__ */ jsx23("span", { children: streaming ? "thinking" : "thoughts" }),
7529
+ getThinkingMessage: (streaming) => /* @__PURE__ */ jsx25("span", { children: streaming ? "thinking" : "thoughts" }),
7567
7530
  onClick: toggleManualOpen
7568
7531
  }
7569
7532
  ),
7570
- /* @__PURE__ */ jsx23(ReasoningContent, { children: item.text })
7533
+ /* @__PURE__ */ jsx25(ReasoningContent, { children: item.text })
7571
7534
  ]
7572
7535
  }
7573
7536
  );
@@ -7576,7 +7539,7 @@ function PlainToolCard({ part, renderers }) {
7576
7539
  const toolPart = toToolPart(part);
7577
7540
  if (!toolPart) return null;
7578
7541
  const { renderer, part: resolved, resolution } = resolveToolRendererForPart(toolPart, renderers);
7579
- return /* @__PURE__ */ jsx23(
7542
+ return /* @__PURE__ */ jsx25(
7580
7543
  "div",
7581
7544
  {
7582
7545
  "data-tool-call-id": resolved.toolCallId,
@@ -7625,13 +7588,13 @@ ${part.text}`;
7625
7588
  return grouped;
7626
7589
  }
7627
7590
  function NoticeBubble({ level, text }) {
7628
- return /* @__PURE__ */ jsx23(
7591
+ return /* @__PURE__ */ jsx25(
7629
7592
  "div",
7630
7593
  {
7631
7594
  "data-boring-agent-part": "message-notice",
7632
7595
  "data-notice-level": level,
7633
7596
  className: noticeSurfaceClass(level, "text-xs"),
7634
- children: /* @__PURE__ */ jsx23("div", { className: noticeTextClass(), children: text })
7597
+ children: /* @__PURE__ */ jsx25("div", { className: noticeTextClass(), children: text })
7635
7598
  }
7636
7599
  );
7637
7600
  }
@@ -7710,7 +7673,7 @@ function MessageActionsBar({
7710
7673
  "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[color:var(--accent)]/40"
7711
7674
  );
7712
7675
  const hiddenActionProps = visible ? {} : { tabIndex: -1 };
7713
- return /* @__PURE__ */ jsx23(
7676
+ return /* @__PURE__ */ jsx25(
7714
7677
  "div",
7715
7678
  {
7716
7679
  "aria-hidden": !visible,
@@ -7718,7 +7681,7 @@ function MessageActionsBar({
7718
7681
  "flex min-h-6 items-center gap-0.5 -mt-1 transition-opacity duration-200",
7719
7682
  visible ? "opacity-100" : "pointer-events-none opacity-0"
7720
7683
  ),
7721
- children: /* @__PURE__ */ jsx23(
7684
+ children: /* @__PURE__ */ jsx25(
7722
7685
  Button11,
7723
7686
  {
7724
7687
  type: "button",
@@ -7729,7 +7692,7 @@ function MessageActionsBar({
7729
7692
  "aria-label": copied ? "Copied" : "Copy message",
7730
7693
  title: copied ? "Copied" : "Copy",
7731
7694
  ...hiddenActionProps,
7732
- children: copied ? /* @__PURE__ */ jsx23(CheckIcon2, { className: "h-3.5 w-3.5 text-[color:var(--accent)]" }) : /* @__PURE__ */ jsx23(CopyIcon3, { className: "h-3.5 w-3.5" })
7695
+ children: copied ? /* @__PURE__ */ jsx25(CheckIcon2, { className: "h-3.5 w-3.5 text-[color:var(--accent)]" }) : /* @__PURE__ */ jsx25(CopyIcon3, { className: "h-3.5 w-3.5" })
7733
7696
  }
7734
7697
  )
7735
7698
  }
@@ -7750,7 +7713,7 @@ function partKey(messageId, part, index) {
7750
7713
  }
7751
7714
 
7752
7715
  // src/front/chat/components/PiConversationSurface.tsx
7753
- import { jsx as jsx24, jsxs as jsxs21 } from "react/jsx-runtime";
7716
+ import { jsx as jsx26, jsxs as jsxs21 } from "react/jsx-runtime";
7754
7717
  var TRANSCRIPT_WINDOW = 60;
7755
7718
  var TRANSCRIPT_WINDOW_STEP = 40;
7756
7719
  var LOAD_OLDER_THRESHOLD_PX = 320;
@@ -7800,10 +7763,10 @@ function PiConversationSurface({
7800
7763
  emptyHero && "py-4 text-center"
7801
7764
  ), children: [
7802
7765
  messages.length === 0 && emptyStateHydrating ? /* @__PURE__ */ jsxs21("div", { className: "flex items-center gap-2 rounded-xl border border-border bg-card/70 px-4 py-3 text-sm text-muted-foreground", children: [
7803
- /* @__PURE__ */ jsx24(Loader22, { className: "h-4 w-4 animate-spin" }),
7766
+ /* @__PURE__ */ jsx26(Loader22, { className: "h-4 w-4 animate-spin" }),
7804
7767
  "Loading chat history\u2026"
7805
7768
  ] }) : null,
7806
- messages.length === 0 && !emptyStateHydrating ? /* @__PURE__ */ jsx24(
7769
+ messages.length === 0 && !emptyStateHydrating ? /* @__PURE__ */ jsx26(
7807
7770
  ChatEmptyState,
7808
7771
  {
7809
7772
  eyebrow: emptyState?.eyebrow,
@@ -7821,8 +7784,8 @@ function PiConversationSurface({
7821
7784
  }
7822
7785
  }
7823
7786
  ) : null,
7824
- hasOlder ? /* @__PURE__ */ jsx24(TranscriptHistoryLoader, { olderCount, onLoadOlder: loadOlder }) : null,
7825
- visibleItems.map(({ message, key }, index) => /* @__PURE__ */ jsx24(
7787
+ hasOlder ? /* @__PURE__ */ jsx26(TranscriptHistoryLoader, { olderCount, onLoadOlder: loadOlder }) : null,
7788
+ visibleItems.map(({ message, key }, index) => /* @__PURE__ */ jsx26(
7826
7789
  PiTimelineMessage,
7827
7790
  {
7828
7791
  message,
@@ -7835,9 +7798,9 @@ function PiConversationSurface({
7835
7798
  },
7836
7799
  key
7837
7800
  )),
7838
- /* @__PURE__ */ jsx24(RuntimeNoticeMessages, { notices: runtimeNotices, onDismiss: onDismissNotice, renderAction: renderNoticeAction })
7801
+ /* @__PURE__ */ jsx26(RuntimeNoticeMessages, { notices: runtimeNotices, onDismiss: onDismissNotice, renderAction: renderNoticeAction })
7839
7802
  ] }),
7840
- /* @__PURE__ */ jsx24(ConversationScrollButton, {})
7803
+ /* @__PURE__ */ jsx26(ConversationScrollButton, {})
7841
7804
  ]
7842
7805
  }
7843
7806
  );
@@ -7869,7 +7832,7 @@ function TranscriptHistoryLoader({ olderCount, onLoadOlder }) {
7869
7832
  }
7870
7833
  if (el.scrollTop > LOAD_OLDER_THRESHOLD_PX) armed.current = true;
7871
7834
  });
7872
- return /* @__PURE__ */ jsx24("div", { className: "flex justify-center", "data-boring-agent-part": "transcript-load-older", children: /* @__PURE__ */ jsxs21(
7835
+ return /* @__PURE__ */ jsx26("div", { className: "flex justify-center", "data-boring-agent-part": "transcript-load-older", children: /* @__PURE__ */ jsxs21(
7873
7836
  "button",
7874
7837
  {
7875
7838
  type: "button",
@@ -7913,7 +7876,7 @@ import { motion as motion2 } from "motion/react";
7913
7876
  import { useEffect as useEffect15, useRef as useRef12 } from "react";
7914
7877
 
7915
7878
  // src/front/composer/ComposerStatusBanner.tsx
7916
- import { jsx as jsx25, jsxs as jsxs22 } from "react/jsx-runtime";
7879
+ import { jsx as jsx27, jsxs as jsxs22 } from "react/jsx-runtime";
7917
7880
  function ComposerStatusBanner({
7918
7881
  tone,
7919
7882
  dataAttribute,
@@ -7942,8 +7905,8 @@ function ComposerStatusBanner({
7942
7905
  maxWidthClassName
7943
7906
  ),
7944
7907
  children: [
7945
- /* @__PURE__ */ jsx25("span", { className: "inline-block h-2 w-2 animate-pulse rounded-full bg-accent", "aria-hidden": "true" }),
7946
- /* @__PURE__ */ jsx25("span", { children: runningContent })
7908
+ /* @__PURE__ */ jsx27("span", { className: "inline-block h-2 w-2 animate-pulse rounded-full bg-accent", "aria-hidden": "true" }),
7909
+ /* @__PURE__ */ jsx27("span", { children: runningContent })
7947
7910
  ]
7948
7911
  }
7949
7912
  );
@@ -7962,12 +7925,12 @@ function ComposerStatusBanner({
7962
7925
  ),
7963
7926
  children: [
7964
7927
  /* @__PURE__ */ jsxs22("div", { className: "flex items-start gap-2", children: [
7965
- /* @__PURE__ */ jsx25("span", { className: "mt-0.5 text-[oklch(0.45_0.13_148)]", "aria-hidden": "true", children: "\u2713" }),
7928
+ /* @__PURE__ */ jsx27("span", { className: "mt-0.5 text-[oklch(0.45_0.13_148)]", "aria-hidden": "true", children: "\u2713" }),
7966
7929
  /* @__PURE__ */ jsxs22("span", { className: "min-w-0 flex-1", children: [
7967
- /* @__PURE__ */ jsx25("span", { className: "block font-medium leading-5", children: title }),
7968
- detail ? /* @__PURE__ */ jsx25("span", { className: "block leading-4 text-muted-foreground", children: detail }) : null
7930
+ /* @__PURE__ */ jsx27("span", { className: "block font-medium leading-5", children: title }),
7931
+ detail ? /* @__PURE__ */ jsx27("span", { className: "block leading-4 text-muted-foreground", children: detail }) : null
7969
7932
  ] }),
7970
- onDismiss ? /* @__PURE__ */ jsx25(
7933
+ onDismiss ? /* @__PURE__ */ jsx27(
7971
7934
  "button",
7972
7935
  {
7973
7936
  type: "button",
@@ -7996,9 +7959,9 @@ function ComposerStatusBanner({
7996
7959
  ),
7997
7960
  children: [
7998
7961
  /* @__PURE__ */ jsxs22("div", { className: "flex items-center gap-2", children: [
7999
- /* @__PURE__ */ jsx25("span", { className: "text-destructive", "aria-hidden": "true", children: "\u26A0" }),
8000
- /* @__PURE__ */ jsx25("span", { className: "flex-1 font-medium", children: title }),
8001
- onRetry ? /* @__PURE__ */ jsx25(
7962
+ /* @__PURE__ */ jsx27("span", { className: "text-destructive", "aria-hidden": "true", children: "\u26A0" }),
7963
+ /* @__PURE__ */ jsx27("span", { className: "flex-1 font-medium", children: title }),
7964
+ onRetry ? /* @__PURE__ */ jsx27(
8002
7965
  "button",
8003
7966
  {
8004
7967
  type: "button",
@@ -8007,7 +7970,7 @@ function ComposerStatusBanner({
8007
7970
  children: retryLabel
8008
7971
  }
8009
7972
  ) : null,
8010
- onDismiss ? /* @__PURE__ */ jsx25(
7973
+ onDismiss ? /* @__PURE__ */ jsx27(
8011
7974
  "button",
8012
7975
  {
8013
7976
  type: "button",
@@ -8018,14 +7981,14 @@ function ComposerStatusBanner({
8018
7981
  }
8019
7982
  ) : null
8020
7983
  ] }),
8021
- message ? /* @__PURE__ */ jsx25("pre", { className: "mt-2 whitespace-pre-wrap break-words text-[11px] text-destructive/90", children: message }) : null
7984
+ message ? /* @__PURE__ */ jsx27("pre", { className: "mt-2 whitespace-pre-wrap break-words text-[11px] text-destructive/90", children: message }) : null
8022
7985
  ]
8023
7986
  }
8024
7987
  );
8025
7988
  }
8026
7989
 
8027
7990
  // src/front/composer/PluginUpdateStatus.tsx
8028
- import { jsx as jsx26, jsxs as jsxs23 } from "react/jsx-runtime";
7991
+ import { jsx as jsx28, jsxs as jsxs23 } from "react/jsx-runtime";
8029
7992
  function PluginUpdateStatus({
8030
7993
  state,
8031
7994
  onDismiss,
@@ -8047,7 +8010,7 @@ function PluginUpdateStatus({
8047
8010
  }, [state?.kind, successDismissKey, successAutoDismissMs]);
8048
8011
  if (!state) return null;
8049
8012
  if (state.kind === "running") {
8050
- return /* @__PURE__ */ jsx26(
8013
+ return /* @__PURE__ */ jsx28(
8051
8014
  ComposerStatusBanner,
8052
8015
  {
8053
8016
  tone: "running",
@@ -8085,7 +8048,7 @@ function PluginUpdateStatus({
8085
8048
  ),
8086
8049
  children: [
8087
8050
  /* @__PURE__ */ jsxs23("div", { className: "flex items-center gap-1.5 font-medium text-[oklch(0.48_0.15_60)]", children: [
8088
- /* @__PURE__ */ jsx26("span", { "aria-hidden": "true", children: "\u26A0" }),
8051
+ /* @__PURE__ */ jsx28("span", { "aria-hidden": "true", children: "\u26A0" }),
8089
8052
  /* @__PURE__ */ jsxs23("span", { children: [
8090
8053
  "Reload diagnostics for ",
8091
8054
  diagnostics.length,
@@ -8093,8 +8056,8 @@ function PluginUpdateStatus({
8093
8056
  diagnostics.length === 1 ? "" : "s"
8094
8057
  ] })
8095
8058
  ] }),
8096
- /* @__PURE__ */ jsx26("ul", { className: "mt-1 ml-4 list-disc text-foreground/85", children: diagnostics.map((diagnostic, index) => /* @__PURE__ */ jsxs23("li", { children: [
8097
- /* @__PURE__ */ jsx26("code", { className: "font-mono text-[10.5px]", children: diagnostic.pluginId ?? diagnostic.source ?? "plugin" }),
8059
+ /* @__PURE__ */ jsx28("ul", { className: "mt-1 ml-4 list-disc text-foreground/85", children: diagnostics.map((diagnostic, index) => /* @__PURE__ */ jsxs23("li", { children: [
8060
+ /* @__PURE__ */ jsx28("code", { className: "font-mono text-[10.5px]", children: diagnostic.pluginId ?? diagnostic.source ?? "plugin" }),
8098
8061
  " \u2014 ",
8099
8062
  diagnostic.message ?? "reload diagnostic"
8100
8063
  ] }, `${diagnostic.pluginId ?? diagnostic.source ?? "plugin"}-${index}`)) })
@@ -8111,7 +8074,7 @@ function PluginUpdateStatus({
8111
8074
  ),
8112
8075
  children: [
8113
8076
  /* @__PURE__ */ jsxs23("div", { className: "flex items-center gap-1.5 font-medium text-[oklch(0.48_0.15_60)]", children: [
8114
- /* @__PURE__ */ jsx26("span", { "aria-hidden": "true", children: "\u26A0" }),
8077
+ /* @__PURE__ */ jsx28("span", { "aria-hidden": "true", children: "\u26A0" }),
8115
8078
  /* @__PURE__ */ jsxs23("span", { children: [
8116
8079
  "Restart needed for ",
8117
8080
  warnings.length,
@@ -8119,12 +8082,12 @@ function PluginUpdateStatus({
8119
8082
  warnings.length === 1 ? "" : "s"
8120
8083
  ] })
8121
8084
  ] }),
8122
- /* @__PURE__ */ jsx26("ul", { className: "mt-1 ml-4 list-disc text-foreground/85", children: warnings.map((w) => /* @__PURE__ */ jsxs23("li", { children: [
8123
- /* @__PURE__ */ jsx26("code", { className: "font-mono text-[10.5px]", children: w.id }),
8085
+ /* @__PURE__ */ jsx28("ul", { className: "mt-1 ml-4 list-disc text-foreground/85", children: warnings.map((w) => /* @__PURE__ */ jsxs23("li", { children: [
8086
+ /* @__PURE__ */ jsx28("code", { className: "font-mono text-[10.5px]", children: w.id }),
8124
8087
  " \u2014 ",
8125
8088
  w.surfaces.join(" + ")
8126
8089
  ] }, w.id)) }),
8127
- /* @__PURE__ */ jsx26("p", { className: "mt-1 text-foreground/70", children: "The interface reloaded successfully, but routes and tools were wired at boot. Restart the workspace process to pick up the new code." })
8090
+ /* @__PURE__ */ jsx28("p", { className: "mt-1 text-foreground/70", children: "The interface reloaded successfully, but routes and tools were wired at boot. Restart the workspace process to pick up the new code." })
8128
8091
  ]
8129
8092
  }
8130
8093
  ) : null
@@ -8132,7 +8095,7 @@ function PluginUpdateStatus({
8132
8095
  }
8133
8096
  );
8134
8097
  }
8135
- return /* @__PURE__ */ jsx26(
8098
+ return /* @__PURE__ */ jsx28(
8136
8099
  ComposerStatusBanner,
8137
8100
  {
8138
8101
  tone: "error",
@@ -8150,7 +8113,7 @@ function PluginUpdateStatus({
8150
8113
 
8151
8114
  // src/front/composer/CommandRunStatus.tsx
8152
8115
  import { useEffect as useEffect16, useRef as useRef13 } from "react";
8153
- import { Fragment as Fragment8, jsx as jsx27, jsxs as jsxs24 } from "react/jsx-runtime";
8116
+ import { Fragment as Fragment9, jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
8154
8117
  function CommandRunStatus({
8155
8118
  state,
8156
8119
  onDismiss,
@@ -8169,13 +8132,13 @@ function CommandRunStatus({
8169
8132
  }, [state?.kind, successKey, successAutoDismissMs]);
8170
8133
  if (!state) return null;
8171
8134
  if (state.kind === "running") {
8172
- return /* @__PURE__ */ jsx27(
8135
+ return /* @__PURE__ */ jsx29(
8173
8136
  ComposerStatusBanner,
8174
8137
  {
8175
8138
  tone: "running",
8176
8139
  dataAttribute: "data-boring-command-run",
8177
8140
  maxWidthClassName,
8178
- runningContent: state.command ? /* @__PURE__ */ jsxs24(Fragment8, { children: [
8141
+ runningContent: state.command ? /* @__PURE__ */ jsxs24(Fragment9, { children: [
8179
8142
  " Running ",
8180
8143
  /* @__PURE__ */ jsxs24("code", { className: "font-mono", children: [
8181
8144
  "/",
@@ -8187,13 +8150,13 @@ function CommandRunStatus({
8187
8150
  );
8188
8151
  }
8189
8152
  if (state.kind === "success") {
8190
- return /* @__PURE__ */ jsx27(
8153
+ return /* @__PURE__ */ jsx29(
8191
8154
  ComposerStatusBanner,
8192
8155
  {
8193
8156
  tone: "success",
8194
8157
  dataAttribute: "data-boring-command-run",
8195
8158
  maxWidthClassName,
8196
- title: state.command ? /* @__PURE__ */ jsxs24(Fragment8, { children: [
8159
+ title: state.command ? /* @__PURE__ */ jsxs24(Fragment9, { children: [
8197
8160
  " Ran ",
8198
8161
  /* @__PURE__ */ jsxs24("code", { className: "font-mono", children: [
8199
8162
  "/",
@@ -8207,13 +8170,13 @@ function CommandRunStatus({
8207
8170
  }
8208
8171
  );
8209
8172
  }
8210
- return /* @__PURE__ */ jsx27(
8173
+ return /* @__PURE__ */ jsx29(
8211
8174
  ComposerStatusBanner,
8212
8175
  {
8213
8176
  tone: "error",
8214
8177
  dataAttribute: "data-boring-command-run",
8215
8178
  maxWidthClassName,
8216
- title: state.command ? /* @__PURE__ */ jsxs24(Fragment8, { children: [
8179
+ title: state.command ? /* @__PURE__ */ jsxs24(Fragment9, { children: [
8217
8180
  " ",
8218
8181
  /* @__PURE__ */ jsxs24("code", { className: "font-mono", children: [
8219
8182
  "/",
@@ -8263,7 +8226,7 @@ function displayProviderLabel(provider) {
8263
8226
  }
8264
8227
 
8265
8228
  // src/front/chatPanelComposerControls.tsx
8266
- import { Fragment as Fragment9, jsx as jsx28, jsxs as jsxs25 } from "react/jsx-runtime";
8229
+ import { Fragment as Fragment10, jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
8267
8230
  var composerActionClass = cn(
8268
8231
  "inline-flex h-8 items-center justify-center gap-1.5 rounded-[var(--radius-md)] border-0 bg-transparent",
8269
8232
  "text-muted-foreground shadow-none transition",
@@ -8331,7 +8294,7 @@ function useDismissOnOutsidePointer(ref, onClose) {
8331
8294
  }
8332
8295
  function ThinkingLevelGlyph({ level }) {
8333
8296
  const lit = level === "off" ? 0 : level === "low" ? 1 : level === "medium" ? 2 : 3;
8334
- return /* @__PURE__ */ jsx28(
8297
+ return /* @__PURE__ */ jsx30(
8335
8298
  "svg",
8336
8299
  {
8337
8300
  "aria-hidden": "true",
@@ -8340,7 +8303,7 @@ function ThinkingLevelGlyph({ level }) {
8340
8303
  viewBox: "0 0 14 14",
8341
8304
  fill: "none",
8342
8305
  className: "shrink-0",
8343
- children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx28(
8306
+ children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx30(
8344
8307
  "rect",
8345
8308
  {
8346
8309
  x: 2 + i * 4,
@@ -8386,7 +8349,7 @@ var ModelSelectTrigger = forwardRef(function ModelSelectTrigger2({
8386
8349
  }, ref) {
8387
8350
  const triggerLabel = modelTriggerLabel(value, options, emptyLabel);
8388
8351
  const triggerDisplay = value ? `${triggerLabel} (${displayProviderLabel(value.provider)})` : triggerLabel;
8389
- return /* @__PURE__ */ jsx28(
8352
+ return /* @__PURE__ */ jsx30(
8390
8353
  "button",
8391
8354
  {
8392
8355
  ref,
@@ -8404,12 +8367,12 @@ var ModelSelectTrigger = forwardRef(function ModelSelectTrigger2({
8404
8367
  className
8405
8368
  ),
8406
8369
  ...props,
8407
- children: trigger === "slash" ? /* @__PURE__ */ jsxs25(Fragment9, { children: [
8408
- /* @__PURE__ */ jsx28("span", { className: "text-muted-foreground", children: "/model: " }),
8409
- /* @__PURE__ */ jsx28("span", { className: "text-foreground", children: triggerDisplay })
8410
- ] }) : /* @__PURE__ */ jsxs25(Fragment9, { children: [
8411
- /* @__PURE__ */ jsx28("span", { className: "min-w-0 truncate", children: triggerDisplay }),
8412
- /* @__PURE__ */ jsx28(ChevronDownIcon5, { className: "h-3 w-3 shrink-0 text-muted-foreground/50", "aria-hidden": "true" })
8370
+ children: trigger === "slash" ? /* @__PURE__ */ jsxs25(Fragment10, { children: [
8371
+ /* @__PURE__ */ jsx30("span", { className: "text-muted-foreground", children: "/model: " }),
8372
+ /* @__PURE__ */ jsx30("span", { className: "text-foreground", children: triggerDisplay })
8373
+ ] }) : /* @__PURE__ */ jsxs25(Fragment10, { children: [
8374
+ /* @__PURE__ */ jsx30("span", { className: "min-w-0 truncate", children: triggerDisplay }),
8375
+ /* @__PURE__ */ jsx30(ChevronDownIcon5, { className: "h-3 w-3 shrink-0 text-muted-foreground/50", "aria-hidden": "true" })
8413
8376
  ] })
8414
8377
  }
8415
8378
  );
@@ -8466,8 +8429,8 @@ function ModelPickerMenu({
8466
8429
  }, [activeIndex, disabled, keyboardOptions, onChange, onClose]);
8467
8430
  const optionIndexOffset = hideDefaultOption ? 0 : 1;
8468
8431
  const optionIndexes = new Map(groupedOptions.map((option, index) => [encodeModelKey(option), index + optionIndexOffset]));
8469
- return /* @__PURE__ */ jsx28("div", { ref: menuRef, "data-boring-agent": "", "data-boring-agent-part": "model-picker-menu", className: cn(composerPickerMenuClass, className), children: /* @__PURE__ */ jsxs25(Command, { className: "bg-transparent text-[color:var(--popover-foreground)]", children: [
8470
- menuOptions.length > 8 && /* @__PURE__ */ jsx28("div", { className: "border-b border-border/60 px-2", children: /* @__PURE__ */ jsx28(
8432
+ return /* @__PURE__ */ jsx30("div", { ref: menuRef, "data-boring-agent": "", "data-boring-agent-part": "model-picker-menu", className: cn(composerPickerMenuClass, className), children: /* @__PURE__ */ jsxs25(Command, { className: "bg-transparent text-[color:var(--popover-foreground)]", children: [
8433
+ menuOptions.length > 8 && /* @__PURE__ */ jsx30("div", { className: "border-b border-border/60 px-2", children: /* @__PURE__ */ jsx30(
8471
8434
  CommandInput,
8472
8435
  {
8473
8436
  placeholder: "Search models\u2026",
@@ -8476,8 +8439,8 @@ function ModelPickerMenu({
8476
8439
  }
8477
8440
  ) }),
8478
8441
  /* @__PURE__ */ jsxs25(CommandList, { className: "max-h-[300px] p-0.5", children: [
8479
- /* @__PURE__ */ jsx28(CommandEmpty, { className: "py-4 text-center text-[13px] text-muted-foreground", children: "No models found" }),
8480
- !hideDefaultOption ? /* @__PURE__ */ jsx28(CommandGroup, { children: /* @__PURE__ */ jsxs25(
8442
+ /* @__PURE__ */ jsx30(CommandEmpty, { className: "py-4 text-center text-[13px] text-muted-foreground", children: "No models found" }),
8443
+ !hideDefaultOption ? /* @__PURE__ */ jsx30(CommandGroup, { children: /* @__PURE__ */ jsxs25(
8481
8444
  CommandItem,
8482
8445
  {
8483
8446
  value: "Default model automatic auto",
@@ -8488,7 +8451,7 @@ function ModelPickerMenu({
8488
8451
  },
8489
8452
  className: selectorItemClass(!value || activeIndex === 0),
8490
8453
  children: [
8491
- /* @__PURE__ */ jsx28(
8454
+ /* @__PURE__ */ jsx30(
8492
8455
  CheckIcon3,
8493
8456
  {
8494
8457
  className: cn(
@@ -8497,12 +8460,12 @@ function ModelPickerMenu({
8497
8460
  )
8498
8461
  }
8499
8462
  ),
8500
- /* @__PURE__ */ jsx28("span", { className: "truncate", children: "Default model" }),
8501
- /* @__PURE__ */ jsx28("span", { className: "ml-auto shrink-0 text-[10px] text-muted-foreground/60", children: "auto" })
8463
+ /* @__PURE__ */ jsx30("span", { className: "truncate", children: "Default model" }),
8464
+ /* @__PURE__ */ jsx30("span", { className: "ml-auto shrink-0 text-[10px] text-muted-foreground/60", children: "auto" })
8502
8465
  ]
8503
8466
  }
8504
8467
  ) }) : null,
8505
- [...groups.entries()].map(([provider, list]) => /* @__PURE__ */ jsx28(
8468
+ [...groups.entries()].map(([provider, list]) => /* @__PURE__ */ jsx30(
8506
8469
  CommandGroup,
8507
8470
  {
8508
8471
  heading: displayProviderLabel(provider),
@@ -8522,7 +8485,7 @@ function ModelPickerMenu({
8522
8485
  },
8523
8486
  className: cn(selectorItemClass(key === currentKey || activeIndex === itemIndex)),
8524
8487
  children: [
8525
- /* @__PURE__ */ jsx28(
8488
+ /* @__PURE__ */ jsx30(
8526
8489
  CheckIcon3,
8527
8490
  {
8528
8491
  className: cn(
@@ -8531,8 +8494,8 @@ function ModelPickerMenu({
8531
8494
  )
8532
8495
  }
8533
8496
  ),
8534
- /* @__PURE__ */ jsx28("span", { className: "min-w-0 flex-1 truncate whitespace-nowrap", children: label }),
8535
- /* @__PURE__ */ jsx28("span", { className: "ml-auto max-w-[45%] shrink-0 truncate whitespace-nowrap text-right text-[10px] text-muted-foreground/60", children: m.id })
8497
+ /* @__PURE__ */ jsx30("span", { className: "min-w-0 flex-1 truncate whitespace-nowrap", children: label }),
8498
+ /* @__PURE__ */ jsx30("span", { className: "ml-auto max-w-[45%] shrink-0 truncate whitespace-nowrap text-right text-[10px] text-muted-foreground/60", children: m.id })
8536
8499
  ]
8537
8500
  },
8538
8501
  key
@@ -8571,7 +8534,7 @@ function ModelSelect({
8571
8534
  previousOpenSignalRef.current = openSignal;
8572
8535
  }, [disabled, openSignal]);
8573
8536
  return /* @__PURE__ */ jsxs25(Popover, { open, onOpenChange: disabled ? void 0 : setOpen, children: [
8574
- /* @__PURE__ */ jsx28(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx28(
8537
+ /* @__PURE__ */ jsx30(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx30(
8575
8538
  ModelSelectTrigger,
8576
8539
  {
8577
8540
  value,
@@ -8585,7 +8548,7 @@ function ModelSelect({
8585
8548
  "aria-describedby": ariaDescribedBy
8586
8549
  }
8587
8550
  ) }),
8588
- /* @__PURE__ */ jsx28(
8551
+ /* @__PURE__ */ jsx30(
8589
8552
  PopoverContent,
8590
8553
  {
8591
8554
  align: trigger === "slash" ? "center" : "start",
@@ -8594,7 +8557,7 @@ function ModelSelect({
8594
8557
  collisionPadding: 12,
8595
8558
  "data-boring-agent": "",
8596
8559
  className: cn("w-[min(92vw,340px)]", selectorContentClass),
8597
- children: /* @__PURE__ */ jsx28(
8560
+ children: /* @__PURE__ */ jsx30(
8598
8561
  ModelPickerMenu,
8599
8562
  {
8600
8563
  value,
@@ -8632,7 +8595,7 @@ function ThinkingSelect({
8632
8595
  previousOpenSignalRef.current = openSignal;
8633
8596
  }, [disabled, openSignal]);
8634
8597
  return /* @__PURE__ */ jsxs25(Popover, { open, onOpenChange: disabled ? void 0 : setOpen, children: [
8635
- /* @__PURE__ */ jsx28(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx28(
8598
+ /* @__PURE__ */ jsx30(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx30(
8636
8599
  ThinkingSelectTrigger,
8637
8600
  {
8638
8601
  value,
@@ -8642,7 +8605,7 @@ function ThinkingSelect({
8642
8605
  className
8643
8606
  }
8644
8607
  ) }),
8645
- /* @__PURE__ */ jsx28(
8608
+ /* @__PURE__ */ jsx30(
8646
8609
  PopoverContent,
8647
8610
  {
8648
8611
  align: trigger === "slash" ? "center" : "end",
@@ -8651,7 +8614,7 @@ function ThinkingSelect({
8651
8614
  collisionPadding: 12,
8652
8615
  "data-boring-agent": "",
8653
8616
  className: cn("w-[min(92vw,240px)]", selectorContentClass),
8654
- children: /* @__PURE__ */ jsx28(
8617
+ children: /* @__PURE__ */ jsx30(
8655
8618
  ThinkingPickerMenu,
8656
8619
  {
8657
8620
  value,
@@ -8695,13 +8658,13 @@ var ThinkingSelectTrigger = forwardRef(function ThinkingSelectTrigger2({
8695
8658
  onClick,
8696
8659
  ...props,
8697
8660
  children: [
8698
- trigger === "button" ? /* @__PURE__ */ jsx28(ThinkingLevelGlyph, { level: value }) : null,
8699
- trigger === "slash" ? /* @__PURE__ */ jsxs25(Fragment9, { children: [
8700
- /* @__PURE__ */ jsx28("span", { className: "text-muted-foreground", children: "/thinking: " }),
8701
- /* @__PURE__ */ jsx28("span", { className: "text-foreground", children: THINKING_LEVEL_STATUS_LABELS[value] })
8702
- ] }) : /* @__PURE__ */ jsxs25(Fragment9, { children: [
8703
- /* @__PURE__ */ jsx28("span", { children: THINKING_LEVEL_LABELS[value] }),
8704
- /* @__PURE__ */ jsx28(ChevronDownIcon5, { className: "h-3 w-3 shrink-0 text-muted-foreground/50", "aria-hidden": "true" })
8661
+ trigger === "button" ? /* @__PURE__ */ jsx30(ThinkingLevelGlyph, { level: value }) : null,
8662
+ trigger === "slash" ? /* @__PURE__ */ jsxs25(Fragment10, { children: [
8663
+ /* @__PURE__ */ jsx30("span", { className: "text-muted-foreground", children: "/thinking: " }),
8664
+ /* @__PURE__ */ jsx30("span", { className: "text-foreground", children: THINKING_LEVEL_STATUS_LABELS[value] })
8665
+ ] }) : /* @__PURE__ */ jsxs25(Fragment10, { children: [
8666
+ /* @__PURE__ */ jsx30("span", { children: THINKING_LEVEL_LABELS[value] }),
8667
+ /* @__PURE__ */ jsx30(ChevronDownIcon5, { className: "h-3 w-3 shrink-0 text-muted-foreground/50", "aria-hidden": "true" })
8705
8668
  ] })
8706
8669
  ]
8707
8670
  }
@@ -8750,7 +8713,7 @@ function ThinkingPickerMenu({
8750
8713
  window.addEventListener("keydown", handler, { capture: true });
8751
8714
  return () => window.removeEventListener("keydown", handler, { capture: true });
8752
8715
  }, [activeIndex, disabled, onChange, onClose]);
8753
- return /* @__PURE__ */ jsx28("div", { ref: menuRef, "data-boring-agent": "", "data-boring-agent-part": "thinking-picker-menu", className: cn(composerPickerMenuClass, className), children: /* @__PURE__ */ jsx28(Command, { className: "bg-transparent text-[color:var(--popover-foreground)]", children: /* @__PURE__ */ jsx28(CommandList, { className: "max-h-[300px] p-0.5", children: THINKING_LEVELS.map((level, index) => /* @__PURE__ */ jsxs25(
8716
+ return /* @__PURE__ */ jsx30("div", { ref: menuRef, "data-boring-agent": "", "data-boring-agent-part": "thinking-picker-menu", className: cn(composerPickerMenuClass, className), children: /* @__PURE__ */ jsx30(Command, { className: "bg-transparent text-[color:var(--popover-foreground)]", children: /* @__PURE__ */ jsx30(CommandList, { className: "max-h-[300px] p-0.5", children: THINKING_LEVELS.map((level, index) => /* @__PURE__ */ jsxs25(
8754
8717
  CommandItem,
8755
8718
  {
8756
8719
  value: `Thinking ${THINKING_LEVEL_LABELS[level]} ${THINKING_LEVEL_DETAILS[level]}`,
@@ -8761,7 +8724,7 @@ function ThinkingPickerMenu({
8761
8724
  },
8762
8725
  className: selectorItemClass(level === value || index === activeIndex),
8763
8726
  children: [
8764
- /* @__PURE__ */ jsx28(
8727
+ /* @__PURE__ */ jsx30(
8765
8728
  CheckIcon3,
8766
8729
  {
8767
8730
  className: cn(
@@ -8770,9 +8733,9 @@ function ThinkingPickerMenu({
8770
8733
  )
8771
8734
  }
8772
8735
  ),
8773
- /* @__PURE__ */ jsx28(ThinkingLevelGlyph, { level }),
8774
- /* @__PURE__ */ jsx28("span", { className: "font-medium", children: THINKING_LEVEL_LABELS[level] }),
8775
- /* @__PURE__ */ jsx28("span", { className: "ml-auto text-[11px] text-muted-foreground/65", children: THINKING_LEVEL_DETAILS[level] })
8736
+ /* @__PURE__ */ jsx30(ThinkingLevelGlyph, { level }),
8737
+ /* @__PURE__ */ jsx30("span", { className: "font-medium", children: THINKING_LEVEL_LABELS[level] }),
8738
+ /* @__PURE__ */ jsx30("span", { className: "ml-auto text-[11px] text-muted-foreground/65", children: THINKING_LEVEL_DETAILS[level] })
8776
8739
  ]
8777
8740
  },
8778
8741
  level
@@ -8813,22 +8776,22 @@ import {
8813
8776
  XIcon as XIcon5
8814
8777
  } from "lucide-react";
8815
8778
  import { nanoid } from "nanoid";
8816
- import { useCallback as useCallback15, useEffect as useEffect18, useMemo as useMemo11, useRef as useRef15, useState as useState17 } from "react";
8779
+ import { useCallback as useCallback15, useEffect as useEffect18, useMemo as useMemo13, useRef as useRef15, useState as useState17 } from "react";
8817
8780
 
8818
8781
  // src/front/primitives/prompt-input-context.ts
8819
- import { createContext as createContext6, useContext as useContext6 } from "react";
8820
- var PromptInputController = createContext6(
8782
+ import { createContext as createContext8, useContext as useContext8 } from "react";
8783
+ var PromptInputController = createContext8(
8821
8784
  null
8822
8785
  );
8823
- var ProviderAttachmentsContext = createContext6(
8786
+ var ProviderAttachmentsContext = createContext8(
8824
8787
  null
8825
8788
  );
8826
- var useOptionalPromptInputController = () => useContext6(PromptInputController);
8827
- var useOptionalProviderAttachments = () => useContext6(ProviderAttachmentsContext);
8828
- var LocalAttachmentsContext = createContext6(null);
8789
+ var useOptionalPromptInputController = () => useContext8(PromptInputController);
8790
+ var useOptionalProviderAttachments = () => useContext8(ProviderAttachmentsContext);
8791
+ var LocalAttachmentsContext = createContext8(null);
8829
8792
  var usePromptInputAttachments = () => {
8830
8793
  const provider = useOptionalProviderAttachments();
8831
- const local = useContext6(LocalAttachmentsContext);
8794
+ const local = useContext8(LocalAttachmentsContext);
8832
8795
  const context = local ?? provider;
8833
8796
  if (!context) {
8834
8797
  throw new Error(
@@ -8837,7 +8800,7 @@ var usePromptInputAttachments = () => {
8837
8800
  }
8838
8801
  return context;
8839
8802
  };
8840
- var LocalReferencedSourcesContext = createContext6(null);
8803
+ var LocalReferencedSourcesContext = createContext8(null);
8841
8804
 
8842
8805
  // src/front/primitives/prompt-input-wrappers.tsx
8843
8806
  import {
@@ -8848,12 +8811,12 @@ import {
8848
8811
  TooltipTrigger as TooltipTrigger3
8849
8812
  } from "@hachej/boring-ui-kit";
8850
8813
  import { Children as Children2 } from "react";
8851
- import { jsx as jsx29, jsxs as jsxs26 } from "react/jsx-runtime";
8814
+ import { jsx as jsx31, jsxs as jsxs26 } from "react/jsx-runtime";
8852
8815
  var PromptInputFooter = ({
8853
8816
  align = "block-end",
8854
8817
  className,
8855
8818
  ...props
8856
- }) => /* @__PURE__ */ jsx29(
8819
+ }) => /* @__PURE__ */ jsx31(
8857
8820
  InputGroupAddon,
8858
8821
  {
8859
8822
  align,
@@ -8863,7 +8826,7 @@ var PromptInputFooter = ({
8863
8826
  );
8864
8827
 
8865
8828
  // src/front/primitives/prompt-input.tsx
8866
- import { Fragment as Fragment10, jsx as jsx30, jsxs as jsxs27 } from "react/jsx-runtime";
8829
+ import { Fragment as Fragment11, jsx as jsx32, jsxs as jsxs27 } from "react/jsx-runtime";
8867
8830
  var PromptInput = ({
8868
8831
  className,
8869
8832
  accept,
@@ -9118,7 +9081,7 @@ var PromptInput = ({
9118
9081
  [add]
9119
9082
  );
9120
9083
  const setFileUrl = usingProvider ? controller.attachments.setFileUrl : setFileUrlLocal;
9121
- const attachmentsCtx = useMemo11(
9084
+ const attachmentsCtx = useMemo13(
9122
9085
  () => ({
9123
9086
  add,
9124
9087
  clear: clearAttachments,
@@ -9130,7 +9093,7 @@ var PromptInput = ({
9130
9093
  }),
9131
9094
  [files, add, remove, clearAttachments, openFileDialog, setFileUrl]
9132
9095
  );
9133
- const refsCtx = useMemo11(
9096
+ const refsCtx = useMemo13(
9134
9097
  () => ({
9135
9098
  add: (incoming) => {
9136
9099
  const array = Array.isArray(incoming) ? incoming : [incoming];
@@ -9206,8 +9169,8 @@ var PromptInput = ({
9206
9169
  },
9207
9170
  [usingProvider, controller, files, onSubmit, clear]
9208
9171
  );
9209
- const inner = /* @__PURE__ */ jsxs27(Fragment10, { children: [
9210
- /* @__PURE__ */ jsx30(
9172
+ const inner = /* @__PURE__ */ jsxs27(Fragment11, { children: [
9173
+ /* @__PURE__ */ jsx32(
9211
9174
  Input,
9212
9175
  {
9213
9176
  accept,
@@ -9220,7 +9183,7 @@ var PromptInput = ({
9220
9183
  type: "file"
9221
9184
  }
9222
9185
  ),
9223
- /* @__PURE__ */ jsx30(
9186
+ /* @__PURE__ */ jsx32(
9224
9187
  "form",
9225
9188
  {
9226
9189
  "data-boring-agent-part": "composer",
@@ -9228,12 +9191,12 @@ var PromptInput = ({
9228
9191
  onSubmit: handleSubmit,
9229
9192
  ref: formRef,
9230
9193
  ...props,
9231
- children: /* @__PURE__ */ jsx30(InputGroup, { className: "overflow-hidden", children })
9194
+ children: /* @__PURE__ */ jsx32(InputGroup, { className: "overflow-hidden", children })
9232
9195
  }
9233
9196
  )
9234
9197
  ] });
9235
- const withReferencedSources = /* @__PURE__ */ jsx30(LocalReferencedSourcesContext.Provider, { value: refsCtx, children: inner });
9236
- return /* @__PURE__ */ jsx30(LocalAttachmentsContext.Provider, { value: attachmentsCtx, children: withReferencedSources });
9198
+ const withReferencedSources = /* @__PURE__ */ jsx32(LocalReferencedSourcesContext.Provider, { value: refsCtx, children: inner });
9199
+ return /* @__PURE__ */ jsx32(LocalAttachmentsContext.Provider, { value: attachmentsCtx, children: withReferencedSources });
9237
9200
  };
9238
9201
  var PromptInputTextarea = ({
9239
9202
  onChange,
@@ -9311,7 +9274,7 @@ var PromptInputTextarea = ({
9311
9274
  } : {
9312
9275
  onChange
9313
9276
  };
9314
- return /* @__PURE__ */ jsx30(
9277
+ return /* @__PURE__ */ jsx32(
9315
9278
  InputGroupTextarea,
9316
9279
  {
9317
9280
  "data-boring-agent-part": "composer-input",
@@ -9338,13 +9301,13 @@ var PromptInputSubmit = ({
9338
9301
  ...props
9339
9302
  }) => {
9340
9303
  const isGenerating = status === "submitted" || status === "streaming";
9341
- let Icon = /* @__PURE__ */ jsx30(CornerDownLeftIcon, { className: "size-4" });
9304
+ let Icon = /* @__PURE__ */ jsx32(CornerDownLeftIcon, { className: "size-4" });
9342
9305
  if (status === "submitted") {
9343
- Icon = /* @__PURE__ */ jsx30(Spinner, {});
9306
+ Icon = /* @__PURE__ */ jsx32(Spinner, {});
9344
9307
  } else if (status === "streaming") {
9345
- Icon = /* @__PURE__ */ jsx30(SquareIcon, { className: "size-4" });
9308
+ Icon = /* @__PURE__ */ jsx32(SquareIcon, { className: "size-4" });
9346
9309
  } else if (status === "error") {
9347
- Icon = /* @__PURE__ */ jsx30(XIcon5, { className: "size-4" });
9310
+ Icon = /* @__PURE__ */ jsx32(XIcon5, { className: "size-4" });
9348
9311
  }
9349
9312
  const handleClick = useCallback15(
9350
9313
  (e) => {
@@ -9357,7 +9320,7 @@ var PromptInputSubmit = ({
9357
9320
  },
9358
9321
  [isGenerating, onStop, onClick]
9359
9322
  );
9360
- return /* @__PURE__ */ jsx30(
9323
+ return /* @__PURE__ */ jsx32(
9361
9324
  InputGroupButton2,
9362
9325
  {
9363
9326
  "aria-label": isGenerating ? "Stop" : "Submit",
@@ -9374,8 +9337,8 @@ var PromptInputSubmit = ({
9374
9337
  };
9375
9338
 
9376
9339
  // src/front/primitives/slash-command-picker.tsx
9377
- import { useEffect as useEffect19, useMemo as useMemo12, useRef as useRef16, useState as useState18 } from "react";
9378
- import { Fragment as Fragment11, jsx as jsx31, jsxs as jsxs28 } from "react/jsx-runtime";
9340
+ import { useEffect as useEffect19, useMemo as useMemo14, useRef as useRef16, useState as useState18 } from "react";
9341
+ import { Fragment as Fragment12, jsx as jsx33, jsxs as jsxs28 } from "react/jsx-runtime";
9379
9342
  var ALL_PLUGINS = "__all__";
9380
9343
  function commandGroup(cmd) {
9381
9344
  if (cmd.source === "skill") return "skills";
@@ -9392,11 +9355,11 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9392
9355
  useEffect19(() => {
9393
9356
  setSearchQuery(query);
9394
9357
  }, [query]);
9395
- const groups = useMemo12(() => {
9358
+ const groups = useMemo14(() => {
9396
9359
  const set = new Set(commands.map(commandGroup));
9397
9360
  return Array.from(set).sort((a, b) => a.localeCompare(b));
9398
9361
  }, [commands]);
9399
- const filtered = useMemo12(() => {
9362
+ const filtered = useMemo14(() => {
9400
9363
  const q = searchQuery.trim().toLowerCase();
9401
9364
  return commands.filter((c) => {
9402
9365
  if (plugin !== ALL_PLUGINS && commandGroup(c) !== plugin) return false;
@@ -9427,7 +9390,7 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9427
9390
  onDismiss
9428
9391
  });
9429
9392
  return /* @__PURE__ */ jsxs28("div", { ref: containerRef, className: "mb-1 w-full overflow-hidden rounded-lg border border-border/60 bg-popover shadow-lg", children: [
9430
- /* @__PURE__ */ jsx31(
9393
+ /* @__PURE__ */ jsx33(
9431
9394
  "input",
9432
9395
  {
9433
9396
  ref: inputRef,
@@ -9442,9 +9405,9 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9442
9405
  autoComplete: "off"
9443
9406
  }
9444
9407
  ),
9445
- /* @__PURE__ */ jsx31("div", { className: "flex flex-wrap gap-1 border-b border-border/50 px-2 py-1.5", role: "tablist", "aria-label": "Filter by plugin", children: [ALL_PLUGINS, ...groups].map((g) => {
9408
+ /* @__PURE__ */ jsx33("div", { className: "flex flex-wrap gap-1 border-b border-border/50 px-2 py-1.5", role: "tablist", "aria-label": "Filter by plugin", children: [ALL_PLUGINS, ...groups].map((g) => {
9446
9409
  const selected = plugin === g;
9447
- return /* @__PURE__ */ jsx31(
9410
+ return /* @__PURE__ */ jsx33(
9448
9411
  "button",
9449
9412
  {
9450
9413
  type: "button",
@@ -9464,7 +9427,7 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9464
9427
  g
9465
9428
  );
9466
9429
  }) }),
9467
- filtered.length === 0 ? /* @__PURE__ */ jsx31("div", { className: "px-3 py-2 text-[11px] text-muted-foreground", children: "No matching commands." }) : /* @__PURE__ */ jsx31("ul", { ref: listRef, className: "max-h-80 overflow-y-auto py-1", role: "listbox", "aria-label": "Commands", children: filtered.map((cmd, i) => /* @__PURE__ */ jsxs28(
9430
+ filtered.length === 0 ? /* @__PURE__ */ jsx33("div", { className: "px-3 py-2 text-[11px] text-muted-foreground", children: "No matching commands." }) : /* @__PURE__ */ jsx33("ul", { ref: listRef, className: "max-h-80 overflow-y-auto py-1", role: "listbox", "aria-label": "Commands", children: filtered.map((cmd, i) => /* @__PURE__ */ jsxs28(
9468
9431
  "li",
9469
9432
  {
9470
9433
  role: "option",
@@ -9487,15 +9450,15 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9487
9450
  "/",
9488
9451
  cmd.name
9489
9452
  ] }),
9490
- cmd.source === "skill" ? /* @__PURE__ */ jsxs28(Fragment11, { children: [
9491
- /* @__PURE__ */ jsx31("span", { className: "rounded-sm bg-accent/15 px-1 py-px text-[9px] font-medium uppercase tracking-wide text-accent-foreground", children: "skill" }),
9492
- cmd.sourcePlugin ? /* @__PURE__ */ jsx31("span", { className: "rounded-sm bg-muted px-1 py-px text-[9px] font-medium text-muted-foreground", children: cmd.sourcePlugin }) : null
9453
+ cmd.source === "skill" ? /* @__PURE__ */ jsxs28(Fragment12, { children: [
9454
+ /* @__PURE__ */ jsx33("span", { className: "rounded-sm bg-accent/15 px-1 py-px text-[9px] font-medium uppercase tracking-wide text-accent-foreground", children: "skill" }),
9455
+ cmd.sourcePlugin ? /* @__PURE__ */ jsx33("span", { className: "rounded-sm bg-muted px-1 py-px text-[9px] font-medium text-muted-foreground", children: cmd.sourcePlugin }) : null
9493
9456
  ] }) : cmd.sourcePlugin ? (
9494
9457
  // Originating plugin/package for non-skill commands.
9495
- /* @__PURE__ */ jsx31("span", { className: "rounded-sm bg-muted px-1 py-px text-[9px] font-medium text-muted-foreground", children: cmd.sourcePlugin })
9458
+ /* @__PURE__ */ jsx33("span", { className: "rounded-sm bg-muted px-1 py-px text-[9px] font-medium text-muted-foreground", children: cmd.sourcePlugin })
9496
9459
  ) : null
9497
9460
  ] }),
9498
- /* @__PURE__ */ jsx31("span", { className: "truncate text-[11px] opacity-50", children: cmd.description })
9461
+ /* @__PURE__ */ jsx33("span", { className: "truncate text-[11px] opacity-50", children: cmd.description })
9499
9462
  ]
9500
9463
  },
9501
9464
  cmd.name
@@ -9506,10 +9469,10 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9506
9469
  // src/front/chat/components/ComposerAttachments.tsx
9507
9470
  import { AlertCircleIcon as AlertCircleIcon2, Loader2 as Loader23, PaperclipIcon as PaperclipIcon2 } from "lucide-react";
9508
9471
  import { IconButton as IconButton3 } from "@hachej/boring-ui-kit";
9509
- import { jsx as jsx32, jsxs as jsxs29 } from "react/jsx-runtime";
9472
+ import { jsx as jsx34, jsxs as jsxs29 } from "react/jsx-runtime";
9510
9473
  function AttachmentButton({ disabled, className }) {
9511
9474
  const attachments = usePromptInputAttachments();
9512
- return /* @__PURE__ */ jsx32(
9475
+ return /* @__PURE__ */ jsx34(
9513
9476
  IconButton3,
9514
9477
  {
9515
9478
  type: "button",
@@ -9522,14 +9485,14 @@ function AttachmentButton({ disabled, className }) {
9522
9485
  className: cn(composerActionClass, "w-8", className),
9523
9486
  "aria-label": "Attach files",
9524
9487
  title: disabled ? "Attachments are available when the composer is ready." : "Attach files",
9525
- children: /* @__PURE__ */ jsx32(PaperclipIcon2, { className: "h-3.5 w-3.5", strokeWidth: 1.75 })
9488
+ children: /* @__PURE__ */ jsx34(PaperclipIcon2, { className: "h-3.5 w-3.5", strokeWidth: 1.75 })
9526
9489
  }
9527
9490
  );
9528
9491
  }
9529
9492
  function AttachmentsList() {
9530
9493
  const attachments = usePromptInputAttachments();
9531
9494
  if (attachments.files.length === 0) return null;
9532
- return /* @__PURE__ */ jsx32(
9495
+ return /* @__PURE__ */ jsx34(
9533
9496
  Attachments,
9534
9497
  {
9535
9498
  "data-boring-agent-part": "composer-attachment-row",
@@ -9548,12 +9511,12 @@ function AttachmentsList() {
9548
9511
  ),
9549
9512
  children: [
9550
9513
  /* @__PURE__ */ jsxs29("div", { className: "relative shrink-0", children: [
9551
- /* @__PURE__ */ jsx32(AttachmentPreview, { className: "!size-7 overflow-hidden !rounded-full bg-background/60" }),
9552
- file.status === "uploading" ? /* @__PURE__ */ jsx32("div", { className: "absolute inset-0 flex items-center justify-center rounded-full bg-background/70", children: /* @__PURE__ */ jsx32(Loader23, { className: "size-3.5 animate-spin text-muted-foreground" }) }) : null,
9553
- file.status === "error" ? /* @__PURE__ */ jsx32("div", { className: "absolute inset-0 flex items-center justify-center rounded-full bg-destructive/20", children: /* @__PURE__ */ jsx32(AlertCircleIcon2, { className: "size-3.5 text-destructive" }) }) : null
9514
+ /* @__PURE__ */ jsx34(AttachmentPreview, { className: "!size-7 overflow-hidden !rounded-full bg-background/60" }),
9515
+ file.status === "uploading" ? /* @__PURE__ */ jsx34("div", { className: "absolute inset-0 flex items-center justify-center rounded-full bg-background/70", children: /* @__PURE__ */ jsx34(Loader23, { className: "size-3.5 animate-spin text-muted-foreground" }) }) : null,
9516
+ file.status === "error" ? /* @__PURE__ */ jsx34("div", { className: "absolute inset-0 flex items-center justify-center rounded-full bg-destructive/20", children: /* @__PURE__ */ jsx34(AlertCircleIcon2, { className: "size-3.5 text-destructive" }) }) : null
9554
9517
  ] }),
9555
- /* @__PURE__ */ jsx32(AttachmentInfo, { className: "min-w-0 !max-w-[180px] truncate text-[13px] font-medium" }),
9556
- /* @__PURE__ */ jsx32(AttachmentRemove, { className: "!size-5 !rounded-full !opacity-100 text-muted-foreground/80 hover:text-foreground" })
9518
+ /* @__PURE__ */ jsx34(AttachmentInfo, { className: "min-w-0 !max-w-[180px] truncate text-[13px] font-medium" }),
9519
+ /* @__PURE__ */ jsx34(AttachmentRemove, { className: "!size-5 !rounded-full !opacity-100 text-muted-foreground/80 hover:text-foreground" })
9557
9520
  ]
9558
9521
  },
9559
9522
  file.id
@@ -9563,7 +9526,7 @@ function AttachmentsList() {
9563
9526
  }
9564
9527
 
9565
9528
  // src/front/chat/components/PiChatComposerSurface.tsx
9566
- import { jsx as jsx33, jsxs as jsxs30 } from "react/jsx-runtime";
9529
+ import { jsx as jsx35, jsxs as jsxs30 } from "react/jsx-runtime";
9567
9530
  var MAX_PROMPT_ATTACHMENTS = 2;
9568
9531
  var MAX_PROMPT_ATTACHMENT_BYTES = 4 * 1024 * 1024;
9569
9532
  var COMPOSER_INPUT_GROUP_MIN_HEIGHT = 56;
@@ -9640,10 +9603,12 @@ function PiChatComposerSurface({
9640
9603
  textareaRef,
9641
9604
  onTextareaChange,
9642
9605
  onTextareaKeyDown,
9606
+ updateDraft,
9643
9607
  onSubmitMessage,
9644
9608
  onStop
9645
9609
  }) {
9646
9610
  const workspaceRequestId = getHeaderValue(requestHeaders, "x-boring-workspace-id");
9611
+ const composerContributions = useComposerContributions();
9647
9612
  const uploadAttachment = useCallback16((file) => uploadFile(file, {
9648
9613
  apiBaseUrl,
9649
9614
  workspaceRequestId,
@@ -9675,7 +9640,7 @@ function PiChatComposerSurface({
9675
9640
  resizeTextarea(textareaRef.current);
9676
9641
  }, [draft, resizeTextarea, textareaRef]);
9677
9642
  return /* @__PURE__ */ jsxs30("div", { className: cn("relative z-20", chrome ? "px-4 pb-4 pt-2 sm:px-6 sm:pb-5" : "px-3 pb-2 pt-1"), children: [
9678
- /* @__PURE__ */ jsx33(
9643
+ /* @__PURE__ */ jsx35(
9679
9644
  "div",
9680
9645
  {
9681
9646
  "data-boring-agent-part": "chat-working-slot",
@@ -9693,7 +9658,7 @@ function PiChatComposerSurface({
9693
9658
  "aria-live": isStreaming ? "polite" : void 0,
9694
9659
  className: "inline-flex items-center gap-2 rounded-full border border-border/50 bg-background/85 px-2.5 py-1 text-[12px] text-muted-foreground/75 shadow-sm backdrop-blur",
9695
9660
  children: [
9696
- /* @__PURE__ */ jsx33(
9661
+ /* @__PURE__ */ jsx35(
9697
9662
  motion2.span,
9698
9663
  {
9699
9664
  "aria-hidden": "true",
@@ -9702,14 +9667,14 @@ function PiChatComposerSurface({
9702
9667
  transition: { duration: 1.2, repeat: Infinity, ease: "easeInOut" }
9703
9668
  }
9704
9669
  ),
9705
- /* @__PURE__ */ jsx33("span", { children: "Working\u2026" })
9670
+ /* @__PURE__ */ jsx35("span", { children: "Working\u2026" })
9706
9671
  ]
9707
9672
  }
9708
9673
  )
9709
9674
  }
9710
9675
  ),
9711
- composerStatusNotice ? /* @__PURE__ */ jsx33(ComposerRuntimeNotice, { notice: composerStatusNotice }) : null,
9712
- composerBlocked && !workspaceWarmupBlocked ? /* @__PURE__ */ jsx33(
9676
+ composerStatusNotice ? /* @__PURE__ */ jsx35(ComposerRuntimeNotice, { notice: composerStatusNotice }) : null,
9677
+ composerBlocked && !workspaceWarmupBlocked ? /* @__PURE__ */ jsx35(
9713
9678
  ComposerBlockerNotice,
9714
9679
  {
9715
9680
  blocker: primaryComposerBlocker,
@@ -9717,8 +9682,8 @@ function PiChatComposerSurface({
9717
9682
  onAction: onComposerBlockerAction
9718
9683
  }
9719
9684
  ) : null,
9720
- queuePreview.length > 0 ? /* @__PURE__ */ jsx33(QueuedComposerNotice, { followUps: queuePreview, onEdit: onEditQueued }) : null,
9721
- hotReloadEnabled ? /* @__PURE__ */ jsx33(
9685
+ queuePreview.length > 0 ? /* @__PURE__ */ jsx35(QueuedComposerNotice, { followUps: queuePreview, onEdit: onEditQueued }) : null,
9686
+ hotReloadEnabled ? /* @__PURE__ */ jsx35(
9722
9687
  PluginUpdateStatus,
9723
9688
  {
9724
9689
  state: pluginUpdateState,
@@ -9726,14 +9691,14 @@ function PiChatComposerSurface({
9726
9691
  onRetry: onRunPluginUpdate
9727
9692
  }
9728
9693
  ) : null,
9729
- /* @__PURE__ */ jsx33(
9694
+ /* @__PURE__ */ jsx35(
9730
9695
  CommandRunStatus,
9731
9696
  {
9732
9697
  state: commandNotifyState,
9733
9698
  onDismiss: onDismissCommandNotify
9734
9699
  }
9735
9700
  ),
9736
- attachmentNotice ? /* @__PURE__ */ jsx33(
9701
+ attachmentNotice ? /* @__PURE__ */ jsx35(
9737
9702
  "div",
9738
9703
  {
9739
9704
  role: "status",
@@ -9743,7 +9708,7 @@ function PiChatComposerSurface({
9743
9708
  }
9744
9709
  ) : null,
9745
9710
  /* @__PURE__ */ jsxs30("div", { className: cn("mx-auto w-full", chrome ? "max-w-3xl" : "max-w-[680px]"), children: [
9746
- mentionState ? /* @__PURE__ */ jsx33(
9711
+ mentionState ? /* @__PURE__ */ jsx35(
9747
9712
  MentionPicker,
9748
9713
  {
9749
9714
  mention: mentionState,
@@ -9755,7 +9720,7 @@ function PiChatComposerSurface({
9755
9720
  onDismiss: onDismissMention
9756
9721
  }
9757
9722
  ) : null,
9758
- slashQuery !== null ? /* @__PURE__ */ jsx33(
9723
+ slashQuery !== null ? /* @__PURE__ */ jsx35(
9759
9724
  SlashCommandPicker,
9760
9725
  {
9761
9726
  query: slashQuery,
@@ -9764,7 +9729,7 @@ function PiChatComposerSurface({
9764
9729
  onDismiss: onDismissSlash
9765
9730
  }
9766
9731
  ) : null,
9767
- mentionState === null && slashQuery === null && modelPickerOpen ? /* @__PURE__ */ jsx33(
9732
+ mentionState === null && slashQuery === null && modelPickerOpen ? /* @__PURE__ */ jsx35(
9768
9733
  ModelPickerMenu,
9769
9734
  {
9770
9735
  value: selectedModel,
@@ -9775,7 +9740,7 @@ function PiChatComposerSurface({
9775
9740
  onClose: () => onSetModelPickerOpen(false)
9776
9741
  }
9777
9742
  ) : null,
9778
- mentionState === null && slashQuery === null && thinkingPickerOpen ? /* @__PURE__ */ jsx33(
9743
+ mentionState === null && slashQuery === null && thinkingPickerOpen ? /* @__PURE__ */ jsx35(
9779
9744
  ThinkingPickerMenu,
9780
9745
  {
9781
9746
  value: selectedThinking,
@@ -9785,7 +9750,17 @@ function PiChatComposerSurface({
9785
9750
  }
9786
9751
  ) : null
9787
9752
  ] }),
9788
- /* @__PURE__ */ jsx33(
9753
+ composerContributions.map(({ id, Top }) => Top ? /* @__PURE__ */ jsx35(
9754
+ "div",
9755
+ {
9756
+ "data-boring-agent-part": "composer-contribution-top",
9757
+ "data-boring-agent-contribution-id": id,
9758
+ className: cn("mx-auto mb-2 w-full empty:hidden", chrome ? "max-w-3xl" : "max-w-[680px]"),
9759
+ children: /* @__PURE__ */ jsx35(Top, {})
9760
+ },
9761
+ id
9762
+ ) : null),
9763
+ /* @__PURE__ */ jsx35(
9789
9764
  "div",
9790
9765
  {
9791
9766
  "data-boring-agent-part": "composer-rail",
@@ -9820,15 +9795,15 @@ function PiChatComposerSurface({
9820
9795
  else onAttachmentNotice(err.message || "Attachment rejected.");
9821
9796
  },
9822
9797
  children: [
9823
- /* @__PURE__ */ jsx33(AttachmentsList, {}),
9798
+ /* @__PURE__ */ jsx35(AttachmentsList, {}),
9824
9799
  /* @__PURE__ */ jsxs30(
9825
9800
  "div",
9826
9801
  {
9827
9802
  "data-boring-agent-part": "composer-input-row",
9828
9803
  className: "flex min-h-[var(--composer-input-group-height)] w-full items-center",
9829
9804
  children: [
9830
- /* @__PURE__ */ jsx33("div", { className: "flex h-14 shrink-0 items-center pl-2", children: /* @__PURE__ */ jsx33(AttachmentButton, { disabled: disabled || isStreaming }) }),
9831
- /* @__PURE__ */ jsx33(
9805
+ /* @__PURE__ */ jsx35("div", { className: "flex h-14 shrink-0 items-center pl-2", children: /* @__PURE__ */ jsx35(AttachmentButton, { disabled: disabled || isStreaming }) }),
9806
+ /* @__PURE__ */ jsx35(
9832
9807
  PromptInputTextarea,
9833
9808
  {
9834
9809
  value: draft,
@@ -9852,32 +9827,44 @@ function PiChatComposerSurface({
9852
9827
  )
9853
9828
  }
9854
9829
  ),
9855
- /* @__PURE__ */ jsx33(
9830
+ /* @__PURE__ */ jsx35(
9856
9831
  PromptInputFooter,
9857
9832
  {
9858
9833
  align: "inline-end",
9859
9834
  "data-boring-agent-part": "composer-submit-addon",
9860
9835
  className: "!order-none !w-auto shrink-0 self-center justify-between border-0 bg-transparent !px-2 !py-0",
9861
- children: /* @__PURE__ */ jsx33("div", { className: "ml-auto flex items-center gap-1.5", children: /* @__PURE__ */ jsx33(
9862
- PromptInputSubmit,
9863
- {
9864
- "data-boring-agent-part": "composer-submit",
9865
- status: submitStatus,
9866
- onStop,
9867
- disabled: submitDisabled,
9868
- className: cn(
9869
- "h-8 w-8 shrink-0 rounded-full",
9870
- "bg-foreground",
9871
- "text-background",
9872
- "transition-all duration-150 ease-[cubic-bezier(0.22,1,0.36,1)]",
9873
- "hover:bg-foreground/90 hover:shadow-[0_0_0_3px_oklch(from_var(--foreground)_l_c_h/0.12)] hover:scale-[1.04]",
9874
- "active:scale-[0.93] active:brightness-95",
9875
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/20",
9876
- "disabled:pointer-events-none disabled:opacity-40",
9877
- "[&>svg]:size-3.5"
9878
- )
9879
- }
9880
- ) })
9836
+ children: /* @__PURE__ */ jsxs30("div", { className: "ml-auto flex items-center gap-1.5", children: [
9837
+ composerContributions.map(({ id, Action }) => Action ? /* @__PURE__ */ jsx35(
9838
+ "span",
9839
+ {
9840
+ "data-boring-agent-part": "composer-contribution-action",
9841
+ "data-boring-agent-contribution-id": id,
9842
+ className: "contents",
9843
+ children: /* @__PURE__ */ jsx35(Action, { updateDraft })
9844
+ },
9845
+ id
9846
+ ) : null),
9847
+ /* @__PURE__ */ jsx35(
9848
+ PromptInputSubmit,
9849
+ {
9850
+ "data-boring-agent-part": "composer-submit",
9851
+ status: submitStatus,
9852
+ onStop,
9853
+ disabled: submitDisabled,
9854
+ className: cn(
9855
+ "h-8 w-8 shrink-0 rounded-full",
9856
+ "bg-foreground",
9857
+ "text-background",
9858
+ "transition-all duration-150 ease-[cubic-bezier(0.22,1,0.36,1)]",
9859
+ "hover:bg-foreground/90 hover:shadow-[0_0_0_3px_oklch(from_var(--foreground)_l_c_h/0.12)] hover:scale-[1.04]",
9860
+ "active:scale-[0.93] active:brightness-95",
9861
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/20",
9862
+ "disabled:pointer-events-none disabled:opacity-40",
9863
+ "[&>svg]:size-3.5"
9864
+ )
9865
+ }
9866
+ )
9867
+ ] })
9881
9868
  }
9882
9869
  )
9883
9870
  ]
@@ -9897,7 +9884,7 @@ function PiChatComposerSurface({
9897
9884
  chrome ? "max-w-3xl" : "max-w-[680px]"
9898
9885
  ),
9899
9886
  children: [
9900
- /* @__PURE__ */ jsx33(
9887
+ /* @__PURE__ */ jsx35(
9901
9888
  ModelSelectTrigger,
9902
9889
  {
9903
9890
  value: selectedModel,
@@ -9914,7 +9901,7 @@ function PiChatComposerSurface({
9914
9901
  }
9915
9902
  }
9916
9903
  ),
9917
- thinkingControl ? /* @__PURE__ */ jsx33(
9904
+ thinkingControl ? /* @__PURE__ */ jsx35(
9918
9905
  ThinkingSelectTrigger,
9919
9906
  {
9920
9907
  value: selectedThinking,
@@ -9941,7 +9928,7 @@ function getHeaderValue(headers, name) {
9941
9928
  }
9942
9929
 
9943
9930
  // src/front/chat/piChatPanelHooks.ts
9944
- import { useCallback as useCallback17, useEffect as useEffect20, useMemo as useMemo13, useRef as useRef17, useState as useState19, useSyncExternalStore } from "react";
9931
+ import { useCallback as useCallback17, useEffect as useEffect20, useMemo as useMemo15, useRef as useRef17, useState as useState19, useSyncExternalStore } from "react";
9945
9932
  function useExternalRemotePiSession({
9946
9933
  sessionId,
9947
9934
  agentTypeId,
@@ -9956,7 +9943,7 @@ function useExternalRemotePiSession({
9956
9943
  const [session, setSession] = useState19();
9957
9944
  const remoteSessionOptionsRef = useRef17(remoteSessionOptions);
9958
9945
  remoteSessionOptionsRef.current = remoteSessionOptions;
9959
- const remoteSessionOptionsKey = useMemo13(
9946
+ const remoteSessionOptionsKey = useMemo15(
9960
9947
  () => remoteSessionOptionsIdentity2(remoteSessionOptions),
9961
9948
  [remoteSessionOptions]
9962
9949
  );
@@ -10129,7 +10116,7 @@ function errorMessage2(error, fallback) {
10129
10116
  }
10130
10117
 
10131
10118
  // src/front/chat/PiChatPanel.tsx
10132
- import { jsx as jsx34, jsxs as jsxs31 } from "react/jsx-runtime";
10119
+ import { jsx as jsx36, jsxs as jsxs31 } from "react/jsx-runtime";
10133
10120
  var DebugDrawer2 = lazy(() => import("../DebugDrawer-RYEBQ6CO.js").then((m) => ({ default: m.DebugDrawer })));
10134
10121
  var EMPTY_COMMANDS = [];
10135
10122
  var EMPTY_COMMAND_NAMES = [];
@@ -10206,9 +10193,9 @@ function PiChatPanel({
10206
10193
  const onTurnCompleteRef = useRef18(onTurnComplete);
10207
10194
  onTurnCompleteRef.current = onTurnComplete;
10208
10195
  const sessionListRefreshRef = useRef18(void 0);
10209
- const requestHeadersKey = useMemo14(() => headersContentKey(requestHeaders), [requestHeaders]);
10210
- const normalizedRequestHeaders = useMemo14(() => normalizedHeadersFromContentKey(requestHeadersKey), [requestHeadersKey]);
10211
- const remoteSessionOptionsWithEvents = useMemo14(() => ({
10196
+ const requestHeadersKey = useMemo16(() => headersContentKey(requestHeaders), [requestHeaders]);
10197
+ const normalizedRequestHeaders = useMemo16(() => normalizedHeadersFromContentKey(requestHeadersKey), [requestHeadersKey]);
10198
+ const remoteSessionOptionsWithEvents = useMemo16(() => ({
10212
10199
  ...remoteSessionOptions,
10213
10200
  ...hydrateMessages ? {} : { autoStart: false },
10214
10201
  onEvent: (event) => {
@@ -10275,7 +10262,7 @@ function PiChatPanel({
10275
10262
  enabled: modelDiscoveryEnabled
10276
10263
  });
10277
10264
  const selectedModel = model === void 0 ? modelDiscovery.model : model;
10278
- const modelOptions = useMemo14(
10265
+ const modelOptions = useMemo16(
10279
10266
  () => modelOptionsForSelection(availableModels ?? modelDiscovery.availableModels, selectedModel),
10280
10267
  [availableModels, modelDiscovery.availableModels, selectedModel]
10281
10268
  );
@@ -10342,6 +10329,11 @@ function PiChatPanel({
10342
10329
  const [localSubmittedSessionId, setLocalSubmittedSessionId] = useState20();
10343
10330
  const localSubmittedSessionRef = useRef18(void 0);
10344
10331
  const { attachmentNotice, setAttachmentNotice } = useAttachmentNotice();
10332
+ const composerContributions = useComposerContributions();
10333
+ const contributedCommands = useMemo16(
10334
+ () => composerContributions.flatMap((contribution) => contribution.commands ?? []),
10335
+ [composerContributions]
10336
+ );
10345
10337
  const markLocalSubmitted = useCallback18((sessionId2) => {
10346
10338
  localSubmittedSessionRef.current = sessionId2;
10347
10339
  setLocalSubmittedSessionId(sessionId2);
@@ -10351,7 +10343,7 @@ function PiChatPanel({
10351
10343
  localSubmittedSessionRef.current = void 0;
10352
10344
  setLocalSubmittedSessionId(void 0);
10353
10345
  }, []);
10354
- const registry = useMemo14(() => {
10346
+ const registry = useMemo16(() => {
10355
10347
  const excludedBuiltins = new Set(excludeBuiltinCommands);
10356
10348
  const effectiveBuiltins = builtinCommands.filter((command) => {
10357
10349
  if (!hotReloadEnabled && command.name === "reload") return false;
@@ -10359,9 +10351,10 @@ function PiChatPanel({
10359
10351
  });
10360
10352
  const next = createCommandRegistry(effectiveBuiltins);
10361
10353
  for (const command of extraCommands ?? []) next.register(command);
10354
+ for (const command of contributedCommands) next.register(command);
10362
10355
  for (const command of commands) next.register(command);
10363
10356
  return next;
10364
- }, [apiBaseUrl, commands, excludeBuiltinCommands, extraCommands, hotReloadEnabled, normalizedRequestHeaders, serverResourcesEnabled, serverSkillsRefreshKey, storageScope]);
10357
+ }, [apiBaseUrl, commands, contributedCommands, excludeBuiltinCommands, extraCommands, hotReloadEnabled, normalizedRequestHeaders, serverResourcesEnabled, serverSkillsRefreshKey, storageScope]);
10365
10358
  const commandsStamp = useServerCommands({
10366
10359
  registry,
10367
10360
  requestHeaders: normalizedRequestHeaders,
@@ -10372,8 +10365,8 @@ function PiChatPanel({
10372
10365
  refreshKey: serverSkillsRefreshKey,
10373
10366
  enabled: serverResourcesEnabled
10374
10367
  });
10375
- const allCommands = useMemo14(() => registry.list(), [registry, commandsStamp]);
10376
- const actionableSlashCommands = useMemo14(
10368
+ const allCommands = useMemo16(() => registry.list(), [registry, commandsStamp]);
10369
+ const actionableSlashCommands = useMemo16(
10377
10370
  () => allCommands.flatMap((command) => command.clickBehavior === "execute" || command.clickBehavior === "insert" ? [{ name: command.name, clickBehavior: command.clickBehavior }] : []),
10378
10371
  [allCommands]
10379
10372
  );
@@ -10381,7 +10374,7 @@ function PiChatPanel({
10381
10374
  const warmupNotice = composerNoticeForWarmup(workspaceWarmupStatus);
10382
10375
  const runtimeDependenciesNotice = composerNoticeForRuntimeDependencies(workspaceWarmupStatus);
10383
10376
  const workspaceWarmupBlocked = Boolean(warmupNotice);
10384
- const activeBlockers = useMemo14(
10377
+ const activeBlockers = useMemo16(
10385
10378
  // A missing active session id means a single/sessionless chat host. In that
10386
10379
  // mode, keep scoped blockers visible instead of hiding the only attention UI.
10387
10380
  // Multi-session hosts should pass a session id so unrelated blockers filter out.
@@ -10391,7 +10384,7 @@ function PiChatPanel({
10391
10384
  const canonicalMessages = selectedChatState ? selectMessagesForRender(selectedChatState) : [];
10392
10385
  const queuePreview = selectedChatState ? selectQueuePreview(selectedChatState) : [];
10393
10386
  const messages = canonicalMessages;
10394
- const userHistory = useMemo14(() => selectComposerHistoryFromCanonicalUsers(canonicalMessages), [canonicalMessages]);
10387
+ const userHistory = useMemo16(() => selectComposerHistoryFromCanonicalUsers(canonicalMessages), [canonicalMessages]);
10395
10388
  const emptyStateHydrating = statusForState(selectedChatState, sessionsLoading || chatStatePending || selectedSessionPending) === "hydrating";
10396
10389
  const emptyHero = emptyPlacement === "hero" && messages.length === 0 && queuePreview.length === 0 && !emptyStateHydrating;
10397
10390
  const debugState = selectedPiSession?.getDebugState();
@@ -10399,7 +10392,7 @@ function PiChatPanel({
10399
10392
  const primaryComposerBlocker = activeBlockers[0];
10400
10393
  const composerBlockerLabel = workspaceWarmupBlocked ? warmupNotice?.title ?? "Preparing workspace..." : primaryComposerBlocker?.label ?? primaryComposerBlocker?.reason ?? "Complete the pending workspace action to continue.";
10401
10394
  const composerStatusNotice = warmupNotice ?? runtimeDependenciesNotice;
10402
- const runtimeNotices = useMemo14(() => {
10395
+ const runtimeNotices = useMemo16(() => {
10403
10396
  const fromState = selectedChatState ? selectRuntimeNotices(selectedChatState) : [];
10404
10397
  const sessionNotice = sessionsError ? [{ id: "session-navigation-error", level: "error", text: sessionsError.message, dismissible: true }] : [];
10405
10398
  const largeStateNotice = debug && debugState?.largeStateWarning ? [{
@@ -10613,7 +10606,7 @@ function PiChatPanel({
10613
10606
  }
10614
10607
  insertSlashCommand(name);
10615
10608
  }, [dismissSlash, insertSlashCommand, openModelPicker, openThinkingPicker, setComposerDraft]);
10616
- const policy = useMemo14(() => {
10609
+ const policy = useMemo16(() => {
10617
10610
  if (!selectedPiSession || !activeChatSessionId || !serverModelSelectionReady) return void 0;
10618
10611
  const policySession = {
10619
10612
  getState: () => {
@@ -10724,11 +10717,11 @@ function PiChatPanel({
10724
10717
  return false;
10725
10718
  }
10726
10719
  }, [activeChatSessionId, clearLocalSubmitted, dropLocalNotice, markLocalSubmitted, onPromptSubmitStarted, policy, selectedPiSession, setComposerDraft, surfaceRunRejected]);
10727
- const availableAssistantSlashCommands = useMemo14(
10720
+ const availableAssistantSlashCommands = useMemo16(
10728
10721
  () => policy ? actionableSlashCommands : actionableSlashCommands.filter((command) => command.clickBehavior === "insert"),
10729
10722
  [actionableSlashCommands, policy]
10730
10723
  );
10731
- const assistantMentionCatalog = useMemo14(() => {
10724
+ const assistantMentionCatalog = useMemo16(() => {
10732
10725
  const skills = /* @__PURE__ */ new Map();
10733
10726
  for (const command of allCommands) {
10734
10727
  if (command.source !== "skill" && command.kind !== "skill") continue;
@@ -10877,8 +10870,8 @@ function PiChatPanel({
10877
10870
  const isStreaming = isPiBusyStatus(status);
10878
10871
  const submitStatus = initialHydrationPromptAllowed ? "ready" : toPromptSubmitStatus(status);
10879
10872
  const submitDisabled = !policy || sessionsLoading || modelSelectionBlocked || composerBlocked && !isStreaming;
10880
- const mergedToolRenderers = useMemo14(() => mergeShadcnToolRenderers(toolRenderers), [toolRenderers]);
10881
- const debugMessages = useMemo14(() => messages.map(toDebugUiMessage), [messages]);
10873
+ const mergedToolRenderers = useMemo16(() => mergeShadcnToolRenderers(toolRenderers), [toolRenderers]);
10874
+ const debugMessages = useMemo16(() => messages.map(toDebugUiMessage), [messages]);
10882
10875
  const onTextareaChange = useCallback18((event) => {
10883
10876
  setModelPickerOpen(false);
10884
10877
  setThinkingPickerOpen(false);
@@ -10912,7 +10905,7 @@ function PiChatPanel({
10912
10905
  }
10913
10906
  handleComposerKeyDown(event);
10914
10907
  }, [handleComposerKeyDown, interrupt, isStreaming, mentionState, slashQuery]);
10915
- return /* @__PURE__ */ jsx34(ArtifactOpenProvider, { onOpenArtifact, children: /* @__PURE__ */ jsxs31(
10908
+ return /* @__PURE__ */ jsx36(ArtifactOpenProvider, { onOpenArtifact, children: /* @__PURE__ */ jsxs31(
10916
10909
  "div",
10917
10910
  {
10918
10911
  "data-boring-agent": "",
@@ -10929,7 +10922,7 @@ function PiChatPanel({
10929
10922
  role: "region",
10930
10923
  "aria-label": "Agent assistant",
10931
10924
  children: [
10932
- showSessionSidebar ? /* @__PURE__ */ jsx34("aside", { "data-boring-agent-part": "pi-chat-session-sidebar", className: "min-h-0 w-64 shrink-0 border-r border-border/60", children: /* @__PURE__ */ jsx34(
10925
+ showSessionSidebar ? /* @__PURE__ */ jsx36("aside", { "data-boring-agent-part": "pi-chat-session-sidebar", className: "min-h-0 w-64 shrink-0 border-r border-border/60", children: /* @__PURE__ */ jsx36(
10933
10926
  SessionList,
10934
10927
  {
10935
10928
  sessions: sessionList,
@@ -10943,7 +10936,7 @@ function PiChatPanel({
10943
10936
  loadingMore: sessions.loadingMore
10944
10937
  }
10945
10938
  ) }) : null,
10946
- /* @__PURE__ */ jsx34("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col", children: /* @__PURE__ */ jsxs31(
10939
+ /* @__PURE__ */ jsx36("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col", children: /* @__PURE__ */ jsxs31(
10947
10940
  "div",
10948
10941
  {
10949
10942
  className: cn(
@@ -10952,7 +10945,7 @@ function PiChatPanel({
10952
10945
  chrome && "mx-3 my-3 rounded-xl bg-[color:var(--surface-chat)] shadow-[0_1px_0_oklch(0_0_0/0.02),0_1px_2px_-1px_oklch(0_0_0/0.04),inset_0_0_0_1px_oklch(from_var(--border)_l_c_h/0.6)]"
10953
10946
  ),
10954
10947
  children: [
10955
- /* @__PURE__ */ jsx34(
10948
+ /* @__PURE__ */ jsx36(
10956
10949
  PiConversationSurface,
10957
10950
  {
10958
10951
  chrome,
@@ -10977,7 +10970,7 @@ function PiChatPanel({
10977
10970
  windowResetKey: activeSessionId
10978
10971
  }
10979
10972
  ),
10980
- /* @__PURE__ */ jsx34(
10973
+ /* @__PURE__ */ jsx36(
10981
10974
  PiChatComposerSurface,
10982
10975
  {
10983
10976
  chrome,
@@ -11034,6 +11027,9 @@ function PiChatPanel({
11034
11027
  textareaRef,
11035
11028
  onTextareaChange,
11036
11029
  onTextareaKeyDown,
11030
+ updateDraft: (update, options) => {
11031
+ setComposerDraft(update(draftRef.current), options?.focus ?? true);
11032
+ },
11037
11033
  onSubmitMessage: ({ text, files }) => sendComposerMessage({ text, files }),
11038
11034
  onStop: stop
11039
11035
  }
@@ -11041,7 +11037,7 @@ function PiChatPanel({
11041
11037
  ]
11042
11038
  }
11043
11039
  ) }),
11044
- debug ? /* @__PURE__ */ jsx34(Suspense, { fallback: null, children: /* @__PURE__ */ jsx34("div", { "aria-label": "Chat debug metadata", className: "contents", role: "region", children: /* @__PURE__ */ jsx34(
11040
+ debug ? /* @__PURE__ */ jsx36(Suspense, { fallback: null, children: /* @__PURE__ */ jsx36("div", { "aria-label": "Chat debug metadata", className: "contents", role: "region", children: /* @__PURE__ */ jsx36(
11045
11041
  DebugDrawer2,
11046
11042
  {
11047
11043
  apiBaseUrl,
@@ -11097,8 +11093,10 @@ function getAgentCommands(options = {}) {
11097
11093
  export {
11098
11094
  ArtifactOpenProvider,
11099
11095
  ChatEmptyState,
11096
+ ChatMessageContributionProvider,
11100
11097
  PiChatPanel as ChatPanel,
11101
11098
  CodeBlock2 as CodeBlock,
11099
+ ComposerContributionProvider,
11102
11100
  Conversation,
11103
11101
  ConversationContent,
11104
11102
  ConversationScrollButton,
@@ -11122,7 +11120,10 @@ export {
11122
11120
  ReasoningContent,
11123
11121
  ReasoningTrigger,
11124
11122
  ThinkingSelect,
11123
+ Tool2 as Tool,
11125
11124
  ToolCallGroup,
11125
+ ToolContent,
11126
+ ToolHeader,
11126
11127
  activeSessionStorageKey,
11127
11128
  builtinCommands,
11128
11129
  clearActiveSessionId,