@hachej/boring-agent 0.1.93 → 0.1.95

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";
@@ -140,12 +140,73 @@ var defaultChatSuggestions = [
140
140
  prompt: "Help me refactor a function \u2014 clean it up without changing behavior."
141
141
  }
142
142
  ];
143
+ function ChatSuggestionGrid({ suggestions, onSelect, className }) {
144
+ if (suggestions.length === 0) return null;
145
+ return /* @__PURE__ */ jsx2(
146
+ "div",
147
+ {
148
+ "data-boring-agent-part": "suggestion-grid",
149
+ style: {
150
+ ["--chat-empty-card-hover"]: "var(--accent-soft)",
151
+ ["--chat-empty-accent"]: "var(--accent)"
152
+ },
153
+ className: cn(
154
+ "grid w-full grid-cols-1 gap-px overflow-hidden rounded-xl bg-border/70 ring-1 ring-border/70 @[480px]:grid-cols-2",
155
+ className
156
+ ),
157
+ children: suggestions.map((suggestion) => {
158
+ const Icon = suggestion.icon;
159
+ return /* @__PURE__ */ jsxs(
160
+ Button,
161
+ {
162
+ type: "button",
163
+ variant: "ghost",
164
+ "data-boring-agent-part": "suggestion-card",
165
+ onClick: () => {
166
+ if (suggestion.onSelect) {
167
+ suggestion.onSelect();
168
+ return;
169
+ }
170
+ onSelect?.(suggestion);
171
+ },
172
+ className: "group h-auto justify-start gap-3 rounded-none bg-background px-4 py-3.5 text-left hover:bg-[color:var(--chat-empty-card-hover)] focus-visible:bg-[color:var(--chat-empty-card-hover)]",
173
+ children: [
174
+ Icon && /* @__PURE__ */ jsx2(
175
+ Icon,
176
+ {
177
+ className: "mt-0.5 h-4 w-4 shrink-0 text-muted-foreground transition-colors group-hover:text-[color:var(--chat-empty-accent)]",
178
+ strokeWidth: 1.75
179
+ }
180
+ ),
181
+ /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [
182
+ /* @__PURE__ */ jsx2("span", { className: "text-[13px] font-medium text-foreground", children: suggestion.label }),
183
+ suggestion.hint && /* @__PURE__ */ jsx2("span", { className: "text-[12px] text-muted-foreground", children: suggestion.hint })
184
+ ] }),
185
+ /* @__PURE__ */ jsx2(
186
+ ArrowUpRight,
187
+ {
188
+ className: cn(
189
+ "mt-0.5 h-3.5 w-3.5 shrink-0 text-muted-foreground opacity-0 transition-all duration-200 ease-[cubic-bezier(0.22,1,0.36,1)]",
190
+ "group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:opacity-100 group-hover:text-[color:var(--chat-empty-accent)]"
191
+ ),
192
+ strokeWidth: 2
193
+ }
194
+ )
195
+ ]
196
+ },
197
+ suggestion.label
198
+ );
199
+ })
200
+ }
201
+ );
202
+ }
143
203
  function ChatEmptyState({
144
204
  eyebrow = "New session",
145
- title = "What are we building?",
205
+ title = "What should we work on?",
146
206
  description = "Ask a question, open a file from the workbench, or start from a template.",
147
207
  suggestions = defaultChatSuggestions,
148
208
  onSelect,
209
+ hero = false,
149
210
  footer,
150
211
  className
151
212
  }) {
@@ -164,65 +225,20 @@ function ChatEmptyState({
164
225
  // @container lets the hero scale to the pane it lives in, not the
165
226
  // viewport — split chat panes get the compact variant.
166
227
  "@container mx-auto flex w-full max-w-[640px] flex-col px-2 pt-12 pb-4",
228
+ hero && "items-center py-0 text-center",
167
229
  className
168
230
  ),
169
231
  children: [
170
- eyebrow && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-[10.5px] font-medium uppercase tracking-[0.16em] text-[color:var(--chat-empty-eyebrow-color)]", children: [
232
+ !hero && eyebrow && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-[10.5px] font-medium uppercase tracking-[0.16em] text-[color:var(--chat-empty-eyebrow-color)]", children: [
171
233
  /* @__PURE__ */ jsx2("span", { className: "inline-block h-px w-4 bg-[color:var(--chat-empty-accent)]", "aria-hidden": "true" }),
172
234
  eyebrow
173
235
  ] }),
174
- title && /* @__PURE__ */ jsx2("h3", { className: "mt-3 text-[24px] font-medium leading-[1.05] tracking-[-0.02em] text-[color:var(--chat-empty-title-color)] @[480px]:text-[34px]", children: title }),
175
- description && /* @__PURE__ */ jsx2("p", { className: "mt-3 hidden max-w-[440px] text-[14px] leading-relaxed text-[color:var(--chat-empty-description-color)] @[420px]:block", children: description }),
176
- suggestions.length > 0 && /* @__PURE__ */ jsx2(
177
- "div",
178
- {
179
- "data-boring-agent-part": "suggestion-grid",
180
- className: "mt-8 grid w-full grid-cols-1 gap-px overflow-hidden rounded-xl bg-border/70 ring-1 ring-border/70 @[480px]:grid-cols-2",
181
- children: suggestions.map((suggestion) => {
182
- const Icon = suggestion.icon;
183
- return /* @__PURE__ */ jsxs(
184
- Button,
185
- {
186
- type: "button",
187
- variant: "ghost",
188
- "data-boring-agent-part": "suggestion-card",
189
- onClick: () => {
190
- if (suggestion.onSelect) {
191
- suggestion.onSelect();
192
- return;
193
- }
194
- onSelect?.(suggestion);
195
- },
196
- className: "group h-auto justify-start gap-3 rounded-none bg-background px-4 py-3.5 text-left hover:bg-[color:var(--chat-empty-card-hover)] focus-visible:bg-[color:var(--chat-empty-card-hover)]",
197
- children: [
198
- Icon && /* @__PURE__ */ jsx2(
199
- Icon,
200
- {
201
- className: "mt-0.5 h-4 w-4 shrink-0 text-muted-foreground transition-colors group-hover:text-[color:var(--chat-empty-accent)]",
202
- strokeWidth: 1.75
203
- }
204
- ),
205
- /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [
206
- /* @__PURE__ */ jsx2("span", { className: "text-[13px] font-medium text-foreground", children: suggestion.label }),
207
- suggestion.hint && /* @__PURE__ */ jsx2("span", { className: "text-[12px] text-muted-foreground", children: suggestion.hint })
208
- ] }),
209
- /* @__PURE__ */ jsx2(
210
- ArrowUpRight,
211
- {
212
- className: cn(
213
- "mt-0.5 h-3.5 w-3.5 shrink-0 text-muted-foreground opacity-0 transition-all duration-200 ease-[cubic-bezier(0.22,1,0.36,1)]",
214
- "group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:opacity-100 group-hover:text-[color:var(--chat-empty-accent)]"
215
- ),
216
- strokeWidth: 2
217
- }
218
- )
219
- ]
220
- },
221
- suggestion.label
222
- );
223
- })
224
- }
225
- ),
236
+ title && /* @__PURE__ */ jsx2("h3", { className: cn(
237
+ "text-[24px] font-medium leading-[1.05] tracking-[-0.02em] text-[color:var(--chat-empty-title-color)] @[480px]:text-[34px]",
238
+ hero ? "m-0" : "mt-3"
239
+ ), children: title }),
240
+ !hero && description && /* @__PURE__ */ jsx2("p", { className: "mt-3 hidden max-w-[440px] text-[14px] leading-relaxed text-[color:var(--chat-empty-description-color)] @[420px]:block", children: description }),
241
+ /* @__PURE__ */ jsx2(ChatSuggestionGrid, { suggestions, onSelect, className: hero ? "mt-4" : "mt-8" }),
226
242
  footer
227
243
  ]
228
244
  }
@@ -346,7 +362,7 @@ var lineClass = {
346
362
  " ": ""
347
363
  };
348
364
  var prefixMap = { "+": "+", "-": "-", " ": " " };
349
- function DiffView({ oldString, newString, path, replaceAll }) {
365
+ function DiffView({ oldString, newString, path, replaceAll, actions }) {
350
366
  const [expanded, setExpanded] = useState(false);
351
367
  if (oldString === newString) {
352
368
  return /* @__PURE__ */ jsx3("div", { "data-testid": "diff-no-change", className: "py-2 italic opacity-60", children: "No changes" });
@@ -356,9 +372,12 @@ function DiffView({ oldString, newString, path, replaceAll }) {
356
372
  const collapsed = !expanded && lines.length > COLLAPSE_THRESHOLD;
357
373
  const visible = collapsed ? lines.slice(0, COLLAPSE_THRESHOLD) : lines;
358
374
  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)" : ""
375
+ /* @__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: [
376
+ /* @__PURE__ */ jsxs2("span", { className: "min-w-0 truncate", children: [
377
+ path,
378
+ replaceAll ? " (replace all)" : ""
379
+ ] }),
380
+ actions ? /* @__PURE__ */ jsx3("span", { className: "flex shrink-0 items-center gap-0.5", children: actions }) : null
362
381
  ] }),
363
382
  /* @__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
383
  /* @__PURE__ */ jsx3("span", { className: "inline-block w-[1.5ch] select-none opacity-50", children: prefixMap[line.type] }),
@@ -1264,9 +1283,9 @@ var statusIcons = {
1264
1283
  "output-error": /* @__PURE__ */ jsx9(XCircleIcon, { className: "size-4 text-destructive" }),
1265
1284
  aborted: /* @__PURE__ */ jsx9(XCircleIcon, { className: "size-4 text-muted-foreground" })
1266
1285
  };
1267
- var getStatusBadge = (status) => /* @__PURE__ */ jsxs8(Badge, { className: "shrink-0 gap-1.5 rounded-full text-xs", variant: "secondary", children: [
1286
+ var getStatusBadge = (status, label = statusLabels[status]) => /* @__PURE__ */ jsxs8(Badge, { className: "shrink-0 gap-1.5 rounded-full text-xs", variant: "secondary", children: [
1268
1287
  statusIcons[status],
1269
- statusLabels[status]
1288
+ label
1270
1289
  ] });
1271
1290
  var ToolHeader = ({
1272
1291
  className,
@@ -1275,6 +1294,7 @@ var ToolHeader = ({
1275
1294
  state,
1276
1295
  icon,
1277
1296
  toolName,
1297
+ statusLabel,
1278
1298
  ...props
1279
1299
  }) => {
1280
1300
  const derivedName = type === "dynamic-tool" || type === "tool-call" ? toolName : type.split("-").slice(1).join("-");
@@ -1291,7 +1311,7 @@ var ToolHeader = ({
1291
1311
  /* @__PURE__ */ jsxs8("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
1292
1312
  icon ?? /* @__PURE__ */ jsx9(WrenchIcon, { className: "size-4 shrink-0 text-muted-foreground" }),
1293
1313
  /* @__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)
1314
+ getStatusBadge(state, statusLabel)
1295
1315
  ] }),
1296
1316
  /* @__PURE__ */ jsx9(
1297
1317
  ChevronDownIcon,
@@ -1357,7 +1377,7 @@ var ToolOutput = ({
1357
1377
 
1358
1378
  // src/front/toolRenderers.tsx
1359
1379
  import { CollapsibleTrigger as CollapsibleTrigger2 } from "@hachej/boring-ui-kit";
1360
- import { ChevronDownIcon as ChevronDownIcon2, ExternalLinkIcon, FileDiffIcon, FilePlus2Icon, FileTextIcon, SearchIcon, SquareTerminalIcon, ZapIcon } from "lucide-react";
1380
+ import { ChevronDownIcon as ChevronDownIcon2, CopyIcon as CopyIcon2, DownloadIcon, ExternalLinkIcon, FileDiffIcon, FilePlus2Icon, FileTextIcon, SearchIcon, SquareTerminalIcon, ZapIcon } from "lucide-react";
1361
1381
 
1362
1382
  // src/front/primitives/artifact.tsx
1363
1383
  import { Button as Button6 } from "@hachej/boring-ui-kit";
@@ -1369,44 +1389,6 @@ import {
1369
1389
  } from "@hachej/boring-ui-kit";
1370
1390
  import { XIcon } from "lucide-react";
1371
1391
  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
1392
  var ArtifactAction = ({
1411
1393
  tooltip,
1412
1394
  label,
@@ -1442,13 +1424,6 @@ var ArtifactAction = ({
1442
1424
  }
1443
1425
  return button;
1444
1426
  };
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
1427
 
1453
1428
  // src/front/runtimeReadinessStatus.ts
1454
1429
  var COPY = {
@@ -1514,7 +1489,7 @@ function getWorkspaceNotReadyStatus(output) {
1514
1489
  }
1515
1490
 
1516
1491
  // src/front/toolRenderers.tsx
1517
- import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
1492
+ import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
1518
1493
  function asRecord2(value) {
1519
1494
  return typeof value === "object" && value !== null ? value : {};
1520
1495
  }
@@ -1672,59 +1647,44 @@ function renderWrite2(part) {
1672
1647
  const path = typeof input.path === "string" ? input.path : "";
1673
1648
  const filesystem = typeof input.filesystem === "string" ? input.filesystem : void 0;
1674
1649
  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 {
1650
+ return /* @__PURE__ */ jsxs10(Tool2, { defaultOpen: part.state === "output-available", children: [
1651
+ /* @__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) }),
1652
+ /* @__PURE__ */ jsxs10(ToolContent, { className: "p-1.5", children: [
1653
+ /* @__PURE__ */ jsx11("div", { className: "max-h-80 overflow-auto overscroll-contain rounded-sm", children: content ? /* @__PURE__ */ jsx11(
1654
+ DiffView,
1655
+ {
1656
+ oldString: "",
1657
+ newString: content,
1658
+ path,
1659
+ actions: /* @__PURE__ */ jsxs10(Fragment3, { children: [
1660
+ /* @__PURE__ */ jsx11(ArtifactAction, { className: "size-6", icon: CopyIcon2, tooltip: "Copy contents", label: "Copy", onClick: () => {
1661
+ void copyTextToClipboard(content);
1662
+ } }),
1663
+ /* @__PURE__ */ jsx11(
1664
+ ArtifactAction,
1665
+ {
1666
+ className: "size-6",
1667
+ icon: DownloadIcon,
1668
+ tooltip: "Download file",
1669
+ label: "Download",
1670
+ onClick: () => {
1710
1671
  const blob = new Blob([content], { type: "text/plain" });
1711
1672
  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();
1673
+ const anchor = document.createElement("a");
1674
+ anchor.href = url;
1675
+ anchor.download = path.split("/").pop() || "artifact.txt";
1676
+ document.body.appendChild(anchor);
1677
+ anchor.click();
1678
+ anchor.remove();
1718
1679
  URL.revokeObjectURL(url);
1719
- } catch {
1720
1680
  }
1721
1681
  }
1722
- }
1723
- )
1724
- ] })
1725
- ] }),
1726
- content && /* @__PURE__ */ jsx11(ArtifactContent, { className: "p-0", children: /* @__PURE__ */ jsx11(CodeBlock2, { code: content, language: lang ?? "text", showLineNumbers: true }) })
1727
- ] }) })
1682
+ )
1683
+ ] })
1684
+ }
1685
+ ) : /* @__PURE__ */ jsx11("p", { className: "px-2 py-1 text-[12px] text-muted-foreground", children: "No file content was included with this tool call." }) }),
1686
+ part.errorText || part.state === "output-error" || part.state === "output-denied" ? /* @__PURE__ */ jsx11(ToolOutput, { output: part.output, errorText: part.errorText }) : null
1687
+ ] })
1728
1688
  ] });
1729
1689
  }
1730
1690
  function renderEdit2(part) {
@@ -1733,25 +1693,26 @@ function renderEdit2(part) {
1733
1693
  const input = asRecord2(part.input);
1734
1694
  const path = typeof input.path === "string" ? input.path : "";
1735
1695
  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 })
1696
+ const edits = Array.isArray(input.edits) ? input.edits.flatMap((value) => {
1697
+ const edit = asRecord2(value);
1698
+ const oldText = typeof edit.oldText === "string" ? edit.oldText : "";
1699
+ const newText = typeof edit.newText === "string" ? edit.newText : "";
1700
+ return oldText || newText ? [{ oldText, newText }] : [];
1701
+ }) : [];
1702
+ const legacyOldText = typeof input.oldString === "string" ? input.oldString : typeof input.oldText === "string" ? input.oldText : "";
1703
+ const legacyNewText = typeof input.newString === "string" ? input.newString : typeof input.newText === "string" ? input.newText : "";
1704
+ const visibleEdits = edits.length > 0 ? edits : legacyOldText || legacyNewText ? [{ oldText: legacyOldText, newText: legacyNewText }] : [];
1705
+ return /* @__PURE__ */ jsxs10(Tool2, { defaultOpen: part.state === "output-available", children: [
1706
+ /* @__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) }),
1707
+ /* @__PURE__ */ jsxs10(ToolContent, { className: "space-y-2 p-2", children: [
1708
+ /* @__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: [
1709
+ visibleEdits.length > 1 ? /* @__PURE__ */ jsxs10("div", { className: "mb-1 px-1 text-[11px] text-muted-foreground", children: [
1710
+ "Change ",
1711
+ index + 1
1712
+ ] }) : null,
1713
+ /* @__PURE__ */ jsx11(DiffView, { oldString: edit.oldText, newString: edit.newText, path })
1714
+ ] }, `${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." }) }),
1715
+ part.errorText || part.state === "output-error" || part.state === "output-denied" ? /* @__PURE__ */ jsx11(ToolOutput, { output: part.output, errorText: part.errorText }) : null
1755
1716
  ] })
1756
1717
  ] });
1757
1718
  }
@@ -2027,7 +1988,7 @@ function usePickerKeyboard({
2027
1988
  }
2028
1989
 
2029
1990
  // src/front/primitives/mention-picker.tsx
2030
- import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
1991
+ import { Fragment as Fragment4, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
2031
1992
  var mentionSearchGlob = toFileSearchGlob;
2032
1993
  function highlight(text, query) {
2033
1994
  if (!query) return null;
@@ -2090,7 +2051,7 @@ function MentionPicker({
2090
2051
  onSelect(path);
2091
2052
  },
2092
2053
  children: [
2093
- /* @__PURE__ */ jsx12("span", { className: "font-medium", children: hl ? /* @__PURE__ */ jsxs11(Fragment3, { children: [
2054
+ /* @__PURE__ */ jsx12("span", { className: "font-medium", children: hl ? /* @__PURE__ */ jsxs11(Fragment4, { children: [
2094
2055
  hl.before,
2095
2056
  /* @__PURE__ */ jsx12("mark", { className: "bg-transparent text-[color:var(--accent)]", children: hl.match }),
2096
2057
  hl.after
@@ -2496,10 +2457,11 @@ var PiComposerPolicyController = class {
2496
2457
  }));
2497
2458
  }
2498
2459
  async runLocalCommand(commandName, args) {
2499
- if (isPiChatBusy(this.options.session.getState().status)) {
2460
+ const command = this.options.registry.get(commandName);
2461
+ if (isPiChatBusy(this.options.session.getState().status) && command?.allowWhileBusy?.(args) !== true) {
2500
2462
  return this.block("busy-slash-command", "Slash commands are not queued while the agent is responding.");
2501
2463
  }
2502
- const result = await Promise.resolve(this.options.registry.get(commandName)?.handler(args, this.options.slashContext));
2464
+ const result = await Promise.resolve(command?.handler(args, this.options.slashContext));
2503
2465
  const message = typeof result === "string" ? result : result && typeof result === "object" ? result.message : void 0;
2504
2466
  const preserveDraft = Boolean(result && typeof result === "object" && result.preserveDraft === true);
2505
2467
  if (message) this.options.onCommandResult?.(message);
@@ -2912,6 +2874,25 @@ function composerNoticeForRuntimeDependencies(status) {
2912
2874
  return null;
2913
2875
  }
2914
2876
 
2877
+ // src/front/chat/composerContributions.tsx
2878
+ import { createContext as createContext3, useContext as useContext3, useMemo as useMemo3 } from "react";
2879
+ import { jsx as jsx13 } from "react/jsx-runtime";
2880
+ var ComposerContributionContext = createContext3([]);
2881
+ function ComposerContributionProvider({
2882
+ contribution,
2883
+ children
2884
+ }) {
2885
+ const parent = useContext3(ComposerContributionContext);
2886
+ const contributions = useMemo3(
2887
+ () => [contribution, ...parent.filter((entry) => entry.id !== contribution.id)],
2888
+ [contribution, parent]
2889
+ );
2890
+ return /* @__PURE__ */ jsx13(ComposerContributionContext.Provider, { value: contributions, children });
2891
+ }
2892
+ function useComposerContributions() {
2893
+ return useContext3(ComposerContributionContext);
2894
+ }
2895
+
2915
2896
  // src/front/chat/pi/piChatMessageMetadata.ts
2916
2897
  function earliestCreatedAt(left, right) {
2917
2898
  if (!left) return right;
@@ -5159,7 +5140,7 @@ function resolveStorage2(storage) {
5159
5140
  }
5160
5141
 
5161
5142
  // 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";
5143
+ import { useCallback as useCallback7, useEffect as useEffect8, useMemo as useMemo4, useRef as useRef8, useState as useState10 } from "react";
5163
5144
  var DEFAULT_SESSIONS_API_PATH = "/api/v1/agent/pi-chat/sessions";
5164
5145
  var SESSION_PAGE_SIZE = 50;
5165
5146
  var DEFAULT_MAX_RETRIES = 60;
@@ -5180,16 +5161,16 @@ function usePiSessions(options = {}) {
5180
5161
  const addressed = typeof options.agentTypeId === "string" && options.agentTypeId.length > 0;
5181
5162
  const sessionsApiPath = options.sessionsApiPath ?? (addressed ? `/api/v1/agents/${encodeURIComponent(options.agentTypeId)}/sessions` : DEFAULT_SESSIONS_API_PATH);
5182
5163
  const storageScope = options.storageScope ?? "default";
5183
- const fetchImpl = useMemo3(() => options.fetch ?? globalThis.fetch.bind(globalThis), [options.fetch]);
5164
+ const fetchImpl = useMemo4(() => options.fetch ?? globalThis.fetch.bind(globalThis), [options.fetch]);
5184
5165
  const createRemoteSession = options.createRemoteSession ?? createRemotePiSession;
5185
5166
  const connectActiveSession = options.connectActiveSession ?? true;
5186
5167
  const retryMaxRetries = options.retry?.maxRetries ?? DEFAULT_MAX_RETRIES;
5187
5168
  const retryBaseMs = options.retry?.baseMs ?? DEFAULT_RETRY_BASE_MS;
5188
5169
  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]);
5170
+ const headersKey = useMemo4(() => headersScopeKey(options.requestHeaders, storageScope), [options.requestHeaders, storageScope]);
5171
+ const normalizedHeaders = useMemo4(() => buildRequestHeaders(options.requestHeaders, storageScope), [headersKey, storageScope]);
5172
+ const requestScopeKey = useMemo4(() => requestScopeIdentity(apiBaseUrl, sessionsApiPath, storageScope, headersKey), [apiBaseUrl, headersKey, sessionsApiPath, storageScope]);
5173
+ const dataSourceKey = useMemo4(() => dataSourceIdentity(apiBaseUrl, sessionsApiPath, storageScope), [apiBaseUrl, sessionsApiPath, storageScope]);
5193
5174
  const [sessions, setSessions] = useState10([]);
5194
5175
  const [dataStorageScope, setDataStorageScope] = useState10(storageScope);
5195
5176
  const [activeSessionId, setActiveSessionId] = useState10(() => options.initialActiveSessionId ?? readActiveSessionId({ storageScope, storage: options.storage }));
@@ -5216,7 +5197,7 @@ function usePiSessions(options = {}) {
5216
5197
  requestScopeRef.current = requestScopeKey;
5217
5198
  const remoteSessionOptionsRef = useRef8(options.remoteSessionOptions);
5218
5199
  remoteSessionOptionsRef.current = options.remoteSessionOptions;
5219
- const remoteSessionOptionsKey = useMemo3(
5200
+ const remoteSessionOptionsKey = useMemo4(
5220
5201
  () => remoteSessionOptionsIdentity(options.remoteSessionOptions),
5221
5202
  [options.remoteSessionOptions]
5222
5203
  );
@@ -5658,10 +5639,10 @@ function clearRetryTimer(ref) {
5658
5639
  }
5659
5640
 
5660
5641
  // src/front/chat/session/SessionList.tsx
5661
- import { useMemo as useMemo4 } from "react";
5642
+ import { useMemo as useMemo5 } from "react";
5662
5643
  import { ChevronLeftIcon, PlusIcon, Trash2Icon } from "lucide-react";
5663
5644
  import { IconButton } from "@hachej/boring-ui-kit";
5664
- import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
5645
+ import { Fragment as Fragment5, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
5665
5646
  var DAY_MS = 24 * 60 * 60 * 1e3;
5666
5647
  function SessionList({
5667
5648
  sessions,
@@ -5676,7 +5657,7 @@ function SessionList({
5676
5657
  onClose,
5677
5658
  className
5678
5659
  }) {
5679
- const groups = useMemo4(() => groupSessions(sessions), [sessions]);
5660
+ const groups = useMemo5(() => groupSessions(sessions), [sessions]);
5680
5661
  return /* @__PURE__ */ jsxs12(
5681
5662
  "div",
5682
5663
  {
@@ -5687,26 +5668,26 @@ function SessionList({
5687
5668
  "aria-busy": loading ? "true" : void 0,
5688
5669
  children: [
5689
5670
  /* @__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" }),
5671
+ /* @__PURE__ */ jsx14("span", { className: "text-[12px] font-medium tracking-tight text-foreground/70", children: "Sessions" }),
5691
5672
  /* @__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
5673
+ 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,
5674
+ 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
5675
  ] })
5695
5676
  ] }),
5696
5677
  /* @__PURE__ */ jsxs12("div", { className: "flex-1 overflow-y-auto py-2.5", children: [
5697
5678
  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
5679
  loading ? "Loading sessions\u2026" : "No sessions yet.",
5699
- !loading ? /* @__PURE__ */ jsxs12(Fragment4, { children: [
5700
- /* @__PURE__ */ jsx13("br", {}),
5680
+ !loading ? /* @__PURE__ */ jsxs12(Fragment5, { children: [
5681
+ /* @__PURE__ */ jsx14("br", {}),
5701
5682
  "Start a new chat to begin."
5702
5683
  ] }) : null
5703
5684
  ] }) : null,
5704
5685
  groups.map((group, index) => /* @__PURE__ */ jsxs12("section", { className: cn(index > 0 && "mt-4"), children: [
5705
5686
  /* @__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 })
5687
+ /* @__PURE__ */ jsx14("span", { children: group.label }),
5688
+ /* @__PURE__ */ jsx14("span", { "aria-hidden": "true", className: "text-[10.5px] tabular-nums text-muted-foreground/40", children: group.items.length })
5708
5689
  ] }),
5709
- /* @__PURE__ */ jsx13("ul", { role: "list", className: "flex flex-col", children: group.items.map((session) => /* @__PURE__ */ jsx13(
5690
+ /* @__PURE__ */ jsx14("ul", { role: "list", className: "flex flex-col", children: group.items.map((session) => /* @__PURE__ */ jsx14(
5710
5691
  SessionRow,
5711
5692
  {
5712
5693
  session,
@@ -5717,7 +5698,7 @@ function SessionList({
5717
5698
  session.id
5718
5699
  )) })
5719
5700
  ] }, group.key)),
5720
- hasMore && onLoadMore ? /* @__PURE__ */ jsx13("div", { className: "px-3 py-3", children: /* @__PURE__ */ jsx13(
5701
+ hasMore && onLoadMore ? /* @__PURE__ */ jsx14("div", { className: "px-3 py-3", children: /* @__PURE__ */ jsx14(
5721
5702
  "button",
5722
5703
  {
5723
5704
  type: "button",
@@ -5749,10 +5730,10 @@ function SessionRow({ session, active, onSwitch, onDelete }) {
5749
5730
  onClick: () => onSwitch?.(session.id),
5750
5731
  children: [
5751
5732
  /* @__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
5733
+ /* @__PURE__ */ jsx14("span", { className: cn(active ? "font-medium text-foreground" : "text-foreground/90"), children: session.title || "Untitled" }),
5734
+ 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
5735
  ] }),
5755
- onDelete ? /* @__PURE__ */ jsx13(
5736
+ onDelete ? /* @__PURE__ */ jsx14(
5756
5737
  IconButton,
5757
5738
  {
5758
5739
  type: "button",
@@ -5764,7 +5745,7 @@ function SessionRow({ session, active, onSwitch, onDelete }) {
5764
5745
  onDelete(session.id);
5765
5746
  },
5766
5747
  "aria-label": `Delete ${session.title || "session"}`,
5767
- children: /* @__PURE__ */ jsx13(Trash2Icon, { className: "h-3 w-3" })
5748
+ children: /* @__PURE__ */ jsx14(Trash2Icon, { className: "h-3 w-3" })
5768
5749
  }
5769
5750
  ) : null
5770
5751
  ]
@@ -6019,7 +6000,7 @@ import { Button as Button7 } from "@hachej/boring-ui-kit";
6019
6000
  import { ArrowDownIcon, DownloadIcon as DownloadIcon2 } from "lucide-react";
6020
6001
  import { useCallback as useCallback8, useEffect as useEffect9 } from "react";
6021
6002
  import { StickToBottom, useStickToBottomContext } from "use-stick-to-bottom";
6022
- import { Fragment as Fragment5, jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
6003
+ import { Fragment as Fragment6, jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
6023
6004
  var ConversationScrollController = ({
6024
6005
  onScrollToBottomReady
6025
6006
  }) => {
@@ -6045,7 +6026,7 @@ var Conversation = ({
6045
6026
  role: "log",
6046
6027
  ...props,
6047
6028
  children: [
6048
- /* @__PURE__ */ jsx14(ConversationScrollController, { onScrollToBottomReady }),
6029
+ /* @__PURE__ */ jsx15(ConversationScrollController, { onScrollToBottomReady }),
6049
6030
  children
6050
6031
  ]
6051
6032
  }
@@ -6053,7 +6034,7 @@ var Conversation = ({
6053
6034
  var ConversationContent = ({
6054
6035
  className,
6055
6036
  ...props
6056
- }) => /* @__PURE__ */ jsx14(
6037
+ }) => /* @__PURE__ */ jsx15(
6057
6038
  StickToBottom.Content,
6058
6039
  {
6059
6040
  className: cn("flex flex-col gap-8 p-4", className),
@@ -6068,7 +6049,7 @@ var ConversationScrollButton = ({
6068
6049
  const handleScrollToBottom = useCallback8(() => {
6069
6050
  scrollToBottom();
6070
6051
  }, [scrollToBottom]);
6071
- return !isAtBottom && /* @__PURE__ */ jsx14(
6052
+ return !isAtBottom && /* @__PURE__ */ jsx15(
6072
6053
  Button7,
6073
6054
  {
6074
6055
  className: cn(
@@ -6081,7 +6062,7 @@ var ConversationScrollButton = ({
6081
6062
  variant: "outline",
6082
6063
  "aria-label": "Scroll to latest message",
6083
6064
  ...props,
6084
- children: /* @__PURE__ */ jsx14(ArrowDownIcon, { className: "size-4" })
6065
+ children: /* @__PURE__ */ jsx15(ArrowDownIcon, { className: "size-4" })
6085
6066
  }
6086
6067
  );
6087
6068
  };
@@ -6117,16 +6098,16 @@ function noticeTextClass(className) {
6117
6098
  import { memo as memo2 } from "react";
6118
6099
  import { AlertTriangleIcon, InfoIcon, Loader2Icon, PlugZapIcon, RefreshCwIcon, XIcon as XIcon2 } from "lucide-react";
6119
6100
  import { Button as Button8 } from "@hachej/boring-ui-kit";
6120
- import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
6101
+ import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
6121
6102
  var RuntimeNotices = memo2(({ notices, onDismiss, onAction, renderAction, className, ...props }) => {
6122
6103
  if (notices.length === 0) return null;
6123
- return /* @__PURE__ */ jsx15(
6104
+ return /* @__PURE__ */ jsx16(
6124
6105
  "div",
6125
6106
  {
6126
6107
  "data-boring-agent-part": "runtime-notices",
6127
6108
  className: cn("flex flex-col gap-2 px-4 py-2", className),
6128
6109
  ...props,
6129
- children: notices.map((notice) => /* @__PURE__ */ jsx15(RuntimeNoticeRow, { notice, onDismiss, onAction, renderAction }, notice.id))
6110
+ children: notices.map((notice) => /* @__PURE__ */ jsx16(RuntimeNoticeRow, { notice, onDismiss, onAction, renderAction }, notice.id))
6130
6111
  }
6131
6112
  );
6132
6113
  });
@@ -6150,11 +6131,11 @@ function RuntimeNoticeRow({ notice, onDismiss, onAction, renderAction }) {
6150
6131
  "flex items-start gap-2.5 motion-reduce:transition-none"
6151
6132
  ),
6152
6133
  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 }) }),
6134
+ /* @__PURE__ */ jsx16(Icon, { className: cn(noticeIconClass(notice.level), kind === "retry" && "animate-spin motion-reduce:animate-none"), "aria-hidden": "true" }),
6135
+ /* @__PURE__ */ jsx16("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx16("p", { className: noticeTextClass(), children: notice.text }) }),
6155
6136
  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(
6137
+ actionLabel && onAction ? /* @__PURE__ */ jsx16(Button8, { type: "button", variant: "ghost", size: "sm", onClick: () => onAction(notice.id), children: actionLabel }) : null,
6138
+ notice.dismissible && onDismiss ? /* @__PURE__ */ jsx16(
6158
6139
  Button8,
6159
6140
  {
6160
6141
  type: "button",
@@ -6163,7 +6144,7 @@ function RuntimeNoticeRow({ notice, onDismiss, onAction, renderAction }) {
6163
6144
  "aria-label": "Dismiss notice",
6164
6145
  onClick: () => onDismiss(notice.id),
6165
6146
  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" })
6147
+ children: /* @__PURE__ */ jsx16(XIcon2, { className: "size-3" })
6167
6148
  }
6168
6149
  ) : null
6169
6150
  ]
@@ -6192,7 +6173,7 @@ function defaultActionLabel(kind) {
6192
6173
  }
6193
6174
 
6194
6175
  // src/front/chat/components/ChatNotices.tsx
6195
- import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
6176
+ import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
6196
6177
  function RuntimeNoticeMessages({
6197
6178
  notices,
6198
6179
  onDismiss,
@@ -6202,7 +6183,7 @@ function RuntimeNoticeMessages({
6202
6183
  (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
6184
  );
6204
6185
  if (visible.length === 0) return null;
6205
- return /* @__PURE__ */ jsx16(
6186
+ return /* @__PURE__ */ jsx17(
6206
6187
  RuntimeNotices,
6207
6188
  {
6208
6189
  notices: visible,
@@ -6214,7 +6195,7 @@ function RuntimeNoticeMessages({
6214
6195
  }
6215
6196
  function ComposerRuntimeNotice({ notice }) {
6216
6197
  const level = notice.code === ErrorCode.enum.AGENT_RUNTIME_NOT_READY ? "info" : "error";
6217
- return /* @__PURE__ */ jsx16(
6198
+ return /* @__PURE__ */ jsx17(
6218
6199
  "div",
6219
6200
  {
6220
6201
  "data-testid": "chat-composer-runtime-notice",
@@ -6222,17 +6203,17 @@ function ComposerRuntimeNotice({ notice }) {
6222
6203
  "aria-live": "polite",
6223
6204
  className: noticeSurfaceClass(level, "mx-auto mb-2 w-full max-w-3xl text-xs"),
6224
6205
  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" }),
6206
+ 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
6207
  /* @__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
6208
+ /* @__PURE__ */ jsx17("div", { className: "font-medium", children: notice.title }),
6209
+ notice.detail ? /* @__PURE__ */ jsx17(NoticeText, { className: "mt-0.5 text-muted-foreground", children: notice.detail }) : null
6229
6210
  ] })
6230
6211
  ] })
6231
6212
  }
6232
6213
  );
6233
6214
  }
6234
6215
  function NoticeText({ className, children }) {
6235
- return /* @__PURE__ */ jsx16("div", { className: noticeTextClass(className), children });
6216
+ return /* @__PURE__ */ jsx17("div", { className: noticeTextClass(className), children });
6236
6217
  }
6237
6218
  function ComposerBlockerNotice({
6238
6219
  blocker,
@@ -6246,10 +6227,10 @@ function ComposerBlockerNotice({
6246
6227
  "aria-live": "polite",
6247
6228
  className: noticeSurfaceClass("info", "relative z-20 mx-auto mb-2 w-full max-w-3xl text-xs"),
6248
6229
  children: [
6249
- /* @__PURE__ */ jsx16("span", { children: label }),
6230
+ /* @__PURE__ */ jsx17("span", { children: label }),
6250
6231
  blocker?.actions?.map((action) => {
6251
6232
  const icon = composerBlockerActionIcon(action.id);
6252
- return icon ? /* @__PURE__ */ jsx16(
6233
+ return icon ? /* @__PURE__ */ jsx17(
6253
6234
  IconButton2,
6254
6235
  {
6255
6236
  type: "button",
@@ -6262,7 +6243,7 @@ function ComposerBlockerNotice({
6262
6243
  children: icon
6263
6244
  },
6264
6245
  action.id
6265
- ) : /* @__PURE__ */ jsx16(
6246
+ ) : /* @__PURE__ */ jsx17(
6266
6247
  "button",
6267
6248
  {
6268
6249
  type: "button",
@@ -6278,8 +6259,8 @@ function ComposerBlockerNotice({
6278
6259
  );
6279
6260
  }
6280
6261
  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" });
6262
+ if (actionId === "open") return /* @__PURE__ */ jsx17(ExternalLinkIcon2, { className: "size-3.5", "aria-hidden": "true" });
6263
+ if (actionId === "cancel") return /* @__PURE__ */ jsx17(XIcon3, { className: "size-3.5", "aria-hidden": "true" });
6283
6264
  return null;
6284
6265
  }
6285
6266
  function QueuedComposerNotice({ followUps, onEdit }) {
@@ -6298,9 +6279,9 @@ function QueuedComposerNotice({ followUps, onEdit }) {
6298
6279
  " queued follow-up",
6299
6280
  followUps.length === 1 ? "" : "s"
6300
6281
  ] }),
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(" - ") })
6282
+ /* @__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
6283
  ] }),
6303
- /* @__PURE__ */ jsx16(
6284
+ /* @__PURE__ */ jsx17(
6304
6285
  IconButton2,
6305
6286
  {
6306
6287
  type: "button",
@@ -6310,7 +6291,7 @@ function QueuedComposerNotice({ followUps, onEdit }) {
6310
6291
  className: "shrink-0 text-[color:var(--muted-foreground)] hover:bg-[color:var(--muted)] hover:text-[color:var(--foreground)]",
6311
6292
  "aria-label": "Edit queued follow-ups",
6312
6293
  title: "Edit queued follow-ups",
6313
- children: /* @__PURE__ */ jsx16(ListRestartIcon, { className: "size-3.5", "aria-hidden": "true" })
6294
+ children: /* @__PURE__ */ jsx17(ListRestartIcon, { className: "size-3.5", "aria-hidden": "true" })
6314
6295
  }
6315
6296
  )
6316
6297
  ]
@@ -6319,7 +6300,7 @@ function QueuedComposerNotice({ followUps, onEdit }) {
6319
6300
  }
6320
6301
 
6321
6302
  // 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";
6303
+ import { useCallback as useCallback13, useEffect as useEffect13, useMemo as useMemo12, useRef as useRef10, useState as useState14 } from "react";
6323
6304
  import { CheckIcon as CheckIcon2, CopyIcon as CopyIcon3 } from "lucide-react";
6324
6305
  import { Button as Button11 } from "@hachej/boring-ui-kit";
6325
6306
 
@@ -6334,8 +6315,8 @@ import {
6334
6315
  VideoIcon,
6335
6316
  XIcon as XIcon4
6336
6317
  } 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";
6318
+ import { createContext as createContext4, useCallback as useCallback9, useContext as useContext4, useMemo as useMemo6 } from "react";
6319
+ import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
6339
6320
  var mediaCategoryIcons = {
6340
6321
  audio: Music2Icon,
6341
6322
  document: FileTextIcon2,
@@ -6370,7 +6351,7 @@ var getAttachmentLabel = (data) => {
6370
6351
  const category = getMediaCategory(data);
6371
6352
  return data.filename || (category === "image" ? "Image" : "Attachment");
6372
6353
  };
6373
- var renderAttachmentImage = (url, filename, isGrid) => isGrid ? /* @__PURE__ */ jsx17(
6354
+ var renderAttachmentImage = (url, filename, isGrid) => isGrid ? /* @__PURE__ */ jsx18(
6374
6355
  "img",
6375
6356
  {
6376
6357
  alt: filename || "Image",
@@ -6379,7 +6360,7 @@ var renderAttachmentImage = (url, filename, isGrid) => isGrid ? /* @__PURE__ */
6379
6360
  src: url,
6380
6361
  width: 96
6381
6362
  }
6382
- ) : /* @__PURE__ */ jsx17(
6363
+ ) : /* @__PURE__ */ jsx18(
6383
6364
  "img",
6384
6365
  {
6385
6366
  alt: filename || "Image",
@@ -6389,11 +6370,11 @@ var renderAttachmentImage = (url, filename, isGrid) => isGrid ? /* @__PURE__ */
6389
6370
  width: 20
6390
6371
  }
6391
6372
  );
6392
- var AttachmentsContext = createContext3(null);
6393
- var AttachmentContext = createContext3(null);
6394
- var useAttachmentsContext = () => useContext3(AttachmentsContext) ?? { variant: "grid" };
6373
+ var AttachmentsContext = createContext4(null);
6374
+ var AttachmentContext = createContext4(null);
6375
+ var useAttachmentsContext = () => useContext4(AttachmentsContext) ?? { variant: "grid" };
6395
6376
  var useAttachmentContext = () => {
6396
- const ctx = useContext3(AttachmentContext);
6377
+ const ctx = useContext4(AttachmentContext);
6397
6378
  if (!ctx) {
6398
6379
  throw new Error("Attachment components must be used within <Attachment>");
6399
6380
  }
@@ -6405,8 +6386,8 @@ var Attachments = ({
6405
6386
  children,
6406
6387
  ...props
6407
6388
  }) => {
6408
- const contextValue = useMemo5(() => ({ variant }), [variant]);
6409
- return /* @__PURE__ */ jsx17(AttachmentsContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx17(
6389
+ const contextValue = useMemo6(() => ({ variant }), [variant]);
6390
+ return /* @__PURE__ */ jsx18(AttachmentsContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx18(
6410
6391
  "div",
6411
6392
  {
6412
6393
  className: cn(
@@ -6429,11 +6410,11 @@ var Attachment = ({
6429
6410
  }) => {
6430
6411
  const { variant } = useAttachmentsContext();
6431
6412
  const mediaCategory = getMediaCategory(data);
6432
- const contextValue = useMemo5(
6413
+ const contextValue = useMemo6(
6433
6414
  () => ({ data, mediaCategory, onRemove, variant }),
6434
6415
  [data, mediaCategory, onRemove, variant]
6435
6416
  );
6436
- return /* @__PURE__ */ jsx17(AttachmentContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx17(
6417
+ return /* @__PURE__ */ jsx18(AttachmentContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx18(
6437
6418
  "div",
6438
6419
  {
6439
6420
  className: cn(
@@ -6463,18 +6444,18 @@ var AttachmentPreview = ({
6463
6444
  }) => {
6464
6445
  const { data, mediaCategory, variant } = useAttachmentContext();
6465
6446
  const iconSize = variant === "inline" ? "size-3" : "size-4";
6466
- const renderIcon = (Icon) => /* @__PURE__ */ jsx17(Icon, { className: cn(iconSize, "text-muted-foreground") });
6447
+ const renderIcon = (Icon) => /* @__PURE__ */ jsx18(Icon, { className: cn(iconSize, "text-muted-foreground") });
6467
6448
  const renderContent = () => {
6468
6449
  if (mediaCategory === "image" && data.type === "file" && data.url) {
6469
6450
  return renderAttachmentImage(data.url, data.filename, variant === "grid");
6470
6451
  }
6471
6452
  if (mediaCategory === "video" && data.type === "file" && data.url) {
6472
- return /* @__PURE__ */ jsx17("video", { className: "size-full object-cover", muted: true, src: data.url });
6453
+ return /* @__PURE__ */ jsx18("video", { className: "size-full object-cover", muted: true, src: data.url });
6473
6454
  }
6474
6455
  const Icon = mediaCategoryIcons[mediaCategory];
6475
6456
  return fallbackIcon ?? renderIcon(Icon);
6476
6457
  };
6477
- return /* @__PURE__ */ jsx17(
6458
+ return /* @__PURE__ */ jsx18(
6478
6459
  "div",
6479
6460
  {
6480
6461
  className: cn(
@@ -6500,8 +6481,8 @@ var AttachmentInfo = ({
6500
6481
  return null;
6501
6482
  }
6502
6483
  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 })
6484
+ /* @__PURE__ */ jsx18("span", { className: "block truncate", children: label }),
6485
+ showMediaType && data.mediaType && /* @__PURE__ */ jsx18("span", { className: "block truncate text-muted-foreground text-xs", children: data.mediaType })
6505
6486
  ] });
6506
6487
  };
6507
6488
  var AttachmentRemove = ({
@@ -6546,8 +6527,8 @@ var AttachmentRemove = ({
6546
6527
  variant: "ghost",
6547
6528
  ...props,
6548
6529
  children: [
6549
- children ?? /* @__PURE__ */ jsx17(XIcon4, {}),
6550
- /* @__PURE__ */ jsx17("span", { className: "sr-only", children: label })
6530
+ children ?? /* @__PURE__ */ jsx18(XIcon4, {}),
6531
+ /* @__PURE__ */ jsx18("span", { className: "sr-only", children: label })
6551
6532
  ]
6552
6533
  }
6553
6534
  );
@@ -6572,22 +6553,22 @@ import { math } from "@streamdown/math";
6572
6553
  import { mermaid } from "@streamdown/mermaid";
6573
6554
  import { ChevronLeftIcon as ChevronLeftIcon2, ChevronRightIcon } from "lucide-react";
6574
6555
  import {
6575
- createContext as createContext4,
6556
+ createContext as createContext5,
6576
6557
  memo as memo3,
6577
6558
  useCallback as useCallback10,
6578
- useContext as useContext4,
6559
+ useContext as useContext5,
6579
6560
  useEffect as useEffect10,
6580
- useMemo as useMemo6,
6561
+ useMemo as useMemo7,
6581
6562
  useState as useState11
6582
6563
  } from "react";
6583
6564
  import { Streamdown } from "streamdown";
6584
- import { jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
6585
- var Message = ({ className, from, ...props }) => /* @__PURE__ */ jsx18(
6565
+ import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
6566
+ var Message = ({ className, from, ...props }) => /* @__PURE__ */ jsx19(
6586
6567
  "div",
6587
6568
  {
6588
6569
  "data-boring-agent-message-role": from,
6589
6570
  className: cn(
6590
- "group flex w-full max-w-[95%] flex-col gap-2",
6571
+ "group flex w-full max-w-full flex-col gap-1.5",
6591
6572
  from === "user" ? "is-user ml-auto justify-end" : "is-assistant",
6592
6573
  className
6593
6574
  ),
@@ -6598,13 +6579,13 @@ var MessageContent = ({
6598
6579
  children,
6599
6580
  className,
6600
6581
  ...props
6601
- }) => /* @__PURE__ */ jsx18(
6582
+ }) => /* @__PURE__ */ jsx19(
6602
6583
  "div",
6603
6584
  {
6604
6585
  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",
6586
+ "is-user:dark flex w-fit min-w-0 max-w-full flex-col gap-2 overflow-visible text-[13px] leading-5",
6587
+ "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",
6588
+ "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
6589
  className
6609
6590
  ),
6610
6591
  ...props,
@@ -6615,7 +6596,7 @@ var MessageActions = ({
6615
6596
  className,
6616
6597
  children,
6617
6598
  ...props
6618
- }) => /* @__PURE__ */ jsx18("div", { className: cn("flex items-center gap-1", className), ...props, children });
6599
+ }) => /* @__PURE__ */ jsx19("div", { className: cn("flex items-center gap-1", className), ...props, children });
6619
6600
  var MessageAction = ({
6620
6601
  tooltip,
6621
6602
  children,
@@ -6626,17 +6607,17 @@ var MessageAction = ({
6626
6607
  }) => {
6627
6608
  const button = /* @__PURE__ */ jsxs17(Button10, { size, type: "button", variant, ...props, children: [
6628
6609
  children,
6629
- /* @__PURE__ */ jsx18("span", { className: "sr-only", children: label || tooltip })
6610
+ /* @__PURE__ */ jsx19("span", { className: "sr-only", children: label || tooltip })
6630
6611
  ] });
6631
6612
  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 }) })
6613
+ return /* @__PURE__ */ jsx19(TooltipProvider2, { children: /* @__PURE__ */ jsxs17(Tooltip2, { children: [
6614
+ /* @__PURE__ */ jsx19(TooltipTrigger2, { asChild: true, children: button }),
6615
+ /* @__PURE__ */ jsx19(TooltipContent2, { children: /* @__PURE__ */ jsx19("p", { children: tooltip }) })
6635
6616
  ] }) });
6636
6617
  }
6637
6618
  return button;
6638
6619
  };
6639
- var MessageBranchContext = createContext4(
6620
+ var MessageBranchContext = createContext5(
6640
6621
  null
6641
6622
  );
6642
6623
  var streamdownPlugins = { cjk, code, math, mermaid };
@@ -6672,9 +6653,9 @@ function extractCodeBlockText(children) {
6672
6653
  var MarkdownPre = ({ children }) => {
6673
6654
  const code3 = extractCodeBlockText(children).replace(/\n$/, "");
6674
6655
  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, {})
6656
+ return /* @__PURE__ */ jsx19(CodeBlock2, { code: code3, language: language ?? "text", className: "my-3", children: /* @__PURE__ */ jsxs17(CodeBlockHeader, { children: [
6657
+ /* @__PURE__ */ jsx19(CodeBlockTitle, { children: /* @__PURE__ */ jsx19(CodeBlockFilename, { children: language ?? "text" }) }),
6658
+ /* @__PURE__ */ jsx19(CodeBlockCopyButton, {})
6678
6659
  ] }) });
6679
6660
  };
6680
6661
  var MarkdownCode = ({
@@ -6684,9 +6665,9 @@ var MarkdownCode = ({
6684
6665
  ...props
6685
6666
  }) => {
6686
6667
  if (inline === false) {
6687
- return /* @__PURE__ */ jsx18("code", { className, ...props, children });
6668
+ return /* @__PURE__ */ jsx19("code", { className, ...props, children });
6688
6669
  }
6689
- return /* @__PURE__ */ jsx18(
6670
+ return /* @__PURE__ */ jsx19(
6690
6671
  "code",
6691
6672
  {
6692
6673
  className: cn(
@@ -6703,7 +6684,7 @@ var markdownComponents = {
6703
6684
  code: MarkdownCode
6704
6685
  };
6705
6686
  var MessageResponse = memo3(
6706
- ({ className, shikiTheme, components, ...props }) => /* @__PURE__ */ jsx18(
6687
+ ({ className, shikiTheme, components, ...props }) => /* @__PURE__ */ jsx19(
6707
6688
  Streamdown,
6708
6689
  {
6709
6690
  className: cn(
@@ -6733,12 +6714,12 @@ import { math as math2 } from "@streamdown/math";
6733
6714
  import { mermaid as mermaid2 } from "@streamdown/mermaid";
6734
6715
  import { BrainIcon, ChevronDownIcon as ChevronDownIcon3 } from "lucide-react";
6735
6716
  import {
6736
- createContext as createContext5,
6717
+ createContext as createContext6,
6737
6718
  memo as memo5,
6738
6719
  useCallback as useCallback11,
6739
- useContext as useContext5,
6720
+ useContext as useContext6,
6740
6721
  useEffect as useEffect11,
6741
- useMemo as useMemo8,
6722
+ useMemo as useMemo9,
6742
6723
  useRef as useRef9,
6743
6724
  useState as useState12
6744
6725
  } from "react";
@@ -6746,8 +6727,8 @@ import { Streamdown as Streamdown2 } from "streamdown";
6746
6727
 
6747
6728
  // src/front/primitives/shimmer.tsx
6748
6729
  import { motion } from "motion/react";
6749
- import { memo as memo4, useMemo as useMemo7 } from "react";
6750
- import { jsx as jsx19 } from "react/jsx-runtime";
6730
+ import { memo as memo4, useMemo as useMemo8 } from "react";
6731
+ import { jsx as jsx20 } from "react/jsx-runtime";
6751
6732
  var motionComponentCache = /* @__PURE__ */ new Map();
6752
6733
  var getMotionComponent = (element) => {
6753
6734
  let component = motionComponentCache.get(element);
@@ -6767,11 +6748,11 @@ var ShimmerComponent = ({
6767
6748
  const MotionComponent = getMotionComponent(
6768
6749
  Component
6769
6750
  );
6770
- const dynamicSpread = useMemo7(
6751
+ const dynamicSpread = useMemo8(
6771
6752
  () => (children?.length ?? 0) * spread,
6772
6753
  [children, spread]
6773
6754
  );
6774
- return /* @__PURE__ */ jsx19(
6755
+ return /* @__PURE__ */ jsx20(
6775
6756
  MotionComponent,
6776
6757
  {
6777
6758
  animate: { backgroundPosition: "0% center" },
@@ -6797,10 +6778,10 @@ var ShimmerComponent = ({
6797
6778
  var Shimmer = memo4(ShimmerComponent);
6798
6779
 
6799
6780
  // 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);
6781
+ import { Fragment as Fragment7, jsx as jsx21, jsxs as jsxs18 } from "react/jsx-runtime";
6782
+ var ReasoningContext = createContext6(null);
6802
6783
  var useReasoning = () => {
6803
- const context = useContext5(ReasoningContext);
6784
+ const context = useContext6(ReasoningContext);
6804
6785
  if (!context) {
6805
6786
  throw new Error("Reasoning components must be used within Reasoning");
6806
6787
  }
@@ -6865,11 +6846,11 @@ var Reasoning = memo5(
6865
6846
  },
6866
6847
  [setIsOpen]
6867
6848
  );
6868
- const contextValue = useMemo8(
6849
+ const contextValue = useMemo9(
6869
6850
  () => ({ duration, isOpen, isStreaming, setIsOpen }),
6870
6851
  [duration, isOpen, isStreaming, setIsOpen]
6871
6852
  );
6872
- return /* @__PURE__ */ jsx20(ReasoningContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx20(
6853
+ return /* @__PURE__ */ jsx21(ReasoningContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx21(
6873
6854
  Collapsible2,
6874
6855
  {
6875
6856
  className: cn("not-prose mb-2", className),
@@ -6883,10 +6864,10 @@ var Reasoning = memo5(
6883
6864
  );
6884
6865
  var defaultGetThinkingMessage = (isStreaming, duration) => {
6885
6866
  if (isStreaming || duration === 0) {
6886
- return /* @__PURE__ */ jsx20(Shimmer, { duration: 1, children: "thinking" });
6867
+ return /* @__PURE__ */ jsx21(Shimmer, { duration: 1, children: "thinking" });
6887
6868
  }
6888
6869
  if (duration === void 0) {
6889
- return /* @__PURE__ */ jsx20("span", { children: "thoughts" });
6870
+ return /* @__PURE__ */ jsx21("span", { children: "thoughts" });
6890
6871
  }
6891
6872
  return /* @__PURE__ */ jsxs18("span", { children: [
6892
6873
  "thoughts \xB7 ",
@@ -6902,7 +6883,7 @@ var ReasoningTrigger = memo5(
6902
6883
  ...props
6903
6884
  }) => {
6904
6885
  const { isStreaming, isOpen, duration } = useReasoning();
6905
- return /* @__PURE__ */ jsx20(
6886
+ return /* @__PURE__ */ jsx21(
6906
6887
  CollapsibleTrigger3,
6907
6888
  {
6908
6889
  className: cn(
@@ -6910,10 +6891,10 @@ var ReasoningTrigger = memo5(
6910
6891
  className
6911
6892
  ),
6912
6893
  ...props,
6913
- children: children ?? /* @__PURE__ */ jsxs18(Fragment6, { children: [
6914
- /* @__PURE__ */ jsx20(BrainIcon, { className: "size-3" }),
6894
+ children: children ?? /* @__PURE__ */ jsxs18(Fragment7, { children: [
6895
+ /* @__PURE__ */ jsx21(BrainIcon, { className: "size-3" }),
6915
6896
  getThinkingMessage(isStreaming, duration),
6916
- /* @__PURE__ */ jsx20(
6897
+ /* @__PURE__ */ jsx21(
6917
6898
  ChevronDownIcon3,
6918
6899
  {
6919
6900
  className: cn(
@@ -6929,7 +6910,7 @@ var ReasoningTrigger = memo5(
6929
6910
  );
6930
6911
  var streamdownPlugins2 = { cjk: cjk2, code: code2, math: math2, mermaid: mermaid2 };
6931
6912
  var ReasoningContent = memo5(
6932
- ({ className, children, ...props }) => /* @__PURE__ */ jsx20(
6913
+ ({ className, children, ...props }) => /* @__PURE__ */ jsx21(
6933
6914
  CollapsibleContent2,
6934
6915
  {
6935
6916
  className: cn(
@@ -6938,7 +6919,7 @@ var ReasoningContent = memo5(
6938
6919
  className
6939
6920
  ),
6940
6921
  ...props,
6941
- children: /* @__PURE__ */ jsx20(Streamdown2, { plugins: streamdownPlugins2, children })
6922
+ children: /* @__PURE__ */ jsx21(Streamdown2, { plugins: streamdownPlugins2, children })
6942
6923
  }
6943
6924
  )
6944
6925
  );
@@ -6948,7 +6929,7 @@ ReasoningContent.displayName = "ReasoningContent";
6948
6929
 
6949
6930
  // src/front/primitives/tool-call-group.tsx
6950
6931
  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";
6932
+ import { memo as memo6, useCallback as useCallback12, useEffect as useEffect12, useMemo as useMemo10, useState as useState13 } from "react";
6952
6933
  import { Collapsible as Collapsible3, CollapsibleContent as CollapsibleContent3, CollapsibleTrigger as CollapsibleTrigger4 } from "@hachej/boring-ui-kit";
6953
6934
 
6954
6935
  // src/front/primitives/tool-call-group-state.ts
@@ -6987,10 +6968,7 @@ var TOOL_GROUP_VISUAL_PRESENTATION = {
6987
6968
  };
6988
6969
 
6989
6970
  // 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
- }
6971
+ import { jsx as jsx22, jsxs as jsxs19 } from "react/jsx-runtime";
6994
6972
  function isInputRunningState(state) {
6995
6973
  return state === "input-streaming" || state === "input-available";
6996
6974
  }
@@ -7040,11 +7018,10 @@ function resolveVisualState({
7040
7018
  return "settled";
7041
7019
  }
7042
7020
  var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
7043
- const toolParts = useMemo9(() => tools.map(({ part, key }) => {
7021
+ const toolParts = useMemo10(() => tools.map(({ part, key }) => {
7044
7022
  const toolPart = toToolPart(part);
7045
7023
  return toolPart ? { part: toolPart, key } : null;
7046
7024
  }).filter((entry) => Boolean(entry)), [tools]);
7047
- const isSettled = toolParts.every(({ part }) => isSettledState(part.state));
7048
7025
  const workspaceNotReady = toolParts.map(({ part }) => getWorkspaceNotReadyStatus(part.output)).find(Boolean);
7049
7026
  const hasError = toolParts.some(({ part }) => part.state === "output-error") && !workspaceNotReady;
7050
7027
  const hasAborted = toolParts.some(({ part }) => part.state === "aborted") && !workspaceNotReady;
@@ -7061,7 +7038,7 @@ var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
7061
7038
  });
7062
7039
  const isRunning = visualState === RUNNING_TOOL_GROUP_VISUAL_STATE;
7063
7040
  const [elapsedSeconds, setElapsedSeconds] = useState13(0);
7064
- const title = useMemo9(() => buildTitle(toolParts.map(({ part }) => part), visualState), [toolParts, visualState]);
7041
+ const title = useMemo10(() => buildTitle(toolParts.map(({ part }) => part), visualState), [toolParts, visualState]);
7065
7042
  const elapsedLabel = isRunning ? `Running ${formatElapsed(elapsedSeconds)}` : null;
7066
7043
  useEffect12(() => {
7067
7044
  if (!isRunning) {
@@ -7089,50 +7066,48 @@ var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
7089
7066
  {
7090
7067
  "aria-label": `Tool calls: ${title}`,
7091
7068
  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",
7069
+ "group/trigger flex min-h-10 w-full items-center gap-2 rounded-[var(--radius-md)] px-2 py-1 text-[13px] font-normal",
7070
+ "border border-border/60 bg-muted/45 text-muted-foreground",
7071
+ "transition-colors hover:bg-muted/70 hover:text-foreground motion-reduce:transition-none",
7072
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
7095
7073
  TOOL_GROUP_VISUAL_PRESENTATION[visualState].triggerClassName
7096
7074
  ),
7097
7075
  children: [
7098
- /* @__PURE__ */ jsx21(
7076
+ /* @__PURE__ */ jsx22("span", { className: "flex size-4 shrink-0 items-center justify-center", "aria-hidden": "true", children: /* @__PURE__ */ jsx22(
7099
7077
  "span",
7100
7078
  {
7101
7079
  "data-boring-agent-part": "tool-group-state-dot",
7102
7080
  className: cn(
7103
- "size-1.5 shrink-0 rounded-full",
7081
+ "size-1.5 rounded-full",
7104
7082
  TOOL_GROUP_VISUAL_PRESENTATION[visualState].dotClassName
7105
7083
  )
7106
7084
  }
7107
- ),
7108
- /* @__PURE__ */ jsx21(
7085
+ ) }),
7086
+ /* @__PURE__ */ jsx22("span", { "data-boring-agent-part": "tool-group-title", children: workspaceNotReady ? workspaceNotReady.message : title }),
7087
+ elapsedLabel ? /* @__PURE__ */ jsx22("span", { className: "ml-1 shrink-0 tabular-nums text-[12px] text-muted-foreground/70", children: elapsedLabel }) : null,
7088
+ /* @__PURE__ */ jsx22("span", { className: "ml-1 shrink-0 tabular-nums text-[12px] text-muted-foreground/70", children: toolParts.length }),
7089
+ /* @__PURE__ */ jsx22(
7109
7090
  ChevronDownIcon4,
7110
7091
  {
7111
7092
  className: cn(
7112
- "size-3 shrink-0 transition-transform duration-150",
7093
+ "ml-auto size-4 shrink-0 transition-transform duration-150 motion-reduce:transition-none",
7113
7094
  isOpen && "rotate-180"
7114
7095
  )
7115
7096
  }
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 })
7097
+ )
7123
7098
  ]
7124
7099
  }
7125
7100
  ),
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(
7101
+ /* @__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
7102
  "div",
7128
7103
  {
7129
7104
  "data-boring-agent-part": "tool-group-details",
7130
7105
  className: "relative mt-1.5 w-full max-w-2xl pl-[18px]",
7131
7106
  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 }) => {
7107
+ /* @__PURE__ */ jsx22("div", { className: "absolute left-[5px] top-0 bottom-2 w-px bg-border/35" }),
7108
+ /* @__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
7109
  const { renderer, part: resolvedPart, resolution } = resolveToolRendererForPart(part, mergedToolRenderers);
7135
- return /* @__PURE__ */ jsx21(
7110
+ return /* @__PURE__ */ jsx22(
7136
7111
  "div",
7137
7112
  {
7138
7113
  className: "min-w-0",
@@ -7153,9 +7128,37 @@ var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
7153
7128
  });
7154
7129
  ToolCallGroup.displayName = "ToolCallGroup";
7155
7130
 
7131
+ // src/front/chat/messageContributions.tsx
7132
+ import { createContext as createContext7, createElement, useContext as useContext7, useMemo as useMemo11 } from "react";
7133
+ import { jsx as jsx23 } from "react/jsx-runtime";
7134
+ var ChatMessageContributionContext = createContext7([]);
7135
+ function ChatMessageContributionProvider({
7136
+ contribution,
7137
+ children
7138
+ }) {
7139
+ const parent = useContext7(ChatMessageContributionContext);
7140
+ const contributions = useMemo11(
7141
+ () => [contribution, ...parent.filter((entry) => entry.id !== contribution.id)],
7142
+ [contribution, parent]
7143
+ );
7144
+ return /* @__PURE__ */ jsx23(ChatMessageContributionContext.Provider, { value: contributions, children });
7145
+ }
7146
+ function useCustomChatMessage(message) {
7147
+ const contributions = useContext7(ChatMessageContributionContext);
7148
+ const contribution = contributions.find((entry) => entry.matches(message));
7149
+ return contribution ? createElement(contribution.Component, { message }) : void 0;
7150
+ }
7151
+ function ChatMessageContributionBoundary({
7152
+ message,
7153
+ children
7154
+ }) {
7155
+ const customMessage = useCustomChatMessage(message);
7156
+ return customMessage !== void 0 ? customMessage : children;
7157
+ }
7158
+
7156
7159
  // src/front/chat/components/MessageMentions.tsx
7157
7160
  import { Children, cloneElement, isValidElement as isValidElement2 } from "react";
7158
- import { Fragment as Fragment7, jsx as jsx22 } from "react/jsx-runtime";
7161
+ import { Fragment as Fragment8, jsx as jsx24 } from "react/jsx-runtime";
7159
7162
  var TERMINAL_BOUNDARY = String.raw`(?=$|\s|[.,;!?)}\]"'”’]+(?=$|\s))`;
7160
7163
  var PREFIX_BOUNDARY = String.raw`(^|[\s(\[{"'“‘])`;
7161
7164
  function escapeRegExp2(value) {
@@ -7264,7 +7267,7 @@ function MessageMentions({
7264
7267
  }).map((segment, segmentIndex) => {
7265
7268
  if (!segment.mention) return segment.text;
7266
7269
  const actionLabel = mentionActionLabel(segment.mention);
7267
- return /* @__PURE__ */ jsx22(
7270
+ return /* @__PURE__ */ jsx24(
7268
7271
  "button",
7269
7272
  {
7270
7273
  type: "button",
@@ -7290,7 +7293,7 @@ function MessageMentions({
7290
7293
  return cloneElement(
7291
7294
  element,
7292
7295
  void 0,
7293
- /* @__PURE__ */ jsx22(
7296
+ /* @__PURE__ */ jsx24(
7294
7297
  MessageMentions,
7295
7298
  {
7296
7299
  catalog,
@@ -7308,19 +7311,19 @@ function MessageMentions({
7308
7311
  return nodes.map(decorate);
7309
7312
  }
7310
7313
  function createMessageMentionMarkdownComponents(catalog, onActivate) {
7311
- const decorate = (children) => /* @__PURE__ */ jsx22(MessageMentions, { catalog, onActivate, children });
7314
+ const decorate = (children) => /* @__PURE__ */ jsx24(MessageMentions, { catalog, onActivate, children });
7312
7315
  const paragraph = ({ node: _node, children, ...props }) => {
7313
7316
  const content = Children.toArray(children).filter((child) => child !== null && child !== "");
7314
7317
  if (content.length === 1 && isValidElement2(content[0])) {
7315
7318
  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 });
7319
+ if (child.props.node?.tagName === "img") return /* @__PURE__ */ jsx24(Fragment8, { children });
7320
+ if (child.props.node?.tagName === "code" && "data-block" in child.props) return /* @__PURE__ */ jsx24(Fragment8, { children });
7318
7321
  }
7319
- return /* @__PURE__ */ jsx22("p", { ...props, children: decorate(children) });
7322
+ return /* @__PURE__ */ jsx24("p", { ...props, children: decorate(children) });
7320
7323
  };
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) });
7324
+ 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) });
7325
+ 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) });
7326
+ 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
7327
  const headingClasses = {
7325
7328
  h1: "text-3xl",
7326
7329
  h2: "text-2xl",
@@ -7329,8 +7332,8 @@ function createMessageMentionMarkdownComponents(catalog, onActivate) {
7329
7332
  h5: "text-base",
7330
7333
  h6: "text-sm"
7331
7334
  };
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(
7335
+ 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) });
7336
+ const inlineCode = ({ node: _node, children, className, ...props }) => /* @__PURE__ */ jsx24(
7334
7337
  "code",
7335
7338
  {
7336
7339
  ...props,
@@ -7354,7 +7357,7 @@ function createMessageMentionMarkdownComponents(catalog, onActivate) {
7354
7357
  }
7355
7358
 
7356
7359
  // src/front/chat/components/PiTimelineMessage.tsx
7357
- import { jsx as jsx23, jsxs as jsxs20 } from "react/jsx-runtime";
7360
+ import { jsx as jsx25, jsxs as jsxs20 } from "react/jsx-runtime";
7358
7361
  var EMPTY_MENTION_CATALOG = { commands: [], skills: [], files: false };
7359
7362
  var COLLAPSIBLE_TOOL_NAMES = /* @__PURE__ */ new Set([
7360
7363
  "read",
@@ -7369,7 +7372,10 @@ var COLLAPSIBLE_TOOL_NAMES = /* @__PURE__ */ new Set([
7369
7372
  function isCollapsibleTool(part) {
7370
7373
  return part.type === "tool-call" && COLLAPSIBLE_TOOL_NAMES.has(part.toolName);
7371
7374
  }
7372
- function PiTimelineMessage({ message, isLast, isStreaming, showThoughts, toolRenderers, mentionCatalog = EMPTY_MENTION_CATALOG, onMentionActivate }) {
7375
+ function PiTimelineMessage(props) {
7376
+ return /* @__PURE__ */ jsx25(ChatMessageContributionBoundary, { message: props.message, children: /* @__PURE__ */ jsx25(DefaultPiTimelineMessage, { ...props }) });
7377
+ }
7378
+ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts, toolRenderers, mentionCatalog = EMPTY_MENTION_CATALOG, onMentionActivate }) {
7373
7379
  const role = message.role;
7374
7380
  const isAssistant = role === "assistant";
7375
7381
  const textParts = message.parts.filter((part) => part.type === "text");
@@ -7377,7 +7383,7 @@ function PiTimelineMessage({ message, isLast, isStreaming, showThoughts, toolRen
7377
7383
  const finalParts = groupRenderableParts(message);
7378
7384
  const attachmentSummaryPaths = role === "user" ? attachmentPathsFromTextParts(textParts) : [];
7379
7385
  const openArtifact = useOpenArtifact();
7380
- const effectiveMentionCatalog = useMemo10(() => ({
7386
+ const effectiveMentionCatalog = useMemo12(() => ({
7381
7387
  commands: mentionCatalog.commands,
7382
7388
  skills: mentionCatalog.skills,
7383
7389
  files: mentionCatalog.files && Boolean(openArtifact)
@@ -7398,7 +7404,7 @@ function PiTimelineMessage({ message, isLast, isStreaming, showThoughts, toolRen
7398
7404
  }, [openArtifact]);
7399
7405
  const hasMentions = effectiveMentionCatalog.files || effectiveMentionCatalog.commands.length > 0 || effectiveMentionCatalog.skills.length > 0;
7400
7406
  const mentionsEnabled = Boolean(isAssistant && !(isStreaming && isLast) && hasMentions);
7401
- const mentionMarkdownComponents = useMemo10(
7407
+ const mentionMarkdownComponents = useMemo12(
7402
7408
  () => mentionsEnabled ? createMessageMentionMarkdownComponents(effectiveMentionCatalog, activateCurrentMention) : void 0,
7403
7409
  [activateCurrentMention, effectiveMentionCatalog, mentionSignature, mentionsEnabled]
7404
7410
  );
@@ -7411,120 +7417,93 @@ function PiTimelineMessage({ message, isLast, isStreaming, showThoughts, toolRen
7411
7417
  "data-boring-agent-message-id": message.id,
7412
7418
  "data-boring-agent-message-role": role,
7413
7419
  "data-boring-agent-message-status": message.status,
7414
- className: "!max-w-full !gap-1.5",
7415
7420
  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;
7421
+ /* @__PURE__ */ jsxs20(MessageContent, { children: [
7422
+ fileParts.length > 0 ? /* @__PURE__ */ jsx25(
7423
+ Attachments,
7424
+ {
7425
+ variant: role === "user" ? "inline" : "list",
7426
+ className: cn(
7427
+ role === "user" ? "mb-2 w-full gap-2" : "gap-1.5",
7428
+ 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
7429
+ ),
7430
+ children: fileParts.map((file, index) => {
7431
+ const openPath = file.path ?? attachmentSummaryPaths[index];
7432
+ const openUrl = file.url && !openPath ? file.url : void 0;
7433
+ const canOpen = Boolean(openArtifact && openPath || openUrl);
7434
+ const openAttachment = () => {
7435
+ if (openPath) {
7436
+ if (file.filesystem) openArtifact?.(openPath, { filesystem: file.filesystem });
7437
+ else openArtifact?.(openPath);
7438
+ return;
7439
+ }
7440
+ if (openUrl) window.open(openUrl, "_blank", "noopener,noreferrer");
7441
+ };
7442
+ const openAttachmentFromKeyboard = (event) => {
7443
+ if (event.key !== "Enter" && event.key !== " ") return;
7444
+ event.preventDefault();
7445
+ openAttachment();
7446
+ };
7447
+ return /* @__PURE__ */ jsxs20(
7448
+ Attachment,
7449
+ {
7450
+ data: toAttachmentData(file, `file-${message.id}-${index}`),
7451
+ ...canOpen ? {
7452
+ role: "button",
7453
+ tabIndex: 0,
7454
+ title: openPath ? `Open ${openPath} in workspace` : `Open ${file.filename ?? "attachment"}`,
7455
+ "aria-label": openPath ? `Open ${file.filename ?? openPath} in workspace` : `Open ${file.filename ?? "attachment"}`,
7456
+ ...openPath ? { "data-workspace-path": openPath } : {},
7457
+ onClick: openAttachment,
7458
+ onKeyDown: openAttachmentFromKeyboard
7459
+ } : void 0,
7460
+ children: [
7461
+ /* @__PURE__ */ jsx25(AttachmentPreview, { className: role === "user" ? "!size-5 shrink-0 rounded-[var(--radius-sm)]" : "size-10 shrink-0 rounded-[var(--radius-md)]" }),
7462
+ /* @__PURE__ */ jsx25(AttachmentInfo, { className: role === "user" ? "min-w-0 max-w-[220px] flex-1 text-[12px]" : "min-w-0 flex-1" })
7463
+ ]
7464
+ },
7465
+ `file-${message.id}-${index}`
7466
+ );
7523
7467
  })
7524
- ]
7525
- }
7526
- ),
7527
- isAssistant && (textParts.length > 0 || shouldReserveStreamingActions) ? /* @__PURE__ */ jsx23(
7468
+ }
7469
+ ) : null,
7470
+ finalParts.map((item) => {
7471
+ if (item.kind === "reasoning") {
7472
+ return /* @__PURE__ */ jsx25(
7473
+ TimelineReasoningPart,
7474
+ {
7475
+ item,
7476
+ showThoughts
7477
+ },
7478
+ item.key
7479
+ );
7480
+ }
7481
+ if (item.kind === "tool-group") {
7482
+ return /* @__PURE__ */ jsx25("div", { "data-boring-agent-part": "message-tools", children: /* @__PURE__ */ jsx25(ToolCallGroup, { tools: item.tools, mergedToolRenderers: toolRenderers }) }, item.key);
7483
+ }
7484
+ if (item.kind === "tool-plain") {
7485
+ return /* @__PURE__ */ jsx25("div", { "data-boring-agent-part": "message-tools", children: /* @__PURE__ */ jsx25(PlainToolCard, { part: item.part, renderers: toolRenderers }) }, item.key);
7486
+ }
7487
+ if (item.part.type === "text") {
7488
+ const text = textForMessageDisplay(item.part.text, role);
7489
+ if (!text) return null;
7490
+ return /* @__PURE__ */ jsx25("div", { "data-boring-agent-part": "message-text", children: /* @__PURE__ */ jsx25(
7491
+ MessageResponse,
7492
+ {
7493
+ components: mentionMarkdownComponents,
7494
+ className: "boring-agent-markdown max-w-none",
7495
+ children: text
7496
+ },
7497
+ `${mentionSignature || "no-message-mentions"}:${mentionsEnabled ? "enabled" : "static"}`
7498
+ ) }, item.key);
7499
+ }
7500
+ if (item.part.type === "notice") {
7501
+ return /* @__PURE__ */ jsx25(NoticeBubble, { level: item.part.level, text: item.part.text }, item.key);
7502
+ }
7503
+ return null;
7504
+ })
7505
+ ] }),
7506
+ isAssistant && (textParts.length > 0 || shouldReserveStreamingActions) ? /* @__PURE__ */ jsx25(
7528
7507
  MessageActionsBar,
7529
7508
  {
7530
7509
  text: textParts.map((part) => part.text).join("\n\n"),
@@ -7559,15 +7538,15 @@ function TimelineReasoningPart({ item, showThoughts }) {
7559
7538
  onOpenChange: handleOpenChange,
7560
7539
  autoClose: false,
7561
7540
  children: [
7562
- /* @__PURE__ */ jsx23(
7541
+ /* @__PURE__ */ jsx25(
7563
7542
  ReasoningTrigger,
7564
7543
  {
7565
7544
  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" }),
7545
+ getThinkingMessage: (streaming) => /* @__PURE__ */ jsx25("span", { children: streaming ? "thinking" : "thoughts" }),
7567
7546
  onClick: toggleManualOpen
7568
7547
  }
7569
7548
  ),
7570
- /* @__PURE__ */ jsx23(ReasoningContent, { children: item.text })
7549
+ /* @__PURE__ */ jsx25(ReasoningContent, { children: item.text })
7571
7550
  ]
7572
7551
  }
7573
7552
  );
@@ -7576,7 +7555,7 @@ function PlainToolCard({ part, renderers }) {
7576
7555
  const toolPart = toToolPart(part);
7577
7556
  if (!toolPart) return null;
7578
7557
  const { renderer, part: resolved, resolution } = resolveToolRendererForPart(toolPart, renderers);
7579
- return /* @__PURE__ */ jsx23(
7558
+ return /* @__PURE__ */ jsx25(
7580
7559
  "div",
7581
7560
  {
7582
7561
  "data-tool-call-id": resolved.toolCallId,
@@ -7625,13 +7604,13 @@ ${part.text}`;
7625
7604
  return grouped;
7626
7605
  }
7627
7606
  function NoticeBubble({ level, text }) {
7628
- return /* @__PURE__ */ jsx23(
7607
+ return /* @__PURE__ */ jsx25(
7629
7608
  "div",
7630
7609
  {
7631
7610
  "data-boring-agent-part": "message-notice",
7632
7611
  "data-notice-level": level,
7633
7612
  className: noticeSurfaceClass(level, "text-xs"),
7634
- children: /* @__PURE__ */ jsx23("div", { className: noticeTextClass(), children: text })
7613
+ children: /* @__PURE__ */ jsx25("div", { className: noticeTextClass(), children: text })
7635
7614
  }
7636
7615
  );
7637
7616
  }
@@ -7710,7 +7689,7 @@ function MessageActionsBar({
7710
7689
  "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[color:var(--accent)]/40"
7711
7690
  );
7712
7691
  const hiddenActionProps = visible ? {} : { tabIndex: -1 };
7713
- return /* @__PURE__ */ jsx23(
7692
+ return /* @__PURE__ */ jsx25(
7714
7693
  "div",
7715
7694
  {
7716
7695
  "aria-hidden": !visible,
@@ -7718,7 +7697,7 @@ function MessageActionsBar({
7718
7697
  "flex min-h-6 items-center gap-0.5 -mt-1 transition-opacity duration-200",
7719
7698
  visible ? "opacity-100" : "pointer-events-none opacity-0"
7720
7699
  ),
7721
- children: /* @__PURE__ */ jsx23(
7700
+ children: /* @__PURE__ */ jsx25(
7722
7701
  Button11,
7723
7702
  {
7724
7703
  type: "button",
@@ -7729,7 +7708,7 @@ function MessageActionsBar({
7729
7708
  "aria-label": copied ? "Copied" : "Copy message",
7730
7709
  title: copied ? "Copied" : "Copy",
7731
7710
  ...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" })
7711
+ 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
7712
  }
7734
7713
  )
7735
7714
  }
@@ -7750,7 +7729,7 @@ function partKey(messageId, part, index) {
7750
7729
  }
7751
7730
 
7752
7731
  // src/front/chat/components/PiConversationSurface.tsx
7753
- import { jsx as jsx24, jsxs as jsxs21 } from "react/jsx-runtime";
7732
+ import { jsx as jsx26, jsxs as jsxs21 } from "react/jsx-runtime";
7754
7733
  var TRANSCRIPT_WINDOW = 60;
7755
7734
  var TRANSCRIPT_WINDOW_STEP = 40;
7756
7735
  var LOAD_OLDER_THRESHOLD_PX = 320;
@@ -7789,7 +7768,7 @@ function PiConversationSurface({
7789
7768
  return /* @__PURE__ */ jsxs21(
7790
7769
  Conversation,
7791
7770
  {
7792
- className: emptyHero ? "max-h-[45vh] flex-none" : "flex-1",
7771
+ className: emptyHero ? cn("flex-none", runtimeNotices.length > 0 ? "max-h-[45vh]" : "max-h-[20vh]") : "flex-1",
7793
7772
  "aria-label": "Agent conversation",
7794
7773
  "aria-live": "polite",
7795
7774
  onScrollToBottomReady,
@@ -7800,18 +7779,18 @@ function PiConversationSurface({
7800
7779
  emptyHero && "py-4 text-center"
7801
7780
  ), children: [
7802
7781
  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" }),
7782
+ /* @__PURE__ */ jsx26(Loader22, { className: "h-4 w-4 animate-spin" }),
7804
7783
  "Loading chat history\u2026"
7805
7784
  ] }) : null,
7806
- messages.length === 0 && !emptyStateHydrating ? /* @__PURE__ */ jsx24(
7785
+ messages.length === 0 && !emptyStateHydrating ? /* @__PURE__ */ jsx26(
7807
7786
  ChatEmptyState,
7808
7787
  {
7809
7788
  eyebrow: emptyState?.eyebrow,
7810
7789
  title: emptyState?.title,
7811
7790
  description: emptyState?.description,
7812
- footer: emptyState?.footer,
7813
- suggestions,
7814
- className: emptyHero ? "items-center text-center [&>p]:mx-auto" : void 0,
7791
+ footer: emptyHero ? void 0 : emptyState?.footer,
7792
+ suggestions: emptyHero ? [] : suggestions,
7793
+ hero: emptyHero,
7815
7794
  onSelect: (suggestion) => {
7816
7795
  const text = suggestion.prompt ?? suggestion.label;
7817
7796
  if (!text.trim()) return;
@@ -7821,8 +7800,8 @@ function PiConversationSurface({
7821
7800
  }
7822
7801
  }
7823
7802
  ) : null,
7824
- hasOlder ? /* @__PURE__ */ jsx24(TranscriptHistoryLoader, { olderCount, onLoadOlder: loadOlder }) : null,
7825
- visibleItems.map(({ message, key }, index) => /* @__PURE__ */ jsx24(
7803
+ hasOlder ? /* @__PURE__ */ jsx26(TranscriptHistoryLoader, { olderCount, onLoadOlder: loadOlder }) : null,
7804
+ visibleItems.map(({ message, key }, index) => /* @__PURE__ */ jsx26(
7826
7805
  PiTimelineMessage,
7827
7806
  {
7828
7807
  message,
@@ -7835,9 +7814,9 @@ function PiConversationSurface({
7835
7814
  },
7836
7815
  key
7837
7816
  )),
7838
- /* @__PURE__ */ jsx24(RuntimeNoticeMessages, { notices: runtimeNotices, onDismiss: onDismissNotice, renderAction: renderNoticeAction })
7817
+ /* @__PURE__ */ jsx26(RuntimeNoticeMessages, { notices: runtimeNotices, onDismiss: onDismissNotice, renderAction: renderNoticeAction })
7839
7818
  ] }),
7840
- /* @__PURE__ */ jsx24(ConversationScrollButton, {})
7819
+ /* @__PURE__ */ jsx26(ConversationScrollButton, {})
7841
7820
  ]
7842
7821
  }
7843
7822
  );
@@ -7869,7 +7848,7 @@ function TranscriptHistoryLoader({ olderCount, onLoadOlder }) {
7869
7848
  }
7870
7849
  if (el.scrollTop > LOAD_OLDER_THRESHOLD_PX) armed.current = true;
7871
7850
  });
7872
- return /* @__PURE__ */ jsx24("div", { className: "flex justify-center", "data-boring-agent-part": "transcript-load-older", children: /* @__PURE__ */ jsxs21(
7851
+ return /* @__PURE__ */ jsx26("div", { className: "flex justify-center", "data-boring-agent-part": "transcript-load-older", children: /* @__PURE__ */ jsxs21(
7873
7852
  "button",
7874
7853
  {
7875
7854
  type: "button",
@@ -7913,7 +7892,7 @@ import { motion as motion2 } from "motion/react";
7913
7892
  import { useEffect as useEffect15, useRef as useRef12 } from "react";
7914
7893
 
7915
7894
  // src/front/composer/ComposerStatusBanner.tsx
7916
- import { jsx as jsx25, jsxs as jsxs22 } from "react/jsx-runtime";
7895
+ import { jsx as jsx27, jsxs as jsxs22 } from "react/jsx-runtime";
7917
7896
  function ComposerStatusBanner({
7918
7897
  tone,
7919
7898
  dataAttribute,
@@ -7942,8 +7921,8 @@ function ComposerStatusBanner({
7942
7921
  maxWidthClassName
7943
7922
  ),
7944
7923
  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 })
7924
+ /* @__PURE__ */ jsx27("span", { className: "inline-block h-2 w-2 animate-pulse rounded-full bg-accent", "aria-hidden": "true" }),
7925
+ /* @__PURE__ */ jsx27("span", { children: runningContent })
7947
7926
  ]
7948
7927
  }
7949
7928
  );
@@ -7962,12 +7941,12 @@ function ComposerStatusBanner({
7962
7941
  ),
7963
7942
  children: [
7964
7943
  /* @__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" }),
7944
+ /* @__PURE__ */ jsx27("span", { className: "mt-0.5 text-[oklch(0.45_0.13_148)]", "aria-hidden": "true", children: "\u2713" }),
7966
7945
  /* @__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
7946
+ /* @__PURE__ */ jsx27("span", { className: "block font-medium leading-5", children: title }),
7947
+ detail ? /* @__PURE__ */ jsx27("span", { className: "block leading-4 text-muted-foreground", children: detail }) : null
7969
7948
  ] }),
7970
- onDismiss ? /* @__PURE__ */ jsx25(
7949
+ onDismiss ? /* @__PURE__ */ jsx27(
7971
7950
  "button",
7972
7951
  {
7973
7952
  type: "button",
@@ -7996,9 +7975,9 @@ function ComposerStatusBanner({
7996
7975
  ),
7997
7976
  children: [
7998
7977
  /* @__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(
7978
+ /* @__PURE__ */ jsx27("span", { className: "text-destructive", "aria-hidden": "true", children: "\u26A0" }),
7979
+ /* @__PURE__ */ jsx27("span", { className: "flex-1 font-medium", children: title }),
7980
+ onRetry ? /* @__PURE__ */ jsx27(
8002
7981
  "button",
8003
7982
  {
8004
7983
  type: "button",
@@ -8007,7 +7986,7 @@ function ComposerStatusBanner({
8007
7986
  children: retryLabel
8008
7987
  }
8009
7988
  ) : null,
8010
- onDismiss ? /* @__PURE__ */ jsx25(
7989
+ onDismiss ? /* @__PURE__ */ jsx27(
8011
7990
  "button",
8012
7991
  {
8013
7992
  type: "button",
@@ -8018,14 +7997,14 @@ function ComposerStatusBanner({
8018
7997
  }
8019
7998
  ) : null
8020
7999
  ] }),
8021
- message ? /* @__PURE__ */ jsx25("pre", { className: "mt-2 whitespace-pre-wrap break-words text-[11px] text-destructive/90", children: message }) : null
8000
+ message ? /* @__PURE__ */ jsx27("pre", { className: "mt-2 whitespace-pre-wrap break-words text-[11px] text-destructive/90", children: message }) : null
8022
8001
  ]
8023
8002
  }
8024
8003
  );
8025
8004
  }
8026
8005
 
8027
8006
  // src/front/composer/PluginUpdateStatus.tsx
8028
- import { jsx as jsx26, jsxs as jsxs23 } from "react/jsx-runtime";
8007
+ import { jsx as jsx28, jsxs as jsxs23 } from "react/jsx-runtime";
8029
8008
  function PluginUpdateStatus({
8030
8009
  state,
8031
8010
  onDismiss,
@@ -8047,7 +8026,7 @@ function PluginUpdateStatus({
8047
8026
  }, [state?.kind, successDismissKey, successAutoDismissMs]);
8048
8027
  if (!state) return null;
8049
8028
  if (state.kind === "running") {
8050
- return /* @__PURE__ */ jsx26(
8029
+ return /* @__PURE__ */ jsx28(
8051
8030
  ComposerStatusBanner,
8052
8031
  {
8053
8032
  tone: "running",
@@ -8085,7 +8064,7 @@ function PluginUpdateStatus({
8085
8064
  ),
8086
8065
  children: [
8087
8066
  /* @__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" }),
8067
+ /* @__PURE__ */ jsx28("span", { "aria-hidden": "true", children: "\u26A0" }),
8089
8068
  /* @__PURE__ */ jsxs23("span", { children: [
8090
8069
  "Reload diagnostics for ",
8091
8070
  diagnostics.length,
@@ -8093,8 +8072,8 @@ function PluginUpdateStatus({
8093
8072
  diagnostics.length === 1 ? "" : "s"
8094
8073
  ] })
8095
8074
  ] }),
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" }),
8075
+ /* @__PURE__ */ jsx28("ul", { className: "mt-1 ml-4 list-disc text-foreground/85", children: diagnostics.map((diagnostic, index) => /* @__PURE__ */ jsxs23("li", { children: [
8076
+ /* @__PURE__ */ jsx28("code", { className: "font-mono text-[10.5px]", children: diagnostic.pluginId ?? diagnostic.source ?? "plugin" }),
8098
8077
  " \u2014 ",
8099
8078
  diagnostic.message ?? "reload diagnostic"
8100
8079
  ] }, `${diagnostic.pluginId ?? diagnostic.source ?? "plugin"}-${index}`)) })
@@ -8111,7 +8090,7 @@ function PluginUpdateStatus({
8111
8090
  ),
8112
8091
  children: [
8113
8092
  /* @__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" }),
8093
+ /* @__PURE__ */ jsx28("span", { "aria-hidden": "true", children: "\u26A0" }),
8115
8094
  /* @__PURE__ */ jsxs23("span", { children: [
8116
8095
  "Restart needed for ",
8117
8096
  warnings.length,
@@ -8119,12 +8098,12 @@ function PluginUpdateStatus({
8119
8098
  warnings.length === 1 ? "" : "s"
8120
8099
  ] })
8121
8100
  ] }),
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 }),
8101
+ /* @__PURE__ */ jsx28("ul", { className: "mt-1 ml-4 list-disc text-foreground/85", children: warnings.map((w) => /* @__PURE__ */ jsxs23("li", { children: [
8102
+ /* @__PURE__ */ jsx28("code", { className: "font-mono text-[10.5px]", children: w.id }),
8124
8103
  " \u2014 ",
8125
8104
  w.surfaces.join(" + ")
8126
8105
  ] }, 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." })
8106
+ /* @__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
8107
  ]
8129
8108
  }
8130
8109
  ) : null
@@ -8132,7 +8111,7 @@ function PluginUpdateStatus({
8132
8111
  }
8133
8112
  );
8134
8113
  }
8135
- return /* @__PURE__ */ jsx26(
8114
+ return /* @__PURE__ */ jsx28(
8136
8115
  ComposerStatusBanner,
8137
8116
  {
8138
8117
  tone: "error",
@@ -8150,7 +8129,7 @@ function PluginUpdateStatus({
8150
8129
 
8151
8130
  // src/front/composer/CommandRunStatus.tsx
8152
8131
  import { useEffect as useEffect16, useRef as useRef13 } from "react";
8153
- import { Fragment as Fragment8, jsx as jsx27, jsxs as jsxs24 } from "react/jsx-runtime";
8132
+ import { Fragment as Fragment9, jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
8154
8133
  function CommandRunStatus({
8155
8134
  state,
8156
8135
  onDismiss,
@@ -8169,13 +8148,13 @@ function CommandRunStatus({
8169
8148
  }, [state?.kind, successKey, successAutoDismissMs]);
8170
8149
  if (!state) return null;
8171
8150
  if (state.kind === "running") {
8172
- return /* @__PURE__ */ jsx27(
8151
+ return /* @__PURE__ */ jsx29(
8173
8152
  ComposerStatusBanner,
8174
8153
  {
8175
8154
  tone: "running",
8176
8155
  dataAttribute: "data-boring-command-run",
8177
8156
  maxWidthClassName,
8178
- runningContent: state.command ? /* @__PURE__ */ jsxs24(Fragment8, { children: [
8157
+ runningContent: state.command ? /* @__PURE__ */ jsxs24(Fragment9, { children: [
8179
8158
  " Running ",
8180
8159
  /* @__PURE__ */ jsxs24("code", { className: "font-mono", children: [
8181
8160
  "/",
@@ -8187,13 +8166,13 @@ function CommandRunStatus({
8187
8166
  );
8188
8167
  }
8189
8168
  if (state.kind === "success") {
8190
- return /* @__PURE__ */ jsx27(
8169
+ return /* @__PURE__ */ jsx29(
8191
8170
  ComposerStatusBanner,
8192
8171
  {
8193
8172
  tone: "success",
8194
8173
  dataAttribute: "data-boring-command-run",
8195
8174
  maxWidthClassName,
8196
- title: state.command ? /* @__PURE__ */ jsxs24(Fragment8, { children: [
8175
+ title: state.command ? /* @__PURE__ */ jsxs24(Fragment9, { children: [
8197
8176
  " Ran ",
8198
8177
  /* @__PURE__ */ jsxs24("code", { className: "font-mono", children: [
8199
8178
  "/",
@@ -8207,13 +8186,13 @@ function CommandRunStatus({
8207
8186
  }
8208
8187
  );
8209
8188
  }
8210
- return /* @__PURE__ */ jsx27(
8189
+ return /* @__PURE__ */ jsx29(
8211
8190
  ComposerStatusBanner,
8212
8191
  {
8213
8192
  tone: "error",
8214
8193
  dataAttribute: "data-boring-command-run",
8215
8194
  maxWidthClassName,
8216
- title: state.command ? /* @__PURE__ */ jsxs24(Fragment8, { children: [
8195
+ title: state.command ? /* @__PURE__ */ jsxs24(Fragment9, { children: [
8217
8196
  " ",
8218
8197
  /* @__PURE__ */ jsxs24("code", { className: "font-mono", children: [
8219
8198
  "/",
@@ -8263,12 +8242,12 @@ function displayProviderLabel(provider) {
8263
8242
  }
8264
8243
 
8265
8244
  // src/front/chatPanelComposerControls.tsx
8266
- import { Fragment as Fragment9, jsx as jsx28, jsxs as jsxs25 } from "react/jsx-runtime";
8245
+ import { Fragment as Fragment10, jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
8267
8246
  var composerActionClass = cn(
8268
8247
  "inline-flex h-8 items-center justify-center gap-1.5 rounded-[var(--radius-md)] border-0 bg-transparent",
8269
- "text-muted-foreground shadow-none transition",
8248
+ "text-muted-foreground shadow-none transition-colors motion-reduce:transition-none",
8270
8249
  "hover:bg-muted/60 hover:text-foreground",
8271
- "focus:outline-none focus-visible:ring-2 focus-visible:ring-ring/40",
8250
+ "focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-popover",
8272
8251
  "disabled:pointer-events-none disabled:opacity-50"
8273
8252
  );
8274
8253
  var THINKING_LEVEL_LABELS = {
@@ -8278,10 +8257,10 @@ var THINKING_LEVEL_LABELS = {
8278
8257
  high: "High"
8279
8258
  };
8280
8259
  var THINKING_LEVEL_STATUS_LABELS = {
8281
- off: "off",
8282
- low: "low",
8283
- medium: "medium",
8284
- high: "high"
8260
+ off: "Off",
8261
+ low: "Low",
8262
+ medium: "Medium",
8263
+ high: "High"
8285
8264
  };
8286
8265
  var THINKING_LEVEL_DETAILS = {
8287
8266
  off: "No extra reasoning",
@@ -8291,7 +8270,7 @@ var THINKING_LEVEL_DETAILS = {
8291
8270
  };
8292
8271
  var selectorTriggerClass = cn(
8293
8272
  composerActionClass,
8294
- "h-7 rounded-lg border border-border/60 bg-transparent px-2 text-[12px] font-medium text-muted-foreground",
8273
+ "h-8 rounded-lg border border-border/60 bg-transparent px-2 text-xs font-medium text-muted-foreground",
8295
8274
  "hover:border-border/80 hover:bg-muted/55 hover:text-foreground"
8296
8275
  );
8297
8276
  var selectorContentClass = cn(
@@ -8299,12 +8278,12 @@ var selectorContentClass = cn(
8299
8278
  "bg-[color:var(--popover)] p-1 text-[color:var(--popover-foreground)] shadow-xl"
8300
8279
  );
8301
8280
  var composerPickerMenuClass = cn(
8302
- "mb-1 w-full overflow-hidden rounded-lg border border-border/60",
8303
- "bg-[color:var(--popover)] text-[color:var(--popover-foreground)] shadow-lg"
8281
+ "w-full overflow-hidden rounded-xl border border-border",
8282
+ "bg-[color:var(--popover)] text-[color:var(--popover-foreground)] shadow-xl"
8304
8283
  );
8305
8284
  function selectorItemClass(selected) {
8306
8285
  return cn(
8307
- "flex items-center gap-2 rounded-md px-2 py-1.5 text-[13px]",
8286
+ "flex min-h-10 items-center gap-2 rounded-md px-2.5 py-2 text-sm text-muted-foreground",
8308
8287
  "data-[selected=true]:bg-[color:oklch(from_var(--accent)_l_c_h/0.15)] data-[selected=true]:text-foreground",
8309
8288
  selected && "bg-foreground/[0.06] text-foreground"
8310
8289
  );
@@ -8331,7 +8310,7 @@ function useDismissOnOutsidePointer(ref, onClose) {
8331
8310
  }
8332
8311
  function ThinkingLevelGlyph({ level }) {
8333
8312
  const lit = level === "off" ? 0 : level === "low" ? 1 : level === "medium" ? 2 : 3;
8334
- return /* @__PURE__ */ jsx28(
8313
+ return /* @__PURE__ */ jsx30(
8335
8314
  "svg",
8336
8315
  {
8337
8316
  "aria-hidden": "true",
@@ -8340,7 +8319,7 @@ function ThinkingLevelGlyph({ level }) {
8340
8319
  viewBox: "0 0 14 14",
8341
8320
  fill: "none",
8342
8321
  className: "shrink-0",
8343
- children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx28(
8322
+ children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx30(
8344
8323
  "rect",
8345
8324
  {
8346
8325
  x: 2 + i * 4,
@@ -8359,7 +8338,8 @@ function ThinkingLevelGlyph({ level }) {
8359
8338
  function modelTriggerLabel(value, options, emptyLabel = "Default model") {
8360
8339
  const current = value ? options.find((m) => m.provider === value.provider && m.id === value.id) : void 0;
8361
8340
  const rawTriggerLabel = current?.label ?? value?.id;
8362
- return value ? rawTriggerLabel && current?.label && current.label !== value.id && /[A-Z]/.test(current.label) ? current.label : displayModelLabel(rawTriggerLabel ?? value.id) : emptyLabel;
8341
+ const label = value ? rawTriggerLabel && current?.label && current.label !== value.id && /[A-Z]/.test(current.label) ? current.label : displayModelLabel(rawTriggerLabel ?? value.id) : emptyLabel;
8342
+ return value ? label.replace(/\s+\([^)]*\)\s*$/, "") : label;
8363
8343
  }
8364
8344
  function modelMenuOptions(_value, options) {
8365
8345
  return options.filter((m) => m.available);
@@ -8386,7 +8366,7 @@ var ModelSelectTrigger = forwardRef(function ModelSelectTrigger2({
8386
8366
  }, ref) {
8387
8367
  const triggerLabel = modelTriggerLabel(value, options, emptyLabel);
8388
8368
  const triggerDisplay = value ? `${triggerLabel} (${displayProviderLabel(value.provider)})` : triggerLabel;
8389
- return /* @__PURE__ */ jsx28(
8369
+ return /* @__PURE__ */ jsx30(
8390
8370
  "button",
8391
8371
  {
8392
8372
  ref,
@@ -8398,18 +8378,22 @@ var ModelSelectTrigger = forwardRef(function ModelSelectTrigger2({
8398
8378
  title: trigger === "slash" ? `Open model picker. Current model: ${triggerDisplay}` : void 0,
8399
8379
  onClick,
8400
8380
  className: cn(
8401
- trigger === "slash" ? "max-w-[min(52vw,260px)] cursor-pointer overflow-hidden text-ellipsis whitespace-nowrap rounded-[var(--radius-md)] px-1.5 py-0.5 transition-colors hover:bg-foreground/[0.045] hover:text-foreground/80 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-foreground/20 disabled:pointer-events-none disabled:opacity-50" : selectorTriggerClass,
8381
+ trigger === "slash" ? "h-8 max-w-[min(68vw,420px)] cursor-pointer overflow-hidden text-ellipsis whitespace-nowrap rounded-[var(--radius-md)] px-2 text-xs transition-colors hover:bg-muted/60 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-popover disabled:pointer-events-none disabled:opacity-50 motion-reduce:transition-none" : selectorTriggerClass,
8402
8382
  trigger === "slash" ? "w-auto" : "w-auto max-w-[min(52vw,260px)] gap-1",
8403
8383
  open && (trigger === "slash" ? "bg-muted/45" : "border-border/80 bg-muted/55 text-foreground"),
8404
8384
  className
8405
8385
  ),
8406
8386
  ...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" })
8387
+ children: trigger === "slash" ? /* @__PURE__ */ jsxs25(Fragment10, { children: [
8388
+ /* @__PURE__ */ jsx30("span", { className: "text-muted-foreground", children: "Model \xB7 " }),
8389
+ /* @__PURE__ */ jsx30("span", { className: "min-w-0 truncate text-foreground", children: triggerLabel }),
8390
+ value ? /* @__PURE__ */ jsxs25("span", { className: "shrink-0 text-muted-foreground", children: [
8391
+ " \xB7 ",
8392
+ displayProviderLabel(value.provider)
8393
+ ] }) : null
8394
+ ] }) : /* @__PURE__ */ jsxs25(Fragment10, { children: [
8395
+ /* @__PURE__ */ jsx30("span", { className: "min-w-0 truncate", children: triggerDisplay }),
8396
+ /* @__PURE__ */ jsx30(ChevronDownIcon5, { className: "h-3 w-3 shrink-0 text-muted-foreground/50", "aria-hidden": "true" })
8413
8397
  ] })
8414
8398
  }
8415
8399
  );
@@ -8466,18 +8450,18 @@ function ModelPickerMenu({
8466
8450
  }, [activeIndex, disabled, keyboardOptions, onChange, onClose]);
8467
8451
  const optionIndexOffset = hideDefaultOption ? 0 : 1;
8468
8452
  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(
8453
+ 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: [
8454
+ menuOptions.length > 8 && /* @__PURE__ */ jsx30("div", { className: "border-b border-border/60 px-2", children: /* @__PURE__ */ jsx30(
8471
8455
  CommandInput,
8472
8456
  {
8473
8457
  placeholder: "Search models\u2026",
8474
8458
  autoFocus: true,
8475
- className: "h-8 w-full border-0 bg-transparent text-[13px] outline-none focus:ring-0"
8459
+ className: "h-8 w-full border-0 bg-transparent text-sm outline-none focus:ring-0"
8476
8460
  }
8477
8461
  ) }),
8478
8462
  /* @__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(
8463
+ /* @__PURE__ */ jsx30(CommandEmpty, { className: "py-4 text-center text-sm text-muted-foreground", children: "No models found" }),
8464
+ !hideDefaultOption ? /* @__PURE__ */ jsx30(CommandGroup, { children: /* @__PURE__ */ jsxs25(
8481
8465
  CommandItem,
8482
8466
  {
8483
8467
  value: "Default model automatic auto",
@@ -8488,7 +8472,7 @@ function ModelPickerMenu({
8488
8472
  },
8489
8473
  className: selectorItemClass(!value || activeIndex === 0),
8490
8474
  children: [
8491
- /* @__PURE__ */ jsx28(
8475
+ /* @__PURE__ */ jsx30(
8492
8476
  CheckIcon3,
8493
8477
  {
8494
8478
  className: cn(
@@ -8497,16 +8481,16 @@ function ModelPickerMenu({
8497
8481
  )
8498
8482
  }
8499
8483
  ),
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" })
8484
+ /* @__PURE__ */ jsx30("span", { className: "truncate", children: "Default model" }),
8485
+ /* @__PURE__ */ jsx30("span", { className: "ml-auto shrink-0 text-xs text-muted-foreground", children: "Auto" })
8502
8486
  ]
8503
8487
  }
8504
8488
  ) }) : null,
8505
- [...groups.entries()].map(([provider, list]) => /* @__PURE__ */ jsx28(
8489
+ [...groups.entries()].map(([provider, list]) => /* @__PURE__ */ jsx30(
8506
8490
  CommandGroup,
8507
8491
  {
8508
8492
  heading: displayProviderLabel(provider),
8509
- className: "[&_[cmdk-group-heading]]:px-2.5 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-[10px] [&_[cmdk-group-heading]]:font-semibold [&_[cmdk-group-heading]]:uppercase [&_[cmdk-group-heading]]:tracking-[0.08em] [&_[cmdk-group-heading]]:text-muted-foreground/60",
8493
+ className: "[&_[cmdk-group-heading]]:px-2.5 [&_[cmdk-group-heading]]:py-2 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-semibold [&_[cmdk-group-heading]]:uppercase [&_[cmdk-group-heading]]:tracking-[0.08em] [&_[cmdk-group-heading]]:text-muted-foreground",
8510
8494
  children: list.map((m) => {
8511
8495
  const key = encodeModelKey(m);
8512
8496
  const label = m.label || displayModelLabel(m.id);
@@ -8522,7 +8506,7 @@ function ModelPickerMenu({
8522
8506
  },
8523
8507
  className: cn(selectorItemClass(key === currentKey || activeIndex === itemIndex)),
8524
8508
  children: [
8525
- /* @__PURE__ */ jsx28(
8509
+ /* @__PURE__ */ jsx30(
8526
8510
  CheckIcon3,
8527
8511
  {
8528
8512
  className: cn(
@@ -8531,8 +8515,8 @@ function ModelPickerMenu({
8531
8515
  )
8532
8516
  }
8533
8517
  ),
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 })
8518
+ /* @__PURE__ */ jsx30("span", { className: "min-w-0 flex-1 truncate whitespace-nowrap", children: label }),
8519
+ /* @__PURE__ */ jsx30("span", { className: "ml-auto max-w-[45%] shrink-0 truncate whitespace-nowrap text-right text-xs text-muted-foreground", children: m.id })
8536
8520
  ]
8537
8521
  },
8538
8522
  key
@@ -8571,7 +8555,7 @@ function ModelSelect({
8571
8555
  previousOpenSignalRef.current = openSignal;
8572
8556
  }, [disabled, openSignal]);
8573
8557
  return /* @__PURE__ */ jsxs25(Popover, { open, onOpenChange: disabled ? void 0 : setOpen, children: [
8574
- /* @__PURE__ */ jsx28(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx28(
8558
+ /* @__PURE__ */ jsx30(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx30(
8575
8559
  ModelSelectTrigger,
8576
8560
  {
8577
8561
  value,
@@ -8585,7 +8569,7 @@ function ModelSelect({
8585
8569
  "aria-describedby": ariaDescribedBy
8586
8570
  }
8587
8571
  ) }),
8588
- /* @__PURE__ */ jsx28(
8572
+ /* @__PURE__ */ jsx30(
8589
8573
  PopoverContent,
8590
8574
  {
8591
8575
  align: trigger === "slash" ? "center" : "start",
@@ -8594,7 +8578,7 @@ function ModelSelect({
8594
8578
  collisionPadding: 12,
8595
8579
  "data-boring-agent": "",
8596
8580
  className: cn("w-[min(92vw,340px)]", selectorContentClass),
8597
- children: /* @__PURE__ */ jsx28(
8581
+ children: /* @__PURE__ */ jsx30(
8598
8582
  ModelPickerMenu,
8599
8583
  {
8600
8584
  value,
@@ -8632,7 +8616,7 @@ function ThinkingSelect({
8632
8616
  previousOpenSignalRef.current = openSignal;
8633
8617
  }, [disabled, openSignal]);
8634
8618
  return /* @__PURE__ */ jsxs25(Popover, { open, onOpenChange: disabled ? void 0 : setOpen, children: [
8635
- /* @__PURE__ */ jsx28(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx28(
8619
+ /* @__PURE__ */ jsx30(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx30(
8636
8620
  ThinkingSelectTrigger,
8637
8621
  {
8638
8622
  value,
@@ -8642,7 +8626,7 @@ function ThinkingSelect({
8642
8626
  className
8643
8627
  }
8644
8628
  ) }),
8645
- /* @__PURE__ */ jsx28(
8629
+ /* @__PURE__ */ jsx30(
8646
8630
  PopoverContent,
8647
8631
  {
8648
8632
  align: trigger === "slash" ? "center" : "end",
@@ -8651,7 +8635,7 @@ function ThinkingSelect({
8651
8635
  collisionPadding: 12,
8652
8636
  "data-boring-agent": "",
8653
8637
  className: cn("w-[min(92vw,240px)]", selectorContentClass),
8654
- children: /* @__PURE__ */ jsx28(
8638
+ children: /* @__PURE__ */ jsx30(
8655
8639
  ThinkingPickerMenu,
8656
8640
  {
8657
8641
  value,
@@ -8683,7 +8667,7 @@ var ThinkingSelectTrigger = forwardRef(function ThinkingSelectTrigger2({
8683
8667
  "data-boring-state": disabled ? "disabled" : void 0,
8684
8668
  disabled,
8685
8669
  className: cn(
8686
- trigger === "slash" ? "cursor-pointer rounded-[var(--radius-md)] px-1.5 py-0.5 transition-colors hover:bg-foreground/[0.045] hover:text-foreground/80 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-foreground/20 disabled:pointer-events-none disabled:opacity-50" : selectorTriggerClass,
8670
+ trigger === "slash" ? "h-8 cursor-pointer rounded-[var(--radius-md)] px-2 text-xs transition-colors hover:bg-muted/60 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-popover disabled:pointer-events-none disabled:opacity-50 motion-reduce:transition-none" : selectorTriggerClass,
8687
8671
  trigger === "button" && "gap-1.5",
8688
8672
  trigger === "button" && value !== "off" && !open && "border-[color:oklch(from_var(--accent)_l_c_h/0.35)] bg-[color:oklch(from_var(--accent)_l_c_h/0.08)] text-foreground",
8689
8673
  open && (trigger === "slash" ? "bg-muted/45" : "border-border/80 bg-muted/55 text-foreground"),
@@ -8695,13 +8679,13 @@ var ThinkingSelectTrigger = forwardRef(function ThinkingSelectTrigger2({
8695
8679
  onClick,
8696
8680
  ...props,
8697
8681
  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" })
8682
+ trigger === "button" ? /* @__PURE__ */ jsx30(ThinkingLevelGlyph, { level: value }) : null,
8683
+ trigger === "slash" ? /* @__PURE__ */ jsxs25(Fragment10, { children: [
8684
+ /* @__PURE__ */ jsx30("span", { className: "text-muted-foreground", children: "Thinking \xB7 " }),
8685
+ /* @__PURE__ */ jsx30("span", { className: "text-foreground", children: THINKING_LEVEL_STATUS_LABELS[value] })
8686
+ ] }) : /* @__PURE__ */ jsxs25(Fragment10, { children: [
8687
+ /* @__PURE__ */ jsx30("span", { children: THINKING_LEVEL_LABELS[value] }),
8688
+ /* @__PURE__ */ jsx30(ChevronDownIcon5, { className: "h-3 w-3 shrink-0 text-muted-foreground/50", "aria-hidden": "true" })
8705
8689
  ] })
8706
8690
  ]
8707
8691
  }
@@ -8750,7 +8734,7 @@ function ThinkingPickerMenu({
8750
8734
  window.addEventListener("keydown", handler, { capture: true });
8751
8735
  return () => window.removeEventListener("keydown", handler, { capture: true });
8752
8736
  }, [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(
8737
+ 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
8738
  CommandItem,
8755
8739
  {
8756
8740
  value: `Thinking ${THINKING_LEVEL_LABELS[level]} ${THINKING_LEVEL_DETAILS[level]}`,
@@ -8761,7 +8745,7 @@ function ThinkingPickerMenu({
8761
8745
  },
8762
8746
  className: selectorItemClass(level === value || index === activeIndex),
8763
8747
  children: [
8764
- /* @__PURE__ */ jsx28(
8748
+ /* @__PURE__ */ jsx30(
8765
8749
  CheckIcon3,
8766
8750
  {
8767
8751
  className: cn(
@@ -8770,9 +8754,9 @@ function ThinkingPickerMenu({
8770
8754
  )
8771
8755
  }
8772
8756
  ),
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] })
8757
+ /* @__PURE__ */ jsx30(ThinkingLevelGlyph, { level }),
8758
+ /* @__PURE__ */ jsx30("span", { className: "font-medium", children: THINKING_LEVEL_LABELS[level] }),
8759
+ /* @__PURE__ */ jsx30("span", { className: "ml-auto text-xs text-muted-foreground", children: THINKING_LEVEL_DETAILS[level] })
8776
8760
  ]
8777
8761
  },
8778
8762
  level
@@ -8813,22 +8797,22 @@ import {
8813
8797
  XIcon as XIcon5
8814
8798
  } from "lucide-react";
8815
8799
  import { nanoid } from "nanoid";
8816
- import { useCallback as useCallback15, useEffect as useEffect18, useMemo as useMemo11, useRef as useRef15, useState as useState17 } from "react";
8800
+ import { useCallback as useCallback15, useEffect as useEffect18, useMemo as useMemo13, useRef as useRef15, useState as useState17 } from "react";
8817
8801
 
8818
8802
  // src/front/primitives/prompt-input-context.ts
8819
- import { createContext as createContext6, useContext as useContext6 } from "react";
8820
- var PromptInputController = createContext6(
8803
+ import { createContext as createContext8, useContext as useContext8 } from "react";
8804
+ var PromptInputController = createContext8(
8821
8805
  null
8822
8806
  );
8823
- var ProviderAttachmentsContext = createContext6(
8807
+ var ProviderAttachmentsContext = createContext8(
8824
8808
  null
8825
8809
  );
8826
- var useOptionalPromptInputController = () => useContext6(PromptInputController);
8827
- var useOptionalProviderAttachments = () => useContext6(ProviderAttachmentsContext);
8828
- var LocalAttachmentsContext = createContext6(null);
8810
+ var useOptionalPromptInputController = () => useContext8(PromptInputController);
8811
+ var useOptionalProviderAttachments = () => useContext8(ProviderAttachmentsContext);
8812
+ var LocalAttachmentsContext = createContext8(null);
8829
8813
  var usePromptInputAttachments = () => {
8830
8814
  const provider = useOptionalProviderAttachments();
8831
- const local = useContext6(LocalAttachmentsContext);
8815
+ const local = useContext8(LocalAttachmentsContext);
8832
8816
  const context = local ?? provider;
8833
8817
  if (!context) {
8834
8818
  throw new Error(
@@ -8837,7 +8821,7 @@ var usePromptInputAttachments = () => {
8837
8821
  }
8838
8822
  return context;
8839
8823
  };
8840
- var LocalReferencedSourcesContext = createContext6(null);
8824
+ var LocalReferencedSourcesContext = createContext8(null);
8841
8825
 
8842
8826
  // src/front/primitives/prompt-input-wrappers.tsx
8843
8827
  import {
@@ -8848,12 +8832,12 @@ import {
8848
8832
  TooltipTrigger as TooltipTrigger3
8849
8833
  } from "@hachej/boring-ui-kit";
8850
8834
  import { Children as Children2 } from "react";
8851
- import { jsx as jsx29, jsxs as jsxs26 } from "react/jsx-runtime";
8835
+ import { jsx as jsx31, jsxs as jsxs26 } from "react/jsx-runtime";
8852
8836
  var PromptInputFooter = ({
8853
8837
  align = "block-end",
8854
8838
  className,
8855
8839
  ...props
8856
- }) => /* @__PURE__ */ jsx29(
8840
+ }) => /* @__PURE__ */ jsx31(
8857
8841
  InputGroupAddon,
8858
8842
  {
8859
8843
  align,
@@ -8863,7 +8847,7 @@ var PromptInputFooter = ({
8863
8847
  );
8864
8848
 
8865
8849
  // src/front/primitives/prompt-input.tsx
8866
- import { Fragment as Fragment10, jsx as jsx30, jsxs as jsxs27 } from "react/jsx-runtime";
8850
+ import { Fragment as Fragment11, jsx as jsx32, jsxs as jsxs27 } from "react/jsx-runtime";
8867
8851
  var PromptInput = ({
8868
8852
  className,
8869
8853
  accept,
@@ -9118,7 +9102,7 @@ var PromptInput = ({
9118
9102
  [add]
9119
9103
  );
9120
9104
  const setFileUrl = usingProvider ? controller.attachments.setFileUrl : setFileUrlLocal;
9121
- const attachmentsCtx = useMemo11(
9105
+ const attachmentsCtx = useMemo13(
9122
9106
  () => ({
9123
9107
  add,
9124
9108
  clear: clearAttachments,
@@ -9130,7 +9114,7 @@ var PromptInput = ({
9130
9114
  }),
9131
9115
  [files, add, remove, clearAttachments, openFileDialog, setFileUrl]
9132
9116
  );
9133
- const refsCtx = useMemo11(
9117
+ const refsCtx = useMemo13(
9134
9118
  () => ({
9135
9119
  add: (incoming) => {
9136
9120
  const array = Array.isArray(incoming) ? incoming : [incoming];
@@ -9206,8 +9190,8 @@ var PromptInput = ({
9206
9190
  },
9207
9191
  [usingProvider, controller, files, onSubmit, clear]
9208
9192
  );
9209
- const inner = /* @__PURE__ */ jsxs27(Fragment10, { children: [
9210
- /* @__PURE__ */ jsx30(
9193
+ const inner = /* @__PURE__ */ jsxs27(Fragment11, { children: [
9194
+ /* @__PURE__ */ jsx32(
9211
9195
  Input,
9212
9196
  {
9213
9197
  accept,
@@ -9220,7 +9204,7 @@ var PromptInput = ({
9220
9204
  type: "file"
9221
9205
  }
9222
9206
  ),
9223
- /* @__PURE__ */ jsx30(
9207
+ /* @__PURE__ */ jsx32(
9224
9208
  "form",
9225
9209
  {
9226
9210
  "data-boring-agent-part": "composer",
@@ -9228,12 +9212,12 @@ var PromptInput = ({
9228
9212
  onSubmit: handleSubmit,
9229
9213
  ref: formRef,
9230
9214
  ...props,
9231
- children: /* @__PURE__ */ jsx30(InputGroup, { className: "overflow-hidden", children })
9215
+ children: /* @__PURE__ */ jsx32(InputGroup, { className: "overflow-hidden", children })
9232
9216
  }
9233
9217
  )
9234
9218
  ] });
9235
- const withReferencedSources = /* @__PURE__ */ jsx30(LocalReferencedSourcesContext.Provider, { value: refsCtx, children: inner });
9236
- return /* @__PURE__ */ jsx30(LocalAttachmentsContext.Provider, { value: attachmentsCtx, children: withReferencedSources });
9219
+ const withReferencedSources = /* @__PURE__ */ jsx32(LocalReferencedSourcesContext.Provider, { value: refsCtx, children: inner });
9220
+ return /* @__PURE__ */ jsx32(LocalAttachmentsContext.Provider, { value: attachmentsCtx, children: withReferencedSources });
9237
9221
  };
9238
9222
  var PromptInputTextarea = ({
9239
9223
  onChange,
@@ -9311,7 +9295,7 @@ var PromptInputTextarea = ({
9311
9295
  } : {
9312
9296
  onChange
9313
9297
  };
9314
- return /* @__PURE__ */ jsx30(
9298
+ return /* @__PURE__ */ jsx32(
9315
9299
  InputGroupTextarea,
9316
9300
  {
9317
9301
  "data-boring-agent-part": "composer-input",
@@ -9338,13 +9322,13 @@ var PromptInputSubmit = ({
9338
9322
  ...props
9339
9323
  }) => {
9340
9324
  const isGenerating = status === "submitted" || status === "streaming";
9341
- let Icon = /* @__PURE__ */ jsx30(CornerDownLeftIcon, { className: "size-4" });
9325
+ let Icon = /* @__PURE__ */ jsx32(CornerDownLeftIcon, { className: "size-4" });
9342
9326
  if (status === "submitted") {
9343
- Icon = /* @__PURE__ */ jsx30(Spinner, {});
9327
+ Icon = /* @__PURE__ */ jsx32(Spinner, {});
9344
9328
  } else if (status === "streaming") {
9345
- Icon = /* @__PURE__ */ jsx30(SquareIcon, { className: "size-4" });
9329
+ Icon = /* @__PURE__ */ jsx32(SquareIcon, { className: "size-4" });
9346
9330
  } else if (status === "error") {
9347
- Icon = /* @__PURE__ */ jsx30(XIcon5, { className: "size-4" });
9331
+ Icon = /* @__PURE__ */ jsx32(XIcon5, { className: "size-4" });
9348
9332
  }
9349
9333
  const handleClick = useCallback15(
9350
9334
  (e) => {
@@ -9357,7 +9341,7 @@ var PromptInputSubmit = ({
9357
9341
  },
9358
9342
  [isGenerating, onStop, onClick]
9359
9343
  );
9360
- return /* @__PURE__ */ jsx30(
9344
+ return /* @__PURE__ */ jsx32(
9361
9345
  InputGroupButton2,
9362
9346
  {
9363
9347
  "aria-label": isGenerating ? "Stop" : "Submit",
@@ -9374,8 +9358,8 @@ var PromptInputSubmit = ({
9374
9358
  };
9375
9359
 
9376
9360
  // 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";
9361
+ import { useEffect as useEffect19, useMemo as useMemo14, useRef as useRef16, useState as useState18 } from "react";
9362
+ import { Fragment as Fragment12, jsx as jsx33, jsxs as jsxs28 } from "react/jsx-runtime";
9379
9363
  var ALL_PLUGINS = "__all__";
9380
9364
  function commandGroup(cmd) {
9381
9365
  if (cmd.source === "skill") return "skills";
@@ -9392,11 +9376,11 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9392
9376
  useEffect19(() => {
9393
9377
  setSearchQuery(query);
9394
9378
  }, [query]);
9395
- const groups = useMemo12(() => {
9379
+ const groups = useMemo14(() => {
9396
9380
  const set = new Set(commands.map(commandGroup));
9397
9381
  return Array.from(set).sort((a, b) => a.localeCompare(b));
9398
9382
  }, [commands]);
9399
- const filtered = useMemo12(() => {
9383
+ const filtered = useMemo14(() => {
9400
9384
  const q = searchQuery.trim().toLowerCase();
9401
9385
  return commands.filter((c) => {
9402
9386
  if (plugin !== ALL_PLUGINS && commandGroup(c) !== plugin) return false;
@@ -9427,7 +9411,7 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9427
9411
  onDismiss
9428
9412
  });
9429
9413
  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(
9414
+ /* @__PURE__ */ jsx33(
9431
9415
  "input",
9432
9416
  {
9433
9417
  ref: inputRef,
@@ -9442,9 +9426,9 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9442
9426
  autoComplete: "off"
9443
9427
  }
9444
9428
  ),
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) => {
9429
+ /* @__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
9430
  const selected = plugin === g;
9447
- return /* @__PURE__ */ jsx31(
9431
+ return /* @__PURE__ */ jsx33(
9448
9432
  "button",
9449
9433
  {
9450
9434
  type: "button",
@@ -9464,7 +9448,7 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9464
9448
  g
9465
9449
  );
9466
9450
  }) }),
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(
9451
+ 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
9452
  "li",
9469
9453
  {
9470
9454
  role: "option",
@@ -9487,15 +9471,15 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9487
9471
  "/",
9488
9472
  cmd.name
9489
9473
  ] }),
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
9474
+ cmd.source === "skill" ? /* @__PURE__ */ jsxs28(Fragment12, { children: [
9475
+ /* @__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" }),
9476
+ 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
9477
  ] }) : cmd.sourcePlugin ? (
9494
9478
  // 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 })
9479
+ /* @__PURE__ */ jsx33("span", { className: "rounded-sm bg-muted px-1 py-px text-[9px] font-medium text-muted-foreground", children: cmd.sourcePlugin })
9496
9480
  ) : null
9497
9481
  ] }),
9498
- /* @__PURE__ */ jsx31("span", { className: "truncate text-[11px] opacity-50", children: cmd.description })
9482
+ /* @__PURE__ */ jsx33("span", { className: "truncate text-[11px] opacity-50", children: cmd.description })
9499
9483
  ]
9500
9484
  },
9501
9485
  cmd.name
@@ -9506,10 +9490,10 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9506
9490
  // src/front/chat/components/ComposerAttachments.tsx
9507
9491
  import { AlertCircleIcon as AlertCircleIcon2, Loader2 as Loader23, PaperclipIcon as PaperclipIcon2 } from "lucide-react";
9508
9492
  import { IconButton as IconButton3 } from "@hachej/boring-ui-kit";
9509
- import { jsx as jsx32, jsxs as jsxs29 } from "react/jsx-runtime";
9493
+ import { jsx as jsx34, jsxs as jsxs29 } from "react/jsx-runtime";
9510
9494
  function AttachmentButton({ disabled, className }) {
9511
9495
  const attachments = usePromptInputAttachments();
9512
- return /* @__PURE__ */ jsx32(
9496
+ return /* @__PURE__ */ jsx34(
9513
9497
  IconButton3,
9514
9498
  {
9515
9499
  type: "button",
@@ -9519,17 +9503,17 @@ function AttachmentButton({ disabled, className }) {
9519
9503
  onClick: () => {
9520
9504
  if (!disabled) attachments.openFileDialog();
9521
9505
  },
9522
- className: cn(composerActionClass, "w-8", className),
9506
+ className: cn(composerActionClass, "!h-10 !w-10", className),
9523
9507
  "aria-label": "Attach files",
9524
9508
  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 })
9509
+ children: /* @__PURE__ */ jsx34(PaperclipIcon2, { className: "h-4 w-4", strokeWidth: 1.75 })
9526
9510
  }
9527
9511
  );
9528
9512
  }
9529
9513
  function AttachmentsList() {
9530
9514
  const attachments = usePromptInputAttachments();
9531
9515
  if (attachments.files.length === 0) return null;
9532
- return /* @__PURE__ */ jsx32(
9516
+ return /* @__PURE__ */ jsx34(
9533
9517
  Attachments,
9534
9518
  {
9535
9519
  "data-boring-agent-part": "composer-attachment-row",
@@ -9548,12 +9532,12 @@ function AttachmentsList() {
9548
9532
  ),
9549
9533
  children: [
9550
9534
  /* @__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
9535
+ /* @__PURE__ */ jsx34(AttachmentPreview, { className: "!size-7 overflow-hidden !rounded-full bg-background/60" }),
9536
+ 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,
9537
+ 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
9538
  ] }),
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" })
9539
+ /* @__PURE__ */ jsx34(AttachmentInfo, { className: "min-w-0 !max-w-[180px] truncate text-[13px] font-medium" }),
9540
+ /* @__PURE__ */ jsx34(AttachmentRemove, { className: "!size-5 !rounded-full !opacity-100 text-muted-foreground/80 hover:text-foreground" })
9557
9541
  ]
9558
9542
  },
9559
9543
  file.id
@@ -9563,7 +9547,7 @@ function AttachmentsList() {
9563
9547
  }
9564
9548
 
9565
9549
  // src/front/chat/components/PiChatComposerSurface.tsx
9566
- import { jsx as jsx33, jsxs as jsxs30 } from "react/jsx-runtime";
9550
+ import { jsx as jsx35, jsxs as jsxs30 } from "react/jsx-runtime";
9567
9551
  var MAX_PROMPT_ATTACHMENTS = 2;
9568
9552
  var MAX_PROMPT_ATTACHMENT_BYTES = 4 * 1024 * 1024;
9569
9553
  var COMPOSER_INPUT_GROUP_MIN_HEIGHT = 56;
@@ -9587,6 +9571,7 @@ function hasSoftWrappedLine(node, style) {
9587
9571
  }
9588
9572
  function PiChatComposerSurface({
9589
9573
  chrome,
9574
+ pickerPlacement = "above",
9590
9575
  isStreaming,
9591
9576
  status,
9592
9577
  disabled,
@@ -9640,10 +9625,12 @@ function PiChatComposerSurface({
9640
9625
  textareaRef,
9641
9626
  onTextareaChange,
9642
9627
  onTextareaKeyDown,
9628
+ updateDraft,
9643
9629
  onSubmitMessage,
9644
9630
  onStop
9645
9631
  }) {
9646
9632
  const workspaceRequestId = getHeaderValue(requestHeaders, "x-boring-workspace-id");
9633
+ const composerContributions = useComposerContributions();
9647
9634
  const uploadAttachment = useCallback16((file) => uploadFile(file, {
9648
9635
  apiBaseUrl,
9649
9636
  workspaceRequestId,
@@ -9674,8 +9661,11 @@ function PiChatComposerSurface({
9674
9661
  useLayoutEffect2(() => {
9675
9662
  resizeTextarea(textareaRef.current);
9676
9663
  }, [draft, resizeTextarea, textareaRef]);
9677
- 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(
9664
+ return /* @__PURE__ */ jsxs30("div", { className: cn(
9665
+ "relative z-20",
9666
+ chrome ? "pb-[calc(1rem+env(safe-area-inset-bottom))] pl-[calc(1rem+env(safe-area-inset-left))] pr-[calc(1rem+env(safe-area-inset-right))] pt-2 sm:pb-[calc(1.25rem+env(safe-area-inset-bottom))] sm:pl-[calc(1.5rem+env(safe-area-inset-left))] sm:pr-[calc(1.5rem+env(safe-area-inset-right))]" : "pb-[calc(0.5rem+env(safe-area-inset-bottom))] pl-[calc(0.75rem+env(safe-area-inset-left))] pr-[calc(0.75rem+env(safe-area-inset-right))] pt-1"
9667
+ ), children: [
9668
+ /* @__PURE__ */ jsx35(
9679
9669
  "div",
9680
9670
  {
9681
9671
  "data-boring-agent-part": "chat-working-slot",
@@ -9693,7 +9683,7 @@ function PiChatComposerSurface({
9693
9683
  "aria-live": isStreaming ? "polite" : void 0,
9694
9684
  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
9685
  children: [
9696
- /* @__PURE__ */ jsx33(
9686
+ /* @__PURE__ */ jsx35(
9697
9687
  motion2.span,
9698
9688
  {
9699
9689
  "aria-hidden": "true",
@@ -9702,14 +9692,14 @@ function PiChatComposerSurface({
9702
9692
  transition: { duration: 1.2, repeat: Infinity, ease: "easeInOut" }
9703
9693
  }
9704
9694
  ),
9705
- /* @__PURE__ */ jsx33("span", { children: "Working\u2026" })
9695
+ /* @__PURE__ */ jsx35("span", { children: "Working\u2026" })
9706
9696
  ]
9707
9697
  }
9708
9698
  )
9709
9699
  }
9710
9700
  ),
9711
- composerStatusNotice ? /* @__PURE__ */ jsx33(ComposerRuntimeNotice, { notice: composerStatusNotice }) : null,
9712
- composerBlocked && !workspaceWarmupBlocked ? /* @__PURE__ */ jsx33(
9701
+ composerStatusNotice ? /* @__PURE__ */ jsx35(ComposerRuntimeNotice, { notice: composerStatusNotice }) : null,
9702
+ composerBlocked && !workspaceWarmupBlocked ? /* @__PURE__ */ jsx35(
9713
9703
  ComposerBlockerNotice,
9714
9704
  {
9715
9705
  blocker: primaryComposerBlocker,
@@ -9717,8 +9707,8 @@ function PiChatComposerSurface({
9717
9707
  onAction: onComposerBlockerAction
9718
9708
  }
9719
9709
  ) : null,
9720
- queuePreview.length > 0 ? /* @__PURE__ */ jsx33(QueuedComposerNotice, { followUps: queuePreview, onEdit: onEditQueued }) : null,
9721
- hotReloadEnabled ? /* @__PURE__ */ jsx33(
9710
+ queuePreview.length > 0 ? /* @__PURE__ */ jsx35(QueuedComposerNotice, { followUps: queuePreview, onEdit: onEditQueued }) : null,
9711
+ hotReloadEnabled ? /* @__PURE__ */ jsx35(
9722
9712
  PluginUpdateStatus,
9723
9713
  {
9724
9714
  state: pluginUpdateState,
@@ -9726,14 +9716,14 @@ function PiChatComposerSurface({
9726
9716
  onRetry: onRunPluginUpdate
9727
9717
  }
9728
9718
  ) : null,
9729
- /* @__PURE__ */ jsx33(
9719
+ /* @__PURE__ */ jsx35(
9730
9720
  CommandRunStatus,
9731
9721
  {
9732
9722
  state: commandNotifyState,
9733
9723
  onDismiss: onDismissCommandNotify
9734
9724
  }
9735
9725
  ),
9736
- attachmentNotice ? /* @__PURE__ */ jsx33(
9726
+ attachmentNotice ? /* @__PURE__ */ jsx35(
9737
9727
  "div",
9738
9728
  {
9739
9729
  role: "status",
@@ -9742,197 +9732,223 @@ function PiChatComposerSurface({
9742
9732
  children: attachmentNotice
9743
9733
  }
9744
9734
  ) : null,
9745
- /* @__PURE__ */ jsxs30("div", { className: cn("mx-auto w-full", chrome ? "max-w-3xl" : "max-w-[680px]"), children: [
9746
- mentionState ? /* @__PURE__ */ jsx33(
9747
- MentionPicker,
9748
- {
9749
- mention: mentionState,
9750
- apiBaseUrl,
9751
- fetch: fetch2,
9752
- requestHeaders,
9753
- storageScope,
9754
- onSelect: onSelectMention,
9755
- onDismiss: onDismissMention
9756
- }
9757
- ) : null,
9758
- slashQuery !== null ? /* @__PURE__ */ jsx33(
9759
- SlashCommandPicker,
9735
+ /* @__PURE__ */ jsxs30("div", { className: cn("@container group/composer relative mx-auto w-full", chrome ? "max-w-3xl" : "max-w-[680px]"), children: [
9736
+ /* @__PURE__ */ jsxs30("div", { className: cn(
9737
+ "absolute inset-x-0 z-30",
9738
+ pickerPlacement === "above-compact" ? "bottom-[calc(100%+4px)] [&_[cmdk-list]]:!max-h-[200px]" : "bottom-[calc(100%+4px)]"
9739
+ ), children: [
9740
+ mentionState ? /* @__PURE__ */ jsx35("div", { className: "mr-px ml-[43px]", children: /* @__PURE__ */ jsx35(
9741
+ MentionPicker,
9742
+ {
9743
+ mention: mentionState,
9744
+ apiBaseUrl,
9745
+ fetch: fetch2,
9746
+ requestHeaders,
9747
+ storageScope,
9748
+ onSelect: onSelectMention,
9749
+ onDismiss: onDismissMention
9750
+ }
9751
+ ) }) : null,
9752
+ slashQuery !== null ? /* @__PURE__ */ jsx35("div", { className: "mr-px ml-[43px]", children: /* @__PURE__ */ jsx35(
9753
+ SlashCommandPicker,
9754
+ {
9755
+ query: slashQuery,
9756
+ commands,
9757
+ onSelect: onSelectSlashCommand,
9758
+ onDismiss: onDismissSlash
9759
+ }
9760
+ ) }) : null,
9761
+ mentionState === null && slashQuery === null && modelPickerOpen ? /* @__PURE__ */ jsx35("div", { className: "mr-px mb-1 ml-[43px]", children: /* @__PURE__ */ jsx35(
9762
+ ModelPickerMenu,
9763
+ {
9764
+ value: selectedModel,
9765
+ onChange: onModelChange,
9766
+ options: modelOptions,
9767
+ disabled: isStreaming || modelControlled,
9768
+ hideDefaultOption: hideDefaultModelOption,
9769
+ onClose: () => onSetModelPickerOpen(false)
9770
+ }
9771
+ ) }) : null,
9772
+ mentionState === null && slashQuery === null && thinkingPickerOpen ? /* @__PURE__ */ jsx35("div", { className: "mr-px mb-1 ml-[43px]", children: /* @__PURE__ */ jsx35(
9773
+ ThinkingPickerMenu,
9774
+ {
9775
+ value: selectedThinking,
9776
+ onChange: onThinkingChange,
9777
+ disabled: isStreaming || thinkingControlled,
9778
+ onClose: () => onSetThinkingPickerOpen(false)
9779
+ }
9780
+ ) }) : null
9781
+ ] }),
9782
+ composerContributions.map(({ id, Top }) => Top ? /* @__PURE__ */ jsx35(
9783
+ "div",
9760
9784
  {
9761
- query: slashQuery,
9762
- commands,
9763
- onSelect: onSelectSlashCommand,
9764
- onDismiss: onDismissSlash
9765
- }
9766
- ) : null,
9767
- mentionState === null && slashQuery === null && modelPickerOpen ? /* @__PURE__ */ jsx33(
9768
- ModelPickerMenu,
9785
+ "data-boring-agent-part": "composer-contribution-top",
9786
+ "data-boring-agent-contribution-id": id,
9787
+ className: cn("mx-auto mb-2 w-full empty:hidden", chrome ? "max-w-3xl" : "max-w-[680px]"),
9788
+ children: /* @__PURE__ */ jsx35(Top, {})
9789
+ },
9790
+ id
9791
+ ) : null),
9792
+ /* @__PURE__ */ jsx35(
9793
+ "div",
9769
9794
  {
9770
- value: selectedModel,
9771
- onChange: onModelChange,
9772
- options: modelOptions,
9773
- disabled: isStreaming || modelControlled,
9774
- hideDefaultOption: hideDefaultModelOption,
9775
- onClose: () => onSetModelPickerOpen(false)
9795
+ "data-boring-agent-part": "composer-rail",
9796
+ "data-composer-multiline": "false",
9797
+ style: { "--composer-input-group-height": `${COMPOSER_INPUT_GROUP_MIN_HEIGHT}px` },
9798
+ className: cn(
9799
+ "relative mx-auto w-full overflow-visible rounded-xl",
9800
+ chrome ? "max-w-3xl bg-transparent shadow-[0_1px_2px_-1px_oklch(0_0_0/0.06),0_6px_18px_-12px_oklch(0_0_0/0.12),inset_0_0_0_1px_oklch(from_var(--border)_l_c_h/0.7)] focus-within:shadow-[0_1px_3px_-1px_oklch(0_0_0/0.08),0_10px_28px_-14px_oklch(0_0_0/0.16),inset_0_0_0_1px_oklch(from_var(--accent)_l_c_h/0.45)]" : "max-w-[680px] bg-transparent shadow-[inset_0_0_0_1px_oklch(from_var(--border)_l_c_h/0.7)] focus-within:shadow-[inset_0_0_0_1px_oklch(from_var(--accent)_l_c_h/0.45)]",
9801
+ "transition-shadow duration-200 ease-[cubic-bezier(0.22,1,0.36,1)]",
9802
+ "[&_[data-slot=input-group]]:!h-auto [&_[data-slot=input-group]]:!min-h-[var(--composer-input-group-height)]",
9803
+ "[&_[data-slot=input-group]]:!flex-col [&_[data-slot=input-group]]:!items-stretch [&_[data-slot=input-group]]:!overflow-hidden",
9804
+ "[&_[data-slot=input-group]]:!border-0 [&_[data-slot=input-group]]:!rounded-xl",
9805
+ "[&_[data-slot=input-group]]:!bg-transparent [&_[data-slot=input-group]]:!shadow-none",
9806
+ "[&_[data-slot=input-group]]:dark:!bg-transparent [&_[data-slot=input-group]]:!ring-0",
9807
+ "[&_[data-slot=input-group]]:has-[:focus]:!ring-0",
9808
+ "[&[data-composer-multiline=true]_[data-boring-agent-part=composer-submit-addon]]:self-end",
9809
+ "[&[data-composer-multiline=true]_[data-boring-agent-part=composer-submit-addon]]:mb-2"
9810
+ ),
9811
+ children: /* @__PURE__ */ jsxs30(
9812
+ PromptInput,
9813
+ {
9814
+ "data-boring-state": status,
9815
+ onSubmit: (message) => onSubmitMessage({ text: message.text, files: message.files }),
9816
+ onUploadFile: uploadAttachment,
9817
+ multiple: true,
9818
+ maxFiles: disabled || isStreaming ? 0 : MAX_PROMPT_ATTACHMENTS,
9819
+ maxFileSize: MAX_PROMPT_ATTACHMENT_BYTES,
9820
+ onError: (err) => {
9821
+ if (err.code === "max_files") onAttachmentNotice(`Up to ${MAX_PROMPT_ATTACHMENTS} attachments per message.`);
9822
+ else if (err.code === "max_file_size") onAttachmentNotice("Files must be under 4 MB each.");
9823
+ else if (err.code === "accept") onAttachmentNotice("That file type isn't supported here.");
9824
+ else onAttachmentNotice(err.message || "Attachment rejected.");
9825
+ },
9826
+ children: [
9827
+ /* @__PURE__ */ jsx35(AttachmentsList, {}),
9828
+ /* @__PURE__ */ jsxs30(
9829
+ "div",
9830
+ {
9831
+ "data-boring-agent-part": "composer-input-row",
9832
+ className: "flex min-h-[var(--composer-input-group-height)] w-full items-center",
9833
+ children: [
9834
+ /* @__PURE__ */ jsx35("div", { className: "flex h-14 shrink-0 items-center pl-2", children: /* @__PURE__ */ jsx35(AttachmentButton, { disabled: disabled || isStreaming }) }),
9835
+ /* @__PURE__ */ jsx35(
9836
+ PromptInputTextarea,
9837
+ {
9838
+ value: draft,
9839
+ placeholder: composerBlocked ? workspaceWarmupBlocked ? composerBlockerLabel : "" : composerPlaceholder ?? "Ask anything\u2026",
9840
+ disabled,
9841
+ readOnly: composerBlocked,
9842
+ "aria-label": "Agent prompt",
9843
+ ref: (node) => {
9844
+ textareaRef.current = node;
9845
+ resizeTextarea(node);
9846
+ },
9847
+ onChange: onTextareaChange,
9848
+ onKeyDown: onTextareaKeyDown,
9849
+ style: { fieldSizing: "fixed" },
9850
+ className: cn(
9851
+ "min-w-0 flex-1 !min-h-10 !max-h-40 resize-none overflow-hidden border-0 bg-transparent shadow-none",
9852
+ "[field-sizing:fixed]",
9853
+ "px-2 py-2 text-sm leading-6 text-foreground",
9854
+ "placeholder:text-muted-foreground/60",
9855
+ "focus-visible:ring-0 focus-visible:ring-offset-0"
9856
+ )
9857
+ }
9858
+ ),
9859
+ /* @__PURE__ */ jsx35(
9860
+ PromptInputFooter,
9861
+ {
9862
+ align: "inline-end",
9863
+ "data-boring-agent-part": "composer-submit-addon",
9864
+ className: "!order-none !w-auto shrink-0 self-center justify-between border-0 bg-transparent !px-2 !py-0",
9865
+ children: /* @__PURE__ */ jsxs30("div", { className: "ml-auto flex items-center gap-1.5", children: [
9866
+ composerContributions.map(({ id, Action }) => Action ? /* @__PURE__ */ jsx35(
9867
+ "span",
9868
+ {
9869
+ "data-boring-agent-part": "composer-contribution-action",
9870
+ "data-boring-agent-contribution-id": id,
9871
+ className: "contents",
9872
+ children: /* @__PURE__ */ jsx35(Action, { updateDraft })
9873
+ },
9874
+ id
9875
+ ) : null),
9876
+ /* @__PURE__ */ jsx35(
9877
+ PromptInputSubmit,
9878
+ {
9879
+ "data-boring-agent-part": "composer-submit",
9880
+ status: submitStatus,
9881
+ onStop,
9882
+ disabled: submitDisabled,
9883
+ className: cn(
9884
+ "h-8 w-8 shrink-0 rounded-full",
9885
+ "bg-foreground text-background",
9886
+ "transition-all duration-150 ease-[cubic-bezier(0.22,1,0.36,1)]",
9887
+ "hover:bg-foreground/90 hover:shadow-[0_0_0_3px_oklch(from_var(--foreground)_l_c_h/0.12)] hover:scale-[1.04]",
9888
+ "active:scale-[0.93] active:brightness-95",
9889
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/20",
9890
+ "disabled:pointer-events-none disabled:opacity-40",
9891
+ "[&>svg]:size-3.5"
9892
+ )
9893
+ }
9894
+ )
9895
+ ] })
9896
+ }
9897
+ )
9898
+ ]
9899
+ }
9900
+ )
9901
+ ]
9902
+ }
9903
+ )
9776
9904
  }
9777
- ) : null,
9778
- mentionState === null && slashQuery === null && thinkingPickerOpen ? /* @__PURE__ */ jsx33(
9779
- ThinkingPickerMenu,
9905
+ ),
9906
+ hideComposerSettings ? null : /* @__PURE__ */ jsx35(
9907
+ "div",
9780
9908
  {
9781
- value: selectedThinking,
9782
- onChange: onThinkingChange,
9783
- disabled: isStreaming || thinkingControlled,
9784
- onClose: () => onSetThinkingPickerOpen(false)
9785
- }
9786
- ) : null
9787
- ] }),
9788
- /* @__PURE__ */ jsx33(
9789
- "div",
9790
- {
9791
- "data-boring-agent-part": "composer-rail",
9792
- "data-composer-multiline": "false",
9793
- style: { "--composer-input-group-height": `${COMPOSER_INPUT_GROUP_MIN_HEIGHT}px` },
9794
- className: cn(
9795
- "relative mx-auto w-full overflow-visible rounded-[28px]",
9796
- chrome ? "max-w-3xl bg-transparent shadow-[0_1px_2px_-1px_oklch(0_0_0/0.06),0_6px_18px_-12px_oklch(0_0_0/0.12),inset_0_0_0_1px_oklch(from_var(--border)_l_c_h/0.7)] focus-within:shadow-[0_1px_3px_-1px_oklch(0_0_0/0.08),0_10px_28px_-14px_oklch(0_0_0/0.16),inset_0_0_0_1px_oklch(from_var(--accent)_l_c_h/0.45)]" : "max-w-[680px] bg-transparent shadow-[inset_0_0_0_1px_oklch(from_var(--border)_l_c_h/0.7)] focus-within:shadow-[inset_0_0_0_1px_oklch(from_var(--accent)_l_c_h/0.45)]",
9797
- "transition-shadow duration-200 ease-[cubic-bezier(0.22,1,0.36,1)]",
9798
- "[&_[data-slot=input-group]]:!h-auto [&_[data-slot=input-group]]:!min-h-[var(--composer-input-group-height)]",
9799
- "[&_[data-slot=input-group]]:!flex-col [&_[data-slot=input-group]]:!items-stretch [&_[data-slot=input-group]]:!overflow-hidden",
9800
- "[&_[data-slot=input-group]]:!border-0 [&_[data-slot=input-group]]:!rounded-[28px]",
9801
- "[&_[data-slot=input-group]]:!bg-transparent [&_[data-slot=input-group]]:!shadow-none",
9802
- "[&_[data-slot=input-group]]:dark:!bg-transparent [&_[data-slot=input-group]]:!ring-0",
9803
- "[&_[data-slot=input-group]]:has-[:focus]:!ring-0",
9804
- "[&[data-composer-multiline=true]_[data-boring-agent-part=composer-submit-addon]]:self-end",
9805
- "[&[data-composer-multiline=true]_[data-boring-agent-part=composer-submit-addon]]:mb-2"
9806
- ),
9807
- children: /* @__PURE__ */ jsxs30(
9808
- PromptInput,
9809
- {
9810
- "data-boring-state": status,
9811
- onSubmit: (message) => onSubmitMessage({ text: message.text, files: message.files }),
9812
- onUploadFile: uploadAttachment,
9813
- multiple: true,
9814
- maxFiles: disabled || isStreaming ? 0 : MAX_PROMPT_ATTACHMENTS,
9815
- maxFileSize: MAX_PROMPT_ATTACHMENT_BYTES,
9816
- onError: (err) => {
9817
- if (err.code === "max_files") onAttachmentNotice(`Up to ${MAX_PROMPT_ATTACHMENTS} attachments per message.`);
9818
- else if (err.code === "max_file_size") onAttachmentNotice("Files must be under 4 MB each.");
9819
- else if (err.code === "accept") onAttachmentNotice("That file type isn't supported here.");
9820
- else onAttachmentNotice(err.message || "Attachment rejected.");
9821
- },
9822
- children: [
9823
- /* @__PURE__ */ jsx33(AttachmentsList, {}),
9824
- /* @__PURE__ */ jsxs30(
9825
- "div",
9826
- {
9827
- "data-boring-agent-part": "composer-input-row",
9828
- className: "flex min-h-[var(--composer-input-group-height)] w-full items-center",
9829
- 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(
9832
- PromptInputTextarea,
9833
- {
9834
- value: draft,
9835
- placeholder: composerBlocked ? workspaceWarmupBlocked ? composerBlockerLabel : "" : composerPlaceholder ?? "Ask anything\u2026",
9836
- disabled,
9837
- readOnly: composerBlocked,
9838
- "aria-label": "Agent prompt",
9839
- ref: (node) => {
9840
- textareaRef.current = node;
9841
- resizeTextarea(node);
9842
- },
9843
- onChange: onTextareaChange,
9844
- onKeyDown: onTextareaKeyDown,
9845
- style: { fieldSizing: "fixed" },
9846
- className: cn(
9847
- "min-w-0 flex-1 !min-h-10 !max-h-40 resize-none overflow-hidden border-0 bg-transparent shadow-none",
9848
- "[field-sizing:fixed]",
9849
- "px-2 py-2 text-[13px] leading-6",
9850
- "placeholder:text-muted-foreground/45",
9851
- "focus-visible:ring-0 focus-visible:ring-offset-0"
9852
- )
9853
- }
9854
- ),
9855
- /* @__PURE__ */ jsx33(
9856
- PromptInputFooter,
9857
- {
9858
- align: "inline-end",
9859
- "data-boring-agent-part": "composer-submit-addon",
9860
- 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
- ) })
9881
- }
9882
- )
9883
- ]
9884
- }
9885
- )
9886
- ]
9887
- }
9888
- )
9889
- }
9890
- ),
9891
- hideComposerSettings ? null : /* @__PURE__ */ jsxs30(
9892
- "div",
9893
- {
9894
- "data-boring-agent-part": "composer-settings-row",
9895
- className: cn(
9896
- "mx-auto mt-1.5 flex w-full items-center justify-center gap-1.5 text-[10.5px] text-muted-foreground/45",
9897
- chrome ? "max-w-3xl" : "max-w-[680px]"
9898
- ),
9899
- children: [
9900
- /* @__PURE__ */ jsx33(
9901
- ModelSelectTrigger,
9902
- {
9903
- value: selectedModel,
9904
- options: modelOptions,
9905
- disabled: isStreaming || modelControlled,
9906
- trigger: "slash",
9907
- open: modelPickerOpen,
9908
- onClick: () => {
9909
- if (modelPickerOpen) {
9910
- onSetModelPickerOpen(false);
9911
- return;
9909
+ "data-boring-agent-part": "composer-settings-row",
9910
+ className: cn(
9911
+ "mx-auto mt-2 grid min-h-8 w-full grid-cols-[minmax(0,1fr)_auto] items-center text-xs text-muted-foreground",
9912
+ chrome ? "max-w-3xl" : "max-w-[680px]"
9913
+ ),
9914
+ children: /* @__PURE__ */ jsxs30("div", { className: "flex min-w-0 items-center justify-center gap-1", children: [
9915
+ /* @__PURE__ */ jsx35(
9916
+ ModelSelectTrigger,
9917
+ {
9918
+ value: selectedModel,
9919
+ options: modelOptions,
9920
+ disabled: isStreaming || modelControlled,
9921
+ trigger: "slash",
9922
+ open: modelPickerOpen,
9923
+ onClick: () => {
9924
+ if (modelPickerOpen) {
9925
+ onSetModelPickerOpen(false);
9926
+ return;
9927
+ }
9928
+ void onOpenModelPicker();
9912
9929
  }
9913
- void onOpenModelPicker();
9914
9930
  }
9915
- }
9916
- ),
9917
- thinkingControl ? /* @__PURE__ */ jsx33(
9918
- ThinkingSelectTrigger,
9919
- {
9920
- value: selectedThinking,
9921
- disabled: isStreaming || thinkingControlled,
9922
- trigger: "slash",
9923
- open: thinkingPickerOpen,
9924
- onClick: () => {
9925
- if (thinkingPickerOpen) {
9926
- onSetThinkingPickerOpen(false);
9927
- return;
9931
+ ),
9932
+ thinkingControl ? /* @__PURE__ */ jsx35(
9933
+ ThinkingSelectTrigger,
9934
+ {
9935
+ value: selectedThinking,
9936
+ disabled: isStreaming || thinkingControlled,
9937
+ trigger: "slash",
9938
+ open: thinkingPickerOpen,
9939
+ onClick: () => {
9940
+ if (thinkingPickerOpen) {
9941
+ onSetThinkingPickerOpen(false);
9942
+ return;
9943
+ }
9944
+ void onOpenThinkingPicker();
9928
9945
  }
9929
- void onOpenThinkingPicker();
9930
9946
  }
9931
- }
9932
- ) : null
9933
- ]
9934
- }
9935
- )
9947
+ ) : null
9948
+ ] })
9949
+ }
9950
+ )
9951
+ ] })
9936
9952
  ] });
9937
9953
  }
9938
9954
  function getHeaderValue(headers, name) {
@@ -9941,7 +9957,7 @@ function getHeaderValue(headers, name) {
9941
9957
  }
9942
9958
 
9943
9959
  // 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";
9960
+ import { useCallback as useCallback17, useEffect as useEffect20, useMemo as useMemo15, useRef as useRef17, useState as useState19, useSyncExternalStore } from "react";
9945
9961
  function useExternalRemotePiSession({
9946
9962
  sessionId,
9947
9963
  agentTypeId,
@@ -9956,7 +9972,7 @@ function useExternalRemotePiSession({
9956
9972
  const [session, setSession] = useState19();
9957
9973
  const remoteSessionOptionsRef = useRef17(remoteSessionOptions);
9958
9974
  remoteSessionOptionsRef.current = remoteSessionOptions;
9959
- const remoteSessionOptionsKey = useMemo13(
9975
+ const remoteSessionOptionsKey = useMemo15(
9960
9976
  () => remoteSessionOptionsIdentity2(remoteSessionOptions),
9961
9977
  [remoteSessionOptions]
9962
9978
  );
@@ -10129,7 +10145,7 @@ function errorMessage2(error, fallback) {
10129
10145
  }
10130
10146
 
10131
10147
  // src/front/chat/PiChatPanel.tsx
10132
- import { jsx as jsx34, jsxs as jsxs31 } from "react/jsx-runtime";
10148
+ import { jsx as jsx36, jsxs as jsxs31 } from "react/jsx-runtime";
10133
10149
  var DebugDrawer2 = lazy(() => import("../DebugDrawer-RYEBQ6CO.js").then((m) => ({ default: m.DebugDrawer })));
10134
10150
  var EMPTY_COMMANDS = [];
10135
10151
  var EMPTY_COMMAND_NAMES = [];
@@ -10159,7 +10175,7 @@ function PiChatPanel({
10159
10175
  hotReloadEnabled = true,
10160
10176
  suggestions = defaultChatSuggestions,
10161
10177
  emptyState,
10162
- emptyPlacement = "default",
10178
+ emptyPlacement = "hero",
10163
10179
  composerPlaceholder,
10164
10180
  initialDraft,
10165
10181
  autoSubmitInitialDraft = false,
@@ -10206,9 +10222,9 @@ function PiChatPanel({
10206
10222
  const onTurnCompleteRef = useRef18(onTurnComplete);
10207
10223
  onTurnCompleteRef.current = onTurnComplete;
10208
10224
  const sessionListRefreshRef = useRef18(void 0);
10209
- const requestHeadersKey = useMemo14(() => headersContentKey(requestHeaders), [requestHeaders]);
10210
- const normalizedRequestHeaders = useMemo14(() => normalizedHeadersFromContentKey(requestHeadersKey), [requestHeadersKey]);
10211
- const remoteSessionOptionsWithEvents = useMemo14(() => ({
10225
+ const requestHeadersKey = useMemo16(() => headersContentKey(requestHeaders), [requestHeaders]);
10226
+ const normalizedRequestHeaders = useMemo16(() => normalizedHeadersFromContentKey(requestHeadersKey), [requestHeadersKey]);
10227
+ const remoteSessionOptionsWithEvents = useMemo16(() => ({
10212
10228
  ...remoteSessionOptions,
10213
10229
  ...hydrateMessages ? {} : { autoStart: false },
10214
10230
  onEvent: (event) => {
@@ -10275,7 +10291,7 @@ function PiChatPanel({
10275
10291
  enabled: modelDiscoveryEnabled
10276
10292
  });
10277
10293
  const selectedModel = model === void 0 ? modelDiscovery.model : model;
10278
- const modelOptions = useMemo14(
10294
+ const modelOptions = useMemo16(
10279
10295
  () => modelOptionsForSelection(availableModels ?? modelDiscovery.availableModels, selectedModel),
10280
10296
  [availableModels, modelDiscovery.availableModels, selectedModel]
10281
10297
  );
@@ -10342,6 +10358,11 @@ function PiChatPanel({
10342
10358
  const [localSubmittedSessionId, setLocalSubmittedSessionId] = useState20();
10343
10359
  const localSubmittedSessionRef = useRef18(void 0);
10344
10360
  const { attachmentNotice, setAttachmentNotice } = useAttachmentNotice();
10361
+ const composerContributions = useComposerContributions();
10362
+ const contributedCommands = useMemo16(
10363
+ () => composerContributions.flatMap((contribution) => contribution.commands ?? []),
10364
+ [composerContributions]
10365
+ );
10345
10366
  const markLocalSubmitted = useCallback18((sessionId2) => {
10346
10367
  localSubmittedSessionRef.current = sessionId2;
10347
10368
  setLocalSubmittedSessionId(sessionId2);
@@ -10351,7 +10372,7 @@ function PiChatPanel({
10351
10372
  localSubmittedSessionRef.current = void 0;
10352
10373
  setLocalSubmittedSessionId(void 0);
10353
10374
  }, []);
10354
- const registry = useMemo14(() => {
10375
+ const registry = useMemo16(() => {
10355
10376
  const excludedBuiltins = new Set(excludeBuiltinCommands);
10356
10377
  const effectiveBuiltins = builtinCommands.filter((command) => {
10357
10378
  if (!hotReloadEnabled && command.name === "reload") return false;
@@ -10359,9 +10380,10 @@ function PiChatPanel({
10359
10380
  });
10360
10381
  const next = createCommandRegistry(effectiveBuiltins);
10361
10382
  for (const command of extraCommands ?? []) next.register(command);
10383
+ for (const command of contributedCommands) next.register(command);
10362
10384
  for (const command of commands) next.register(command);
10363
10385
  return next;
10364
- }, [apiBaseUrl, commands, excludeBuiltinCommands, extraCommands, hotReloadEnabled, normalizedRequestHeaders, serverResourcesEnabled, serverSkillsRefreshKey, storageScope]);
10386
+ }, [apiBaseUrl, commands, contributedCommands, excludeBuiltinCommands, extraCommands, hotReloadEnabled, normalizedRequestHeaders, serverResourcesEnabled, serverSkillsRefreshKey, storageScope]);
10365
10387
  const commandsStamp = useServerCommands({
10366
10388
  registry,
10367
10389
  requestHeaders: normalizedRequestHeaders,
@@ -10372,8 +10394,8 @@ function PiChatPanel({
10372
10394
  refreshKey: serverSkillsRefreshKey,
10373
10395
  enabled: serverResourcesEnabled
10374
10396
  });
10375
- const allCommands = useMemo14(() => registry.list(), [registry, commandsStamp]);
10376
- const actionableSlashCommands = useMemo14(
10397
+ const allCommands = useMemo16(() => registry.list(), [registry, commandsStamp]);
10398
+ const actionableSlashCommands = useMemo16(
10377
10399
  () => allCommands.flatMap((command) => command.clickBehavior === "execute" || command.clickBehavior === "insert" ? [{ name: command.name, clickBehavior: command.clickBehavior }] : []),
10378
10400
  [allCommands]
10379
10401
  );
@@ -10381,7 +10403,7 @@ function PiChatPanel({
10381
10403
  const warmupNotice = composerNoticeForWarmup(workspaceWarmupStatus);
10382
10404
  const runtimeDependenciesNotice = composerNoticeForRuntimeDependencies(workspaceWarmupStatus);
10383
10405
  const workspaceWarmupBlocked = Boolean(warmupNotice);
10384
- const activeBlockers = useMemo14(
10406
+ const activeBlockers = useMemo16(
10385
10407
  // A missing active session id means a single/sessionless chat host. In that
10386
10408
  // mode, keep scoped blockers visible instead of hiding the only attention UI.
10387
10409
  // Multi-session hosts should pass a session id so unrelated blockers filter out.
@@ -10391,7 +10413,7 @@ function PiChatPanel({
10391
10413
  const canonicalMessages = selectedChatState ? selectMessagesForRender(selectedChatState) : [];
10392
10414
  const queuePreview = selectedChatState ? selectQueuePreview(selectedChatState) : [];
10393
10415
  const messages = canonicalMessages;
10394
- const userHistory = useMemo14(() => selectComposerHistoryFromCanonicalUsers(canonicalMessages), [canonicalMessages]);
10416
+ const userHistory = useMemo16(() => selectComposerHistoryFromCanonicalUsers(canonicalMessages), [canonicalMessages]);
10395
10417
  const emptyStateHydrating = statusForState(selectedChatState, sessionsLoading || chatStatePending || selectedSessionPending) === "hydrating";
10396
10418
  const emptyHero = emptyPlacement === "hero" && messages.length === 0 && queuePreview.length === 0 && !emptyStateHydrating;
10397
10419
  const debugState = selectedPiSession?.getDebugState();
@@ -10399,7 +10421,7 @@ function PiChatPanel({
10399
10421
  const primaryComposerBlocker = activeBlockers[0];
10400
10422
  const composerBlockerLabel = workspaceWarmupBlocked ? warmupNotice?.title ?? "Preparing workspace..." : primaryComposerBlocker?.label ?? primaryComposerBlocker?.reason ?? "Complete the pending workspace action to continue.";
10401
10423
  const composerStatusNotice = warmupNotice ?? runtimeDependenciesNotice;
10402
- const runtimeNotices = useMemo14(() => {
10424
+ const runtimeNotices = useMemo16(() => {
10403
10425
  const fromState = selectedChatState ? selectRuntimeNotices(selectedChatState) : [];
10404
10426
  const sessionNotice = sessionsError ? [{ id: "session-navigation-error", level: "error", text: sessionsError.message, dismissible: true }] : [];
10405
10427
  const largeStateNotice = debug && debugState?.largeStateWarning ? [{
@@ -10613,7 +10635,7 @@ function PiChatPanel({
10613
10635
  }
10614
10636
  insertSlashCommand(name);
10615
10637
  }, [dismissSlash, insertSlashCommand, openModelPicker, openThinkingPicker, setComposerDraft]);
10616
- const policy = useMemo14(() => {
10638
+ const policy = useMemo16(() => {
10617
10639
  if (!selectedPiSession || !activeChatSessionId || !serverModelSelectionReady) return void 0;
10618
10640
  const policySession = {
10619
10641
  getState: () => {
@@ -10724,11 +10746,11 @@ function PiChatPanel({
10724
10746
  return false;
10725
10747
  }
10726
10748
  }, [activeChatSessionId, clearLocalSubmitted, dropLocalNotice, markLocalSubmitted, onPromptSubmitStarted, policy, selectedPiSession, setComposerDraft, surfaceRunRejected]);
10727
- const availableAssistantSlashCommands = useMemo14(
10749
+ const availableAssistantSlashCommands = useMemo16(
10728
10750
  () => policy ? actionableSlashCommands : actionableSlashCommands.filter((command) => command.clickBehavior === "insert"),
10729
10751
  [actionableSlashCommands, policy]
10730
10752
  );
10731
- const assistantMentionCatalog = useMemo14(() => {
10753
+ const assistantMentionCatalog = useMemo16(() => {
10732
10754
  const skills = /* @__PURE__ */ new Map();
10733
10755
  for (const command of allCommands) {
10734
10756
  if (command.source !== "skill" && command.kind !== "skill") continue;
@@ -10877,8 +10899,8 @@ function PiChatPanel({
10877
10899
  const isStreaming = isPiBusyStatus(status);
10878
10900
  const submitStatus = initialHydrationPromptAllowed ? "ready" : toPromptSubmitStatus(status);
10879
10901
  const submitDisabled = !policy || sessionsLoading || modelSelectionBlocked || composerBlocked && !isStreaming;
10880
- const mergedToolRenderers = useMemo14(() => mergeShadcnToolRenderers(toolRenderers), [toolRenderers]);
10881
- const debugMessages = useMemo14(() => messages.map(toDebugUiMessage), [messages]);
10902
+ const mergedToolRenderers = useMemo16(() => mergeShadcnToolRenderers(toolRenderers), [toolRenderers]);
10903
+ const debugMessages = useMemo16(() => messages.map(toDebugUiMessage), [messages]);
10882
10904
  const onTextareaChange = useCallback18((event) => {
10883
10905
  setModelPickerOpen(false);
10884
10906
  setThinkingPickerOpen(false);
@@ -10912,7 +10934,79 @@ function PiChatPanel({
10912
10934
  }
10913
10935
  handleComposerKeyDown(event);
10914
10936
  }, [handleComposerKeyDown, interrupt, isStreaming, mentionState, slashQuery]);
10915
- return /* @__PURE__ */ jsx34(ArtifactOpenProvider, { onOpenArtifact, children: /* @__PURE__ */ jsxs31(
10937
+ const selectEmptySuggestion = useCallback18((suggestion) => {
10938
+ const text = suggestion.prompt ?? suggestion.label;
10939
+ if (!text.trim()) return;
10940
+ void sendComposerMessage({ text, files: [], source: "suggestion" }).then((result) => {
10941
+ if (result === false) setComposerDraft(text);
10942
+ });
10943
+ }, [sendComposerMessage, setComposerDraft]);
10944
+ const composerSurface = /* @__PURE__ */ jsx36(
10945
+ PiChatComposerSurface,
10946
+ {
10947
+ chrome,
10948
+ pickerPlacement: emptyHero ? "above-compact" : "above",
10949
+ isStreaming,
10950
+ status,
10951
+ disabled,
10952
+ submitStatus,
10953
+ submitDisabled,
10954
+ composerBlocked,
10955
+ composerBlockerLabel,
10956
+ composerPlaceholder,
10957
+ composerStatusNotice,
10958
+ workspaceWarmupBlocked,
10959
+ primaryComposerBlocker,
10960
+ onComposerBlockerAction,
10961
+ queuePreview,
10962
+ onEditQueued: editQueued,
10963
+ hotReloadEnabled,
10964
+ pluginUpdateState,
10965
+ onDismissPluginUpdate: dismissPluginUpdate,
10966
+ onRunPluginUpdate: runPluginUpdate,
10967
+ commandNotifyState,
10968
+ onDismissCommandNotify: dismissCommandNotify,
10969
+ attachmentNotice,
10970
+ onAttachmentNotice: setAttachmentNotice,
10971
+ mentionState,
10972
+ slashQuery,
10973
+ apiBaseUrl,
10974
+ fetch: fetch2,
10975
+ requestHeaders: normalizedRequestHeaders,
10976
+ storageScope,
10977
+ onSelectMention: selectMention,
10978
+ onDismissMention: dismissMention,
10979
+ commands: allCommands,
10980
+ onSelectSlashCommand: selectSlashCommand,
10981
+ onDismissSlash: dismissSlash,
10982
+ modelPickerOpen,
10983
+ selectedModel,
10984
+ modelOptions,
10985
+ modelControlled: model !== void 0,
10986
+ hideDefaultModelOption,
10987
+ hideComposerSettings,
10988
+ onModelChange: modelDiscovery.setModel,
10989
+ onSetModelPickerOpen: setModelPickerOpen,
10990
+ onOpenModelPicker: openModelPicker,
10991
+ thinkingPickerOpen,
10992
+ selectedThinking,
10993
+ thinkingControl,
10994
+ thinkingControlled: thinkingLevel !== void 0,
10995
+ onThinkingChange: setStoredThinkingLevel,
10996
+ onSetThinkingPickerOpen: setThinkingPickerOpen,
10997
+ onOpenThinkingPicker: openThinkingPicker,
10998
+ draft,
10999
+ textareaRef,
11000
+ onTextareaChange,
11001
+ onTextareaKeyDown,
11002
+ updateDraft: (update, options) => {
11003
+ setComposerDraft(update(draftRef.current), options?.focus ?? true);
11004
+ },
11005
+ onSubmitMessage: ({ text, files }) => sendComposerMessage({ text, files }),
11006
+ onStop: stop
11007
+ }
11008
+ );
11009
+ return /* @__PURE__ */ jsx36(ArtifactOpenProvider, { onOpenArtifact, children: /* @__PURE__ */ jsxs31(
10916
11010
  "div",
10917
11011
  {
10918
11012
  "data-boring-agent": "",
@@ -10929,7 +11023,7 @@ function PiChatPanel({
10929
11023
  role: "region",
10930
11024
  "aria-label": "Agent assistant",
10931
11025
  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(
11026
+ 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
11027
  SessionList,
10934
11028
  {
10935
11029
  sessions: sessionList,
@@ -10943,16 +11037,16 @@ function PiChatPanel({
10943
11037
  loadingMore: sessions.loadingMore
10944
11038
  }
10945
11039
  ) }) : null,
10946
- /* @__PURE__ */ jsx34("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col", children: /* @__PURE__ */ jsxs31(
11040
+ /* @__PURE__ */ jsx36("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col", children: /* @__PURE__ */ jsxs31(
10947
11041
  "div",
10948
11042
  {
10949
11043
  className: cn(
10950
- "flex h-full min-h-0 flex-col overflow-hidden",
10951
- emptyHero && "justify-center",
11044
+ "flex h-full min-h-0 flex-col",
11045
+ emptyHero ? "justify-center overflow-y-auto py-6" : "overflow-hidden",
10952
11046
  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
11047
  ),
10954
11048
  children: [
10955
- /* @__PURE__ */ jsx34(
11049
+ /* @__PURE__ */ jsx36(
10956
11050
  PiConversationSurface,
10957
11051
  {
10958
11052
  chrome,
@@ -10977,71 +11071,22 @@ function PiChatPanel({
10977
11071
  windowResetKey: activeSessionId
10978
11072
  }
10979
11073
  ),
10980
- /* @__PURE__ */ jsx34(
10981
- PiChatComposerSurface,
11074
+ composerSurface,
11075
+ emptyHero ? /* @__PURE__ */ jsxs31(
11076
+ "div",
10982
11077
  {
10983
- chrome,
10984
- isStreaming,
10985
- status,
10986
- disabled,
10987
- submitStatus,
10988
- submitDisabled,
10989
- composerBlocked,
10990
- composerBlockerLabel,
10991
- composerPlaceholder,
10992
- composerStatusNotice,
10993
- workspaceWarmupBlocked,
10994
- primaryComposerBlocker,
10995
- onComposerBlockerAction,
10996
- queuePreview,
10997
- onEditQueued: editQueued,
10998
- hotReloadEnabled,
10999
- pluginUpdateState,
11000
- onDismissPluginUpdate: dismissPluginUpdate,
11001
- onRunPluginUpdate: runPluginUpdate,
11002
- commandNotifyState,
11003
- onDismissCommandNotify: dismissCommandNotify,
11004
- attachmentNotice,
11005
- onAttachmentNotice: setAttachmentNotice,
11006
- mentionState,
11007
- slashQuery,
11008
- apiBaseUrl,
11009
- fetch: fetch2,
11010
- requestHeaders: normalizedRequestHeaders,
11011
- storageScope,
11012
- onSelectMention: selectMention,
11013
- onDismissMention: dismissMention,
11014
- commands: allCommands,
11015
- onSelectSlashCommand: selectSlashCommand,
11016
- onDismissSlash: dismissSlash,
11017
- modelPickerOpen,
11018
- selectedModel,
11019
- modelOptions,
11020
- modelControlled: model !== void 0,
11021
- hideDefaultModelOption,
11022
- hideComposerSettings,
11023
- onModelChange: modelDiscovery.setModel,
11024
- onSetModelPickerOpen: setModelPickerOpen,
11025
- onOpenModelPicker: openModelPicker,
11026
- thinkingPickerOpen,
11027
- selectedThinking,
11028
- thinkingControl,
11029
- thinkingControlled: thinkingLevel !== void 0,
11030
- onThinkingChange: setStoredThinkingLevel,
11031
- onSetThinkingPickerOpen: setThinkingPickerOpen,
11032
- onOpenThinkingPicker: openThinkingPicker,
11033
- draft,
11034
- textareaRef,
11035
- onTextareaChange,
11036
- onTextareaKeyDown,
11037
- onSubmitMessage: ({ text, files }) => sendComposerMessage({ text, files }),
11038
- onStop: stop
11078
+ "data-boring-agent-part": "empty-hero-suggestions",
11079
+ className: "@container mx-auto w-full max-w-[640px] px-2 pb-4",
11080
+ children: [
11081
+ /* @__PURE__ */ jsx36(ChatSuggestionGrid, { suggestions, onSelect: selectEmptySuggestion, className: "mt-4" }),
11082
+ emptyState?.footer
11083
+ ]
11039
11084
  }
11040
- )
11085
+ ) : null
11041
11086
  ]
11042
11087
  }
11043
11088
  ) }),
11044
- debug ? /* @__PURE__ */ jsx34(Suspense, { fallback: null, children: /* @__PURE__ */ jsx34("div", { "aria-label": "Chat debug metadata", className: "contents", role: "region", children: /* @__PURE__ */ jsx34(
11089
+ debug ? /* @__PURE__ */ jsx36(Suspense, { fallback: null, children: /* @__PURE__ */ jsx36("div", { "aria-label": "Chat debug metadata", className: "contents", role: "region", children: /* @__PURE__ */ jsx36(
11045
11090
  DebugDrawer2,
11046
11091
  {
11047
11092
  apiBaseUrl,
@@ -11097,8 +11142,10 @@ function getAgentCommands(options = {}) {
11097
11142
  export {
11098
11143
  ArtifactOpenProvider,
11099
11144
  ChatEmptyState,
11145
+ ChatMessageContributionProvider,
11100
11146
  PiChatPanel as ChatPanel,
11101
11147
  CodeBlock2 as CodeBlock,
11148
+ ComposerContributionProvider,
11102
11149
  Conversation,
11103
11150
  ConversationContent,
11104
11151
  ConversationScrollButton,
@@ -11122,7 +11169,10 @@ export {
11122
11169
  ReasoningContent,
11123
11170
  ReasoningTrigger,
11124
11171
  ThinkingSelect,
11172
+ Tool2 as Tool,
11125
11173
  ToolCallGroup,
11174
+ ToolContent,
11175
+ ToolHeader,
11126
11176
  activeSessionStorageKey,
11127
11177
  builtinCommands,
11128
11178
  clearActiveSessionId,