@hachej/boring-agent 0.1.91 → 0.1.93

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.
@@ -15,7 +15,7 @@ import {
15
15
  WORKSPACE_COMMAND_NOTIFY_EVENT,
16
16
  extractToolUiMetadata,
17
17
  sanitizeToolUiMetadata
18
- } from "../chunk-PZC76CIA.js";
18
+ } from "../chunk-OFQ7J6SI.js";
19
19
  import {
20
20
  ErrorCode
21
21
  } from "../chunk-2GBHYJVC.js";
@@ -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 useMemo13, useRef as useRef17, useState as useState20 } from "react";
71
+ import { lazy, Suspense, useCallback as useCallback18, useEffect as useEffect21, useMemo as useMemo14, useRef as useRef18, useState as useState20 } from "react";
72
72
 
73
73
  // src/front/ArtifactOpenContext.tsx
74
74
  import { createContext, useContext } from "react";
@@ -279,6 +279,7 @@ var builtinCommands = [
279
279
  {
280
280
  name: "reload",
281
281
  description: "Reload agent plugins",
282
+ clickBehavior: "execute",
282
283
  handler(_, ctx) {
283
284
  if (ctx.pluginUpdate) return ctx.pluginUpdate.run();
284
285
  return ctx.reloadAgentPlugins();
@@ -6010,7 +6011,7 @@ function searchPiSessions(sessions, query, options = {}) {
6010
6011
 
6011
6012
  // src/front/chat/components/PiConversationSurface.tsx
6012
6013
  import { Loader2 as Loader22 } from "lucide-react";
6013
- import { useCallback as useCallback14, useEffect as useEffect14, useLayoutEffect, useRef as useRef10, useState as useState15 } from "react";
6014
+ import { useCallback as useCallback14, useEffect as useEffect14, useLayoutEffect, useRef as useRef11, useState as useState15 } from "react";
6014
6015
  import { useStickToBottomContext as useStickToBottomContext2 } from "use-stick-to-bottom";
6015
6016
 
6016
6017
  // src/front/primitives/conversation.tsx
@@ -6318,7 +6319,7 @@ function QueuedComposerNotice({ followUps, onEdit }) {
6318
6319
  }
6319
6320
 
6320
6321
  // src/front/chat/components/PiTimelineMessage.tsx
6321
- import { useCallback as useCallback13, useEffect as useEffect13, useState as useState14 } from "react";
6322
+ import { useCallback as useCallback13, useEffect as useEffect13, useMemo as useMemo10, useRef as useRef10, useState as useState14 } from "react";
6322
6323
  import { CheckIcon as CheckIcon2, CopyIcon as CopyIcon3 } from "lucide-react";
6323
6324
  import { Button as Button11 } from "@hachej/boring-ui-kit";
6324
6325
 
@@ -6560,6 +6561,11 @@ import {
6560
6561
  TooltipProvider as TooltipProvider2,
6561
6562
  TooltipTrigger as TooltipTrigger2
6562
6563
  } from "@hachej/boring-ui-kit";
6564
+
6565
+ // src/front/primitives/markdownStyles.ts
6566
+ var INLINE_CODE_CLASS_NAME = "rounded-[0.3em] bg-muted/55 px-[0.32em] py-[0.08em] font-mono text-[0.9em] font-medium text-foreground/90";
6567
+
6568
+ // src/front/primitives/message.tsx
6563
6569
  import { cjk } from "@streamdown/cjk";
6564
6570
  import { code } from "@streamdown/code";
6565
6571
  import { math } from "@streamdown/math";
@@ -6684,8 +6690,7 @@ var MarkdownCode = ({
6684
6690
  "code",
6685
6691
  {
6686
6692
  className: cn(
6687
- "rounded-[0.3em] bg-muted/55 px-[0.32em] py-[0.08em]",
6688
- "font-mono text-[0.9em] font-medium text-foreground/90",
6693
+ INLINE_CODE_CLASS_NAME,
6689
6694
  className
6690
6695
  ),
6691
6696
  ...props,
@@ -6711,7 +6716,7 @@ var MessageResponse = memo3(
6711
6716
  ...props
6712
6717
  }
6713
6718
  ),
6714
- (prevProps, nextProps) => prevProps.children === nextProps.children && nextProps.isAnimating === prevProps.isAnimating
6719
+ (prevProps, nextProps) => prevProps.children === nextProps.children && nextProps.isAnimating === prevProps.isAnimating && nextProps.components === prevProps.components
6715
6720
  );
6716
6721
  MessageResponse.displayName = "MessageResponse";
6717
6722
 
@@ -7148,8 +7153,209 @@ var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
7148
7153
  });
7149
7154
  ToolCallGroup.displayName = "ToolCallGroup";
7150
7155
 
7156
+ // src/front/chat/components/MessageMentions.tsx
7157
+ import { Children, cloneElement, isValidElement as isValidElement2 } from "react";
7158
+ import { Fragment as Fragment7, jsx as jsx22 } from "react/jsx-runtime";
7159
+ var TERMINAL_BOUNDARY = String.raw`(?=$|\s|[.,;!?)}\]"'”’]+(?=$|\s))`;
7160
+ var PREFIX_BOUNDARY = String.raw`(^|[\s(\[{"'“‘])`;
7161
+ function escapeRegExp2(value) {
7162
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7163
+ }
7164
+ function registeredNamePattern(names) {
7165
+ const safeNames = [...new Set(names)].filter((name) => /^\w[\w-]*$/.test(name)).sort((a, b) => b.length - a.length);
7166
+ return safeNames.length > 0 ? safeNames.map(escapeRegExp2).join("|") : void 0;
7167
+ }
7168
+ function addRegisteredCandidates(text, prefix, names, createMention, candidates) {
7169
+ const namesPattern = registeredNamePattern(names);
7170
+ if (!namesPattern || !text.includes(prefix)) return;
7171
+ const pattern = new RegExp(`${PREFIX_BOUNDARY}\\${prefix}(${namesPattern})${TERMINAL_BOUNDARY}`, "g");
7172
+ for (const match of text.matchAll(pattern)) {
7173
+ if (match.index === void 0) continue;
7174
+ const boundary = match[1] ?? "";
7175
+ const name = match[2];
7176
+ const mention = createMention(name);
7177
+ if (!mention) continue;
7178
+ const start = match.index + boundary.length;
7179
+ candidates.push({ start, end: start + name.length + 1, mention });
7180
+ }
7181
+ }
7182
+ function isSafeWorkspaceMentionPath(path) {
7183
+ if (!path || path.startsWith("/") || path.includes("\\") || path.includes("://")) return false;
7184
+ const segments = path.split("/");
7185
+ if (!segments.every((segment) => segment.length > 0 && segment !== "." && segment !== "..")) return false;
7186
+ const basename = segments.at(-1) ?? "";
7187
+ return basename.startsWith(".") || /\.[A-Za-z0-9]{1,12}$/.test(basename);
7188
+ }
7189
+ function addFileCandidates(text, candidates) {
7190
+ if (!text.includes("@")) return;
7191
+ const pattern = new RegExp(`${PREFIX_BOUNDARY}@([A-Za-z0-9_.-]+(?:/[A-Za-z0-9_.-]+)*)${TERMINAL_BOUNDARY}`, "g");
7192
+ for (const match of text.matchAll(pattern)) {
7193
+ if (match.index === void 0) continue;
7194
+ const boundary = match[1] ?? "";
7195
+ const path = match[2].replace(/[.,;!?]+$/, "");
7196
+ if (!isSafeWorkspaceMentionPath(path)) continue;
7197
+ const start = match.index + boundary.length;
7198
+ const label = `@${path}`;
7199
+ candidates.push({ start, end: start + label.length, mention: { kind: "file", path, label } });
7200
+ }
7201
+ }
7202
+ function collectMessageMentionCandidates(text, catalog) {
7203
+ const candidates = [];
7204
+ const commandsByName = new Map(catalog.commands.map((command) => [command.name, command]));
7205
+ addRegisteredCandidates(text, "/", [...commandsByName.keys()], (name) => {
7206
+ const command = commandsByName.get(name);
7207
+ return command ? { kind: "command", name, label: `/${name}`, behavior: command.clickBehavior } : void 0;
7208
+ }, candidates);
7209
+ const skillsByName = new Map(catalog.skills.map((skill) => [skill.name, skill]));
7210
+ addRegisteredCandidates(text, "!", [...skillsByName.keys()], (name) => {
7211
+ const skill = skillsByName.get(name);
7212
+ return skill ? { kind: "skill", name, commandName: skill.commandName, label: `!${name}` } : void 0;
7213
+ }, candidates);
7214
+ if (catalog.files) addFileCandidates(text, candidates);
7215
+ return candidates;
7216
+ }
7217
+ function splitMessageMentions(text, catalog, context = {}) {
7218
+ const before = context.before ?? "";
7219
+ const after = context.after ?? "";
7220
+ const source = `${before}${text}${after}`;
7221
+ const contentStart = before.length;
7222
+ const contentEnd = contentStart + text.length;
7223
+ const candidates = collectMessageMentionCandidates(source, catalog).filter((candidate) => candidate.start >= contentStart && candidate.end <= contentEnd).map((candidate) => ({ ...candidate, start: candidate.start - contentStart, end: candidate.end - contentStart })).sort((a, b) => a.start - b.start || b.end - a.end);
7224
+ const segments = [];
7225
+ let cursor = 0;
7226
+ for (const candidate of candidates) {
7227
+ if (candidate.start < cursor) continue;
7228
+ if (candidate.start > cursor) segments.push({ text: text.slice(cursor, candidate.start) });
7229
+ segments.push({ text: candidate.mention.label, mention: candidate.mention });
7230
+ cursor = candidate.end;
7231
+ }
7232
+ if (cursor < text.length) segments.push({ text: text.slice(cursor) });
7233
+ return segments.length > 0 ? segments : [{ text }];
7234
+ }
7235
+ function mentionActionLabel(mention) {
7236
+ if (mention.kind === "command") return mention.behavior === "execute" ? `Run ${mention.label} command` : `Insert ${mention.label} command`;
7237
+ if (mention.kind === "skill") return `Insert ${mention.label} skill`;
7238
+ return `Open ${mention.path}`;
7239
+ }
7240
+ function visibleText(node) {
7241
+ if (typeof node === "string" || typeof node === "number") return String(node);
7242
+ if (!isValidElement2(node)) return "";
7243
+ const element = node;
7244
+ return Children.toArray(element.props.children).map(visibleText).join("");
7245
+ }
7246
+ function MessageMentions({
7247
+ children,
7248
+ catalog,
7249
+ onActivate,
7250
+ before = "",
7251
+ after = ""
7252
+ }) {
7253
+ const nodes = Children.toArray(children);
7254
+ const boundaryText = (index, direction) => {
7255
+ const sibling = nodes[index + direction];
7256
+ if (sibling !== void 0) return visibleText(sibling);
7257
+ return direction === -1 ? before : after;
7258
+ };
7259
+ const decorate = (child, index) => {
7260
+ if (typeof child === "string") {
7261
+ return splitMessageMentions(child, catalog, {
7262
+ before: boundaryText(index, -1),
7263
+ after: boundaryText(index, 1)
7264
+ }).map((segment, segmentIndex) => {
7265
+ if (!segment.mention) return segment.text;
7266
+ const actionLabel = mentionActionLabel(segment.mention);
7267
+ return /* @__PURE__ */ jsx22(
7268
+ "button",
7269
+ {
7270
+ type: "button",
7271
+ "aria-label": actionLabel,
7272
+ title: actionLabel,
7273
+ "data-message-mention-kind": segment.mention.kind,
7274
+ className: cn(
7275
+ "mx-0.5 inline-flex items-center rounded-[var(--radius-sm)] border border-border/70 bg-muted/55 px-1.5 py-0.5",
7276
+ "font-mono text-[0.9em] font-medium leading-none text-foreground underline-offset-2",
7277
+ "hover:bg-muted hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1"
7278
+ ),
7279
+ onClick: () => onActivate(segment.mention),
7280
+ children: segment.text
7281
+ },
7282
+ `${index}:${segmentIndex}`
7283
+ );
7284
+ });
7285
+ }
7286
+ if (isValidElement2(child)) {
7287
+ const element = child;
7288
+ const tagName = element.props.node?.tagName;
7289
+ if (tagName === "strong" || tagName === "em" || tagName === "del" || element.type === "em" || element.type === "del") {
7290
+ return cloneElement(
7291
+ element,
7292
+ void 0,
7293
+ /* @__PURE__ */ jsx22(
7294
+ MessageMentions,
7295
+ {
7296
+ catalog,
7297
+ onActivate,
7298
+ before: boundaryText(index, -1),
7299
+ after: boundaryText(index, 1),
7300
+ children: element.props.children
7301
+ }
7302
+ )
7303
+ );
7304
+ }
7305
+ }
7306
+ return child;
7307
+ };
7308
+ return nodes.map(decorate);
7309
+ }
7310
+ function createMessageMentionMarkdownComponents(catalog, onActivate) {
7311
+ const decorate = (children) => /* @__PURE__ */ jsx22(MessageMentions, { catalog, onActivate, children });
7312
+ const paragraph = ({ node: _node, children, ...props }) => {
7313
+ const content = Children.toArray(children).filter((child) => child !== null && child !== "");
7314
+ if (content.length === 1 && isValidElement2(content[0])) {
7315
+ 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 });
7318
+ }
7319
+ return /* @__PURE__ */ jsx22("p", { ...props, children: decorate(children) });
7320
+ };
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 headingClasses = {
7325
+ h1: "text-3xl",
7326
+ h2: "text-2xl",
7327
+ h3: "text-xl",
7328
+ h4: "text-lg",
7329
+ h5: "text-base",
7330
+ h6: "text-sm"
7331
+ };
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(
7334
+ "code",
7335
+ {
7336
+ ...props,
7337
+ className: cn(INLINE_CODE_CLASS_NAME, className),
7338
+ children
7339
+ }
7340
+ );
7341
+ return {
7342
+ p: paragraph,
7343
+ th: tableHeader,
7344
+ td: tableCell,
7345
+ li: listItem,
7346
+ h1: heading("h1"),
7347
+ h2: heading("h2"),
7348
+ h3: heading("h3"),
7349
+ h4: heading("h4"),
7350
+ h5: heading("h5"),
7351
+ h6: heading("h6"),
7352
+ inlineCode
7353
+ };
7354
+ }
7355
+
7151
7356
  // src/front/chat/components/PiTimelineMessage.tsx
7152
- import { jsx as jsx22, jsxs as jsxs20 } from "react/jsx-runtime";
7357
+ import { jsx as jsx23, jsxs as jsxs20 } from "react/jsx-runtime";
7358
+ var EMPTY_MENTION_CATALOG = { commands: [], skills: [], files: false };
7153
7359
  var COLLAPSIBLE_TOOL_NAMES = /* @__PURE__ */ new Set([
7154
7360
  "read",
7155
7361
  "ls",
@@ -7163,7 +7369,7 @@ var COLLAPSIBLE_TOOL_NAMES = /* @__PURE__ */ new Set([
7163
7369
  function isCollapsibleTool(part) {
7164
7370
  return part.type === "tool-call" && COLLAPSIBLE_TOOL_NAMES.has(part.toolName);
7165
7371
  }
7166
- function PiTimelineMessage({ message, isLast, isStreaming, showThoughts, toolRenderers }) {
7372
+ function PiTimelineMessage({ message, isLast, isStreaming, showThoughts, toolRenderers, mentionCatalog = EMPTY_MENTION_CATALOG, onMentionActivate }) {
7167
7373
  const role = message.role;
7168
7374
  const isAssistant = role === "assistant";
7169
7375
  const textParts = message.parts.filter((part) => part.type === "text");
@@ -7171,6 +7377,31 @@ function PiTimelineMessage({ message, isLast, isStreaming, showThoughts, toolRen
7171
7377
  const finalParts = groupRenderableParts(message);
7172
7378
  const attachmentSummaryPaths = role === "user" ? attachmentPathsFromTextParts(textParts) : [];
7173
7379
  const openArtifact = useOpenArtifact();
7380
+ const effectiveMentionCatalog = useMemo10(() => ({
7381
+ commands: mentionCatalog.commands,
7382
+ skills: mentionCatalog.skills,
7383
+ files: mentionCatalog.files && Boolean(openArtifact)
7384
+ }), [mentionCatalog, openArtifact]);
7385
+ const mentionSignature = [
7386
+ ...effectiveMentionCatalog.commands.map((command) => `command:${command.name}:${command.clickBehavior}`),
7387
+ ...effectiveMentionCatalog.skills.map((skill) => `skill:${skill.name}:${skill.commandName}`),
7388
+ effectiveMentionCatalog.files ? "files:enabled" : "files:disabled"
7389
+ ].join("|");
7390
+ const mentionActivateRef = useRef10(onMentionActivate);
7391
+ mentionActivateRef.current = onMentionActivate;
7392
+ const activateCurrentMention = useCallback13((mention) => {
7393
+ if (mention.kind === "file") {
7394
+ openArtifact?.(mention.path);
7395
+ return;
7396
+ }
7397
+ mentionActivateRef.current?.(mention);
7398
+ }, [openArtifact]);
7399
+ const hasMentions = effectiveMentionCatalog.files || effectiveMentionCatalog.commands.length > 0 || effectiveMentionCatalog.skills.length > 0;
7400
+ const mentionsEnabled = Boolean(isAssistant && !(isStreaming && isLast) && hasMentions);
7401
+ const mentionMarkdownComponents = useMemo10(
7402
+ () => mentionsEnabled ? createMessageMentionMarkdownComponents(effectiveMentionCatalog, activateCurrentMention) : void 0,
7403
+ [activateCurrentMention, effectiveMentionCatalog, mentionSignature, mentionsEnabled]
7404
+ );
7174
7405
  const shouldReserveStreamingActions = isStreaming && isAssistant && isLast;
7175
7406
  return /* @__PURE__ */ jsxs20(
7176
7407
  Message,
@@ -7194,7 +7425,7 @@ function PiTimelineMessage({ message, isLast, isStreaming, showThoughts, toolRen
7194
7425
  ) : "!w-full !bg-transparent !p-0"
7195
7426
  ),
7196
7427
  children: [
7197
- fileParts.length > 0 ? /* @__PURE__ */ jsx22(
7428
+ fileParts.length > 0 ? /* @__PURE__ */ jsx23(
7198
7429
  Attachments,
7199
7430
  {
7200
7431
  variant: role === "user" ? "inline" : "list",
@@ -7233,8 +7464,8 @@ function PiTimelineMessage({ message, isLast, isStreaming, showThoughts, toolRen
7233
7464
  onKeyDown: openAttachmentFromKeyboard
7234
7465
  } : void 0,
7235
7466
  children: [
7236
- /* @__PURE__ */ jsx22(AttachmentPreview, { className: role === "user" ? "!size-5 shrink-0 rounded-[var(--radius-sm)]" : "size-10 shrink-0 rounded-[var(--radius-md)]" }),
7237
- /* @__PURE__ */ jsx22(AttachmentInfo, { className: role === "user" ? "min-w-0 max-w-[220px] flex-1 text-[12px]" : "min-w-0 flex-1" })
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" })
7238
7469
  ]
7239
7470
  },
7240
7471
  `file-${message.id}-${index}`
@@ -7244,7 +7475,7 @@ function PiTimelineMessage({ message, isLast, isStreaming, showThoughts, toolRen
7244
7475
  ) : null,
7245
7476
  finalParts.map((item) => {
7246
7477
  if (item.kind === "reasoning") {
7247
- return /* @__PURE__ */ jsx22(
7478
+ return /* @__PURE__ */ jsx23(
7248
7479
  TimelineReasoningPart,
7249
7480
  {
7250
7481
  item,
@@ -7254,17 +7485,18 @@ function PiTimelineMessage({ message, isLast, isStreaming, showThoughts, toolRen
7254
7485
  );
7255
7486
  }
7256
7487
  if (item.kind === "tool-group") {
7257
- return /* @__PURE__ */ jsx22("div", { "data-boring-agent-part": "message-tools", children: /* @__PURE__ */ jsx22(ToolCallGroup, { tools: item.tools, mergedToolRenderers: toolRenderers }) }, item.key);
7488
+ return /* @__PURE__ */ jsx23("div", { "data-boring-agent-part": "message-tools", children: /* @__PURE__ */ jsx23(ToolCallGroup, { tools: item.tools, mergedToolRenderers: toolRenderers }) }, item.key);
7258
7489
  }
7259
7490
  if (item.kind === "tool-plain") {
7260
- return /* @__PURE__ */ jsx22("div", { "data-boring-agent-part": "message-tools", children: /* @__PURE__ */ jsx22(PlainToolCard, { part: item.part, renderers: toolRenderers }) }, item.key);
7491
+ return /* @__PURE__ */ jsx23("div", { "data-boring-agent-part": "message-tools", children: /* @__PURE__ */ jsx23(PlainToolCard, { part: item.part, renderers: toolRenderers }) }, item.key);
7261
7492
  }
7262
7493
  if (item.part.type === "text") {
7263
7494
  const text = textForMessageDisplay(item.part.text, role);
7264
7495
  if (!text) return null;
7265
- return /* @__PURE__ */ jsx22("div", { "data-boring-agent-part": "message-text", children: /* @__PURE__ */ jsx22(
7496
+ return /* @__PURE__ */ jsx23("div", { "data-boring-agent-part": "message-text", children: /* @__PURE__ */ jsx23(
7266
7497
  MessageResponse,
7267
7498
  {
7499
+ components: mentionMarkdownComponents,
7268
7500
  className: cn(
7269
7501
  "max-w-none",
7270
7502
  "prose prose-invert prose-neutral",
@@ -7280,18 +7512,19 @@ function PiTimelineMessage({ message, isLast, isStreaming, showThoughts, toolRen
7280
7512
  "prose-pre:bg-transparent prose-pre:p-0"
7281
7513
  ),
7282
7514
  children: text
7283
- }
7515
+ },
7516
+ `${mentionSignature || "no-message-mentions"}:${mentionsEnabled ? "enabled" : "static"}`
7284
7517
  ) }, item.key);
7285
7518
  }
7286
7519
  if (item.part.type === "notice") {
7287
- return /* @__PURE__ */ jsx22(NoticeBubble, { level: item.part.level, text: item.part.text }, item.key);
7520
+ return /* @__PURE__ */ jsx23(NoticeBubble, { level: item.part.level, text: item.part.text }, item.key);
7288
7521
  }
7289
7522
  return null;
7290
7523
  })
7291
7524
  ]
7292
7525
  }
7293
7526
  ),
7294
- isAssistant && (textParts.length > 0 || shouldReserveStreamingActions) ? /* @__PURE__ */ jsx22(
7527
+ isAssistant && (textParts.length > 0 || shouldReserveStreamingActions) ? /* @__PURE__ */ jsx23(
7295
7528
  MessageActionsBar,
7296
7529
  {
7297
7530
  text: textParts.map((part) => part.text).join("\n\n"),
@@ -7326,15 +7559,15 @@ function TimelineReasoningPart({ item, showThoughts }) {
7326
7559
  onOpenChange: handleOpenChange,
7327
7560
  autoClose: false,
7328
7561
  children: [
7329
- /* @__PURE__ */ jsx22(
7562
+ /* @__PURE__ */ jsx23(
7330
7563
  ReasoningTrigger,
7331
7564
  {
7332
7565
  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",
7333
- getThinkingMessage: (streaming) => /* @__PURE__ */ jsx22("span", { children: streaming ? "thinking" : "thoughts" }),
7566
+ getThinkingMessage: (streaming) => /* @__PURE__ */ jsx23("span", { children: streaming ? "thinking" : "thoughts" }),
7334
7567
  onClick: toggleManualOpen
7335
7568
  }
7336
7569
  ),
7337
- /* @__PURE__ */ jsx22(ReasoningContent, { children: item.text })
7570
+ /* @__PURE__ */ jsx23(ReasoningContent, { children: item.text })
7338
7571
  ]
7339
7572
  }
7340
7573
  );
@@ -7343,7 +7576,7 @@ function PlainToolCard({ part, renderers }) {
7343
7576
  const toolPart = toToolPart(part);
7344
7577
  if (!toolPart) return null;
7345
7578
  const { renderer, part: resolved, resolution } = resolveToolRendererForPart(toolPart, renderers);
7346
- return /* @__PURE__ */ jsx22(
7579
+ return /* @__PURE__ */ jsx23(
7347
7580
  "div",
7348
7581
  {
7349
7582
  "data-tool-call-id": resolved.toolCallId,
@@ -7392,13 +7625,13 @@ ${part.text}`;
7392
7625
  return grouped;
7393
7626
  }
7394
7627
  function NoticeBubble({ level, text }) {
7395
- return /* @__PURE__ */ jsx22(
7628
+ return /* @__PURE__ */ jsx23(
7396
7629
  "div",
7397
7630
  {
7398
7631
  "data-boring-agent-part": "message-notice",
7399
7632
  "data-notice-level": level,
7400
7633
  className: noticeSurfaceClass(level, "text-xs"),
7401
- children: /* @__PURE__ */ jsx22("div", { className: noticeTextClass(), children: text })
7634
+ children: /* @__PURE__ */ jsx23("div", { className: noticeTextClass(), children: text })
7402
7635
  }
7403
7636
  );
7404
7637
  }
@@ -7477,7 +7710,7 @@ function MessageActionsBar({
7477
7710
  "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[color:var(--accent)]/40"
7478
7711
  );
7479
7712
  const hiddenActionProps = visible ? {} : { tabIndex: -1 };
7480
- return /* @__PURE__ */ jsx22(
7713
+ return /* @__PURE__ */ jsx23(
7481
7714
  "div",
7482
7715
  {
7483
7716
  "aria-hidden": !visible,
@@ -7485,7 +7718,7 @@ function MessageActionsBar({
7485
7718
  "flex min-h-6 items-center gap-0.5 -mt-1 transition-opacity duration-200",
7486
7719
  visible ? "opacity-100" : "pointer-events-none opacity-0"
7487
7720
  ),
7488
- children: /* @__PURE__ */ jsx22(
7721
+ children: /* @__PURE__ */ jsx23(
7489
7722
  Button11,
7490
7723
  {
7491
7724
  type: "button",
@@ -7496,7 +7729,7 @@ function MessageActionsBar({
7496
7729
  "aria-label": copied ? "Copied" : "Copy message",
7497
7730
  title: copied ? "Copied" : "Copy",
7498
7731
  ...hiddenActionProps,
7499
- children: copied ? /* @__PURE__ */ jsx22(CheckIcon2, { className: "h-3.5 w-3.5 text-[color:var(--accent)]" }) : /* @__PURE__ */ jsx22(CopyIcon3, { className: "h-3.5 w-3.5" })
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" })
7500
7733
  }
7501
7734
  )
7502
7735
  }
@@ -7517,7 +7750,7 @@ function partKey(messageId, part, index) {
7517
7750
  }
7518
7751
 
7519
7752
  // src/front/chat/components/PiConversationSurface.tsx
7520
- import { jsx as jsx23, jsxs as jsxs21 } from "react/jsx-runtime";
7753
+ import { jsx as jsx24, jsxs as jsxs21 } from "react/jsx-runtime";
7521
7754
  var TRANSCRIPT_WINDOW = 60;
7522
7755
  var TRANSCRIPT_WINDOW_STEP = 40;
7523
7756
  var LOAD_OLDER_THRESHOLD_PX = 320;
@@ -7531,6 +7764,8 @@ function PiConversationSurface({
7531
7764
  isStreaming,
7532
7765
  showThoughts,
7533
7766
  toolRenderers,
7767
+ mentionCatalog,
7768
+ onMentionActivate,
7534
7769
  runtimeNotices,
7535
7770
  onDismissNotice,
7536
7771
  renderNoticeAction,
@@ -7565,10 +7800,10 @@ function PiConversationSurface({
7565
7800
  emptyHero && "py-4 text-center"
7566
7801
  ), children: [
7567
7802
  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: [
7568
- /* @__PURE__ */ jsx23(Loader22, { className: "h-4 w-4 animate-spin" }),
7803
+ /* @__PURE__ */ jsx24(Loader22, { className: "h-4 w-4 animate-spin" }),
7569
7804
  "Loading chat history\u2026"
7570
7805
  ] }) : null,
7571
- messages.length === 0 && !emptyStateHydrating ? /* @__PURE__ */ jsx23(
7806
+ messages.length === 0 && !emptyStateHydrating ? /* @__PURE__ */ jsx24(
7572
7807
  ChatEmptyState,
7573
7808
  {
7574
7809
  eyebrow: emptyState?.eyebrow,
@@ -7586,29 +7821,31 @@ function PiConversationSurface({
7586
7821
  }
7587
7822
  }
7588
7823
  ) : null,
7589
- hasOlder ? /* @__PURE__ */ jsx23(TranscriptHistoryLoader, { olderCount, onLoadOlder: loadOlder }) : null,
7590
- visibleItems.map(({ message, key }, index) => /* @__PURE__ */ jsx23(
7824
+ hasOlder ? /* @__PURE__ */ jsx24(TranscriptHistoryLoader, { olderCount, onLoadOlder: loadOlder }) : null,
7825
+ visibleItems.map(({ message, key }, index) => /* @__PURE__ */ jsx24(
7591
7826
  PiTimelineMessage,
7592
7827
  {
7593
7828
  message,
7594
7829
  isLast: index === visibleItems.length - 1,
7595
7830
  isStreaming,
7596
7831
  showThoughts,
7597
- toolRenderers
7832
+ toolRenderers,
7833
+ mentionCatalog,
7834
+ onMentionActivate
7598
7835
  },
7599
7836
  key
7600
7837
  )),
7601
- /* @__PURE__ */ jsx23(RuntimeNoticeMessages, { notices: runtimeNotices, onDismiss: onDismissNotice, renderAction: renderNoticeAction })
7838
+ /* @__PURE__ */ jsx24(RuntimeNoticeMessages, { notices: runtimeNotices, onDismiss: onDismissNotice, renderAction: renderNoticeAction })
7602
7839
  ] }),
7603
- /* @__PURE__ */ jsx23(ConversationScrollButton, {})
7840
+ /* @__PURE__ */ jsx24(ConversationScrollButton, {})
7604
7841
  ]
7605
7842
  }
7606
7843
  );
7607
7844
  }
7608
7845
  function TranscriptHistoryLoader({ olderCount, onLoadOlder }) {
7609
7846
  const { scrollRef } = useStickToBottomContext2();
7610
- const pendingAnchor = useRef10(null);
7611
- const armed = useRef10(true);
7847
+ const pendingAnchor = useRef11(null);
7848
+ const armed = useRef11(true);
7612
7849
  useEffect14(() => {
7613
7850
  const el = scrollRef.current;
7614
7851
  if (!el) return;
@@ -7632,7 +7869,7 @@ function TranscriptHistoryLoader({ olderCount, onLoadOlder }) {
7632
7869
  }
7633
7870
  if (el.scrollTop > LOAD_OLDER_THRESHOLD_PX) armed.current = true;
7634
7871
  });
7635
- return /* @__PURE__ */ jsx23("div", { className: "flex justify-center", "data-boring-agent-part": "transcript-load-older", children: /* @__PURE__ */ jsxs21(
7872
+ return /* @__PURE__ */ jsx24("div", { className: "flex justify-center", "data-boring-agent-part": "transcript-load-older", children: /* @__PURE__ */ jsxs21(
7636
7873
  "button",
7637
7874
  {
7638
7875
  type: "button",
@@ -7673,10 +7910,10 @@ import { useCallback as useCallback16, useLayoutEffect as useLayoutEffect2 } fro
7673
7910
  import { motion as motion2 } from "motion/react";
7674
7911
 
7675
7912
  // src/front/composer/PluginUpdateStatus.tsx
7676
- import { useEffect as useEffect15, useRef as useRef11 } from "react";
7913
+ import { useEffect as useEffect15, useRef as useRef12 } from "react";
7677
7914
 
7678
7915
  // src/front/composer/ComposerStatusBanner.tsx
7679
- import { jsx as jsx24, jsxs as jsxs22 } from "react/jsx-runtime";
7916
+ import { jsx as jsx25, jsxs as jsxs22 } from "react/jsx-runtime";
7680
7917
  function ComposerStatusBanner({
7681
7918
  tone,
7682
7919
  dataAttribute,
@@ -7705,8 +7942,8 @@ function ComposerStatusBanner({
7705
7942
  maxWidthClassName
7706
7943
  ),
7707
7944
  children: [
7708
- /* @__PURE__ */ jsx24("span", { className: "inline-block h-2 w-2 animate-pulse rounded-full bg-accent", "aria-hidden": "true" }),
7709
- /* @__PURE__ */ jsx24("span", { children: runningContent })
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 })
7710
7947
  ]
7711
7948
  }
7712
7949
  );
@@ -7725,12 +7962,12 @@ function ComposerStatusBanner({
7725
7962
  ),
7726
7963
  children: [
7727
7964
  /* @__PURE__ */ jsxs22("div", { className: "flex items-start gap-2", children: [
7728
- /* @__PURE__ */ jsx24("span", { className: "mt-0.5 text-[oklch(0.45_0.13_148)]", "aria-hidden": "true", children: "\u2713" }),
7965
+ /* @__PURE__ */ jsx25("span", { className: "mt-0.5 text-[oklch(0.45_0.13_148)]", "aria-hidden": "true", children: "\u2713" }),
7729
7966
  /* @__PURE__ */ jsxs22("span", { className: "min-w-0 flex-1", children: [
7730
- /* @__PURE__ */ jsx24("span", { className: "block font-medium leading-5", children: title }),
7731
- detail ? /* @__PURE__ */ jsx24("span", { className: "block leading-4 text-muted-foreground", children: detail }) : null
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
7732
7969
  ] }),
7733
- onDismiss ? /* @__PURE__ */ jsx24(
7970
+ onDismiss ? /* @__PURE__ */ jsx25(
7734
7971
  "button",
7735
7972
  {
7736
7973
  type: "button",
@@ -7759,9 +7996,9 @@ function ComposerStatusBanner({
7759
7996
  ),
7760
7997
  children: [
7761
7998
  /* @__PURE__ */ jsxs22("div", { className: "flex items-center gap-2", children: [
7762
- /* @__PURE__ */ jsx24("span", { className: "text-destructive", "aria-hidden": "true", children: "\u26A0" }),
7763
- /* @__PURE__ */ jsx24("span", { className: "flex-1 font-medium", children: title }),
7764
- onRetry ? /* @__PURE__ */ jsx24(
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(
7765
8002
  "button",
7766
8003
  {
7767
8004
  type: "button",
@@ -7770,7 +8007,7 @@ function ComposerStatusBanner({
7770
8007
  children: retryLabel
7771
8008
  }
7772
8009
  ) : null,
7773
- onDismiss ? /* @__PURE__ */ jsx24(
8010
+ onDismiss ? /* @__PURE__ */ jsx25(
7774
8011
  "button",
7775
8012
  {
7776
8013
  type: "button",
@@ -7781,14 +8018,14 @@ function ComposerStatusBanner({
7781
8018
  }
7782
8019
  ) : null
7783
8020
  ] }),
7784
- message ? /* @__PURE__ */ jsx24("pre", { className: "mt-2 whitespace-pre-wrap break-words text-[11px] text-destructive/90", children: message }) : null
8021
+ message ? /* @__PURE__ */ jsx25("pre", { className: "mt-2 whitespace-pre-wrap break-words text-[11px] text-destructive/90", children: message }) : null
7785
8022
  ]
7786
8023
  }
7787
8024
  );
7788
8025
  }
7789
8026
 
7790
8027
  // src/front/composer/PluginUpdateStatus.tsx
7791
- import { jsx as jsx25, jsxs as jsxs23 } from "react/jsx-runtime";
8028
+ import { jsx as jsx26, jsxs as jsxs23 } from "react/jsx-runtime";
7792
8029
  function PluginUpdateStatus({
7793
8030
  state,
7794
8031
  onDismiss,
@@ -7796,7 +8033,7 @@ function PluginUpdateStatus({
7796
8033
  successAutoDismissMs = 1400,
7797
8034
  maxWidthClassName = "max-w-3xl"
7798
8035
  }) {
7799
- const onDismissRef = useRef11(onDismiss);
8036
+ const onDismissRef = useRef12(onDismiss);
7800
8037
  useEffect15(() => {
7801
8038
  onDismissRef.current = onDismiss;
7802
8039
  }, [onDismiss]);
@@ -7810,7 +8047,7 @@ function PluginUpdateStatus({
7810
8047
  }, [state?.kind, successDismissKey, successAutoDismissMs]);
7811
8048
  if (!state) return null;
7812
8049
  if (state.kind === "running") {
7813
- return /* @__PURE__ */ jsx25(
8050
+ return /* @__PURE__ */ jsx26(
7814
8051
  ComposerStatusBanner,
7815
8052
  {
7816
8053
  tone: "running",
@@ -7848,7 +8085,7 @@ function PluginUpdateStatus({
7848
8085
  ),
7849
8086
  children: [
7850
8087
  /* @__PURE__ */ jsxs23("div", { className: "flex items-center gap-1.5 font-medium text-[oklch(0.48_0.15_60)]", children: [
7851
- /* @__PURE__ */ jsx25("span", { "aria-hidden": "true", children: "\u26A0" }),
8088
+ /* @__PURE__ */ jsx26("span", { "aria-hidden": "true", children: "\u26A0" }),
7852
8089
  /* @__PURE__ */ jsxs23("span", { children: [
7853
8090
  "Reload diagnostics for ",
7854
8091
  diagnostics.length,
@@ -7856,8 +8093,8 @@ function PluginUpdateStatus({
7856
8093
  diagnostics.length === 1 ? "" : "s"
7857
8094
  ] })
7858
8095
  ] }),
7859
- /* @__PURE__ */ jsx25("ul", { className: "mt-1 ml-4 list-disc text-foreground/85", children: diagnostics.map((diagnostic, index) => /* @__PURE__ */ jsxs23("li", { children: [
7860
- /* @__PURE__ */ jsx25("code", { className: "font-mono text-[10.5px]", children: diagnostic.pluginId ?? diagnostic.source ?? "plugin" }),
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" }),
7861
8098
  " \u2014 ",
7862
8099
  diagnostic.message ?? "reload diagnostic"
7863
8100
  ] }, `${diagnostic.pluginId ?? diagnostic.source ?? "plugin"}-${index}`)) })
@@ -7874,7 +8111,7 @@ function PluginUpdateStatus({
7874
8111
  ),
7875
8112
  children: [
7876
8113
  /* @__PURE__ */ jsxs23("div", { className: "flex items-center gap-1.5 font-medium text-[oklch(0.48_0.15_60)]", children: [
7877
- /* @__PURE__ */ jsx25("span", { "aria-hidden": "true", children: "\u26A0" }),
8114
+ /* @__PURE__ */ jsx26("span", { "aria-hidden": "true", children: "\u26A0" }),
7878
8115
  /* @__PURE__ */ jsxs23("span", { children: [
7879
8116
  "Restart needed for ",
7880
8117
  warnings.length,
@@ -7882,12 +8119,12 @@ function PluginUpdateStatus({
7882
8119
  warnings.length === 1 ? "" : "s"
7883
8120
  ] })
7884
8121
  ] }),
7885
- /* @__PURE__ */ jsx25("ul", { className: "mt-1 ml-4 list-disc text-foreground/85", children: warnings.map((w) => /* @__PURE__ */ jsxs23("li", { children: [
7886
- /* @__PURE__ */ jsx25("code", { className: "font-mono text-[10.5px]", children: w.id }),
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 }),
7887
8124
  " \u2014 ",
7888
8125
  w.surfaces.join(" + ")
7889
8126
  ] }, w.id)) }),
7890
- /* @__PURE__ */ jsx25("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." })
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." })
7891
8128
  ]
7892
8129
  }
7893
8130
  ) : null
@@ -7895,7 +8132,7 @@ function PluginUpdateStatus({
7895
8132
  }
7896
8133
  );
7897
8134
  }
7898
- return /* @__PURE__ */ jsx25(
8135
+ return /* @__PURE__ */ jsx26(
7899
8136
  ComposerStatusBanner,
7900
8137
  {
7901
8138
  tone: "error",
@@ -7912,15 +8149,15 @@ function PluginUpdateStatus({
7912
8149
  }
7913
8150
 
7914
8151
  // src/front/composer/CommandRunStatus.tsx
7915
- import { useEffect as useEffect16, useRef as useRef12 } from "react";
7916
- import { Fragment as Fragment7, jsx as jsx26, jsxs as jsxs24 } from "react/jsx-runtime";
8152
+ import { useEffect as useEffect16, useRef as useRef13 } from "react";
8153
+ import { Fragment as Fragment8, jsx as jsx27, jsxs as jsxs24 } from "react/jsx-runtime";
7917
8154
  function CommandRunStatus({
7918
8155
  state,
7919
8156
  onDismiss,
7920
8157
  successAutoDismissMs = 1400,
7921
8158
  maxWidthClassName = "max-w-3xl"
7922
8159
  }) {
7923
- const onDismissRef = useRef12(onDismiss);
8160
+ const onDismissRef = useRef13(onDismiss);
7924
8161
  useEffect16(() => {
7925
8162
  onDismissRef.current = onDismiss;
7926
8163
  }, [onDismiss]);
@@ -7932,13 +8169,13 @@ function CommandRunStatus({
7932
8169
  }, [state?.kind, successKey, successAutoDismissMs]);
7933
8170
  if (!state) return null;
7934
8171
  if (state.kind === "running") {
7935
- return /* @__PURE__ */ jsx26(
8172
+ return /* @__PURE__ */ jsx27(
7936
8173
  ComposerStatusBanner,
7937
8174
  {
7938
8175
  tone: "running",
7939
8176
  dataAttribute: "data-boring-command-run",
7940
8177
  maxWidthClassName,
7941
- runningContent: state.command ? /* @__PURE__ */ jsxs24(Fragment7, { children: [
8178
+ runningContent: state.command ? /* @__PURE__ */ jsxs24(Fragment8, { children: [
7942
8179
  " Running ",
7943
8180
  /* @__PURE__ */ jsxs24("code", { className: "font-mono", children: [
7944
8181
  "/",
@@ -7950,13 +8187,13 @@ function CommandRunStatus({
7950
8187
  );
7951
8188
  }
7952
8189
  if (state.kind === "success") {
7953
- return /* @__PURE__ */ jsx26(
8190
+ return /* @__PURE__ */ jsx27(
7954
8191
  ComposerStatusBanner,
7955
8192
  {
7956
8193
  tone: "success",
7957
8194
  dataAttribute: "data-boring-command-run",
7958
8195
  maxWidthClassName,
7959
- title: state.command ? /* @__PURE__ */ jsxs24(Fragment7, { children: [
8196
+ title: state.command ? /* @__PURE__ */ jsxs24(Fragment8, { children: [
7960
8197
  " Ran ",
7961
8198
  /* @__PURE__ */ jsxs24("code", { className: "font-mono", children: [
7962
8199
  "/",
@@ -7970,13 +8207,13 @@ function CommandRunStatus({
7970
8207
  }
7971
8208
  );
7972
8209
  }
7973
- return /* @__PURE__ */ jsx26(
8210
+ return /* @__PURE__ */ jsx27(
7974
8211
  ComposerStatusBanner,
7975
8212
  {
7976
8213
  tone: "error",
7977
8214
  dataAttribute: "data-boring-command-run",
7978
8215
  maxWidthClassName,
7979
- title: state.command ? /* @__PURE__ */ jsxs24(Fragment7, { children: [
8216
+ title: state.command ? /* @__PURE__ */ jsxs24(Fragment8, { children: [
7980
8217
  " ",
7981
8218
  /* @__PURE__ */ jsxs24("code", { className: "font-mono", children: [
7982
8219
  "/",
@@ -7992,7 +8229,7 @@ function CommandRunStatus({
7992
8229
  }
7993
8230
 
7994
8231
  // src/front/chatPanelComposerControls.tsx
7995
- import { forwardRef, useEffect as useEffect17, useRef as useRef13, useState as useState16 } from "react";
8232
+ import { forwardRef, useEffect as useEffect17, useRef as useRef14, useState as useState16 } from "react";
7996
8233
  import { CheckIcon as CheckIcon3, ChevronDownIcon as ChevronDownIcon5 } from "lucide-react";
7997
8234
  import {
7998
8235
  Command,
@@ -8026,7 +8263,7 @@ function displayProviderLabel(provider) {
8026
8263
  }
8027
8264
 
8028
8265
  // src/front/chatPanelComposerControls.tsx
8029
- import { Fragment as Fragment8, jsx as jsx27, jsxs as jsxs25 } from "react/jsx-runtime";
8266
+ import { Fragment as Fragment9, jsx as jsx28, jsxs as jsxs25 } from "react/jsx-runtime";
8030
8267
  var composerActionClass = cn(
8031
8268
  "inline-flex h-8 items-center justify-center gap-1.5 rounded-[var(--radius-md)] border-0 bg-transparent",
8032
8269
  "text-muted-foreground shadow-none transition",
@@ -8094,7 +8331,7 @@ function useDismissOnOutsidePointer(ref, onClose) {
8094
8331
  }
8095
8332
  function ThinkingLevelGlyph({ level }) {
8096
8333
  const lit = level === "off" ? 0 : level === "low" ? 1 : level === "medium" ? 2 : 3;
8097
- return /* @__PURE__ */ jsx27(
8334
+ return /* @__PURE__ */ jsx28(
8098
8335
  "svg",
8099
8336
  {
8100
8337
  "aria-hidden": "true",
@@ -8103,7 +8340,7 @@ function ThinkingLevelGlyph({ level }) {
8103
8340
  viewBox: "0 0 14 14",
8104
8341
  fill: "none",
8105
8342
  className: "shrink-0",
8106
- children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx27(
8343
+ children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx28(
8107
8344
  "rect",
8108
8345
  {
8109
8346
  x: 2 + i * 4,
@@ -8149,7 +8386,7 @@ var ModelSelectTrigger = forwardRef(function ModelSelectTrigger2({
8149
8386
  }, ref) {
8150
8387
  const triggerLabel = modelTriggerLabel(value, options, emptyLabel);
8151
8388
  const triggerDisplay = value ? `${triggerLabel} (${displayProviderLabel(value.provider)})` : triggerLabel;
8152
- return /* @__PURE__ */ jsx27(
8389
+ return /* @__PURE__ */ jsx28(
8153
8390
  "button",
8154
8391
  {
8155
8392
  ref,
@@ -8167,12 +8404,12 @@ var ModelSelectTrigger = forwardRef(function ModelSelectTrigger2({
8167
8404
  className
8168
8405
  ),
8169
8406
  ...props,
8170
- children: trigger === "slash" ? /* @__PURE__ */ jsxs25(Fragment8, { children: [
8171
- /* @__PURE__ */ jsx27("span", { className: "text-muted-foreground", children: "/model: " }),
8172
- /* @__PURE__ */ jsx27("span", { className: "text-foreground", children: triggerDisplay })
8173
- ] }) : /* @__PURE__ */ jsxs25(Fragment8, { children: [
8174
- /* @__PURE__ */ jsx27("span", { className: "min-w-0 truncate", children: triggerDisplay }),
8175
- /* @__PURE__ */ jsx27(ChevronDownIcon5, { className: "h-3 w-3 shrink-0 text-muted-foreground/50", "aria-hidden": "true" })
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" })
8176
8413
  ] })
8177
8414
  }
8178
8415
  );
@@ -8193,8 +8430,8 @@ function ModelPickerMenu({
8193
8430
  const keyboardOptions = hideDefaultOption ? groupedOptions : [null, ...groupedOptions];
8194
8431
  const selectedIndex = currentKey ? Math.max(0, keyboardOptions.findIndex((option) => option && encodeModelKey(option) === currentKey)) : 0;
8195
8432
  const [activeIndex, setActiveIndex] = useState16(selectedIndex);
8196
- const activeIndexRef = useRef13(selectedIndex);
8197
- const menuRef = useRef13(null);
8433
+ const activeIndexRef = useRef14(selectedIndex);
8434
+ const menuRef = useRef14(null);
8198
8435
  useDismissOnOutsidePointer(menuRef, onClose);
8199
8436
  const setKeyboardActiveIndex = (next) => {
8200
8437
  const resolved = typeof next === "function" ? next(activeIndexRef.current) : next;
@@ -8229,8 +8466,8 @@ function ModelPickerMenu({
8229
8466
  }, [activeIndex, disabled, keyboardOptions, onChange, onClose]);
8230
8467
  const optionIndexOffset = hideDefaultOption ? 0 : 1;
8231
8468
  const optionIndexes = new Map(groupedOptions.map((option, index) => [encodeModelKey(option), index + optionIndexOffset]));
8232
- return /* @__PURE__ */ jsx27("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: [
8233
- menuOptions.length > 8 && /* @__PURE__ */ jsx27("div", { className: "border-b border-border/60 px-2", children: /* @__PURE__ */ jsx27(
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(
8234
8471
  CommandInput,
8235
8472
  {
8236
8473
  placeholder: "Search models\u2026",
@@ -8239,8 +8476,8 @@ function ModelPickerMenu({
8239
8476
  }
8240
8477
  ) }),
8241
8478
  /* @__PURE__ */ jsxs25(CommandList, { className: "max-h-[300px] p-0.5", children: [
8242
- /* @__PURE__ */ jsx27(CommandEmpty, { className: "py-4 text-center text-[13px] text-muted-foreground", children: "No models found" }),
8243
- !hideDefaultOption ? /* @__PURE__ */ jsx27(CommandGroup, { children: /* @__PURE__ */ jsxs25(
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(
8244
8481
  CommandItem,
8245
8482
  {
8246
8483
  value: "Default model automatic auto",
@@ -8251,7 +8488,7 @@ function ModelPickerMenu({
8251
8488
  },
8252
8489
  className: selectorItemClass(!value || activeIndex === 0),
8253
8490
  children: [
8254
- /* @__PURE__ */ jsx27(
8491
+ /* @__PURE__ */ jsx28(
8255
8492
  CheckIcon3,
8256
8493
  {
8257
8494
  className: cn(
@@ -8260,12 +8497,12 @@ function ModelPickerMenu({
8260
8497
  )
8261
8498
  }
8262
8499
  ),
8263
- /* @__PURE__ */ jsx27("span", { className: "truncate", children: "Default model" }),
8264
- /* @__PURE__ */ jsx27("span", { className: "ml-auto shrink-0 text-[10px] text-muted-foreground/60", children: "auto" })
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" })
8265
8502
  ]
8266
8503
  }
8267
8504
  ) }) : null,
8268
- [...groups.entries()].map(([provider, list]) => /* @__PURE__ */ jsx27(
8505
+ [...groups.entries()].map(([provider, list]) => /* @__PURE__ */ jsx28(
8269
8506
  CommandGroup,
8270
8507
  {
8271
8508
  heading: displayProviderLabel(provider),
@@ -8285,7 +8522,7 @@ function ModelPickerMenu({
8285
8522
  },
8286
8523
  className: cn(selectorItemClass(key === currentKey || activeIndex === itemIndex)),
8287
8524
  children: [
8288
- /* @__PURE__ */ jsx27(
8525
+ /* @__PURE__ */ jsx28(
8289
8526
  CheckIcon3,
8290
8527
  {
8291
8528
  className: cn(
@@ -8294,8 +8531,8 @@ function ModelPickerMenu({
8294
8531
  )
8295
8532
  }
8296
8533
  ),
8297
- /* @__PURE__ */ jsx27("span", { className: "min-w-0 flex-1 truncate whitespace-nowrap", children: label }),
8298
- /* @__PURE__ */ jsx27("span", { className: "ml-auto max-w-[45%] shrink-0 truncate whitespace-nowrap text-right text-[10px] text-muted-foreground/60", children: m.id })
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 })
8299
8536
  ]
8300
8537
  },
8301
8538
  key
@@ -8321,7 +8558,7 @@ function ModelSelect({
8321
8558
  hideDefaultOption = false
8322
8559
  }) {
8323
8560
  const [open, setOpen] = useState16(false);
8324
- const previousOpenSignalRef = useRef13(openSignal);
8561
+ const previousOpenSignalRef = useRef14(openSignal);
8325
8562
  useEffect17(() => {
8326
8563
  if (disabled) setOpen(false);
8327
8564
  }, [disabled]);
@@ -8334,7 +8571,7 @@ function ModelSelect({
8334
8571
  previousOpenSignalRef.current = openSignal;
8335
8572
  }, [disabled, openSignal]);
8336
8573
  return /* @__PURE__ */ jsxs25(Popover, { open, onOpenChange: disabled ? void 0 : setOpen, children: [
8337
- /* @__PURE__ */ jsx27(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx27(
8574
+ /* @__PURE__ */ jsx28(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx28(
8338
8575
  ModelSelectTrigger,
8339
8576
  {
8340
8577
  value,
@@ -8348,7 +8585,7 @@ function ModelSelect({
8348
8585
  "aria-describedby": ariaDescribedBy
8349
8586
  }
8350
8587
  ) }),
8351
- /* @__PURE__ */ jsx27(
8588
+ /* @__PURE__ */ jsx28(
8352
8589
  PopoverContent,
8353
8590
  {
8354
8591
  align: trigger === "slash" ? "center" : "start",
@@ -8357,7 +8594,7 @@ function ModelSelect({
8357
8594
  collisionPadding: 12,
8358
8595
  "data-boring-agent": "",
8359
8596
  className: cn("w-[min(92vw,340px)]", selectorContentClass),
8360
- children: /* @__PURE__ */ jsx27(
8597
+ children: /* @__PURE__ */ jsx28(
8361
8598
  ModelPickerMenu,
8362
8599
  {
8363
8600
  value,
@@ -8382,7 +8619,7 @@ function ThinkingSelect({
8382
8619
  className
8383
8620
  }) {
8384
8621
  const [open, setOpen] = useState16(false);
8385
- const previousOpenSignalRef = useRef13(openSignal);
8622
+ const previousOpenSignalRef = useRef14(openSignal);
8386
8623
  useEffect17(() => {
8387
8624
  if (disabled) setOpen(false);
8388
8625
  }, [disabled]);
@@ -8395,7 +8632,7 @@ function ThinkingSelect({
8395
8632
  previousOpenSignalRef.current = openSignal;
8396
8633
  }, [disabled, openSignal]);
8397
8634
  return /* @__PURE__ */ jsxs25(Popover, { open, onOpenChange: disabled ? void 0 : setOpen, children: [
8398
- /* @__PURE__ */ jsx27(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx27(
8635
+ /* @__PURE__ */ jsx28(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx28(
8399
8636
  ThinkingSelectTrigger,
8400
8637
  {
8401
8638
  value,
@@ -8405,7 +8642,7 @@ function ThinkingSelect({
8405
8642
  className
8406
8643
  }
8407
8644
  ) }),
8408
- /* @__PURE__ */ jsx27(
8645
+ /* @__PURE__ */ jsx28(
8409
8646
  PopoverContent,
8410
8647
  {
8411
8648
  align: trigger === "slash" ? "center" : "end",
@@ -8414,7 +8651,7 @@ function ThinkingSelect({
8414
8651
  collisionPadding: 12,
8415
8652
  "data-boring-agent": "",
8416
8653
  className: cn("w-[min(92vw,240px)]", selectorContentClass),
8417
- children: /* @__PURE__ */ jsx27(
8654
+ children: /* @__PURE__ */ jsx28(
8418
8655
  ThinkingPickerMenu,
8419
8656
  {
8420
8657
  value,
@@ -8458,13 +8695,13 @@ var ThinkingSelectTrigger = forwardRef(function ThinkingSelectTrigger2({
8458
8695
  onClick,
8459
8696
  ...props,
8460
8697
  children: [
8461
- trigger === "button" ? /* @__PURE__ */ jsx27(ThinkingLevelGlyph, { level: value }) : null,
8462
- trigger === "slash" ? /* @__PURE__ */ jsxs25(Fragment8, { children: [
8463
- /* @__PURE__ */ jsx27("span", { className: "text-muted-foreground", children: "/thinking: " }),
8464
- /* @__PURE__ */ jsx27("span", { className: "text-foreground", children: THINKING_LEVEL_STATUS_LABELS[value] })
8465
- ] }) : /* @__PURE__ */ jsxs25(Fragment8, { children: [
8466
- /* @__PURE__ */ jsx27("span", { children: THINKING_LEVEL_LABELS[value] }),
8467
- /* @__PURE__ */ jsx27(ChevronDownIcon5, { className: "h-3 w-3 shrink-0 text-muted-foreground/50", "aria-hidden": "true" })
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" })
8468
8705
  ] })
8469
8706
  ]
8470
8707
  }
@@ -8479,8 +8716,8 @@ function ThinkingPickerMenu({
8479
8716
  }) {
8480
8717
  const selectedIndex = Math.max(0, THINKING_LEVELS.indexOf(value));
8481
8718
  const [activeIndex, setActiveIndex] = useState16(selectedIndex);
8482
- const activeIndexRef = useRef13(selectedIndex);
8483
- const menuRef = useRef13(null);
8719
+ const activeIndexRef = useRef14(selectedIndex);
8720
+ const menuRef = useRef14(null);
8484
8721
  useDismissOnOutsidePointer(menuRef, onClose);
8485
8722
  const setKeyboardActiveIndex = (next) => {
8486
8723
  const resolved = typeof next === "function" ? next(activeIndexRef.current) : next;
@@ -8513,7 +8750,7 @@ function ThinkingPickerMenu({
8513
8750
  window.addEventListener("keydown", handler, { capture: true });
8514
8751
  return () => window.removeEventListener("keydown", handler, { capture: true });
8515
8752
  }, [activeIndex, disabled, onChange, onClose]);
8516
- return /* @__PURE__ */ jsx27("div", { ref: menuRef, "data-boring-agent": "", "data-boring-agent-part": "thinking-picker-menu", className: cn(composerPickerMenuClass, className), children: /* @__PURE__ */ jsx27(Command, { className: "bg-transparent text-[color:var(--popover-foreground)]", children: /* @__PURE__ */ jsx27(CommandList, { className: "max-h-[300px] p-0.5", children: THINKING_LEVELS.map((level, index) => /* @__PURE__ */ jsxs25(
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(
8517
8754
  CommandItem,
8518
8755
  {
8519
8756
  value: `Thinking ${THINKING_LEVEL_LABELS[level]} ${THINKING_LEVEL_DETAILS[level]}`,
@@ -8524,7 +8761,7 @@ function ThinkingPickerMenu({
8524
8761
  },
8525
8762
  className: selectorItemClass(level === value || index === activeIndex),
8526
8763
  children: [
8527
- /* @__PURE__ */ jsx27(
8764
+ /* @__PURE__ */ jsx28(
8528
8765
  CheckIcon3,
8529
8766
  {
8530
8767
  className: cn(
@@ -8533,9 +8770,9 @@ function ThinkingPickerMenu({
8533
8770
  )
8534
8771
  }
8535
8772
  ),
8536
- /* @__PURE__ */ jsx27(ThinkingLevelGlyph, { level }),
8537
- /* @__PURE__ */ jsx27("span", { className: "font-medium", children: THINKING_LEVEL_LABELS[level] }),
8538
- /* @__PURE__ */ jsx27("span", { className: "ml-auto text-[11px] text-muted-foreground/65", children: THINKING_LEVEL_DETAILS[level] })
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] })
8539
8776
  ]
8540
8777
  },
8541
8778
  level
@@ -8576,7 +8813,7 @@ import {
8576
8813
  XIcon as XIcon5
8577
8814
  } from "lucide-react";
8578
8815
  import { nanoid } from "nanoid";
8579
- import { useCallback as useCallback15, useEffect as useEffect18, useMemo as useMemo10, useRef as useRef14, useState as useState17 } from "react";
8816
+ import { useCallback as useCallback15, useEffect as useEffect18, useMemo as useMemo11, useRef as useRef15, useState as useState17 } from "react";
8580
8817
 
8581
8818
  // src/front/primitives/prompt-input-context.ts
8582
8819
  import { createContext as createContext6, useContext as useContext6 } from "react";
@@ -8610,13 +8847,13 @@ import {
8610
8847
  TooltipContent as TooltipContent3,
8611
8848
  TooltipTrigger as TooltipTrigger3
8612
8849
  } from "@hachej/boring-ui-kit";
8613
- import { Children } from "react";
8614
- import { jsx as jsx28, jsxs as jsxs26 } from "react/jsx-runtime";
8850
+ import { Children as Children2 } from "react";
8851
+ import { jsx as jsx29, jsxs as jsxs26 } from "react/jsx-runtime";
8615
8852
  var PromptInputFooter = ({
8616
8853
  align = "block-end",
8617
8854
  className,
8618
8855
  ...props
8619
- }) => /* @__PURE__ */ jsx28(
8856
+ }) => /* @__PURE__ */ jsx29(
8620
8857
  InputGroupAddon,
8621
8858
  {
8622
8859
  align,
@@ -8626,7 +8863,7 @@ var PromptInputFooter = ({
8626
8863
  );
8627
8864
 
8628
8865
  // src/front/primitives/prompt-input.tsx
8629
- import { Fragment as Fragment9, jsx as jsx29, jsxs as jsxs27 } from "react/jsx-runtime";
8866
+ import { Fragment as Fragment10, jsx as jsx30, jsxs as jsxs27 } from "react/jsx-runtime";
8630
8867
  var PromptInput = ({
8631
8868
  className,
8632
8869
  accept,
@@ -8643,12 +8880,12 @@ var PromptInput = ({
8643
8880
  }) => {
8644
8881
  const controller = useOptionalPromptInputController();
8645
8882
  const usingProvider = !!controller;
8646
- const providerTextValueRef = useRef14("");
8883
+ const providerTextValueRef = useRef15("");
8647
8884
  if (usingProvider) {
8648
8885
  providerTextValueRef.current = controller.textInput.value;
8649
8886
  }
8650
- const inputRef = useRef14(null);
8651
- const formRef = useRef14(null);
8887
+ const inputRef = useRef15(null);
8888
+ const formRef = useRef15(null);
8652
8889
  const [items, setItems] = useState17([]);
8653
8890
  const setFileUrlLocal = useCallback15((id, url, status, path) => {
8654
8891
  setItems((prev) => prev.map((f) => {
@@ -8659,7 +8896,7 @@ var PromptInput = ({
8659
8896
  }, []);
8660
8897
  const files = usingProvider ? controller.attachments.files : items;
8661
8898
  const [referencedSources, setReferencedSources] = useState17([]);
8662
- const filesRef = useRef14(files);
8899
+ const filesRef = useRef15(files);
8663
8900
  useEffect18(() => {
8664
8901
  filesRef.current = files;
8665
8902
  }, [files]);
@@ -8881,7 +9118,7 @@ var PromptInput = ({
8881
9118
  [add]
8882
9119
  );
8883
9120
  const setFileUrl = usingProvider ? controller.attachments.setFileUrl : setFileUrlLocal;
8884
- const attachmentsCtx = useMemo10(
9121
+ const attachmentsCtx = useMemo11(
8885
9122
  () => ({
8886
9123
  add,
8887
9124
  clear: clearAttachments,
@@ -8893,7 +9130,7 @@ var PromptInput = ({
8893
9130
  }),
8894
9131
  [files, add, remove, clearAttachments, openFileDialog, setFileUrl]
8895
9132
  );
8896
- const refsCtx = useMemo10(
9133
+ const refsCtx = useMemo11(
8897
9134
  () => ({
8898
9135
  add: (incoming) => {
8899
9136
  const array = Array.isArray(incoming) ? incoming : [incoming];
@@ -8969,8 +9206,8 @@ var PromptInput = ({
8969
9206
  },
8970
9207
  [usingProvider, controller, files, onSubmit, clear]
8971
9208
  );
8972
- const inner = /* @__PURE__ */ jsxs27(Fragment9, { children: [
8973
- /* @__PURE__ */ jsx29(
9209
+ const inner = /* @__PURE__ */ jsxs27(Fragment10, { children: [
9210
+ /* @__PURE__ */ jsx30(
8974
9211
  Input,
8975
9212
  {
8976
9213
  accept,
@@ -8983,7 +9220,7 @@ var PromptInput = ({
8983
9220
  type: "file"
8984
9221
  }
8985
9222
  ),
8986
- /* @__PURE__ */ jsx29(
9223
+ /* @__PURE__ */ jsx30(
8987
9224
  "form",
8988
9225
  {
8989
9226
  "data-boring-agent-part": "composer",
@@ -8991,12 +9228,12 @@ var PromptInput = ({
8991
9228
  onSubmit: handleSubmit,
8992
9229
  ref: formRef,
8993
9230
  ...props,
8994
- children: /* @__PURE__ */ jsx29(InputGroup, { className: "overflow-hidden", children })
9231
+ children: /* @__PURE__ */ jsx30(InputGroup, { className: "overflow-hidden", children })
8995
9232
  }
8996
9233
  )
8997
9234
  ] });
8998
- const withReferencedSources = /* @__PURE__ */ jsx29(LocalReferencedSourcesContext.Provider, { value: refsCtx, children: inner });
8999
- return /* @__PURE__ */ jsx29(LocalAttachmentsContext.Provider, { value: attachmentsCtx, children: withReferencedSources });
9235
+ const withReferencedSources = /* @__PURE__ */ jsx30(LocalReferencedSourcesContext.Provider, { value: refsCtx, children: inner });
9236
+ return /* @__PURE__ */ jsx30(LocalAttachmentsContext.Provider, { value: attachmentsCtx, children: withReferencedSources });
9000
9237
  };
9001
9238
  var PromptInputTextarea = ({
9002
9239
  onChange,
@@ -9074,7 +9311,7 @@ var PromptInputTextarea = ({
9074
9311
  } : {
9075
9312
  onChange
9076
9313
  };
9077
- return /* @__PURE__ */ jsx29(
9314
+ return /* @__PURE__ */ jsx30(
9078
9315
  InputGroupTextarea,
9079
9316
  {
9080
9317
  "data-boring-agent-part": "composer-input",
@@ -9101,13 +9338,13 @@ var PromptInputSubmit = ({
9101
9338
  ...props
9102
9339
  }) => {
9103
9340
  const isGenerating = status === "submitted" || status === "streaming";
9104
- let Icon = /* @__PURE__ */ jsx29(CornerDownLeftIcon, { className: "size-4" });
9341
+ let Icon = /* @__PURE__ */ jsx30(CornerDownLeftIcon, { className: "size-4" });
9105
9342
  if (status === "submitted") {
9106
- Icon = /* @__PURE__ */ jsx29(Spinner, {});
9343
+ Icon = /* @__PURE__ */ jsx30(Spinner, {});
9107
9344
  } else if (status === "streaming") {
9108
- Icon = /* @__PURE__ */ jsx29(SquareIcon, { className: "size-4" });
9345
+ Icon = /* @__PURE__ */ jsx30(SquareIcon, { className: "size-4" });
9109
9346
  } else if (status === "error") {
9110
- Icon = /* @__PURE__ */ jsx29(XIcon5, { className: "size-4" });
9347
+ Icon = /* @__PURE__ */ jsx30(XIcon5, { className: "size-4" });
9111
9348
  }
9112
9349
  const handleClick = useCallback15(
9113
9350
  (e) => {
@@ -9120,7 +9357,7 @@ var PromptInputSubmit = ({
9120
9357
  },
9121
9358
  [isGenerating, onStop, onClick]
9122
9359
  );
9123
- return /* @__PURE__ */ jsx29(
9360
+ return /* @__PURE__ */ jsx30(
9124
9361
  InputGroupButton2,
9125
9362
  {
9126
9363
  "aria-label": isGenerating ? "Stop" : "Submit",
@@ -9137,8 +9374,8 @@ var PromptInputSubmit = ({
9137
9374
  };
9138
9375
 
9139
9376
  // src/front/primitives/slash-command-picker.tsx
9140
- import { useEffect as useEffect19, useMemo as useMemo11, useRef as useRef15, useState as useState18 } from "react";
9141
- import { Fragment as Fragment10, jsx as jsx30, jsxs as jsxs28 } from "react/jsx-runtime";
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";
9142
9379
  var ALL_PLUGINS = "__all__";
9143
9380
  function commandGroup(cmd) {
9144
9381
  if (cmd.source === "skill") return "skills";
@@ -9149,17 +9386,17 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9149
9386
  const [plugin, setPlugin] = useState18(ALL_PLUGINS);
9150
9387
  const [searchQuery, setSearchQuery] = useState18(query);
9151
9388
  const [activeIdx, setActiveIdx] = useState18(0);
9152
- const containerRef = useRef15(null);
9153
- const listRef = useRef15(null);
9154
- const inputRef = useRef15(null);
9389
+ const containerRef = useRef16(null);
9390
+ const listRef = useRef16(null);
9391
+ const inputRef = useRef16(null);
9155
9392
  useEffect19(() => {
9156
9393
  setSearchQuery(query);
9157
9394
  }, [query]);
9158
- const groups = useMemo11(() => {
9395
+ const groups = useMemo12(() => {
9159
9396
  const set = new Set(commands.map(commandGroup));
9160
9397
  return Array.from(set).sort((a, b) => a.localeCompare(b));
9161
9398
  }, [commands]);
9162
- const filtered = useMemo11(() => {
9399
+ const filtered = useMemo12(() => {
9163
9400
  const q = searchQuery.trim().toLowerCase();
9164
9401
  return commands.filter((c) => {
9165
9402
  if (plugin !== ALL_PLUGINS && commandGroup(c) !== plugin) return false;
@@ -9190,7 +9427,7 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9190
9427
  onDismiss
9191
9428
  });
9192
9429
  return /* @__PURE__ */ jsxs28("div", { ref: containerRef, className: "mb-1 w-full overflow-hidden rounded-lg border border-border/60 bg-popover shadow-lg", children: [
9193
- /* @__PURE__ */ jsx30(
9430
+ /* @__PURE__ */ jsx31(
9194
9431
  "input",
9195
9432
  {
9196
9433
  ref: inputRef,
@@ -9205,9 +9442,9 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9205
9442
  autoComplete: "off"
9206
9443
  }
9207
9444
  ),
9208
- /* @__PURE__ */ jsx30("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) => {
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) => {
9209
9446
  const selected = plugin === g;
9210
- return /* @__PURE__ */ jsx30(
9447
+ return /* @__PURE__ */ jsx31(
9211
9448
  "button",
9212
9449
  {
9213
9450
  type: "button",
@@ -9227,7 +9464,7 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9227
9464
  g
9228
9465
  );
9229
9466
  }) }),
9230
- filtered.length === 0 ? /* @__PURE__ */ jsx30("div", { className: "px-3 py-2 text-[11px] text-muted-foreground", children: "No matching commands." }) : /* @__PURE__ */ jsx30("ul", { ref: listRef, className: "max-h-80 overflow-y-auto py-1", role: "listbox", "aria-label": "Commands", children: filtered.map((cmd, i) => /* @__PURE__ */ jsxs28(
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(
9231
9468
  "li",
9232
9469
  {
9233
9470
  role: "option",
@@ -9250,15 +9487,15 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9250
9487
  "/",
9251
9488
  cmd.name
9252
9489
  ] }),
9253
- cmd.source === "skill" ? /* @__PURE__ */ jsxs28(Fragment10, { children: [
9254
- /* @__PURE__ */ jsx30("span", { className: "rounded-sm bg-accent/15 px-1 py-px text-[9px] font-medium uppercase tracking-wide text-accent-foreground", children: "skill" }),
9255
- cmd.sourcePlugin ? /* @__PURE__ */ jsx30("span", { className: "rounded-sm bg-muted px-1 py-px text-[9px] font-medium text-muted-foreground", children: cmd.sourcePlugin }) : null
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
9256
9493
  ] }) : cmd.sourcePlugin ? (
9257
9494
  // Originating plugin/package for non-skill commands.
9258
- /* @__PURE__ */ jsx30("span", { className: "rounded-sm bg-muted px-1 py-px text-[9px] font-medium text-muted-foreground", children: cmd.sourcePlugin })
9495
+ /* @__PURE__ */ jsx31("span", { className: "rounded-sm bg-muted px-1 py-px text-[9px] font-medium text-muted-foreground", children: cmd.sourcePlugin })
9259
9496
  ) : null
9260
9497
  ] }),
9261
- /* @__PURE__ */ jsx30("span", { className: "truncate text-[11px] opacity-50", children: cmd.description })
9498
+ /* @__PURE__ */ jsx31("span", { className: "truncate text-[11px] opacity-50", children: cmd.description })
9262
9499
  ]
9263
9500
  },
9264
9501
  cmd.name
@@ -9269,10 +9506,10 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9269
9506
  // src/front/chat/components/ComposerAttachments.tsx
9270
9507
  import { AlertCircleIcon as AlertCircleIcon2, Loader2 as Loader23, PaperclipIcon as PaperclipIcon2 } from "lucide-react";
9271
9508
  import { IconButton as IconButton3 } from "@hachej/boring-ui-kit";
9272
- import { jsx as jsx31, jsxs as jsxs29 } from "react/jsx-runtime";
9509
+ import { jsx as jsx32, jsxs as jsxs29 } from "react/jsx-runtime";
9273
9510
  function AttachmentButton({ disabled, className }) {
9274
9511
  const attachments = usePromptInputAttachments();
9275
- return /* @__PURE__ */ jsx31(
9512
+ return /* @__PURE__ */ jsx32(
9276
9513
  IconButton3,
9277
9514
  {
9278
9515
  type: "button",
@@ -9285,14 +9522,14 @@ function AttachmentButton({ disabled, className }) {
9285
9522
  className: cn(composerActionClass, "w-8", className),
9286
9523
  "aria-label": "Attach files",
9287
9524
  title: disabled ? "Attachments are available when the composer is ready." : "Attach files",
9288
- children: /* @__PURE__ */ jsx31(PaperclipIcon2, { className: "h-3.5 w-3.5", strokeWidth: 1.75 })
9525
+ children: /* @__PURE__ */ jsx32(PaperclipIcon2, { className: "h-3.5 w-3.5", strokeWidth: 1.75 })
9289
9526
  }
9290
9527
  );
9291
9528
  }
9292
9529
  function AttachmentsList() {
9293
9530
  const attachments = usePromptInputAttachments();
9294
9531
  if (attachments.files.length === 0) return null;
9295
- return /* @__PURE__ */ jsx31(
9532
+ return /* @__PURE__ */ jsx32(
9296
9533
  Attachments,
9297
9534
  {
9298
9535
  "data-boring-agent-part": "composer-attachment-row",
@@ -9311,12 +9548,12 @@ function AttachmentsList() {
9311
9548
  ),
9312
9549
  children: [
9313
9550
  /* @__PURE__ */ jsxs29("div", { className: "relative shrink-0", children: [
9314
- /* @__PURE__ */ jsx31(AttachmentPreview, { className: "!size-7 overflow-hidden !rounded-full bg-background/60" }),
9315
- file.status === "uploading" ? /* @__PURE__ */ jsx31("div", { className: "absolute inset-0 flex items-center justify-center rounded-full bg-background/70", children: /* @__PURE__ */ jsx31(Loader23, { className: "size-3.5 animate-spin text-muted-foreground" }) }) : null,
9316
- file.status === "error" ? /* @__PURE__ */ jsx31("div", { className: "absolute inset-0 flex items-center justify-center rounded-full bg-destructive/20", children: /* @__PURE__ */ jsx31(AlertCircleIcon2, { className: "size-3.5 text-destructive" }) }) : null
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
9317
9554
  ] }),
9318
- /* @__PURE__ */ jsx31(AttachmentInfo, { className: "min-w-0 !max-w-[180px] truncate text-[13px] font-medium" }),
9319
- /* @__PURE__ */ jsx31(AttachmentRemove, { className: "!size-5 !rounded-full !opacity-100 text-muted-foreground/80 hover:text-foreground" })
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" })
9320
9557
  ]
9321
9558
  },
9322
9559
  file.id
@@ -9326,7 +9563,7 @@ function AttachmentsList() {
9326
9563
  }
9327
9564
 
9328
9565
  // src/front/chat/components/PiChatComposerSurface.tsx
9329
- import { jsx as jsx32, jsxs as jsxs30 } from "react/jsx-runtime";
9566
+ import { jsx as jsx33, jsxs as jsxs30 } from "react/jsx-runtime";
9330
9567
  var MAX_PROMPT_ATTACHMENTS = 2;
9331
9568
  var MAX_PROMPT_ATTACHMENT_BYTES = 4 * 1024 * 1024;
9332
9569
  var COMPOSER_INPUT_GROUP_MIN_HEIGHT = 56;
@@ -9438,7 +9675,7 @@ function PiChatComposerSurface({
9438
9675
  resizeTextarea(textareaRef.current);
9439
9676
  }, [draft, resizeTextarea, textareaRef]);
9440
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: [
9441
- /* @__PURE__ */ jsx32(
9678
+ /* @__PURE__ */ jsx33(
9442
9679
  "div",
9443
9680
  {
9444
9681
  "data-boring-agent-part": "chat-working-slot",
@@ -9456,7 +9693,7 @@ function PiChatComposerSurface({
9456
9693
  "aria-live": isStreaming ? "polite" : void 0,
9457
9694
  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",
9458
9695
  children: [
9459
- /* @__PURE__ */ jsx32(
9696
+ /* @__PURE__ */ jsx33(
9460
9697
  motion2.span,
9461
9698
  {
9462
9699
  "aria-hidden": "true",
@@ -9465,14 +9702,14 @@ function PiChatComposerSurface({
9465
9702
  transition: { duration: 1.2, repeat: Infinity, ease: "easeInOut" }
9466
9703
  }
9467
9704
  ),
9468
- /* @__PURE__ */ jsx32("span", { children: "Working\u2026" })
9705
+ /* @__PURE__ */ jsx33("span", { children: "Working\u2026" })
9469
9706
  ]
9470
9707
  }
9471
9708
  )
9472
9709
  }
9473
9710
  ),
9474
- composerStatusNotice ? /* @__PURE__ */ jsx32(ComposerRuntimeNotice, { notice: composerStatusNotice }) : null,
9475
- composerBlocked && !workspaceWarmupBlocked ? /* @__PURE__ */ jsx32(
9711
+ composerStatusNotice ? /* @__PURE__ */ jsx33(ComposerRuntimeNotice, { notice: composerStatusNotice }) : null,
9712
+ composerBlocked && !workspaceWarmupBlocked ? /* @__PURE__ */ jsx33(
9476
9713
  ComposerBlockerNotice,
9477
9714
  {
9478
9715
  blocker: primaryComposerBlocker,
@@ -9480,8 +9717,8 @@ function PiChatComposerSurface({
9480
9717
  onAction: onComposerBlockerAction
9481
9718
  }
9482
9719
  ) : null,
9483
- queuePreview.length > 0 ? /* @__PURE__ */ jsx32(QueuedComposerNotice, { followUps: queuePreview, onEdit: onEditQueued }) : null,
9484
- hotReloadEnabled ? /* @__PURE__ */ jsx32(
9720
+ queuePreview.length > 0 ? /* @__PURE__ */ jsx33(QueuedComposerNotice, { followUps: queuePreview, onEdit: onEditQueued }) : null,
9721
+ hotReloadEnabled ? /* @__PURE__ */ jsx33(
9485
9722
  PluginUpdateStatus,
9486
9723
  {
9487
9724
  state: pluginUpdateState,
@@ -9489,14 +9726,14 @@ function PiChatComposerSurface({
9489
9726
  onRetry: onRunPluginUpdate
9490
9727
  }
9491
9728
  ) : null,
9492
- /* @__PURE__ */ jsx32(
9729
+ /* @__PURE__ */ jsx33(
9493
9730
  CommandRunStatus,
9494
9731
  {
9495
9732
  state: commandNotifyState,
9496
9733
  onDismiss: onDismissCommandNotify
9497
9734
  }
9498
9735
  ),
9499
- attachmentNotice ? /* @__PURE__ */ jsx32(
9736
+ attachmentNotice ? /* @__PURE__ */ jsx33(
9500
9737
  "div",
9501
9738
  {
9502
9739
  role: "status",
@@ -9506,7 +9743,7 @@ function PiChatComposerSurface({
9506
9743
  }
9507
9744
  ) : null,
9508
9745
  /* @__PURE__ */ jsxs30("div", { className: cn("mx-auto w-full", chrome ? "max-w-3xl" : "max-w-[680px]"), children: [
9509
- mentionState ? /* @__PURE__ */ jsx32(
9746
+ mentionState ? /* @__PURE__ */ jsx33(
9510
9747
  MentionPicker,
9511
9748
  {
9512
9749
  mention: mentionState,
@@ -9518,7 +9755,7 @@ function PiChatComposerSurface({
9518
9755
  onDismiss: onDismissMention
9519
9756
  }
9520
9757
  ) : null,
9521
- slashQuery !== null ? /* @__PURE__ */ jsx32(
9758
+ slashQuery !== null ? /* @__PURE__ */ jsx33(
9522
9759
  SlashCommandPicker,
9523
9760
  {
9524
9761
  query: slashQuery,
@@ -9527,7 +9764,7 @@ function PiChatComposerSurface({
9527
9764
  onDismiss: onDismissSlash
9528
9765
  }
9529
9766
  ) : null,
9530
- mentionState === null && slashQuery === null && modelPickerOpen ? /* @__PURE__ */ jsx32(
9767
+ mentionState === null && slashQuery === null && modelPickerOpen ? /* @__PURE__ */ jsx33(
9531
9768
  ModelPickerMenu,
9532
9769
  {
9533
9770
  value: selectedModel,
@@ -9538,7 +9775,7 @@ function PiChatComposerSurface({
9538
9775
  onClose: () => onSetModelPickerOpen(false)
9539
9776
  }
9540
9777
  ) : null,
9541
- mentionState === null && slashQuery === null && thinkingPickerOpen ? /* @__PURE__ */ jsx32(
9778
+ mentionState === null && slashQuery === null && thinkingPickerOpen ? /* @__PURE__ */ jsx33(
9542
9779
  ThinkingPickerMenu,
9543
9780
  {
9544
9781
  value: selectedThinking,
@@ -9548,7 +9785,7 @@ function PiChatComposerSurface({
9548
9785
  }
9549
9786
  ) : null
9550
9787
  ] }),
9551
- /* @__PURE__ */ jsx32(
9788
+ /* @__PURE__ */ jsx33(
9552
9789
  "div",
9553
9790
  {
9554
9791
  "data-boring-agent-part": "composer-rail",
@@ -9583,15 +9820,15 @@ function PiChatComposerSurface({
9583
9820
  else onAttachmentNotice(err.message || "Attachment rejected.");
9584
9821
  },
9585
9822
  children: [
9586
- /* @__PURE__ */ jsx32(AttachmentsList, {}),
9823
+ /* @__PURE__ */ jsx33(AttachmentsList, {}),
9587
9824
  /* @__PURE__ */ jsxs30(
9588
9825
  "div",
9589
9826
  {
9590
9827
  "data-boring-agent-part": "composer-input-row",
9591
9828
  className: "flex min-h-[var(--composer-input-group-height)] w-full items-center",
9592
9829
  children: [
9593
- /* @__PURE__ */ jsx32("div", { className: "flex h-14 shrink-0 items-center pl-2", children: /* @__PURE__ */ jsx32(AttachmentButton, { disabled: disabled || isStreaming }) }),
9594
- /* @__PURE__ */ jsx32(
9830
+ /* @__PURE__ */ jsx33("div", { className: "flex h-14 shrink-0 items-center pl-2", children: /* @__PURE__ */ jsx33(AttachmentButton, { disabled: disabled || isStreaming }) }),
9831
+ /* @__PURE__ */ jsx33(
9595
9832
  PromptInputTextarea,
9596
9833
  {
9597
9834
  value: draft,
@@ -9615,13 +9852,13 @@ function PiChatComposerSurface({
9615
9852
  )
9616
9853
  }
9617
9854
  ),
9618
- /* @__PURE__ */ jsx32(
9855
+ /* @__PURE__ */ jsx33(
9619
9856
  PromptInputFooter,
9620
9857
  {
9621
9858
  align: "inline-end",
9622
9859
  "data-boring-agent-part": "composer-submit-addon",
9623
9860
  className: "!order-none !w-auto shrink-0 self-center justify-between border-0 bg-transparent !px-2 !py-0",
9624
- children: /* @__PURE__ */ jsx32("div", { className: "ml-auto flex items-center gap-1.5", children: /* @__PURE__ */ jsx32(
9861
+ children: /* @__PURE__ */ jsx33("div", { className: "ml-auto flex items-center gap-1.5", children: /* @__PURE__ */ jsx33(
9625
9862
  PromptInputSubmit,
9626
9863
  {
9627
9864
  "data-boring-agent-part": "composer-submit",
@@ -9660,7 +9897,7 @@ function PiChatComposerSurface({
9660
9897
  chrome ? "max-w-3xl" : "max-w-[680px]"
9661
9898
  ),
9662
9899
  children: [
9663
- /* @__PURE__ */ jsx32(
9900
+ /* @__PURE__ */ jsx33(
9664
9901
  ModelSelectTrigger,
9665
9902
  {
9666
9903
  value: selectedModel,
@@ -9677,7 +9914,7 @@ function PiChatComposerSurface({
9677
9914
  }
9678
9915
  }
9679
9916
  ),
9680
- thinkingControl ? /* @__PURE__ */ jsx32(
9917
+ thinkingControl ? /* @__PURE__ */ jsx33(
9681
9918
  ThinkingSelectTrigger,
9682
9919
  {
9683
9920
  value: selectedThinking,
@@ -9704,7 +9941,7 @@ function getHeaderValue(headers, name) {
9704
9941
  }
9705
9942
 
9706
9943
  // src/front/chat/piChatPanelHooks.ts
9707
- import { useCallback as useCallback17, useEffect as useEffect20, useMemo as useMemo12, useRef as useRef16, useState as useState19, useSyncExternalStore } from "react";
9944
+ import { useCallback as useCallback17, useEffect as useEffect20, useMemo as useMemo13, useRef as useRef17, useState as useState19, useSyncExternalStore } from "react";
9708
9945
  function useExternalRemotePiSession({
9709
9946
  sessionId,
9710
9947
  agentTypeId,
@@ -9717,9 +9954,9 @@ function useExternalRemotePiSession({
9717
9954
  remoteSessionOptions
9718
9955
  }) {
9719
9956
  const [session, setSession] = useState19();
9720
- const remoteSessionOptionsRef = useRef16(remoteSessionOptions);
9957
+ const remoteSessionOptionsRef = useRef17(remoteSessionOptions);
9721
9958
  remoteSessionOptionsRef.current = remoteSessionOptions;
9722
- const remoteSessionOptionsKey = useMemo12(
9959
+ const remoteSessionOptionsKey = useMemo13(
9723
9960
  () => remoteSessionOptionsIdentity2(remoteSessionOptions),
9724
9961
  [remoteSessionOptions]
9725
9962
  );
@@ -9892,7 +10129,7 @@ function errorMessage2(error, fallback) {
9892
10129
  }
9893
10130
 
9894
10131
  // src/front/chat/PiChatPanel.tsx
9895
- import { jsx as jsx33, jsxs as jsxs31 } from "react/jsx-runtime";
10132
+ import { jsx as jsx34, jsxs as jsxs31 } from "react/jsx-runtime";
9896
10133
  var DebugDrawer2 = lazy(() => import("../DebugDrawer-RYEBQ6CO.js").then((m) => ({ default: m.DebugDrawer })));
9897
10134
  var EMPTY_COMMANDS = [];
9898
10135
  var EMPTY_COMMAND_NAMES = [];
@@ -9964,14 +10201,14 @@ function PiChatPanel({
9964
10201
  }) {
9965
10202
  const externalSessionId = sessionId?.trim() || void 0;
9966
10203
  const showSessionSidebar = showSessions ?? externalSessionId === void 0;
9967
- const onDataRef = useRef17(onData);
10204
+ const onDataRef = useRef18(onData);
9968
10205
  onDataRef.current = onData;
9969
- const onTurnCompleteRef = useRef17(onTurnComplete);
10206
+ const onTurnCompleteRef = useRef18(onTurnComplete);
9970
10207
  onTurnCompleteRef.current = onTurnComplete;
9971
- const sessionListRefreshRef = useRef17(void 0);
9972
- const requestHeadersKey = useMemo13(() => headersContentKey(requestHeaders), [requestHeaders]);
9973
- const normalizedRequestHeaders = useMemo13(() => normalizedHeadersFromContentKey(requestHeadersKey), [requestHeadersKey]);
9974
- const remoteSessionOptionsWithEvents = useMemo13(() => ({
10208
+ const sessionListRefreshRef = useRef18(void 0);
10209
+ const requestHeadersKey = useMemo14(() => headersContentKey(requestHeaders), [requestHeaders]);
10210
+ const normalizedRequestHeaders = useMemo14(() => normalizedHeadersFromContentKey(requestHeadersKey), [requestHeadersKey]);
10211
+ const remoteSessionOptionsWithEvents = useMemo14(() => ({
9975
10212
  ...remoteSessionOptions,
9976
10213
  ...hydrateMessages ? {} : { autoStart: false },
9977
10214
  onEvent: (event) => {
@@ -10038,7 +10275,7 @@ function PiChatPanel({
10038
10275
  enabled: modelDiscoveryEnabled
10039
10276
  });
10040
10277
  const selectedModel = model === void 0 ? modelDiscovery.model : model;
10041
- const modelOptions = useMemo13(
10278
+ const modelOptions = useMemo14(
10042
10279
  () => modelOptionsForSelection(availableModels ?? modelDiscovery.availableModels, selectedModel),
10043
10280
  [availableModels, modelDiscovery.availableModels, selectedModel]
10044
10281
  );
@@ -10076,13 +10313,13 @@ function PiChatPanel({
10076
10313
  const [modelPickerOpen, setModelPickerOpen] = useState20(false);
10077
10314
  const [thinkingPickerOpen, setThinkingPickerOpen] = useState20(false);
10078
10315
  const [draft, setDraft] = useState20(() => initialDraft ?? "");
10079
- const draftRef = useRef17(draft);
10316
+ const draftRef = useRef18(draft);
10080
10317
  draftRef.current = draft;
10081
- const initialDraftGuard = useRef17(new InitialDraftAutoSubmitGuard());
10082
- const pendingAutoSubmitSettleRef = useRef17(void 0);
10083
- const acceptedAutoSubmitSettleRef = useRef17(void 0);
10084
- const resetInProgressRef = useRef17(false);
10085
- const autoCreateInFlightRef = useRef17(false);
10318
+ const initialDraftGuard = useRef18(new InitialDraftAutoSubmitGuard());
10319
+ const pendingAutoSubmitSettleRef = useRef18(void 0);
10320
+ const acceptedAutoSubmitSettleRef = useRef18(void 0);
10321
+ const resetInProgressRef = useRef18(false);
10322
+ const autoCreateInFlightRef = useRef18(false);
10086
10323
  const settlePendingAutoSubmit = useCallback18((sessionId2) => {
10087
10324
  const pendingSessionId = pendingAutoSubmitSettleRef.current;
10088
10325
  if (!pendingSessionId || sessionId2 && pendingSessionId !== sessionId2) return false;
@@ -10091,19 +10328,19 @@ function PiChatPanel({
10091
10328
  onAutoSubmitInitialDraftSettled?.();
10092
10329
  return true;
10093
10330
  }, [onAutoSubmitInitialDraftSettled]);
10094
- const prevStatusRef = useRef17("idle");
10095
- const statusRef = useRef17("idle");
10096
- const scrollToBottomRef = useRef17(() => {
10331
+ const prevStatusRef = useRef18("idle");
10332
+ const statusRef = useRef18("idle");
10333
+ const scrollToBottomRef = useRef18(() => {
10097
10334
  });
10098
- const textareaRef = useRef17(null);
10335
+ const textareaRef = useRef18(null);
10099
10336
  const [localNotices, setLocalNotices] = useState20([]);
10100
10337
  const [dismissedNoticeIds, setDismissedNoticeIds] = useState20(() => /* @__PURE__ */ new Set());
10101
10338
  const [pluginUpdateState, setPluginUpdateState] = useState20(null);
10102
10339
  const [commandNotifyState, setCommandNotifyState] = useState20(null);
10103
- const commandRunIdRef = useRef17(0);
10340
+ const commandRunIdRef = useRef18(0);
10104
10341
  const [serverSkillsRefreshKey, setServerSkillsRefreshKey] = useState20(0);
10105
10342
  const [localSubmittedSessionId, setLocalSubmittedSessionId] = useState20();
10106
- const localSubmittedSessionRef = useRef17(void 0);
10343
+ const localSubmittedSessionRef = useRef18(void 0);
10107
10344
  const { attachmentNotice, setAttachmentNotice } = useAttachmentNotice();
10108
10345
  const markLocalSubmitted = useCallback18((sessionId2) => {
10109
10346
  localSubmittedSessionRef.current = sessionId2;
@@ -10114,7 +10351,7 @@ function PiChatPanel({
10114
10351
  localSubmittedSessionRef.current = void 0;
10115
10352
  setLocalSubmittedSessionId(void 0);
10116
10353
  }, []);
10117
- const registry = useMemo13(() => {
10354
+ const registry = useMemo14(() => {
10118
10355
  const excludedBuiltins = new Set(excludeBuiltinCommands);
10119
10356
  const effectiveBuiltins = builtinCommands.filter((command) => {
10120
10357
  if (!hotReloadEnabled && command.name === "reload") return false;
@@ -10135,12 +10372,16 @@ function PiChatPanel({
10135
10372
  refreshKey: serverSkillsRefreshKey,
10136
10373
  enabled: serverResourcesEnabled
10137
10374
  });
10138
- const allCommands = useMemo13(() => registry.list(), [registry, commandsStamp]);
10375
+ const allCommands = useMemo14(() => registry.list(), [registry, commandsStamp]);
10376
+ const actionableSlashCommands = useMemo14(
10377
+ () => allCommands.flatMap((command) => command.clickBehavior === "execute" || command.clickBehavior === "insert" ? [{ name: command.name, clickBehavior: command.clickBehavior }] : []),
10378
+ [allCommands]
10379
+ );
10139
10380
  const activeChatSessionId = selectedChatState?.sessionId;
10140
10381
  const warmupNotice = composerNoticeForWarmup(workspaceWarmupStatus);
10141
10382
  const runtimeDependenciesNotice = composerNoticeForRuntimeDependencies(workspaceWarmupStatus);
10142
10383
  const workspaceWarmupBlocked = Boolean(warmupNotice);
10143
- const activeBlockers = useMemo13(
10384
+ const activeBlockers = useMemo14(
10144
10385
  // A missing active session id means a single/sessionless chat host. In that
10145
10386
  // mode, keep scoped blockers visible instead of hiding the only attention UI.
10146
10387
  // Multi-session hosts should pass a session id so unrelated blockers filter out.
@@ -10150,7 +10391,7 @@ function PiChatPanel({
10150
10391
  const canonicalMessages = selectedChatState ? selectMessagesForRender(selectedChatState) : [];
10151
10392
  const queuePreview = selectedChatState ? selectQueuePreview(selectedChatState) : [];
10152
10393
  const messages = canonicalMessages;
10153
- const userHistory = useMemo13(() => selectComposerHistoryFromCanonicalUsers(canonicalMessages), [canonicalMessages]);
10394
+ const userHistory = useMemo14(() => selectComposerHistoryFromCanonicalUsers(canonicalMessages), [canonicalMessages]);
10154
10395
  const emptyStateHydrating = statusForState(selectedChatState, sessionsLoading || chatStatePending || selectedSessionPending) === "hydrating";
10155
10396
  const emptyHero = emptyPlacement === "hero" && messages.length === 0 && queuePreview.length === 0 && !emptyStateHydrating;
10156
10397
  const debugState = selectedPiSession?.getDebugState();
@@ -10158,7 +10399,7 @@ function PiChatPanel({
10158
10399
  const primaryComposerBlocker = activeBlockers[0];
10159
10400
  const composerBlockerLabel = workspaceWarmupBlocked ? warmupNotice?.title ?? "Preparing workspace..." : primaryComposerBlocker?.label ?? primaryComposerBlocker?.reason ?? "Complete the pending workspace action to continue.";
10160
10401
  const composerStatusNotice = warmupNotice ?? runtimeDependenciesNotice;
10161
- const runtimeNotices = useMemo13(() => {
10402
+ const runtimeNotices = useMemo14(() => {
10162
10403
  const fromState = selectedChatState ? selectRuntimeNotices(selectedChatState) : [];
10163
10404
  const sessionNotice = sessionsError ? [{ id: "session-navigation-error", level: "error", text: sessionsError.message, dismissible: true }] : [];
10164
10405
  const largeStateNotice = debug && debugState?.largeStateWarning ? [{
@@ -10372,7 +10613,7 @@ function PiChatPanel({
10372
10613
  }
10373
10614
  insertSlashCommand(name);
10374
10615
  }, [dismissSlash, insertSlashCommand, openModelPicker, openThinkingPicker, setComposerDraft]);
10375
- const policy = useMemo13(() => {
10616
+ const policy = useMemo14(() => {
10376
10617
  if (!selectedPiSession || !activeChatSessionId || !serverModelSelectionReady) return void 0;
10377
10618
  const policySession = {
10378
10619
  getState: () => {
@@ -10450,16 +10691,16 @@ function PiChatPanel({
10450
10691
  ...errorCode ? { errorCode } : {}
10451
10692
  });
10452
10693
  }, [addLocalNotice, dropLocalNotice]);
10453
- const sendComposerMessage = useCallback18(async ({ text, files, source = "composer" }) => {
10694
+ const sendComposerMessage = useCallback18(async ({ text, files, source = "composer", preserveExistingDraft = false }) => {
10454
10695
  if (!policy) {
10455
10696
  addLocalNotice({ id: "composer-no-session", level: "warning", text: "Create or select a chat session before sending.", dismissible: true });
10456
10697
  return false;
10457
10698
  }
10458
10699
  const submittedDraft = text;
10459
10700
  const restoreSubmittedDraft = () => {
10460
- if (draftRef.current === "") setComposerDraft(submittedDraft);
10701
+ if (!preserveExistingDraft && draftRef.current === "") setComposerDraft(submittedDraft);
10461
10702
  };
10462
- setComposerDraft("", false);
10703
+ if (!preserveExistingDraft) setComposerDraft("", false);
10463
10704
  scrollToBottomRef.current();
10464
10705
  try {
10465
10706
  const result = await policy.submit({ text, files, source });
@@ -10483,6 +10724,53 @@ function PiChatPanel({
10483
10724
  return false;
10484
10725
  }
10485
10726
  }, [activeChatSessionId, clearLocalSubmitted, dropLocalNotice, markLocalSubmitted, onPromptSubmitStarted, policy, selectedPiSession, setComposerDraft, surfaceRunRejected]);
10727
+ const availableAssistantSlashCommands = useMemo14(
10728
+ () => policy ? actionableSlashCommands : actionableSlashCommands.filter((command) => command.clickBehavior === "insert"),
10729
+ [actionableSlashCommands, policy]
10730
+ );
10731
+ const assistantMentionCatalog = useMemo14(() => {
10732
+ const skills = /* @__PURE__ */ new Map();
10733
+ for (const command of allCommands) {
10734
+ if (command.source !== "skill" && command.kind !== "skill") continue;
10735
+ const name = command.name.replace(/^skill:/, "");
10736
+ if (!name) continue;
10737
+ const priority = command.kind === "skill" ? 2 : 1;
10738
+ const existing = skills.get(name);
10739
+ if (!existing || priority > existing.priority) skills.set(name, { name, commandName: command.name, priority });
10740
+ }
10741
+ return {
10742
+ commands: availableAssistantSlashCommands,
10743
+ skills: [...skills.values()].map(({ name, commandName }) => ({ name, commandName })),
10744
+ files: true
10745
+ };
10746
+ }, [allCommands, availableAssistantSlashCommands]);
10747
+ const insertMentionDraft = useCallback18((invocation) => {
10748
+ const currentDraft = draftRef.current;
10749
+ const separator = currentDraft && !/\s$/.test(invocation) ? "\n" : "";
10750
+ setComposerDraft(`${invocation}${separator}${currentDraft}`, true);
10751
+ }, [setComposerDraft]);
10752
+ const activateAssistantMention = useCallback18((mention) => {
10753
+ const command = registry.get(mention.kind === "skill" ? mention.commandName : mention.name);
10754
+ if (mention.kind === "skill") {
10755
+ if (command?.source !== "skill" && command?.kind !== "skill") return;
10756
+ const invocation = command.kind === "skill" && !mention.commandName.includes(":") ? `/${mention.commandName} ` : `skill: ${mention.name}
10757
+
10758
+ `;
10759
+ insertMentionDraft(invocation);
10760
+ return;
10761
+ }
10762
+ if (command?.clickBehavior === "insert") {
10763
+ insertMentionDraft(`/${mention.name} `);
10764
+ return;
10765
+ }
10766
+ if (command?.clickBehavior !== "execute" || !policy) return;
10767
+ void sendComposerMessage({
10768
+ text: `/${mention.name}`,
10769
+ files: [],
10770
+ source: "composer",
10771
+ preserveExistingDraft: true
10772
+ });
10773
+ }, [insertMentionDraft, policy, registry, sendComposerMessage]);
10486
10774
  const editQueued = useCallback18(() => {
10487
10775
  if (!policy) return;
10488
10776
  void policy.editQueued().then((result) => {
@@ -10589,8 +10877,8 @@ function PiChatPanel({
10589
10877
  const isStreaming = isPiBusyStatus(status);
10590
10878
  const submitStatus = initialHydrationPromptAllowed ? "ready" : toPromptSubmitStatus(status);
10591
10879
  const submitDisabled = !policy || sessionsLoading || modelSelectionBlocked || composerBlocked && !isStreaming;
10592
- const mergedToolRenderers = useMemo13(() => mergeShadcnToolRenderers(toolRenderers), [toolRenderers]);
10593
- const debugMessages = useMemo13(() => messages.map(toDebugUiMessage), [messages]);
10880
+ const mergedToolRenderers = useMemo14(() => mergeShadcnToolRenderers(toolRenderers), [toolRenderers]);
10881
+ const debugMessages = useMemo14(() => messages.map(toDebugUiMessage), [messages]);
10594
10882
  const onTextareaChange = useCallback18((event) => {
10595
10883
  setModelPickerOpen(false);
10596
10884
  setThinkingPickerOpen(false);
@@ -10624,7 +10912,7 @@ function PiChatPanel({
10624
10912
  }
10625
10913
  handleComposerKeyDown(event);
10626
10914
  }, [handleComposerKeyDown, interrupt, isStreaming, mentionState, slashQuery]);
10627
- return /* @__PURE__ */ jsx33(ArtifactOpenProvider, { onOpenArtifact, children: /* @__PURE__ */ jsxs31(
10915
+ return /* @__PURE__ */ jsx34(ArtifactOpenProvider, { onOpenArtifact, children: /* @__PURE__ */ jsxs31(
10628
10916
  "div",
10629
10917
  {
10630
10918
  "data-boring-agent": "",
@@ -10641,7 +10929,7 @@ function PiChatPanel({
10641
10929
  role: "region",
10642
10930
  "aria-label": "Agent assistant",
10643
10931
  children: [
10644
- showSessionSidebar ? /* @__PURE__ */ jsx33("aside", { "data-boring-agent-part": "pi-chat-session-sidebar", className: "min-h-0 w-64 shrink-0 border-r border-border/60", children: /* @__PURE__ */ jsx33(
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(
10645
10933
  SessionList,
10646
10934
  {
10647
10935
  sessions: sessionList,
@@ -10655,7 +10943,7 @@ function PiChatPanel({
10655
10943
  loadingMore: sessions.loadingMore
10656
10944
  }
10657
10945
  ) }) : null,
10658
- /* @__PURE__ */ jsx33("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col", children: /* @__PURE__ */ jsxs31(
10946
+ /* @__PURE__ */ jsx34("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col", children: /* @__PURE__ */ jsxs31(
10659
10947
  "div",
10660
10948
  {
10661
10949
  className: cn(
@@ -10664,7 +10952,7 @@ function PiChatPanel({
10664
10952
  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)]"
10665
10953
  ),
10666
10954
  children: [
10667
- /* @__PURE__ */ jsx33(
10955
+ /* @__PURE__ */ jsx34(
10668
10956
  PiConversationSurface,
10669
10957
  {
10670
10958
  chrome,
@@ -10676,6 +10964,8 @@ function PiChatPanel({
10676
10964
  isStreaming,
10677
10965
  showThoughts,
10678
10966
  toolRenderers: mergedToolRenderers,
10967
+ mentionCatalog: assistantMentionCatalog,
10968
+ onMentionActivate: activateAssistantMention,
10679
10969
  runtimeNotices,
10680
10970
  onDismissNotice: clearLocalNotice,
10681
10971
  renderNoticeAction,
@@ -10687,7 +10977,7 @@ function PiChatPanel({
10687
10977
  windowResetKey: activeSessionId
10688
10978
  }
10689
10979
  ),
10690
- /* @__PURE__ */ jsx33(
10980
+ /* @__PURE__ */ jsx34(
10691
10981
  PiChatComposerSurface,
10692
10982
  {
10693
10983
  chrome,
@@ -10751,7 +11041,7 @@ function PiChatPanel({
10751
11041
  ]
10752
11042
  }
10753
11043
  ) }),
10754
- debug ? /* @__PURE__ */ jsx33(Suspense, { fallback: null, children: /* @__PURE__ */ jsx33("div", { "aria-label": "Chat debug metadata", className: "contents", role: "region", children: /* @__PURE__ */ jsx33(
11044
+ debug ? /* @__PURE__ */ jsx34(Suspense, { fallback: null, children: /* @__PURE__ */ jsx34("div", { "aria-label": "Chat debug metadata", className: "contents", role: "region", children: /* @__PURE__ */ jsx34(
10755
11045
  DebugDrawer2,
10756
11046
  {
10757
11047
  apiBaseUrl,