@hachej/boring-agent 0.1.35 → 0.1.36

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.
@@ -1,6 +1,7 @@
1
1
  import {
2
- WORKSPACE_AGENT_PLUGINS_RELOADED_EVENT
3
- } from "../chunk-NZN2PRET.js";
2
+ WORKSPACE_AGENT_PLUGINS_RELOADED_EVENT,
3
+ WORKSPACE_COMMAND_NOTIFY_EVENT
4
+ } from "../chunk-DGRKMMXO.js";
4
5
  import {
5
6
  CommandReceiptSchema,
6
7
  ErrorCode,
@@ -56,7 +57,7 @@ async function uploadFile(file, opts = {}) {
56
57
  }
57
58
 
58
59
  // src/front/chat/PiChatPanel.tsx
59
- import { lazy, Suspense, useCallback as useCallback19, useEffect as useEffect20, useMemo as useMemo12, useRef as useRef14, useState as useState20 } from "react";
60
+ import { lazy, Suspense, useCallback as useCallback18, useEffect as useEffect21, useMemo as useMemo12, useRef as useRef16, useState as useState20 } from "react";
60
61
 
61
62
  // src/front/ArtifactOpenContext.tsx
62
63
  import { createContext, useContext } from "react";
@@ -234,6 +235,9 @@ function createCommandRegistry(initial) {
234
235
  register(cmd) {
235
236
  commands.set(cmd.name, cmd);
236
237
  },
238
+ unregister(name) {
239
+ commands.delete(name);
240
+ },
237
241
  get(name) {
238
242
  return commands.get(name);
239
243
  },
@@ -254,6 +258,13 @@ var builtinCommands = [
254
258
  return "Session reset.";
255
259
  }
256
260
  },
261
+ {
262
+ name: "clear",
263
+ description: "Hide messages from display",
264
+ handler(_, ctx) {
265
+ ctx.clearMessages();
266
+ }
267
+ },
257
268
  {
258
269
  name: "reload",
259
270
  description: "Reload agent plugins",
@@ -295,7 +306,12 @@ var builtinCommands = [
295
306
  handler(_, ctx) {
296
307
  const cmds = ctx.listCommands();
297
308
  if (cmds.length === 0) return "No commands available.";
298
- return cmds.map((c) => `/${c.name} \u2014 ${c.description}`).join("\n");
309
+ const escape = (text) => text.replace(/\|/g, "\\|").replace(/\n/g, " ");
310
+ return [
311
+ "| Command | Description |",
312
+ "| --- | --- |",
313
+ ...cmds.map((c) => `| \`/${c.name}\` | ${escape(c.description ?? "")} |`)
314
+ ].join("\n");
299
315
  }
300
316
  }
301
317
  ];
@@ -1976,17 +1992,18 @@ function usePickerKeyboard({
1976
1992
  useEffect3(() => {
1977
1993
  const handler = (e) => {
1978
1994
  if (e.key === "ArrowDown") {
1979
- if (count <= 0) return;
1980
1995
  e.preventDefault();
1981
- setActiveIdx((i) => Math.min(i + 1, count - 1));
1996
+ setActiveIdx((i) => count === 0 ? 0 : (i + 1) % count);
1982
1997
  } else if (e.key === "ArrowUp") {
1983
- if (count <= 0) return;
1984
1998
  e.preventDefault();
1985
- setActiveIdx((i) => Math.max(i - 1, 0));
1999
+ setActiveIdx((i) => count === 0 ? 0 : (i - 1 + count) % count);
1986
2000
  } else if (e.key === "Enter" || e.key === "Tab") {
1987
- if (count <= 0) return;
1988
- e.preventDefault();
1989
- selectRef.current(activeIdx);
2001
+ if (count > 0) {
2002
+ e.preventDefault();
2003
+ selectRef.current(activeIdx);
2004
+ } else {
2005
+ dismissRef.current();
2006
+ }
1990
2007
  } else if (e.key === "Escape") {
1991
2008
  e.preventDefault();
1992
2009
  dismissRef.current();
@@ -2680,53 +2697,104 @@ function scopedHeaders2(headers, storageScope) {
2680
2697
  return result;
2681
2698
  }
2682
2699
 
2683
- // src/front/hooks/useServerSkills.ts
2684
- import { useEffect as useEffect6, useState as useState8 } from "react";
2685
- function useServerSkills({
2686
- apiBaseUrl,
2687
- fetch: fetchImpl,
2700
+ // src/front/hooks/useServerCommands.ts
2701
+ import { useEffect as useEffect6, useRef as useRef7, useState as useState8 } from "react";
2702
+ function toSlashCommand(command, getSessionId, apiBaseUrl, requestHeaders, fetchImpl) {
2703
+ return {
2704
+ name: command.name,
2705
+ description: command.description ?? "",
2706
+ source: command.source,
2707
+ ...command.sourcePlugin ? { sourcePlugin: command.sourcePlugin } : {},
2708
+ handler: async (args) => {
2709
+ const base = apiBaseUrl?.replace(/\/$/, "") ?? "";
2710
+ const params = new URLSearchParams({ sessionId: getSessionId() });
2711
+ const url = `${base}/api/v1/agent/commands/execute?${params.toString()}`;
2712
+ try {
2713
+ const res = await fetchImpl(url, {
2714
+ method: "POST",
2715
+ headers: { ...requestHeaders ?? {}, "content-type": "application/json" },
2716
+ body: JSON.stringify({ name: command.name, args })
2717
+ });
2718
+ if (!res.ok) {
2719
+ const body = await res.json().catch(() => ({}));
2720
+ if (typeof globalThis.dispatchEvent === "function") {
2721
+ globalThis.dispatchEvent(new CustomEvent(WORKSPACE_COMMAND_NOTIFY_EVENT, {
2722
+ detail: { message: body.error ?? `/${command.name} failed`, tone: "error", command: command.name }
2723
+ }));
2724
+ }
2725
+ }
2726
+ } catch {
2727
+ if (typeof globalThis.dispatchEvent === "function") {
2728
+ globalThis.dispatchEvent(new CustomEvent(WORKSPACE_COMMAND_NOTIFY_EVENT, {
2729
+ detail: { message: `/${command.name} could not be reached`, tone: "error", command: command.name }
2730
+ }));
2731
+ }
2732
+ }
2733
+ }
2734
+ };
2735
+ }
2736
+ function useServerCommands({
2688
2737
  registry,
2689
2738
  requestHeaders,
2739
+ sessionId,
2740
+ apiBaseUrl,
2741
+ fetch: fetchImpl,
2690
2742
  storageScope,
2691
- refreshKey,
2692
- enabled = true
2743
+ enabled = true,
2744
+ refreshKey = 0
2693
2745
  }) {
2694
- const [skillsStamp, setSkillsStamp] = useState8(0);
2746
+ const [stamp, setStamp] = useState8(0);
2747
+ const registeredNamesRef = useRef7(/* @__PURE__ */ new Set());
2748
+ const sessionIdRef = useRef7(sessionId);
2695
2749
  useEffect6(() => {
2696
- if (!enabled) return;
2750
+ sessionIdRef.current = sessionId;
2751
+ }, [sessionId]);
2752
+ useEffect6(() => {
2753
+ const clearRegistered = () => {
2754
+ if (registeredNamesRef.current.size === 0) return false;
2755
+ for (const name of registeredNamesRef.current) registry.unregister(name);
2756
+ registeredNamesRef.current = /* @__PURE__ */ new Set();
2757
+ return true;
2758
+ };
2759
+ if (!enabled) {
2760
+ if (clearRegistered()) setStamp((n) => n + 1);
2761
+ return;
2762
+ }
2697
2763
  let aborted = false;
2698
2764
  const nextFetch = fetchImpl ?? globalThis.fetch.bind(globalThis);
2699
- const path = refreshKey ? "/api/v1/agent/skills?refresh=1" : "/api/v1/agent/skills";
2700
- nextFetch(agentResourceUrl2(apiBaseUrl, path), {
2701
- headers: scopedHeaders3(requestHeaders, storageScope)
2702
- }).then((res) => res.ok ? res.json() : null).then((payload) => {
2703
- if (aborted || !payload?.skills) return;
2704
- let added = 0;
2705
- for (const skill of payload.skills) {
2706
- if (!registry.get(skill.name)) {
2707
- registry.register({ name: skill.name, description: skill.description, kind: "skill", handler: () => {
2708
- } });
2709
- added++;
2710
- }
2765
+ const base = apiBaseUrl?.replace(/\/$/, "") ?? "";
2766
+ const url = `${base}/api/v1/agent/commands?sessionId=${encodeURIComponent(sessionIdRef.current)}`;
2767
+ const headers = scopedHeaders3(requestHeaders, storageScope);
2768
+ nextFetch(url, { headers }).then(async (res) => {
2769
+ if (!res.ok) throw new Error(`commands request failed (${res.status})`);
2770
+ return await res.json();
2771
+ }).then((payload) => {
2772
+ if (aborted) return;
2773
+ const removed = clearRegistered();
2774
+ let added = false;
2775
+ for (const serverCommand of payload.commands ?? []) {
2776
+ const command = toSlashCommand(serverCommand, () => sessionIdRef.current, apiBaseUrl, headers, nextFetch);
2777
+ if (registry.get(command.name)) continue;
2778
+ registry.register(command);
2779
+ registeredNamesRef.current.add(command.name);
2780
+ added = true;
2711
2781
  }
2712
- if (added > 0) setSkillsStamp((n) => n + 1);
2782
+ if (removed || added) setStamp((n) => n + 1);
2713
2783
  }).catch(() => {
2784
+ if (aborted) return;
2785
+ if (clearRegistered()) setStamp((n) => n + 1);
2714
2786
  });
2715
2787
  return () => {
2716
2788
  aborted = true;
2717
2789
  };
2718
2790
  }, [apiBaseUrl, enabled, fetchImpl, refreshKey, requestHeaders, registry, storageScope]);
2719
- return skillsStamp;
2720
- }
2721
- function agentResourceUrl2(apiBaseUrl, path) {
2722
- const base = apiBaseUrl?.replace(/\/$/, "") ?? "";
2723
- return `${base}${path}`;
2791
+ return stamp;
2724
2792
  }
2725
2793
  function scopedHeaders3(headers, storageScope) {
2726
2794
  if (!headers && !storageScope) return void 0;
2727
2795
  const result = { ...headers ?? {} };
2728
- const hasStorageScope = Object.keys(result).some((key) => key.toLowerCase() === "x-boring-storage-scope");
2729
- if (storageScope && !hasStorageScope) result["x-boring-storage-scope"] = storageScope;
2796
+ const hasScope = Object.keys(result).some((k) => k.toLowerCase() === "x-boring-storage-scope");
2797
+ if (storageScope && !hasScope) result["x-boring-storage-scope"] = storageScope;
2730
2798
  return result;
2731
2799
  }
2732
2800
 
@@ -3320,7 +3388,7 @@ function resolveStorage2(storage) {
3320
3388
  }
3321
3389
 
3322
3390
  // src/front/chat/session/usePiSessions.ts
3323
- import { useCallback as useCallback7, useEffect as useEffect8, useMemo as useMemo3, useRef as useRef7, useState as useState10 } from "react";
3391
+ import { useCallback as useCallback7, useEffect as useEffect8, useMemo as useMemo3, useRef as useRef8, useState as useState10 } from "react";
3324
3392
 
3325
3393
  // src/front/chat/pi/piChatAssistantCommit.ts
3326
3394
  function commitFinalMessage(state, messageId, final) {
@@ -4895,20 +4963,20 @@ function usePiSessions(options = {}) {
4895
4963
  const [loadingMore, setLoadingMore] = useState10(false);
4896
4964
  const [hasMore, setHasMore] = useState10(false);
4897
4965
  const [error, setError] = useState10(void 0);
4898
- const mountedRef = useRef7(false);
4899
- const refreshVersionRef = useRef7(0);
4900
- const retryTimerRef = useRef7(void 0);
4901
- const sessionsRef = useRef7([]);
4902
- const activeSessionIdRef = useRef7(activeSessionId);
4903
- const hasMoreRef = useRef7(hasMore);
4904
- const canonicalLoadedCountRef = useRef7(0);
4905
- const loadMoreRequestSeqRef = useRef7(0);
4906
- const loadMoreInFlightRef = useRef7(false);
4907
- const pendingCreatedRef = useRef7(/* @__PURE__ */ new Map());
4908
- const pendingCreatedScopeRef = useRef7(requestScopeKey);
4909
- const dataStorageScopeRef = useRef7(storageScope);
4910
- const loadedDataSourceRef = useRef7(dataSourceKey);
4911
- const requestScopeRef = useRef7(requestScopeKey);
4966
+ const mountedRef = useRef8(false);
4967
+ const refreshVersionRef = useRef8(0);
4968
+ const retryTimerRef = useRef8(void 0);
4969
+ const sessionsRef = useRef8([]);
4970
+ const activeSessionIdRef = useRef8(activeSessionId);
4971
+ const hasMoreRef = useRef8(hasMore);
4972
+ const canonicalLoadedCountRef = useRef8(0);
4973
+ const loadMoreRequestSeqRef = useRef8(0);
4974
+ const loadMoreInFlightRef = useRef8(false);
4975
+ const pendingCreatedRef = useRef8(/* @__PURE__ */ new Map());
4976
+ const pendingCreatedScopeRef = useRef8(requestScopeKey);
4977
+ const dataStorageScopeRef = useRef8(storageScope);
4978
+ const loadedDataSourceRef = useRef8(dataSourceKey);
4979
+ const requestScopeRef = useRef8(requestScopeKey);
4912
4980
  requestScopeRef.current = requestScopeKey;
4913
4981
  useEffect8(() => {
4914
4982
  sessionsRef.current = sessions;
@@ -5429,7 +5497,7 @@ function sortByUpdatedDesc(a, b) {
5429
5497
 
5430
5498
  // src/front/chat/components/PiConversationSurface.tsx
5431
5499
  import { Loader2 as Loader22 } from "lucide-react";
5432
- import { useCallback as useCallback14, useEffect as useEffect14, useLayoutEffect, useRef as useRef9, useState as useState15 } from "react";
5500
+ import { useCallback as useCallback14, useEffect as useEffect14, useLayoutEffect, useRef as useRef10, useState as useState15 } from "react";
5433
5501
  import { useStickToBottomContext as useStickToBottomContext2 } from "use-stick-to-bottom";
5434
5502
 
5435
5503
  // src/front/primitives/conversation.tsx
@@ -6125,7 +6193,7 @@ import {
6125
6193
  useContext as useContext5,
6126
6194
  useEffect as useEffect11,
6127
6195
  useMemo as useMemo8,
6128
- useRef as useRef8,
6196
+ useRef as useRef9,
6129
6197
  useState as useState12
6130
6198
  } from "react";
6131
6199
  import { Streamdown as Streamdown2 } from "streamdown";
@@ -6217,9 +6285,9 @@ var Reasoning = memo5(
6217
6285
  defaultProp: void 0,
6218
6286
  prop: durationProp
6219
6287
  });
6220
- const hasEverStreamedRef = useRef8(isStreaming);
6288
+ const hasEverStreamedRef = useRef9(isStreaming);
6221
6289
  const [hasAutoClosed, setHasAutoClosed] = useState12(false);
6222
- const startTimeRef = useRef8(null);
6290
+ const startTimeRef = useRef9(null);
6223
6291
  useEffect11(() => {
6224
6292
  if (isStreaming) {
6225
6293
  hasEverStreamedRef.current = true;
@@ -6947,8 +7015,8 @@ function PiConversationSurface({
6947
7015
  }
6948
7016
  function TranscriptHistoryLoader({ olderCount, onLoadOlder }) {
6949
7017
  const { scrollRef } = useStickToBottomContext2();
6950
- const pendingAnchor = useRef9(null);
6951
- const armed = useRef9(true);
7018
+ const pendingAnchor = useRef10(null);
7019
+ const armed = useRef10(true);
6952
7020
  useEffect14(() => {
6953
7021
  const el = scrollRef.current;
6954
7022
  if (!el) return;
@@ -7009,38 +7077,34 @@ function buildMessageRenderItems(messages) {
7009
7077
  }
7010
7078
 
7011
7079
  // src/front/chat/components/PiChatComposerSurface.tsx
7012
- import { useCallback as useCallback17, useLayoutEffect as useLayoutEffect2 } from "react";
7080
+ import { useCallback as useCallback16, useLayoutEffect as useLayoutEffect2 } from "react";
7013
7081
  import { motion as motion2 } from "motion/react";
7014
7082
 
7015
7083
  // src/front/composer/PluginUpdateStatus.tsx
7016
- import { useEffect as useEffect15, useRef as useRef10 } from "react";
7017
- import { AlertCircleIcon as AlertCircleIcon2, XIcon as XIcon4 } from "lucide-react";
7084
+ import { useEffect as useEffect15, useRef as useRef11 } from "react";
7085
+
7086
+ // src/front/composer/ComposerStatusBanner.tsx
7018
7087
  import { jsx as jsx24, jsxs as jsxs22 } from "react/jsx-runtime";
7019
- function PluginUpdateStatus({
7020
- state,
7088
+ function ComposerStatusBanner({
7089
+ tone,
7090
+ dataAttribute,
7091
+ runningContent,
7092
+ title,
7093
+ detail,
7094
+ message,
7095
+ children,
7021
7096
  onDismiss,
7022
7097
  onRetry,
7023
- successAutoDismissMs = 1400,
7098
+ retryLabel = "Try again",
7099
+ dismissAriaLabel = "Dismiss status",
7024
7100
  maxWidthClassName = "max-w-3xl"
7025
7101
  }) {
7026
- const onDismissRef = useRef10(onDismiss);
7027
- useEffect15(() => {
7028
- onDismissRef.current = onDismiss;
7029
- }, [onDismiss]);
7030
- const successDismissKey = state?.kind === "success" ? `${state.reloaded}:${(state.restartWarnings?.length ?? 0) > 0 || (state.diagnostics?.length ?? 0) > 0}` : null;
7031
- useEffect15(() => {
7032
- if (!state || state.kind !== "success" || successAutoDismissMs <= 0) return;
7033
- const hasWarningsOrDiagnostics = (state.restartWarnings?.length ?? 0) > 0 || (state.diagnostics?.length ?? 0) > 0;
7034
- if (hasWarningsOrDiagnostics) return;
7035
- const timeout = window.setTimeout(() => onDismissRef.current(), successAutoDismissMs);
7036
- return () => window.clearTimeout(timeout);
7037
- }, [state?.kind, successDismissKey, successAutoDismissMs]);
7038
- if (!state) return null;
7039
- if (state.kind === "running") {
7102
+ const toneAttr = { [dataAttribute]: tone };
7103
+ if (tone === "running") {
7040
7104
  return /* @__PURE__ */ jsxs22(
7041
7105
  "div",
7042
7106
  {
7043
- "data-boring-plugin-update": "running",
7107
+ ...toneAttr,
7044
7108
  role: "status",
7045
7109
  "aria-live": "polite",
7046
7110
  className: cn(
@@ -7050,22 +7114,16 @@ function PluginUpdateStatus({
7050
7114
  ),
7051
7115
  children: [
7052
7116
  /* @__PURE__ */ jsx24("span", { className: "inline-block h-2 w-2 animate-pulse rounded-full bg-accent", "aria-hidden": "true" }),
7053
- /* @__PURE__ */ jsx24("span", { children: "Updating plugins\u2026" })
7117
+ /* @__PURE__ */ jsx24("span", { children: runningContent })
7054
7118
  ]
7055
7119
  }
7056
7120
  );
7057
7121
  }
7058
- if (state.kind === "success") {
7059
- const warnings = state.restartWarnings ?? [];
7060
- const diagnostics = state.diagnostics ?? [];
7061
- const frontEvents = state.frontEvents ?? [];
7062
- const hasWarningsOrDiagnostics = warnings.length > 0 || diagnostics.length > 0;
7063
- const title = state.reloaded ? hasWarningsOrDiagnostics ? "Reload finished with warnings" : "Reload complete" : "Reload queued";
7064
- const detail = state.reloaded ? hasWarningsOrDiagnostics ? "Some plugin changes need attention." : frontEvents.length > 0 ? `${frontEvents.length} plugin module${frontEvents.length === 1 ? "" : "s"} refreshed. Changes are live.` : "Changes are live." : void 0;
7122
+ if (tone === "success") {
7065
7123
  return /* @__PURE__ */ jsxs22(
7066
7124
  "div",
7067
7125
  {
7068
- "data-boring-plugin-update": "success",
7126
+ ...toneAttr,
7069
7127
  role: "status",
7070
7128
  "aria-live": "polite",
7071
7129
  className: cn(
@@ -7080,18 +7138,115 @@ function PluginUpdateStatus({
7080
7138
  /* @__PURE__ */ jsx24("span", { className: "block font-medium leading-5", children: title }),
7081
7139
  detail ? /* @__PURE__ */ jsx24("span", { className: "block leading-4 text-muted-foreground", children: detail }) : null
7082
7140
  ] }),
7083
- /* @__PURE__ */ jsx24(
7141
+ onDismiss ? /* @__PURE__ */ jsx24(
7084
7142
  "button",
7085
7143
  {
7086
7144
  type: "button",
7087
7145
  onClick: onDismiss,
7088
7146
  className: "-mr-1 rounded border border-transparent px-1.5 py-0.5 text-[13px] leading-none text-muted-foreground hover:bg-muted hover:text-foreground",
7089
- "aria-label": "Dismiss plugin update status",
7147
+ "aria-label": dismissAriaLabel,
7090
7148
  children: "\xD7"
7091
7149
  }
7092
- )
7150
+ ) : null
7093
7151
  ] }),
7094
- diagnostics.length > 0 ? /* @__PURE__ */ jsxs22(
7152
+ children
7153
+ ]
7154
+ }
7155
+ );
7156
+ }
7157
+ return /* @__PURE__ */ jsxs22(
7158
+ "div",
7159
+ {
7160
+ ...toneAttr,
7161
+ role: "status",
7162
+ "aria-live": "polite",
7163
+ className: cn(
7164
+ "mx-auto mb-2 w-full rounded-[var(--radius-md)] border border-destructive/40 bg-destructive/10",
7165
+ "px-3 py-2 text-xs text-foreground",
7166
+ maxWidthClassName
7167
+ ),
7168
+ children: [
7169
+ /* @__PURE__ */ jsxs22("div", { className: "flex items-center gap-2", children: [
7170
+ /* @__PURE__ */ jsx24("span", { className: "text-destructive", "aria-hidden": "true", children: "\u26A0" }),
7171
+ /* @__PURE__ */ jsx24("span", { className: "flex-1 font-medium", children: title }),
7172
+ onRetry ? /* @__PURE__ */ jsx24(
7173
+ "button",
7174
+ {
7175
+ type: "button",
7176
+ onClick: onRetry,
7177
+ className: "rounded border border-destructive/40 px-2 py-0.5 text-[11px] font-medium hover:bg-destructive/10",
7178
+ children: retryLabel
7179
+ }
7180
+ ) : null,
7181
+ onDismiss ? /* @__PURE__ */ jsx24(
7182
+ "button",
7183
+ {
7184
+ type: "button",
7185
+ onClick: onDismiss,
7186
+ className: "rounded border border-transparent px-2 py-0.5 text-[11px] font-medium text-muted-foreground hover:bg-muted hover:text-foreground",
7187
+ "aria-label": dismissAriaLabel,
7188
+ children: "Dismiss"
7189
+ }
7190
+ ) : null
7191
+ ] }),
7192
+ message ? /* @__PURE__ */ jsx24("pre", { className: "mt-2 whitespace-pre-wrap break-words text-[11px] text-destructive/90", children: message }) : null
7193
+ ]
7194
+ }
7195
+ );
7196
+ }
7197
+
7198
+ // src/front/composer/PluginUpdateStatus.tsx
7199
+ import { jsx as jsx25, jsxs as jsxs23 } from "react/jsx-runtime";
7200
+ function PluginUpdateStatus({
7201
+ state,
7202
+ onDismiss,
7203
+ onRetry,
7204
+ successAutoDismissMs = 1400,
7205
+ maxWidthClassName = "max-w-3xl"
7206
+ }) {
7207
+ const onDismissRef = useRef11(onDismiss);
7208
+ useEffect15(() => {
7209
+ onDismissRef.current = onDismiss;
7210
+ }, [onDismiss]);
7211
+ const successDismissKey = state?.kind === "success" ? `${state.reloaded}:${(state.restartWarnings?.length ?? 0) > 0 || (state.diagnostics?.length ?? 0) > 0}` : null;
7212
+ useEffect15(() => {
7213
+ if (!state || state.kind !== "success" || successAutoDismissMs <= 0) return;
7214
+ const hasWarningsOrDiagnostics = (state.restartWarnings?.length ?? 0) > 0 || (state.diagnostics?.length ?? 0) > 0;
7215
+ if (hasWarningsOrDiagnostics) return;
7216
+ const timeout = window.setTimeout(() => onDismissRef.current(), successAutoDismissMs);
7217
+ return () => window.clearTimeout(timeout);
7218
+ }, [state?.kind, successDismissKey, successAutoDismissMs]);
7219
+ if (!state) return null;
7220
+ if (state.kind === "running") {
7221
+ return /* @__PURE__ */ jsx25(
7222
+ ComposerStatusBanner,
7223
+ {
7224
+ tone: "running",
7225
+ dataAttribute: "data-boring-plugin-update",
7226
+ maxWidthClassName,
7227
+ runningContent: "Updating plugins\u2026"
7228
+ }
7229
+ );
7230
+ }
7231
+ if (state.kind === "success") {
7232
+ const warnings = state.restartWarnings ?? [];
7233
+ const diagnostics = state.diagnostics ?? [];
7234
+ const frontEvents = state.frontEvents ?? [];
7235
+ const hasWarningsOrDiagnostics = warnings.length > 0 || diagnostics.length > 0;
7236
+ const title = state.reloaded ? hasWarningsOrDiagnostics ? "Reload finished with warnings" : "Reload complete" : "Reload queued";
7237
+ const detail = state.reloaded ? hasWarningsOrDiagnostics ? "Some plugin changes need attention." : frontEvents.length > 0 ? `${frontEvents.length} plugin module${frontEvents.length === 1 ? "" : "s"} refreshed. Changes are live.` : "Changes are live." : void 0;
7238
+ return /* @__PURE__ */ jsxs23(
7239
+ ComposerStatusBanner,
7240
+ {
7241
+ tone: "success",
7242
+ dataAttribute: "data-boring-plugin-update",
7243
+ maxWidthClassName,
7244
+ title,
7245
+ detail,
7246
+ onDismiss,
7247
+ dismissAriaLabel: "Dismiss plugin update status",
7248
+ children: [
7249
+ diagnostics.length > 0 ? /* @__PURE__ */ jsxs23(
7095
7250
  "div",
7096
7251
  {
7097
7252
  "data-boring-plugin-update-diagnostics": "",
@@ -7100,24 +7255,24 @@ function PluginUpdateStatus({
7100
7255
  "px-2 py-1.5 text-[11px] text-foreground"
7101
7256
  ),
7102
7257
  children: [
7103
- /* @__PURE__ */ jsxs22("div", { className: "flex items-center gap-1.5 font-medium text-[oklch(0.48_0.15_60)]", children: [
7104
- /* @__PURE__ */ jsx24("span", { "aria-hidden": "true", children: "\u26A0" }),
7105
- /* @__PURE__ */ jsxs22("span", { children: [
7258
+ /* @__PURE__ */ jsxs23("div", { className: "flex items-center gap-1.5 font-medium text-[oklch(0.48_0.15_60)]", children: [
7259
+ /* @__PURE__ */ jsx25("span", { "aria-hidden": "true", children: "\u26A0" }),
7260
+ /* @__PURE__ */ jsxs23("span", { children: [
7106
7261
  "Reload diagnostics for ",
7107
7262
  diagnostics.length,
7108
7263
  " plugin",
7109
7264
  diagnostics.length === 1 ? "" : "s"
7110
7265
  ] })
7111
7266
  ] }),
7112
- /* @__PURE__ */ jsx24("ul", { className: "mt-1 ml-4 list-disc text-foreground/85", children: diagnostics.map((diagnostic, index) => /* @__PURE__ */ jsxs22("li", { children: [
7113
- /* @__PURE__ */ jsx24("code", { className: "font-mono text-[10.5px]", children: diagnostic.pluginId ?? diagnostic.source ?? "plugin" }),
7267
+ /* @__PURE__ */ jsx25("ul", { className: "mt-1 ml-4 list-disc text-foreground/85", children: diagnostics.map((diagnostic, index) => /* @__PURE__ */ jsxs23("li", { children: [
7268
+ /* @__PURE__ */ jsx25("code", { className: "font-mono text-[10.5px]", children: diagnostic.pluginId ?? diagnostic.source ?? "plugin" }),
7114
7269
  " \u2014 ",
7115
7270
  diagnostic.message ?? "reload diagnostic"
7116
7271
  ] }, `${diagnostic.pluginId ?? diagnostic.source ?? "plugin"}-${index}`)) })
7117
7272
  ]
7118
7273
  }
7119
7274
  ) : null,
7120
- warnings.length > 0 ? /* @__PURE__ */ jsxs22(
7275
+ warnings.length > 0 ? /* @__PURE__ */ jsxs23(
7121
7276
  "div",
7122
7277
  {
7123
7278
  "data-boring-plugin-update-restart-warning": "",
@@ -7126,21 +7281,21 @@ function PluginUpdateStatus({
7126
7281
  "px-2 py-1.5 text-[11px] text-foreground"
7127
7282
  ),
7128
7283
  children: [
7129
- /* @__PURE__ */ jsxs22("div", { className: "flex items-center gap-1.5 font-medium text-[oklch(0.48_0.15_60)]", children: [
7130
- /* @__PURE__ */ jsx24("span", { "aria-hidden": "true", children: "\u26A0" }),
7131
- /* @__PURE__ */ jsxs22("span", { children: [
7284
+ /* @__PURE__ */ jsxs23("div", { className: "flex items-center gap-1.5 font-medium text-[oklch(0.48_0.15_60)]", children: [
7285
+ /* @__PURE__ */ jsx25("span", { "aria-hidden": "true", children: "\u26A0" }),
7286
+ /* @__PURE__ */ jsxs23("span", { children: [
7132
7287
  "Restart needed for ",
7133
7288
  warnings.length,
7134
7289
  " plugin",
7135
7290
  warnings.length === 1 ? "" : "s"
7136
7291
  ] })
7137
7292
  ] }),
7138
- /* @__PURE__ */ jsx24("ul", { className: "mt-1 ml-4 list-disc text-foreground/85", children: warnings.map((w) => /* @__PURE__ */ jsxs22("li", { children: [
7139
- /* @__PURE__ */ jsx24("code", { className: "font-mono text-[10.5px]", children: w.id }),
7293
+ /* @__PURE__ */ jsx25("ul", { className: "mt-1 ml-4 list-disc text-foreground/85", children: warnings.map((w) => /* @__PURE__ */ jsxs23("li", { children: [
7294
+ /* @__PURE__ */ jsx25("code", { className: "font-mono text-[10.5px]", children: w.id }),
7140
7295
  " \u2014 ",
7141
7296
  w.surfaces.join(" + ")
7142
7297
  ] }, w.id)) }),
7143
- /* @__PURE__ */ jsx24("p", { className: "mt-1 text-foreground/70", children: "The front bundle reloaded successfully, but routes and agent tools were wired at boot. Stop and restart the workspace process (Ctrl-C, then re-run your dev command) to pick up the new code." })
7298
+ /* @__PURE__ */ jsx25("p", { className: "mt-1 text-foreground/70", children: "The front bundle reloaded successfully, but routes and agent tools were wired at boot. Stop and restart the workspace process (Ctrl-C, then re-run your dev command) to pick up the new code." })
7144
7299
  ]
7145
7300
  }
7146
7301
  ) : null
@@ -7148,45 +7303,104 @@ function PluginUpdateStatus({
7148
7303
  }
7149
7304
  );
7150
7305
  }
7151
- return /* @__PURE__ */ jsxs22(
7152
- "div",
7306
+ return /* @__PURE__ */ jsx25(
7307
+ ComposerStatusBanner,
7153
7308
  {
7154
- "data-boring-plugin-update": "error",
7155
- role: "status",
7156
- "aria-live": "polite",
7157
- className: noticeSurfaceClass("error", cn("mx-auto mb-2 w-full text-xs", maxWidthClassName)),
7158
- children: [
7159
- /* @__PURE__ */ jsxs22("div", { className: "flex items-start gap-2.5", children: [
7160
- /* @__PURE__ */ jsx24(AlertCircleIcon2, { className: noticeIconClass("error", "size-3.5"), "aria-hidden": "true" }),
7161
- /* @__PURE__ */ jsx24("span", { className: "flex-1 font-medium", children: "Plugin update failed." }),
7162
- /* @__PURE__ */ jsx24(
7163
- "button",
7164
- {
7165
- type: "button",
7166
- onClick: onRetry,
7167
- className: "shrink-0 rounded border border-destructive/25 px-2 py-0.5 text-[11px] font-medium hover:bg-destructive/10",
7168
- children: "Try again"
7169
- }
7170
- ),
7171
- /* @__PURE__ */ jsx24(
7172
- "button",
7173
- {
7174
- type: "button",
7175
- onClick: onDismiss,
7176
- className: "-mr-1 -mt-1 inline-flex size-6 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted hover:text-foreground",
7177
- "aria-label": "Dismiss plugin update status",
7178
- children: /* @__PURE__ */ jsx24(XIcon4, { className: "size-3", "aria-hidden": "true" })
7179
- }
7180
- )
7309
+ tone: "error",
7310
+ dataAttribute: "data-boring-plugin-update",
7311
+ maxWidthClassName,
7312
+ title: "Plugin update failed.",
7313
+ message: state.message,
7314
+ onRetry,
7315
+ onDismiss,
7316
+ retryLabel: "Try again",
7317
+ dismissAriaLabel: "Dismiss plugin update status"
7318
+ }
7319
+ );
7320
+ }
7321
+
7322
+ // src/front/composer/CommandRunStatus.tsx
7323
+ import { useEffect as useEffect16, useRef as useRef12 } from "react";
7324
+ import { Fragment as Fragment7, jsx as jsx26, jsxs as jsxs24 } from "react/jsx-runtime";
7325
+ function CommandRunStatus({
7326
+ state,
7327
+ onDismiss,
7328
+ successAutoDismissMs = 1400,
7329
+ maxWidthClassName = "max-w-3xl"
7330
+ }) {
7331
+ const onDismissRef = useRef12(onDismiss);
7332
+ useEffect16(() => {
7333
+ onDismissRef.current = onDismiss;
7334
+ }, [onDismiss]);
7335
+ const successKey = state?.kind === "success" ? `${state.runId}:${state.command}:${state.detail ?? ""}` : null;
7336
+ useEffect16(() => {
7337
+ if (!state || state.kind !== "success" || successAutoDismissMs <= 0) return;
7338
+ const timeout = window.setTimeout(() => onDismissRef.current(), successAutoDismissMs);
7339
+ return () => window.clearTimeout(timeout);
7340
+ }, [state?.kind, successKey, successAutoDismissMs]);
7341
+ if (!state) return null;
7342
+ if (state.kind === "running") {
7343
+ return /* @__PURE__ */ jsx26(
7344
+ ComposerStatusBanner,
7345
+ {
7346
+ tone: "running",
7347
+ dataAttribute: "data-boring-command-run",
7348
+ maxWidthClassName,
7349
+ runningContent: state.command ? /* @__PURE__ */ jsxs24(Fragment7, { children: [
7350
+ " Running ",
7351
+ /* @__PURE__ */ jsxs24("code", { className: "font-mono", children: [
7352
+ "/",
7353
+ state.command
7354
+ ] }),
7355
+ "\u2026 "
7356
+ ] }) : "Running command\u2026"
7357
+ }
7358
+ );
7359
+ }
7360
+ if (state.kind === "success") {
7361
+ return /* @__PURE__ */ jsx26(
7362
+ ComposerStatusBanner,
7363
+ {
7364
+ tone: "success",
7365
+ dataAttribute: "data-boring-command-run",
7366
+ maxWidthClassName,
7367
+ title: state.command ? /* @__PURE__ */ jsxs24(Fragment7, { children: [
7368
+ " Ran ",
7369
+ /* @__PURE__ */ jsxs24("code", { className: "font-mono", children: [
7370
+ "/",
7371
+ state.command
7372
+ ] }),
7373
+ " "
7374
+ ] }) : "Command ran",
7375
+ detail: state.detail,
7376
+ onDismiss,
7377
+ dismissAriaLabel: "Dismiss command status"
7378
+ }
7379
+ );
7380
+ }
7381
+ return /* @__PURE__ */ jsx26(
7382
+ ComposerStatusBanner,
7383
+ {
7384
+ tone: "error",
7385
+ dataAttribute: "data-boring-command-run",
7386
+ maxWidthClassName,
7387
+ title: state.command ? /* @__PURE__ */ jsxs24(Fragment7, { children: [
7388
+ " ",
7389
+ /* @__PURE__ */ jsxs24("code", { className: "font-mono", children: [
7390
+ "/",
7391
+ state.command
7181
7392
  ] }),
7182
- /* @__PURE__ */ jsx24("pre", { className: noticeTextClass("mt-2 text-[11px] text-muted-foreground"), children: state.message })
7183
- ]
7393
+ " failed. "
7394
+ ] }) : "Command failed.",
7395
+ message: state.message,
7396
+ onDismiss,
7397
+ dismissAriaLabel: "Dismiss command status"
7184
7398
  }
7185
7399
  );
7186
7400
  }
7187
7401
 
7188
7402
  // src/front/chatPanelComposerControls.tsx
7189
- import { forwardRef, useEffect as useEffect16, useRef as useRef11, useState as useState16 } from "react";
7403
+ import { forwardRef, useEffect as useEffect17, useRef as useRef13, useState as useState16 } from "react";
7190
7404
  import { CheckIcon as CheckIcon3, ChevronDownIcon as ChevronDownIcon5 } from "lucide-react";
7191
7405
  import {
7192
7406
  Command,
@@ -7220,7 +7434,7 @@ function displayProviderLabel(provider) {
7220
7434
  }
7221
7435
 
7222
7436
  // src/front/chatPanelComposerControls.tsx
7223
- import { Fragment as Fragment7, jsx as jsx25, jsxs as jsxs23 } from "react/jsx-runtime";
7437
+ import { Fragment as Fragment8, jsx as jsx27, jsxs as jsxs25 } from "react/jsx-runtime";
7224
7438
  var composerActionClass = cn(
7225
7439
  "inline-flex h-8 items-center justify-center gap-1.5 rounded-[var(--radius-md)] border-0 bg-transparent",
7226
7440
  "text-muted-foreground shadow-none transition",
@@ -7270,7 +7484,7 @@ function isTextInputTarget(target) {
7270
7484
  return target instanceof HTMLInputElement;
7271
7485
  }
7272
7486
  function useDismissOnOutsidePointer(ref, onClose) {
7273
- useEffect16(() => {
7487
+ useEffect17(() => {
7274
7488
  if (!onClose) return;
7275
7489
  const handler = (event) => {
7276
7490
  const target = event.target;
@@ -7288,7 +7502,7 @@ function useDismissOnOutsidePointer(ref, onClose) {
7288
7502
  }
7289
7503
  function ThinkingLevelGlyph({ level }) {
7290
7504
  const lit = level === "off" ? 0 : level === "low" ? 1 : level === "medium" ? 2 : 3;
7291
- return /* @__PURE__ */ jsx25(
7505
+ return /* @__PURE__ */ jsx27(
7292
7506
  "svg",
7293
7507
  {
7294
7508
  "aria-hidden": "true",
@@ -7297,7 +7511,7 @@ function ThinkingLevelGlyph({ level }) {
7297
7511
  viewBox: "0 0 14 14",
7298
7512
  fill: "none",
7299
7513
  className: "shrink-0",
7300
- children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx25(
7514
+ children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx27(
7301
7515
  "rect",
7302
7516
  {
7303
7517
  x: 2 + i * 4,
@@ -7354,7 +7568,7 @@ var ModelSelectTrigger = forwardRef(function ModelSelectTrigger2({
7354
7568
  }, ref) {
7355
7569
  const triggerLabel = modelTriggerLabel(value, options);
7356
7570
  const triggerDisplay = value ? `${triggerLabel} (${displayProviderLabel(value.provider)})` : triggerLabel;
7357
- return /* @__PURE__ */ jsx25(
7571
+ return /* @__PURE__ */ jsx27(
7358
7572
  "button",
7359
7573
  {
7360
7574
  ref,
@@ -7372,12 +7586,12 @@ var ModelSelectTrigger = forwardRef(function ModelSelectTrigger2({
7372
7586
  className
7373
7587
  ),
7374
7588
  ...props,
7375
- children: trigger === "slash" ? /* @__PURE__ */ jsxs23(Fragment7, { children: [
7376
- /* @__PURE__ */ jsx25("span", { className: "text-muted-foreground", children: "/model: " }),
7377
- /* @__PURE__ */ jsx25("span", { className: "text-foreground", children: triggerDisplay })
7378
- ] }) : /* @__PURE__ */ jsxs23(Fragment7, { children: [
7379
- /* @__PURE__ */ jsx25("span", { className: "min-w-0 truncate", children: triggerDisplay }),
7380
- /* @__PURE__ */ jsx25(ChevronDownIcon5, { className: "h-3 w-3 shrink-0 text-muted-foreground/50", "aria-hidden": "true" })
7589
+ children: trigger === "slash" ? /* @__PURE__ */ jsxs25(Fragment8, { children: [
7590
+ /* @__PURE__ */ jsx27("span", { className: "text-muted-foreground", children: "/model: " }),
7591
+ /* @__PURE__ */ jsx27("span", { className: "text-foreground", children: triggerDisplay })
7592
+ ] }) : /* @__PURE__ */ jsxs25(Fragment8, { children: [
7593
+ /* @__PURE__ */ jsx27("span", { className: "min-w-0 truncate", children: triggerDisplay }),
7594
+ /* @__PURE__ */ jsx27(ChevronDownIcon5, { className: "h-3 w-3 shrink-0 text-muted-foreground/50", "aria-hidden": "true" })
7381
7595
  ] })
7382
7596
  }
7383
7597
  );
@@ -7397,18 +7611,18 @@ function ModelPickerMenu({
7397
7611
  const keyboardOptions = [null, ...groupedOptions];
7398
7612
  const selectedIndex = currentKey ? Math.max(0, keyboardOptions.findIndex((option) => option && encodeModelKey(option) === currentKey)) : 0;
7399
7613
  const [activeIndex, setActiveIndex] = useState16(selectedIndex);
7400
- const activeIndexRef = useRef11(selectedIndex);
7401
- const menuRef = useRef11(null);
7614
+ const activeIndexRef = useRef13(selectedIndex);
7615
+ const menuRef = useRef13(null);
7402
7616
  useDismissOnOutsidePointer(menuRef, onClose);
7403
7617
  const setKeyboardActiveIndex = (next) => {
7404
7618
  const resolved = typeof next === "function" ? next(activeIndexRef.current) : next;
7405
7619
  activeIndexRef.current = resolved;
7406
7620
  setActiveIndex(resolved);
7407
7621
  };
7408
- useEffect16(() => {
7622
+ useEffect17(() => {
7409
7623
  setKeyboardActiveIndex(selectedIndex);
7410
7624
  }, [selectedIndex]);
7411
- useEffect16(() => {
7625
+ useEffect17(() => {
7412
7626
  const handler = (event) => {
7413
7627
  if (event.key === "Escape") {
7414
7628
  event.preventDefault();
@@ -7432,8 +7646,8 @@ function ModelPickerMenu({
7432
7646
  return () => window.removeEventListener("keydown", handler, { capture: true });
7433
7647
  }, [activeIndex, disabled, keyboardOptions, onChange, onClose]);
7434
7648
  const optionIndexes = new Map(groupedOptions.map((option, index) => [encodeModelKey(option), index + 1]));
7435
- return /* @__PURE__ */ jsx25("div", { ref: menuRef, "data-boring-agent": "", "data-boring-agent-part": "model-picker-menu", className: cn(composerPickerMenuClass, className), children: /* @__PURE__ */ jsxs23(Command, { className: "bg-transparent text-[color:var(--popover-foreground)]", children: [
7436
- menuOptions.length > 8 && /* @__PURE__ */ jsx25("div", { className: "border-b border-border/60 px-2", children: /* @__PURE__ */ jsx25(
7649
+ 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: [
7650
+ menuOptions.length > 8 && /* @__PURE__ */ jsx27("div", { className: "border-b border-border/60 px-2", children: /* @__PURE__ */ jsx27(
7437
7651
  CommandInput,
7438
7652
  {
7439
7653
  placeholder: "Search models\u2026",
@@ -7441,9 +7655,9 @@ function ModelPickerMenu({
7441
7655
  className: "h-8 w-full border-0 bg-transparent text-[13px] outline-none focus:ring-0"
7442
7656
  }
7443
7657
  ) }),
7444
- /* @__PURE__ */ jsxs23(CommandList, { className: "max-h-[300px] p-0.5", children: [
7445
- /* @__PURE__ */ jsx25(CommandEmpty, { className: "py-4 text-center text-[13px] text-muted-foreground", children: "No models found" }),
7446
- /* @__PURE__ */ jsx25(CommandGroup, { children: /* @__PURE__ */ jsxs23(
7658
+ /* @__PURE__ */ jsxs25(CommandList, { className: "max-h-[300px] p-0.5", children: [
7659
+ /* @__PURE__ */ jsx27(CommandEmpty, { className: "py-4 text-center text-[13px] text-muted-foreground", children: "No models found" }),
7660
+ /* @__PURE__ */ jsx27(CommandGroup, { children: /* @__PURE__ */ jsxs25(
7447
7661
  CommandItem,
7448
7662
  {
7449
7663
  value: "Pi default automatic auto",
@@ -7454,7 +7668,7 @@ function ModelPickerMenu({
7454
7668
  },
7455
7669
  className: selectorItemClass(!value || activeIndex === 0),
7456
7670
  children: [
7457
- /* @__PURE__ */ jsx25(
7671
+ /* @__PURE__ */ jsx27(
7458
7672
  CheckIcon3,
7459
7673
  {
7460
7674
  className: cn(
@@ -7463,12 +7677,12 @@ function ModelPickerMenu({
7463
7677
  )
7464
7678
  }
7465
7679
  ),
7466
- /* @__PURE__ */ jsx25("span", { className: "truncate", children: "Pi default" }),
7467
- /* @__PURE__ */ jsx25("span", { className: "ml-auto shrink-0 text-[10px] text-muted-foreground/60", children: "auto" })
7680
+ /* @__PURE__ */ jsx27("span", { className: "truncate", children: "Pi default" }),
7681
+ /* @__PURE__ */ jsx27("span", { className: "ml-auto shrink-0 text-[10px] text-muted-foreground/60", children: "auto" })
7468
7682
  ]
7469
7683
  }
7470
7684
  ) }),
7471
- [...groups.entries()].map(([provider, list]) => /* @__PURE__ */ jsx25(
7685
+ [...groups.entries()].map(([provider, list]) => /* @__PURE__ */ jsx27(
7472
7686
  CommandGroup,
7473
7687
  {
7474
7688
  heading: displayProviderLabel(provider),
@@ -7477,7 +7691,7 @@ function ModelPickerMenu({
7477
7691
  const key = encodeModelKey(m);
7478
7692
  const label = m.label || displayModelLabel(m.id);
7479
7693
  const itemIndex = optionIndexes.get(key) ?? 0;
7480
- return /* @__PURE__ */ jsxs23(
7694
+ return /* @__PURE__ */ jsxs25(
7481
7695
  CommandItem,
7482
7696
  {
7483
7697
  value: `${label} ${m.id} ${displayProviderLabel(m.provider)}`,
@@ -7488,7 +7702,7 @@ function ModelPickerMenu({
7488
7702
  },
7489
7703
  className: cn(selectorItemClass(key === currentKey || activeIndex === itemIndex)),
7490
7704
  children: [
7491
- /* @__PURE__ */ jsx25(
7705
+ /* @__PURE__ */ jsx27(
7492
7706
  CheckIcon3,
7493
7707
  {
7494
7708
  className: cn(
@@ -7497,8 +7711,8 @@ function ModelPickerMenu({
7497
7711
  )
7498
7712
  }
7499
7713
  ),
7500
- /* @__PURE__ */ jsx25("span", { className: "min-w-0 flex-1 truncate whitespace-nowrap", children: label }),
7501
- /* @__PURE__ */ jsx25("span", { className: "ml-auto max-w-[45%] shrink-0 truncate whitespace-nowrap text-right text-[10px] text-muted-foreground/60", children: m.id })
7714
+ /* @__PURE__ */ jsx27("span", { className: "min-w-0 flex-1 truncate whitespace-nowrap", children: label }),
7715
+ /* @__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 })
7502
7716
  ]
7503
7717
  },
7504
7718
  key
@@ -7519,7 +7733,7 @@ var ThinkingSelectTrigger = forwardRef(function ThinkingSelectTrigger2({
7519
7733
  className,
7520
7734
  ...props
7521
7735
  }, ref) {
7522
- return /* @__PURE__ */ jsxs23(
7736
+ return /* @__PURE__ */ jsxs25(
7523
7737
  "button",
7524
7738
  {
7525
7739
  ref,
@@ -7540,13 +7754,13 @@ var ThinkingSelectTrigger = forwardRef(function ThinkingSelectTrigger2({
7540
7754
  onClick,
7541
7755
  ...props,
7542
7756
  children: [
7543
- trigger === "button" ? /* @__PURE__ */ jsx25(ThinkingLevelGlyph, { level: value }) : null,
7544
- trigger === "slash" ? /* @__PURE__ */ jsxs23(Fragment7, { children: [
7545
- /* @__PURE__ */ jsx25("span", { className: "text-muted-foreground", children: "/thinking: " }),
7546
- /* @__PURE__ */ jsx25("span", { className: "text-foreground", children: THINKING_LEVEL_STATUS_LABELS[value] })
7547
- ] }) : /* @__PURE__ */ jsxs23(Fragment7, { children: [
7548
- /* @__PURE__ */ jsx25("span", { children: THINKING_LEVEL_LABELS[value] }),
7549
- /* @__PURE__ */ jsx25(ChevronDownIcon5, { className: "h-3 w-3 shrink-0 text-muted-foreground/50", "aria-hidden": "true" })
7757
+ trigger === "button" ? /* @__PURE__ */ jsx27(ThinkingLevelGlyph, { level: value }) : null,
7758
+ trigger === "slash" ? /* @__PURE__ */ jsxs25(Fragment8, { children: [
7759
+ /* @__PURE__ */ jsx27("span", { className: "text-muted-foreground", children: "/thinking: " }),
7760
+ /* @__PURE__ */ jsx27("span", { className: "text-foreground", children: THINKING_LEVEL_STATUS_LABELS[value] })
7761
+ ] }) : /* @__PURE__ */ jsxs25(Fragment8, { children: [
7762
+ /* @__PURE__ */ jsx27("span", { children: THINKING_LEVEL_LABELS[value] }),
7763
+ /* @__PURE__ */ jsx27(ChevronDownIcon5, { className: "h-3 w-3 shrink-0 text-muted-foreground/50", "aria-hidden": "true" })
7550
7764
  ] })
7551
7765
  ]
7552
7766
  }
@@ -7561,18 +7775,18 @@ function ThinkingPickerMenu({
7561
7775
  }) {
7562
7776
  const selectedIndex = Math.max(0, THINKING_LEVELS.indexOf(value));
7563
7777
  const [activeIndex, setActiveIndex] = useState16(selectedIndex);
7564
- const activeIndexRef = useRef11(selectedIndex);
7565
- const menuRef = useRef11(null);
7778
+ const activeIndexRef = useRef13(selectedIndex);
7779
+ const menuRef = useRef13(null);
7566
7780
  useDismissOnOutsidePointer(menuRef, onClose);
7567
7781
  const setKeyboardActiveIndex = (next) => {
7568
7782
  const resolved = typeof next === "function" ? next(activeIndexRef.current) : next;
7569
7783
  activeIndexRef.current = resolved;
7570
7784
  setActiveIndex(resolved);
7571
7785
  };
7572
- useEffect16(() => {
7786
+ useEffect17(() => {
7573
7787
  setKeyboardActiveIndex(selectedIndex);
7574
7788
  }, [selectedIndex]);
7575
- useEffect16(() => {
7789
+ useEffect17(() => {
7576
7790
  const handler = (event) => {
7577
7791
  if (event.key === "Escape") {
7578
7792
  event.preventDefault();
@@ -7595,7 +7809,7 @@ function ThinkingPickerMenu({
7595
7809
  window.addEventListener("keydown", handler, { capture: true });
7596
7810
  return () => window.removeEventListener("keydown", handler, { capture: true });
7597
7811
  }, [activeIndex, disabled, onChange, onClose]);
7598
- return /* @__PURE__ */ jsx25("div", { ref: menuRef, "data-boring-agent": "", "data-boring-agent-part": "thinking-picker-menu", className: cn(composerPickerMenuClass, className), children: /* @__PURE__ */ jsx25(Command, { className: "bg-transparent text-[color:var(--popover-foreground)]", children: /* @__PURE__ */ jsx25(CommandList, { className: "max-h-[300px] p-0.5", children: THINKING_LEVELS.map((level, index) => /* @__PURE__ */ jsxs23(
7812
+ 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(
7599
7813
  CommandItem,
7600
7814
  {
7601
7815
  value: `Thinking ${THINKING_LEVEL_LABELS[level]} ${THINKING_LEVEL_DETAILS[level]}`,
@@ -7606,7 +7820,7 @@ function ThinkingPickerMenu({
7606
7820
  },
7607
7821
  className: selectorItemClass(level === value || index === activeIndex),
7608
7822
  children: [
7609
- /* @__PURE__ */ jsx25(
7823
+ /* @__PURE__ */ jsx27(
7610
7824
  CheckIcon3,
7611
7825
  {
7612
7826
  className: cn(
@@ -7615,9 +7829,9 @@ function ThinkingPickerMenu({
7615
7829
  )
7616
7830
  }
7617
7831
  ),
7618
- /* @__PURE__ */ jsx25(ThinkingLevelGlyph, { level }),
7619
- /* @__PURE__ */ jsx25("span", { className: "font-medium", children: THINKING_LEVEL_LABELS[level] }),
7620
- /* @__PURE__ */ jsx25("span", { className: "ml-auto text-[11px] text-muted-foreground/65", children: THINKING_LEVEL_DETAILS[level] })
7832
+ /* @__PURE__ */ jsx27(ThinkingLevelGlyph, { level }),
7833
+ /* @__PURE__ */ jsx27("span", { className: "font-medium", children: THINKING_LEVEL_LABELS[level] }),
7834
+ /* @__PURE__ */ jsx27("span", { className: "ml-auto text-[11px] text-muted-foreground/65", children: THINKING_LEVEL_DETAILS[level] })
7621
7835
  ]
7622
7836
  },
7623
7837
  level
@@ -7655,10 +7869,10 @@ import {
7655
7869
  Monitor,
7656
7870
  PlusIcon as PlusIcon2,
7657
7871
  SquareIcon,
7658
- XIcon as XIcon5
7872
+ XIcon as XIcon4
7659
7873
  } from "lucide-react";
7660
7874
  import { nanoid } from "nanoid";
7661
- import { useCallback as useCallback15, useEffect as useEffect17, useMemo as useMemo10, useRef as useRef12, useState as useState17 } from "react";
7875
+ import { useCallback as useCallback15, useEffect as useEffect18, useMemo as useMemo10, useRef as useRef14, useState as useState17 } from "react";
7662
7876
 
7663
7877
  // src/front/primitives/prompt-input-context.ts
7664
7878
  import { createContext as createContext6, useContext as useContext6 } from "react";
@@ -7693,12 +7907,12 @@ import {
7693
7907
  TooltipTrigger as TooltipTrigger3
7694
7908
  } from "@hachej/boring-ui-kit";
7695
7909
  import { Children } from "react";
7696
- import { jsx as jsx26, jsxs as jsxs24 } from "react/jsx-runtime";
7910
+ import { jsx as jsx28, jsxs as jsxs26 } from "react/jsx-runtime";
7697
7911
  var PromptInputFooter = ({
7698
7912
  align = "block-end",
7699
7913
  className,
7700
7914
  ...props
7701
- }) => /* @__PURE__ */ jsx26(
7915
+ }) => /* @__PURE__ */ jsx28(
7702
7916
  InputGroupAddon,
7703
7917
  {
7704
7918
  align,
@@ -7708,7 +7922,7 @@ var PromptInputFooter = ({
7708
7922
  );
7709
7923
 
7710
7924
  // src/front/primitives/prompt-input.tsx
7711
- import { Fragment as Fragment8, jsx as jsx27, jsxs as jsxs25 } from "react/jsx-runtime";
7925
+ import { Fragment as Fragment9, jsx as jsx29, jsxs as jsxs27 } from "react/jsx-runtime";
7712
7926
  var PromptInput = ({
7713
7927
  className,
7714
7928
  accept,
@@ -7725,12 +7939,12 @@ var PromptInput = ({
7725
7939
  }) => {
7726
7940
  const controller = useOptionalPromptInputController();
7727
7941
  const usingProvider = !!controller;
7728
- const providerTextValueRef = useRef12("");
7942
+ const providerTextValueRef = useRef14("");
7729
7943
  if (usingProvider) {
7730
7944
  providerTextValueRef.current = controller.textInput.value;
7731
7945
  }
7732
- const inputRef = useRef12(null);
7733
- const formRef = useRef12(null);
7946
+ const inputRef = useRef14(null);
7947
+ const formRef = useRef14(null);
7734
7948
  const [items, setItems] = useState17([]);
7735
7949
  const setFileUrlLocal = useCallback15((id, url, status) => {
7736
7950
  setItems((prev) => prev.map((f) => {
@@ -7741,8 +7955,8 @@ var PromptInput = ({
7741
7955
  }, []);
7742
7956
  const files = usingProvider ? controller.attachments.files : items;
7743
7957
  const [referencedSources, setReferencedSources] = useState17([]);
7744
- const filesRef = useRef12(files);
7745
- useEffect17(() => {
7958
+ const filesRef = useRef14(files);
7959
+ useEffect18(() => {
7746
7960
  filesRef.current = files;
7747
7961
  }, [files]);
7748
7962
  const openFileDialogLocal = useCallback15(() => {
@@ -7878,18 +8092,18 @@ var PromptInput = ({
7878
8092
  clearAttachments();
7879
8093
  clearReferencedSources();
7880
8094
  }, [clearAttachments, clearReferencedSources]);
7881
- useEffect17(() => {
8095
+ useEffect18(() => {
7882
8096
  if (!usingProvider) {
7883
8097
  return;
7884
8098
  }
7885
8099
  controller.__registerFileInput(inputRef, () => inputRef.current?.click());
7886
8100
  }, [usingProvider, controller]);
7887
- useEffect17(() => {
8101
+ useEffect18(() => {
7888
8102
  if (syncHiddenInput && inputRef.current && files.length === 0) {
7889
8103
  inputRef.current.value = "";
7890
8104
  }
7891
8105
  }, [files, syncHiddenInput]);
7892
- useEffect17(() => {
8106
+ useEffect18(() => {
7893
8107
  const form = formRef.current;
7894
8108
  if (!form) {
7895
8109
  return;
@@ -7917,7 +8131,7 @@ var PromptInput = ({
7917
8131
  form.removeEventListener("drop", onDrop);
7918
8132
  };
7919
8133
  }, [add, globalDrop]);
7920
- useEffect17(() => {
8134
+ useEffect18(() => {
7921
8135
  if (!globalDrop) {
7922
8136
  return;
7923
8137
  }
@@ -7941,7 +8155,7 @@ var PromptInput = ({
7941
8155
  document.removeEventListener("drop", onDrop);
7942
8156
  };
7943
8157
  }, [add, globalDrop]);
7944
- useEffect17(
8158
+ useEffect18(
7945
8159
  () => () => {
7946
8160
  if (!usingProvider) {
7947
8161
  for (const f of filesRef.current) {
@@ -8051,8 +8265,8 @@ var PromptInput = ({
8051
8265
  },
8052
8266
  [usingProvider, controller, files, onSubmit, clear]
8053
8267
  );
8054
- const inner = /* @__PURE__ */ jsxs25(Fragment8, { children: [
8055
- /* @__PURE__ */ jsx27(
8268
+ const inner = /* @__PURE__ */ jsxs27(Fragment9, { children: [
8269
+ /* @__PURE__ */ jsx29(
8056
8270
  Input,
8057
8271
  {
8058
8272
  accept,
@@ -8065,7 +8279,7 @@ var PromptInput = ({
8065
8279
  type: "file"
8066
8280
  }
8067
8281
  ),
8068
- /* @__PURE__ */ jsx27(
8282
+ /* @__PURE__ */ jsx29(
8069
8283
  "form",
8070
8284
  {
8071
8285
  "data-boring-agent-part": "composer",
@@ -8073,12 +8287,12 @@ var PromptInput = ({
8073
8287
  onSubmit: handleSubmit,
8074
8288
  ref: formRef,
8075
8289
  ...props,
8076
- children: /* @__PURE__ */ jsx27(InputGroup, { className: "overflow-hidden", children })
8290
+ children: /* @__PURE__ */ jsx29(InputGroup, { className: "overflow-hidden", children })
8077
8291
  }
8078
8292
  )
8079
8293
  ] });
8080
- const withReferencedSources = /* @__PURE__ */ jsx27(LocalReferencedSourcesContext.Provider, { value: refsCtx, children: inner });
8081
- return /* @__PURE__ */ jsx27(LocalAttachmentsContext.Provider, { value: attachmentsCtx, children: withReferencedSources });
8294
+ const withReferencedSources = /* @__PURE__ */ jsx29(LocalReferencedSourcesContext.Provider, { value: refsCtx, children: inner });
8295
+ return /* @__PURE__ */ jsx29(LocalAttachmentsContext.Provider, { value: attachmentsCtx, children: withReferencedSources });
8082
8296
  };
8083
8297
  var PromptInputTextarea = ({
8084
8298
  onChange,
@@ -8156,7 +8370,7 @@ var PromptInputTextarea = ({
8156
8370
  } : {
8157
8371
  onChange
8158
8372
  };
8159
- return /* @__PURE__ */ jsx27(
8373
+ return /* @__PURE__ */ jsx29(
8160
8374
  InputGroupTextarea,
8161
8375
  {
8162
8376
  "data-boring-agent-part": "composer-input",
@@ -8183,13 +8397,13 @@ var PromptInputSubmit = ({
8183
8397
  ...props
8184
8398
  }) => {
8185
8399
  const isGenerating = status === "submitted" || status === "streaming";
8186
- let Icon = /* @__PURE__ */ jsx27(CornerDownLeftIcon, { className: "size-4" });
8400
+ let Icon = /* @__PURE__ */ jsx29(CornerDownLeftIcon, { className: "size-4" });
8187
8401
  if (status === "submitted") {
8188
- Icon = /* @__PURE__ */ jsx27(Spinner, {});
8402
+ Icon = /* @__PURE__ */ jsx29(Spinner, {});
8189
8403
  } else if (status === "streaming") {
8190
- Icon = /* @__PURE__ */ jsx27(SquareIcon, { className: "size-4" });
8404
+ Icon = /* @__PURE__ */ jsx29(SquareIcon, { className: "size-4" });
8191
8405
  } else if (status === "error") {
8192
- Icon = /* @__PURE__ */ jsx27(XIcon5, { className: "size-4" });
8406
+ Icon = /* @__PURE__ */ jsx29(XIcon4, { className: "size-4" });
8193
8407
  }
8194
8408
  const handleClick = useCallback15(
8195
8409
  (e) => {
@@ -8202,7 +8416,7 @@ var PromptInputSubmit = ({
8202
8416
  },
8203
8417
  [isGenerating, onStop, onClick]
8204
8418
  );
8205
- return /* @__PURE__ */ jsx27(
8419
+ return /* @__PURE__ */ jsx29(
8206
8420
  InputGroupButton2,
8207
8421
  {
8208
8422
  "aria-label": isGenerating ? "Stop" : "Submit",
@@ -8219,54 +8433,142 @@ var PromptInputSubmit = ({
8219
8433
  };
8220
8434
 
8221
8435
  // src/front/primitives/slash-command-picker.tsx
8222
- import { useCallback as useCallback16, useEffect as useEffect18, useMemo as useMemo11, useRef as useRef13, useState as useState18 } from "react";
8223
- import { jsx as jsx28, jsxs as jsxs26 } from "react/jsx-runtime";
8436
+ import { useEffect as useEffect19, useMemo as useMemo11, useRef as useRef15, useState as useState18 } from "react";
8437
+ import { Fragment as Fragment10, jsx as jsx30, jsxs as jsxs28 } from "react/jsx-runtime";
8438
+ var ALL_PLUGINS = "__all__";
8439
+ function commandGroup(cmd) {
8440
+ if (cmd.source === "skill") return "skills";
8441
+ if (cmd.sourcePlugin) return cmd.sourcePlugin;
8442
+ return "built-in";
8443
+ }
8224
8444
  function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
8225
- const filtered = useMemo11(
8226
- () => commands.filter((c) => c.name.toLowerCase().startsWith(query.toLowerCase())),
8227
- [commands, query]
8228
- );
8445
+ const [plugin, setPlugin] = useState18(ALL_PLUGINS);
8446
+ const [searchQuery, setSearchQuery] = useState18(query);
8229
8447
  const [activeIdx, setActiveIdx] = useState18(0);
8230
- const listRef = useRef13(null);
8231
- useEffect18(() => setActiveIdx(0), [filtered.length]);
8232
- const handleSelect = useCallback16((idx) => {
8233
- if (filtered[idx]) onSelect(filtered[idx].name);
8234
- }, [filtered, onSelect]);
8235
- usePickerKeyboard({ count: filtered.length, activeIdx, setActiveIdx, listRef, onSelect: handleSelect, onDismiss });
8236
- if (filtered.length === 0) return null;
8237
- return /* @__PURE__ */ jsx28("div", { className: "mb-1 w-full overflow-hidden rounded-lg border border-border/60 bg-popover shadow-lg", children: /* @__PURE__ */ jsx28("ul", { ref: listRef, className: "max-h-48 overflow-y-auto py-1", role: "listbox", "aria-label": "Commands", children: filtered.map((cmd, i) => /* @__PURE__ */ jsxs26(
8238
- "li",
8239
- {
8240
- role: "option",
8241
- "aria-selected": i === activeIdx,
8242
- className: cn(
8243
- "flex cursor-pointer flex-col gap-0.5 px-3 py-1.5 text-[12px]",
8244
- i === activeIdx ? "bg-accent/10 text-foreground" : "text-muted-foreground hover:bg-muted/40"
8245
- ),
8246
- onMouseEnter: () => setActiveIdx(i),
8247
- onMouseDown: (e) => {
8248
- e.preventDefault();
8249
- onSelect(cmd.name);
8250
- },
8251
- children: [
8252
- /* @__PURE__ */ jsxs26("span", { className: "font-medium text-foreground/80", children: [
8253
- "/",
8254
- cmd.name
8255
- ] }),
8256
- /* @__PURE__ */ jsx28("span", { className: "truncate text-[11px] opacity-50", children: cmd.description })
8257
- ]
8448
+ const containerRef = useRef15(null);
8449
+ const listRef = useRef15(null);
8450
+ const inputRef = useRef15(null);
8451
+ useEffect19(() => {
8452
+ setSearchQuery(query);
8453
+ }, [query]);
8454
+ const groups = useMemo11(() => {
8455
+ const set = new Set(commands.map(commandGroup));
8456
+ return Array.from(set).sort((a, b) => a.localeCompare(b));
8457
+ }, [commands]);
8458
+ const filtered = useMemo11(() => {
8459
+ const q = searchQuery.trim().toLowerCase();
8460
+ return commands.filter((c) => {
8461
+ if (plugin !== ALL_PLUGINS && commandGroup(c) !== plugin) return false;
8462
+ if (!q) return true;
8463
+ return c.name.toLowerCase().includes(q) || c.description.toLowerCase().includes(q);
8464
+ });
8465
+ }, [commands, searchQuery, plugin]);
8466
+ useEffect19(() => {
8467
+ setActiveIdx((i) => filtered.length === 0 ? 0 : Math.min(i, filtered.length - 1));
8468
+ }, [filtered.length]);
8469
+ useEffect19(() => {
8470
+ const handler = (e) => {
8471
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
8472
+ onDismiss();
8473
+ }
8474
+ };
8475
+ document.addEventListener("mousedown", handler);
8476
+ return () => document.removeEventListener("mousedown", handler);
8477
+ }, [onDismiss]);
8478
+ usePickerKeyboard({
8479
+ count: filtered.length,
8480
+ activeIdx,
8481
+ setActiveIdx,
8482
+ listRef,
8483
+ onSelect: (idx) => {
8484
+ if (filtered[idx]) onSelect(filtered[idx].name);
8258
8485
  },
8259
- cmd.name
8260
- )) }) });
8486
+ onDismiss
8487
+ });
8488
+ return /* @__PURE__ */ jsxs28("div", { ref: containerRef, className: "mb-1 w-full overflow-hidden rounded-lg border border-border/60 bg-popover shadow-lg", children: [
8489
+ /* @__PURE__ */ jsx30(
8490
+ "input",
8491
+ {
8492
+ ref: inputRef,
8493
+ "aria-label": "Search commands",
8494
+ type: "text",
8495
+ value: searchQuery,
8496
+ onChange: (e) => {
8497
+ setSearchQuery(e.target.value);
8498
+ setActiveIdx(0);
8499
+ },
8500
+ className: "w-full border-b border-border/50 bg-transparent px-3 py-1.5 text-[12px] outline-none",
8501
+ autoComplete: "off"
8502
+ }
8503
+ ),
8504
+ groups.length > 1 && /* @__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) => {
8505
+ const selected = plugin === g;
8506
+ return /* @__PURE__ */ jsx30(
8507
+ "button",
8508
+ {
8509
+ type: "button",
8510
+ role: "tab",
8511
+ "aria-selected": selected,
8512
+ onMouseDown: (e) => {
8513
+ e.preventDefault();
8514
+ setPlugin(g);
8515
+ setActiveIdx(0);
8516
+ },
8517
+ className: cn(
8518
+ "rounded-full px-2 py-px text-[10px] font-medium transition-colors",
8519
+ selected ? "bg-accent/15 text-accent-foreground" : "bg-muted/60 text-muted-foreground hover:bg-muted"
8520
+ ),
8521
+ children: g === ALL_PLUGINS ? "All" : g
8522
+ },
8523
+ g
8524
+ );
8525
+ }) }),
8526
+ 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(
8527
+ "li",
8528
+ {
8529
+ role: "option",
8530
+ "aria-selected": i === activeIdx,
8531
+ title: cmd.description || void 0,
8532
+ className: cn(
8533
+ "flex cursor-pointer flex-col gap-0.5 px-3 py-1.5 text-[12px]",
8534
+ // Single highlight: hovering moves the active row (onMouseEnter),
8535
+ // so the active style is the only highlight — no separate hover bg.
8536
+ i === activeIdx ? "bg-accent/10 text-foreground" : "text-muted-foreground"
8537
+ ),
8538
+ onMouseEnter: () => setActiveIdx(i),
8539
+ onMouseDown: (e) => {
8540
+ e.preventDefault();
8541
+ onSelect(cmd.name);
8542
+ },
8543
+ children: [
8544
+ /* @__PURE__ */ jsxs28("span", { className: "flex items-center gap-1.5", children: [
8545
+ /* @__PURE__ */ jsxs28("span", { className: "font-medium text-foreground/80", children: [
8546
+ "/",
8547
+ cmd.name
8548
+ ] }),
8549
+ cmd.source === "skill" ? /* @__PURE__ */ jsxs28(Fragment10, { children: [
8550
+ /* @__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" }),
8551
+ 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
8552
+ ] }) : cmd.sourcePlugin ? (
8553
+ // Originating plugin/package for non-skill commands.
8554
+ /* @__PURE__ */ jsx30("span", { className: "rounded-sm bg-muted px-1 py-px text-[9px] font-medium text-muted-foreground", children: cmd.sourcePlugin })
8555
+ ) : null
8556
+ ] }),
8557
+ /* @__PURE__ */ jsx30("span", { className: "truncate text-[11px] opacity-50", children: cmd.description })
8558
+ ]
8559
+ },
8560
+ cmd.name
8561
+ )) })
8562
+ ] });
8261
8563
  }
8262
8564
 
8263
8565
  // src/front/chat/components/ComposerAttachments.tsx
8264
- import { AlertCircleIcon as AlertCircleIcon3, Loader2 as Loader23, PaperclipIcon as PaperclipIcon2 } from "lucide-react";
8566
+ import { AlertCircleIcon as AlertCircleIcon2, Loader2 as Loader23, PaperclipIcon as PaperclipIcon2 } from "lucide-react";
8265
8567
  import { IconButton as IconButton3 } from "@hachej/boring-ui-kit";
8266
- import { jsx as jsx29, jsxs as jsxs27 } from "react/jsx-runtime";
8568
+ import { jsx as jsx31, jsxs as jsxs29 } from "react/jsx-runtime";
8267
8569
  function AttachmentButton({ disabled, className }) {
8268
8570
  const attachments = usePromptInputAttachments();
8269
- return /* @__PURE__ */ jsx29(
8571
+ return /* @__PURE__ */ jsx31(
8270
8572
  IconButton3,
8271
8573
  {
8272
8574
  type: "button",
@@ -8279,21 +8581,21 @@ function AttachmentButton({ disabled, className }) {
8279
8581
  className: cn(composerActionClass, "w-8", className),
8280
8582
  "aria-label": "Attach files",
8281
8583
  title: disabled ? "Attachments are available when the composer is ready." : "Attach files",
8282
- children: /* @__PURE__ */ jsx29(PaperclipIcon2, { className: "h-3.5 w-3.5", strokeWidth: 1.75 })
8584
+ children: /* @__PURE__ */ jsx31(PaperclipIcon2, { className: "h-3.5 w-3.5", strokeWidth: 1.75 })
8283
8585
  }
8284
8586
  );
8285
8587
  }
8286
8588
  function AttachmentsList() {
8287
8589
  const attachments = usePromptInputAttachments();
8288
8590
  if (attachments.files.length === 0) return null;
8289
- return /* @__PURE__ */ jsx29(
8591
+ return /* @__PURE__ */ jsx31(
8290
8592
  Attachments,
8291
8593
  {
8292
8594
  "data-boring-agent-part": "composer-attachment-row",
8293
8595
  "data-align": "block-start",
8294
8596
  variant: "inline",
8295
8597
  className: "w-full flex-wrap items-center justify-start gap-2 px-5 pb-0 pt-3",
8296
- children: attachments.files.map((file) => /* @__PURE__ */ jsxs27(
8598
+ children: attachments.files.map((file) => /* @__PURE__ */ jsxs29(
8297
8599
  Attachment,
8298
8600
  {
8299
8601
  data: file,
@@ -8304,13 +8606,13 @@ function AttachmentsList() {
8304
8606
  file.status === "error" && "!border-destructive/50 !bg-destructive/10"
8305
8607
  ),
8306
8608
  children: [
8307
- /* @__PURE__ */ jsxs27("div", { className: "relative shrink-0", children: [
8308
- /* @__PURE__ */ jsx29(AttachmentPreview, { className: "!size-7 overflow-hidden !rounded-full bg-background/60" }),
8309
- file.status === "uploading" ? /* @__PURE__ */ jsx29("div", { className: "absolute inset-0 flex items-center justify-center rounded-full bg-background/70", children: /* @__PURE__ */ jsx29(Loader23, { className: "size-3.5 animate-spin text-muted-foreground" }) }) : null,
8310
- file.status === "error" ? /* @__PURE__ */ jsx29("div", { className: "absolute inset-0 flex items-center justify-center rounded-full bg-destructive/20", children: /* @__PURE__ */ jsx29(AlertCircleIcon3, { className: "size-3.5 text-destructive" }) }) : null
8609
+ /* @__PURE__ */ jsxs29("div", { className: "relative shrink-0", children: [
8610
+ /* @__PURE__ */ jsx31(AttachmentPreview, { className: "!size-7 overflow-hidden !rounded-full bg-background/60" }),
8611
+ 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,
8612
+ 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
8311
8613
  ] }),
8312
- /* @__PURE__ */ jsx29(AttachmentInfo, { className: "min-w-0 !max-w-[180px] truncate text-[13px] font-medium" }),
8313
- /* @__PURE__ */ jsx29(AttachmentRemove, { className: "!size-5 !rounded-full !opacity-100 text-muted-foreground/80 hover:text-foreground" })
8614
+ /* @__PURE__ */ jsx31(AttachmentInfo, { className: "min-w-0 !max-w-[180px] truncate text-[13px] font-medium" }),
8615
+ /* @__PURE__ */ jsx31(AttachmentRemove, { className: "!size-5 !rounded-full !opacity-100 text-muted-foreground/80 hover:text-foreground" })
8314
8616
  ]
8315
8617
  },
8316
8618
  file.id
@@ -8320,7 +8622,7 @@ function AttachmentsList() {
8320
8622
  }
8321
8623
 
8322
8624
  // src/front/chat/components/PiChatComposerSurface.tsx
8323
- import { jsx as jsx30, jsxs as jsxs28 } from "react/jsx-runtime";
8625
+ import { jsx as jsx32, jsxs as jsxs30 } from "react/jsx-runtime";
8324
8626
  var MAX_PROMPT_ATTACHMENTS = 2;
8325
8627
  var MAX_PROMPT_ATTACHMENT_BYTES = 4 * 1024 * 1024;
8326
8628
  var COMPOSER_INPUT_GROUP_MIN_HEIGHT = 56;
@@ -8362,6 +8664,8 @@ function PiChatComposerSurface({
8362
8664
  pluginUpdateState,
8363
8665
  onDismissPluginUpdate,
8364
8666
  onRunPluginUpdate,
8667
+ commandNotifyState,
8668
+ onDismissCommandNotify,
8365
8669
  attachmentNotice,
8366
8670
  onAttachmentNotice,
8367
8671
  mentionState,
@@ -8396,7 +8700,7 @@ function PiChatComposerSurface({
8396
8700
  onSubmitMessage,
8397
8701
  onStop
8398
8702
  }) {
8399
- const resizeTextarea = useCallback17((node) => {
8703
+ const resizeTextarea = useCallback16((node) => {
8400
8704
  if (!node) return;
8401
8705
  node.style.height = "auto";
8402
8706
  const style = window.getComputedStyle(node);
@@ -8420,8 +8724,8 @@ function PiChatComposerSurface({
8420
8724
  useLayoutEffect2(() => {
8421
8725
  resizeTextarea(textareaRef.current);
8422
8726
  }, [draft, resizeTextarea, textareaRef]);
8423
- return /* @__PURE__ */ jsxs28("div", { className: cn(chrome ? "px-4 pb-4 pt-2 sm:px-6 sm:pb-5" : "px-3 pb-2 pt-1"), children: [
8424
- /* @__PURE__ */ jsx30(
8727
+ return /* @__PURE__ */ jsxs30("div", { className: cn(chrome ? "px-4 pb-4 pt-2 sm:px-6 sm:pb-5" : "px-3 pb-2 pt-1"), children: [
8728
+ /* @__PURE__ */ jsx32(
8425
8729
  "div",
8426
8730
  {
8427
8731
  "data-boring-agent-part": "chat-working-slot",
@@ -8431,7 +8735,7 @@ function PiChatComposerSurface({
8431
8735
  isStreaming ? "mb-2 max-h-8 opacity-100" : "mb-0 max-h-0 opacity-0"
8432
8736
  ),
8433
8737
  "aria-hidden": !isStreaming,
8434
- children: /* @__PURE__ */ jsxs28(
8738
+ children: /* @__PURE__ */ jsxs30(
8435
8739
  "div",
8436
8740
  {
8437
8741
  "data-testid": isStreaming ? "chat-working" : void 0,
@@ -8439,7 +8743,7 @@ function PiChatComposerSurface({
8439
8743
  "aria-live": isStreaming ? "polite" : void 0,
8440
8744
  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",
8441
8745
  children: [
8442
- /* @__PURE__ */ jsx30(
8746
+ /* @__PURE__ */ jsx32(
8443
8747
  motion2.span,
8444
8748
  {
8445
8749
  "aria-hidden": "true",
@@ -8448,14 +8752,14 @@ function PiChatComposerSurface({
8448
8752
  transition: { duration: 1.2, repeat: Infinity, ease: "easeInOut" }
8449
8753
  }
8450
8754
  ),
8451
- /* @__PURE__ */ jsx30("span", { children: "Working\u2026" })
8755
+ /* @__PURE__ */ jsx32("span", { children: "Working\u2026" })
8452
8756
  ]
8453
8757
  }
8454
8758
  )
8455
8759
  }
8456
8760
  ),
8457
- composerStatusNotice ? /* @__PURE__ */ jsx30(ComposerRuntimeNotice, { notice: composerStatusNotice }) : null,
8458
- composerBlocked && !workspaceWarmupBlocked ? /* @__PURE__ */ jsx30(
8761
+ composerStatusNotice ? /* @__PURE__ */ jsx32(ComposerRuntimeNotice, { notice: composerStatusNotice }) : null,
8762
+ composerBlocked && !workspaceWarmupBlocked ? /* @__PURE__ */ jsx32(
8459
8763
  ComposerBlockerNotice,
8460
8764
  {
8461
8765
  blocker: primaryComposerBlocker,
@@ -8463,8 +8767,8 @@ function PiChatComposerSurface({
8463
8767
  onAction: onComposerBlockerAction
8464
8768
  }
8465
8769
  ) : null,
8466
- queuePreview.length > 0 ? /* @__PURE__ */ jsx30(QueuedComposerNotice, { followUps: queuePreview, onEdit: onEditQueued }) : null,
8467
- hotReloadEnabled ? /* @__PURE__ */ jsx30(
8770
+ queuePreview.length > 0 ? /* @__PURE__ */ jsx32(QueuedComposerNotice, { followUps: queuePreview, onEdit: onEditQueued }) : null,
8771
+ hotReloadEnabled ? /* @__PURE__ */ jsx32(
8468
8772
  PluginUpdateStatus,
8469
8773
  {
8470
8774
  state: pluginUpdateState,
@@ -8472,7 +8776,14 @@ function PiChatComposerSurface({
8472
8776
  onRetry: onRunPluginUpdate
8473
8777
  }
8474
8778
  ) : null,
8475
- attachmentNotice ? /* @__PURE__ */ jsx30(
8779
+ /* @__PURE__ */ jsx32(
8780
+ CommandRunStatus,
8781
+ {
8782
+ state: commandNotifyState,
8783
+ onDismiss: onDismissCommandNotify
8784
+ }
8785
+ ),
8786
+ attachmentNotice ? /* @__PURE__ */ jsx32(
8476
8787
  "div",
8477
8788
  {
8478
8789
  role: "status",
@@ -8481,8 +8792,8 @@ function PiChatComposerSurface({
8481
8792
  children: attachmentNotice
8482
8793
  }
8483
8794
  ) : null,
8484
- /* @__PURE__ */ jsxs28("div", { className: cn("mx-auto w-full", chrome ? "max-w-3xl" : "max-w-[680px]"), children: [
8485
- mentionState ? /* @__PURE__ */ jsx30(
8795
+ /* @__PURE__ */ jsxs30("div", { className: cn("mx-auto w-full", chrome ? "max-w-3xl" : "max-w-[680px]"), children: [
8796
+ mentionState ? /* @__PURE__ */ jsx32(
8486
8797
  MentionPicker,
8487
8798
  {
8488
8799
  mention: mentionState,
@@ -8494,7 +8805,7 @@ function PiChatComposerSurface({
8494
8805
  onDismiss: onDismissMention
8495
8806
  }
8496
8807
  ) : null,
8497
- slashQuery !== null ? /* @__PURE__ */ jsx30(
8808
+ slashQuery !== null ? /* @__PURE__ */ jsx32(
8498
8809
  SlashCommandPicker,
8499
8810
  {
8500
8811
  query: slashQuery,
@@ -8503,7 +8814,7 @@ function PiChatComposerSurface({
8503
8814
  onDismiss: onDismissSlash
8504
8815
  }
8505
8816
  ) : null,
8506
- mentionState === null && slashQuery === null && modelPickerOpen ? /* @__PURE__ */ jsx30(
8817
+ mentionState === null && slashQuery === null && modelPickerOpen ? /* @__PURE__ */ jsx32(
8507
8818
  ModelPickerMenu,
8508
8819
  {
8509
8820
  value: selectedModel,
@@ -8513,7 +8824,7 @@ function PiChatComposerSurface({
8513
8824
  onClose: () => onSetModelPickerOpen(false)
8514
8825
  }
8515
8826
  ) : null,
8516
- mentionState === null && slashQuery === null && thinkingPickerOpen ? /* @__PURE__ */ jsx30(
8827
+ mentionState === null && slashQuery === null && thinkingPickerOpen ? /* @__PURE__ */ jsx32(
8517
8828
  ThinkingPickerMenu,
8518
8829
  {
8519
8830
  value: selectedThinking,
@@ -8523,7 +8834,7 @@ function PiChatComposerSurface({
8523
8834
  }
8524
8835
  ) : null
8525
8836
  ] }),
8526
- /* @__PURE__ */ jsx30(
8837
+ /* @__PURE__ */ jsx32(
8527
8838
  "div",
8528
8839
  {
8529
8840
  "data-boring-agent-part": "composer-rail",
@@ -8542,7 +8853,7 @@ function PiChatComposerSurface({
8542
8853
  "[&[data-composer-multiline=true]_[data-boring-agent-part=composer-submit-addon]]:self-end",
8543
8854
  "[&[data-composer-multiline=true]_[data-boring-agent-part=composer-submit-addon]]:mb-2"
8544
8855
  ),
8545
- children: /* @__PURE__ */ jsxs28(
8856
+ children: /* @__PURE__ */ jsxs30(
8546
8857
  PromptInput,
8547
8858
  {
8548
8859
  "data-boring-state": status,
@@ -8557,15 +8868,15 @@ function PiChatComposerSurface({
8557
8868
  else onAttachmentNotice(err.message || "Attachment rejected.");
8558
8869
  },
8559
8870
  children: [
8560
- /* @__PURE__ */ jsx30(AttachmentsList, {}),
8561
- /* @__PURE__ */ jsxs28(
8871
+ /* @__PURE__ */ jsx32(AttachmentsList, {}),
8872
+ /* @__PURE__ */ jsxs30(
8562
8873
  "div",
8563
8874
  {
8564
8875
  "data-boring-agent-part": "composer-input-row",
8565
8876
  className: "flex min-h-[var(--composer-input-group-height)] w-full items-center",
8566
8877
  children: [
8567
- /* @__PURE__ */ jsx30("div", { className: "flex h-14 shrink-0 items-center pl-2", children: /* @__PURE__ */ jsx30(AttachmentButton, { disabled: disabled || isStreaming }) }),
8568
- /* @__PURE__ */ jsx30(
8878
+ /* @__PURE__ */ jsx32("div", { className: "flex h-14 shrink-0 items-center pl-2", children: /* @__PURE__ */ jsx32(AttachmentButton, { disabled: disabled || isStreaming }) }),
8879
+ /* @__PURE__ */ jsx32(
8569
8880
  PromptInputTextarea,
8570
8881
  {
8571
8882
  value: draft,
@@ -8589,13 +8900,13 @@ function PiChatComposerSurface({
8589
8900
  )
8590
8901
  }
8591
8902
  ),
8592
- /* @__PURE__ */ jsx30(
8903
+ /* @__PURE__ */ jsx32(
8593
8904
  PromptInputFooter,
8594
8905
  {
8595
8906
  align: "inline-end",
8596
8907
  "data-boring-agent-part": "composer-submit-addon",
8597
8908
  className: "!order-none !w-auto shrink-0 self-center justify-between border-0 bg-transparent !px-2 !py-0",
8598
- children: /* @__PURE__ */ jsx30("div", { className: "ml-auto flex items-center gap-1.5", children: /* @__PURE__ */ jsx30(
8909
+ children: /* @__PURE__ */ jsx32("div", { className: "ml-auto flex items-center gap-1.5", children: /* @__PURE__ */ jsx32(
8599
8910
  PromptInputSubmit,
8600
8911
  {
8601
8912
  "data-boring-agent-part": "composer-submit",
@@ -8625,7 +8936,7 @@ function PiChatComposerSurface({
8625
8936
  )
8626
8937
  }
8627
8938
  ),
8628
- /* @__PURE__ */ jsxs28(
8939
+ /* @__PURE__ */ jsxs30(
8629
8940
  "div",
8630
8941
  {
8631
8942
  "data-boring-agent-part": "composer-settings-row",
@@ -8634,7 +8945,7 @@ function PiChatComposerSurface({
8634
8945
  chrome ? "max-w-3xl" : "max-w-[680px]"
8635
8946
  ),
8636
8947
  children: [
8637
- /* @__PURE__ */ jsx30(
8948
+ /* @__PURE__ */ jsx32(
8638
8949
  ModelSelectTrigger,
8639
8950
  {
8640
8951
  value: selectedModel,
@@ -8651,7 +8962,7 @@ function PiChatComposerSurface({
8651
8962
  }
8652
8963
  }
8653
8964
  ),
8654
- thinkingControl ? /* @__PURE__ */ jsx30(
8965
+ thinkingControl ? /* @__PURE__ */ jsx32(
8655
8966
  ThinkingSelectTrigger,
8656
8967
  {
8657
8968
  value: selectedThinking,
@@ -8674,7 +8985,7 @@ function PiChatComposerSurface({
8674
8985
  }
8675
8986
 
8676
8987
  // src/front/chat/piChatPanelHooks.ts
8677
- import { useCallback as useCallback18, useEffect as useEffect19, useState as useState19, useSyncExternalStore } from "react";
8988
+ import { useCallback as useCallback17, useEffect as useEffect20, useState as useState19, useSyncExternalStore } from "react";
8678
8989
  function useExternalRemotePiSession({
8679
8990
  sessionId,
8680
8991
  workspaceId,
@@ -8686,7 +8997,7 @@ function useExternalRemotePiSession({
8686
8997
  remoteSessionOptions
8687
8998
  }) {
8688
8999
  const [session, setSession] = useState19();
8689
- useEffect19(() => {
9000
+ useEffect20(() => {
8690
9001
  if (!sessionId) {
8691
9002
  setSession(void 0);
8692
9003
  return;
@@ -8707,10 +9018,10 @@ function useExternalRemotePiSession({
8707
9018
  }
8708
9019
  function useRemotePiSessionState(session) {
8709
9020
  return useSyncExternalStore(
8710
- useCallback18((listener) => session?.subscribe(listener) ?? (() => {
9021
+ useCallback17((listener) => session?.subscribe(listener) ?? (() => {
8711
9022
  }), [session]),
8712
- useCallback18(() => session?.getState(), [session]),
8713
- useCallback18(() => session?.getState(), [session])
9023
+ useCallback17(() => session?.getState(), [session]),
9024
+ useCallback17(() => session?.getState(), [session])
8714
9025
  );
8715
9026
  }
8716
9027
 
@@ -8826,7 +9137,7 @@ function errorMessage2(error, fallback) {
8826
9137
  }
8827
9138
 
8828
9139
  // src/front/chat/PiChatPanel.tsx
8829
- import { jsx as jsx31, jsxs as jsxs29 } from "react/jsx-runtime";
9140
+ import { jsx as jsx33, jsxs as jsxs31 } from "react/jsx-runtime";
8830
9141
  var DebugDrawer2 = lazy(() => import("../DebugDrawer-2PUDZ2RL.js").then((m) => ({ default: m.DebugDrawer })));
8831
9142
  var EMPTY_COMMANDS = [];
8832
9143
  var EMPTY_BLOCKERS = [];
@@ -8880,9 +9191,9 @@ function PiChatPanel({
8880
9191
  }) {
8881
9192
  const externalSessionId = sessionId?.trim() || void 0;
8882
9193
  const showSessionSidebar = showSessions ?? externalSessionId === void 0;
8883
- const onDataRef = useRef14(onData);
9194
+ const onDataRef = useRef16(onData);
8884
9195
  onDataRef.current = onData;
8885
- const sessionListRefreshRef = useRef14(void 0);
9196
+ const sessionListRefreshRef = useRef16(void 0);
8886
9197
  const requestHeadersKey = useMemo12(() => headersContentKey(requestHeaders), [requestHeaders]);
8887
9198
  const normalizedRequestHeaders = useMemo12(() => normalizedHeadersFromContentKey(requestHeadersKey), [requestHeadersKey]);
8888
9199
  const remoteSessionOptionsWithEvents = useMemo12(() => ({
@@ -8905,7 +9216,7 @@ function PiChatPanel({
8905
9216
  remoteSessionOptions: remoteSessionOptionsWithEvents,
8906
9217
  enabled: externalSessionId === void 0
8907
9218
  });
8908
- useEffect20(() => {
9219
+ useEffect21(() => {
8909
9220
  if (externalSessionId) {
8910
9221
  sessionListRefreshRef.current = void 0;
8911
9222
  return;
@@ -8959,19 +9270,19 @@ function PiChatPanel({
8959
9270
  () => readPiComposerSettings({ storageScope, storage }).showThoughts
8960
9271
  );
8961
9272
  const [composerSettingsOwner, setComposerSettingsOwner] = useState20(() => ({ storageScope, storage }));
8962
- useEffect20(() => {
9273
+ useEffect21(() => {
8963
9274
  const settings = readPiComposerSettings({ storageScope, storage });
8964
9275
  setStoredThinkingLevel(thinkingControl ? settings.thinkingLevel : DEFAULT_THINKING);
8965
9276
  setShowThoughts(settings.showThoughts);
8966
9277
  setComposerSettingsOwner({ storageScope, storage });
8967
9278
  }, [storage, storageScope, thinkingControl]);
8968
9279
  const composerSettingsLoaded = composerSettingsOwner.storageScope === storageScope && composerSettingsOwner.storage === storage;
8969
- useEffect20(() => {
9280
+ useEffect21(() => {
8970
9281
  if (!thinkingControl) return;
8971
9282
  if (!composerSettingsLoaded) return;
8972
9283
  writePiComposerThinking(storedThinkingLevel, { storageScope, storage });
8973
9284
  }, [composerSettingsLoaded, storage, storageScope, storedThinkingLevel, thinkingControl]);
8974
- useEffect20(() => {
9285
+ useEffect21(() => {
8975
9286
  if (!composerSettingsLoaded) return;
8976
9287
  writePiComposerShowThoughts(showThoughts, { storageScope, storage });
8977
9288
  }, [composerSettingsLoaded, showThoughts, storage, storageScope]);
@@ -8980,14 +9291,14 @@ function PiChatPanel({
8980
9291
  const [modelPickerOpen, setModelPickerOpen] = useState20(false);
8981
9292
  const [thinkingPickerOpen, setThinkingPickerOpen] = useState20(false);
8982
9293
  const [draft, setDraft] = useState20(() => initialDraft ?? "");
8983
- const draftRef = useRef14(draft);
9294
+ const draftRef = useRef16(draft);
8984
9295
  draftRef.current = draft;
8985
- const initialDraftGuard = useRef14(new InitialDraftAutoSubmitGuard());
8986
- const pendingAutoSubmitSettleRef = useRef14(void 0);
8987
- const acceptedAutoSubmitSettleRef = useRef14(void 0);
8988
- const resetInProgressRef = useRef14(false);
8989
- const autoCreateInFlightRef = useRef14(false);
8990
- const settlePendingAutoSubmit = useCallback19((sessionId2) => {
9296
+ const initialDraftGuard = useRef16(new InitialDraftAutoSubmitGuard());
9297
+ const pendingAutoSubmitSettleRef = useRef16(void 0);
9298
+ const acceptedAutoSubmitSettleRef = useRef16(void 0);
9299
+ const resetInProgressRef = useRef16(false);
9300
+ const autoCreateInFlightRef = useRef16(false);
9301
+ const settlePendingAutoSubmit = useCallback18((sessionId2) => {
8991
9302
  const pendingSessionId = pendingAutoSubmitSettleRef.current;
8992
9303
  if (!pendingSessionId || sessionId2 && pendingSessionId !== sessionId2) return false;
8993
9304
  pendingAutoSubmitSettleRef.current = void 0;
@@ -8995,23 +9306,25 @@ function PiChatPanel({
8995
9306
  onAutoSubmitInitialDraftSettled?.();
8996
9307
  return true;
8997
9308
  }, [onAutoSubmitInitialDraftSettled]);
8998
- const prevStatusRef = useRef14("idle");
8999
- const statusRef = useRef14("idle");
9000
- const scrollToBottomRef = useRef14(() => {
9309
+ const prevStatusRef = useRef16("idle");
9310
+ const statusRef = useRef16("idle");
9311
+ const scrollToBottomRef = useRef16(() => {
9001
9312
  });
9002
- const textareaRef = useRef14(null);
9313
+ const textareaRef = useRef16(null);
9003
9314
  const [localNotices, setLocalNotices] = useState20([]);
9004
9315
  const [dismissedNoticeIds, setDismissedNoticeIds] = useState20(() => /* @__PURE__ */ new Set());
9005
9316
  const [pluginUpdateState, setPluginUpdateState] = useState20(null);
9317
+ const [commandNotifyState, setCommandNotifyState] = useState20(null);
9318
+ const commandRunIdRef = useRef16(0);
9006
9319
  const [serverSkillsRefreshKey, setServerSkillsRefreshKey] = useState20(0);
9007
9320
  const [localSubmittedSessionId, setLocalSubmittedSessionId] = useState20();
9008
- const localSubmittedSessionRef = useRef14(void 0);
9321
+ const localSubmittedSessionRef = useRef16(void 0);
9009
9322
  const { attachmentNotice, setAttachmentNotice } = useAttachmentNotice();
9010
- const markLocalSubmitted = useCallback19((sessionId2) => {
9323
+ const markLocalSubmitted = useCallback18((sessionId2) => {
9011
9324
  localSubmittedSessionRef.current = sessionId2;
9012
9325
  setLocalSubmittedSessionId(sessionId2);
9013
9326
  }, []);
9014
- const clearLocalSubmitted = useCallback19((sessionId2) => {
9327
+ const clearLocalSubmitted = useCallback18((sessionId2) => {
9015
9328
  if (sessionId2 && localSubmittedSessionRef.current !== sessionId2) return;
9016
9329
  localSubmittedSessionRef.current = void 0;
9017
9330
  setLocalSubmittedSessionId(void 0);
@@ -9023,16 +9336,17 @@ function PiChatPanel({
9023
9336
  for (const command of commands) next.register(command);
9024
9337
  return next;
9025
9338
  }, [apiBaseUrl, commands, extraCommands, hotReloadEnabled, normalizedRequestHeaders, serverResourcesEnabled, serverSkillsRefreshKey, storageScope]);
9026
- const skillsStamp = useServerSkills({
9027
- apiBaseUrl,
9028
- fetch: fetch2,
9339
+ const commandsStamp = useServerCommands({
9029
9340
  registry,
9030
- refreshKey: serverSkillsRefreshKey,
9031
9341
  requestHeaders: normalizedRequestHeaders,
9342
+ sessionId: activeSessionId ?? "default",
9343
+ apiBaseUrl,
9344
+ fetch: fetch2,
9032
9345
  storageScope,
9346
+ refreshKey: serverSkillsRefreshKey,
9033
9347
  enabled: serverResourcesEnabled
9034
9348
  });
9035
- const allCommands = useMemo12(() => registry.list(), [registry, skillsStamp]);
9349
+ const allCommands = useMemo12(() => registry.list(), [registry, commandsStamp]);
9036
9350
  const activeChatSessionId = selectedChatState?.sessionId;
9037
9351
  const warmupNotice = composerNoticeForWarmup(workspaceWarmupStatus);
9038
9352
  const runtimeDependenciesNotice = composerNoticeForRuntimeDependencies(workspaceWarmupStatus);
@@ -9063,23 +9377,23 @@ function PiChatPanel({
9063
9377
  }] : [];
9064
9378
  return [...fromState, ...sessionNotice, ...largeStateNotice, ...localNotices].filter((notice) => !dismissedNoticeIds.has(notice.id));
9065
9379
  }, [debug, debugState?.largeStateWarning, dismissedNoticeIds, localNotices, selectedChatState, sessionsError]);
9066
- const addLocalNotice = useCallback19((notice) => {
9380
+ const addLocalNotice = useCallback18((notice) => {
9067
9381
  setLocalNotices((previous) => {
9068
9382
  const next = previous.filter((candidate) => candidate.id !== notice.id);
9069
9383
  return [...next, notice];
9070
9384
  });
9071
9385
  }, []);
9072
- const clearLocalNotice = useCallback19((id) => {
9386
+ const clearLocalNotice = useCallback18((id) => {
9073
9387
  setDismissedNoticeIds((previous) => new Set(previous).add(id));
9074
9388
  setLocalNotices((previous) => previous.filter((notice) => notice.id !== id));
9075
9389
  }, []);
9076
- const createSession = useCallback19(() => {
9390
+ const createSession = useCallback18(() => {
9077
9391
  if (externalSessionId) return;
9078
9392
  void sessions.create().catch((error) => {
9079
9393
  addLocalNotice({ id: "session-create-error", level: "error", text: errorMessage2(error, "Could not create a Pi session."), dismissible: true });
9080
9394
  });
9081
9395
  }, [addLocalNotice, externalSessionId, sessions.create]);
9082
- useEffect20(() => {
9396
+ useEffect21(() => {
9083
9397
  if (externalSessionId || sessionsLoading || sessionsError || activeSessionId || sessionList.length > 0) return;
9084
9398
  if (resetInProgressRef.current) return;
9085
9399
  if (autoCreateInFlightRef.current) return;
@@ -9089,18 +9403,18 @@ function PiChatPanel({
9089
9403
  addLocalNotice({ id: "session-auto-create-error", level: "error", text: errorMessage2(error, "Could not create a Pi session."), dismissible: true });
9090
9404
  });
9091
9405
  }, [activeSessionId, addLocalNotice, externalSessionId, sessionList.length, sessions.create, sessionsError, sessionsLoading]);
9092
- useEffect20(() => {
9406
+ useEffect21(() => {
9093
9407
  if (externalSessionId || sessionsError || activeSessionId || sessionList.length > 0) {
9094
9408
  autoCreateInFlightRef.current = false;
9095
9409
  }
9096
9410
  }, [activeSessionId, externalSessionId, sessionList.length, sessionsError]);
9097
- const deleteSession = useCallback19((sessionId2) => {
9411
+ const deleteSession = useCallback18((sessionId2) => {
9098
9412
  if (externalSessionId) return;
9099
9413
  void sessions.delete(sessionId2).catch((error) => {
9100
9414
  addLocalNotice({ id: `session-delete-error:${sessionId2}`, level: "error", text: errorMessage2(error, "Could not delete the Pi session."), dismissible: true });
9101
9415
  });
9102
9416
  }, [addLocalNotice, externalSessionId, sessions.delete]);
9103
- const resetSession = useCallback19(() => {
9417
+ const resetSession = useCallback18(() => {
9104
9418
  const currentSessionId = activeSessionId;
9105
9419
  if (externalSessionId) {
9106
9420
  void onSessionReset?.();
@@ -9118,11 +9432,11 @@ function PiChatPanel({
9118
9432
  resetInProgressRef.current = false;
9119
9433
  });
9120
9434
  }, [activeSessionId, addLocalNotice, externalSessionId, onSessionReset, sessions.create, sessions.delete]);
9121
- const reloadAgentPlugins = useCallback19(async () => {
9435
+ const reloadAgentPlugins = useCallback18(async () => {
9122
9436
  if (!onReloadAgentPlugins) throw new Error("Agent plugin reload is not configured.");
9123
9437
  return await onReloadAgentPlugins();
9124
9438
  }, [onReloadAgentPlugins]);
9125
- const runPluginUpdate = useCallback19(async () => {
9439
+ const runPluginUpdate = useCallback18(async () => {
9126
9440
  setPluginUpdateState({ kind: "running" });
9127
9441
  try {
9128
9442
  const message = await reloadAgentPlugins();
@@ -9140,8 +9454,26 @@ function PiChatPanel({
9140
9454
  return `Plugin update failed: ${message}`;
9141
9455
  }
9142
9456
  }, [reloadAgentPlugins]);
9143
- const dismissPluginUpdate = useCallback19(() => setPluginUpdateState(null), []);
9144
- useEffect20(() => {
9457
+ const dismissPluginUpdate = useCallback18(() => setPluginUpdateState(null), []);
9458
+ const dismissCommandNotify = useCallback18(() => setCommandNotifyState(null), []);
9459
+ useEffect21(() => {
9460
+ if (typeof window === "undefined") return;
9461
+ const onCommandNotify = (event) => {
9462
+ const payload = event.detail;
9463
+ if (!payload || typeof payload.message !== "string") return;
9464
+ const tone = payload.tone;
9465
+ const command = payload.command ?? "";
9466
+ if (tone === "error") {
9467
+ setCommandNotifyState({ kind: "error", command, message: payload.message });
9468
+ } else {
9469
+ const runId = ++commandRunIdRef.current;
9470
+ setCommandNotifyState({ kind: "success", command, detail: payload.message, runId });
9471
+ }
9472
+ };
9473
+ window.addEventListener(WORKSPACE_COMMAND_NOTIFY_EVENT, onCommandNotify);
9474
+ return () => window.removeEventListener(WORKSPACE_COMMAND_NOTIFY_EVENT, onCommandNotify);
9475
+ }, []);
9476
+ useEffect21(() => {
9145
9477
  if (!hotReloadEnabled || typeof window === "undefined") return;
9146
9478
  const onBrowserPluginReload = (event) => {
9147
9479
  const parsed = parseBrowserPluginReloadDetail(event.detail);
@@ -9179,7 +9511,7 @@ function PiChatPanel({
9179
9511
  textareaRef,
9180
9512
  disabled: mentionState !== null || slashQuery !== null || modelPickerOpen || thinkingPickerOpen
9181
9513
  });
9182
- const setComposerDraft = useCallback19((next, focus = true) => {
9514
+ const setComposerDraft = useCallback18((next, focus = true) => {
9183
9515
  draftRef.current = next;
9184
9516
  setDraft(next);
9185
9517
  if (textareaRef.current) {
@@ -9187,11 +9519,11 @@ function PiChatPanel({
9187
9519
  if (focus) textareaRef.current.focus();
9188
9520
  }
9189
9521
  }, []);
9190
- const warnComposer = useCallback19((message) => {
9522
+ const warnComposer = useCallback18((message) => {
9191
9523
  onComposerWarning?.(message);
9192
9524
  addLocalNotice({ id: `composer-warning:${Date.now()}`, level: "warning", text: message, dismissible: true });
9193
9525
  }, [addLocalNotice, onComposerWarning]);
9194
- const openModelPicker = useCallback19(() => {
9526
+ const openModelPicker = useCallback18(() => {
9195
9527
  if (isPiBusyStatus(statusRef.current)) {
9196
9528
  warnComposer("Model picker is unavailable while the agent is running.");
9197
9529
  return false;
@@ -9204,7 +9536,7 @@ function PiChatPanel({
9204
9536
  setModelPickerOpen(true);
9205
9537
  return true;
9206
9538
  }, [model, warnComposer]);
9207
- const openThinkingPicker = useCallback19(() => {
9539
+ const openThinkingPicker = useCallback18(() => {
9208
9540
  if (isPiBusyStatus(statusRef.current)) {
9209
9541
  warnComposer("Thinking picker is unavailable while the agent is running.");
9210
9542
  return false;
@@ -9217,21 +9549,21 @@ function PiChatPanel({
9217
9549
  setThinkingPickerOpen(true);
9218
9550
  return true;
9219
9551
  }, [thinkingControl, thinkingLevel, warnComposer]);
9220
- const selectComposerModel = useCallback19((query) => {
9552
+ const selectComposerModel = useCallback18((query) => {
9221
9553
  if (model !== void 0) return "Model selection is controlled by the host.";
9222
9554
  const match = resolveModelSlashSelection(query, modelOptions);
9223
9555
  if (!match) return `No model matched "${query}".`;
9224
9556
  modelDiscovery.setModel(match);
9225
9557
  return `Model set to ${match.label ?? match.id}.`;
9226
9558
  }, [model, modelDiscovery, modelOptions]);
9227
- const selectComposerThinking = useCallback19((query) => {
9559
+ const selectComposerThinking = useCallback18((query) => {
9228
9560
  if (!thinkingControl || thinkingLevel !== void 0) return "Thinking level is controlled by the host.";
9229
9561
  const match = resolveThinkingSlashSelection(query);
9230
9562
  if (!match) return `No thinking level matched "${query}".`;
9231
9563
  setStoredThinkingLevel(match);
9232
9564
  return `Thinking set to ${thinkingLabel(match)}.`;
9233
9565
  }, [thinkingControl, thinkingLevel]);
9234
- const selectSlashCommand = useCallback19((name) => {
9566
+ const selectSlashCommand = useCallback18((name) => {
9235
9567
  if (name === "model") {
9236
9568
  dismissSlash();
9237
9569
  if (openModelPicker()) setComposerDraft("");
@@ -9309,7 +9641,7 @@ function PiChatPanel({
9309
9641
  }
9310
9642
  });
9311
9643
  }, [activeChatSessionId, addLocalNotice, clearMentionedFiles, composerBlocked, composerBlockerLabel, effectiveMentionedFiles, markLocalSubmitted, onBeforeSubmit, onCommandResult, onComposerWarning, onMentionedFilesConsumed, openModelPicker, openThinkingPicker, registry, reloadAgentPlugins, resetSession, runPluginUpdate, selectComposerModel, selectComposerThinking, selectedModel, selectedPiSession, selectedThinking, setComposerDraft, submitThinkingControl]);
9312
- const sendComposerMessage = useCallback19(async ({ text, files, source = "composer" }) => {
9644
+ const sendComposerMessage = useCallback18(async ({ text, files, source = "composer" }) => {
9313
9645
  if (!policy) {
9314
9646
  addLocalNotice({ id: "composer-no-session", level: "warning", text: "Create or select a Pi session before sending.", dismissible: true });
9315
9647
  return false;
@@ -9337,7 +9669,7 @@ function PiChatPanel({
9337
9669
  throw error;
9338
9670
  }
9339
9671
  }, [activeChatSessionId, addLocalNotice, clearLocalSubmitted, markLocalSubmitted, policy, selectedPiSession, setComposerDraft]);
9340
- const editQueued = useCallback19(() => {
9672
+ const editQueued = useCallback18(() => {
9341
9673
  if (!policy) return;
9342
9674
  void policy.editQueued().then((result) => {
9343
9675
  if (result.type === "clear-failed") {
@@ -9345,23 +9677,24 @@ function PiChatPanel({
9345
9677
  }
9346
9678
  });
9347
9679
  }, [addLocalNotice, policy]);
9348
- const stop = useCallback19(() => {
9680
+ const stop = useCallback18(() => {
9349
9681
  onComposerStop?.();
9350
9682
  void policy?.stop().catch((error) => {
9351
9683
  addLocalNotice({ id: "stop-error", level: "error", text: errorMessage2(error, "Could not stop the Pi session."), dismissible: true });
9352
9684
  });
9353
9685
  }, [addLocalNotice, onComposerStop, policy]);
9354
- const interrupt = useCallback19(() => {
9686
+ const interrupt = useCallback18(() => {
9355
9687
  void policy?.interrupt().catch((error) => {
9356
9688
  addLocalNotice({ id: "interrupt-error", level: "error", text: errorMessage2(error, "Could not interrupt the Pi session."), dismissible: true });
9357
9689
  });
9358
9690
  }, [addLocalNotice, policy]);
9359
- useEffect20(() => {
9691
+ useEffect21(() => {
9360
9692
  setPluginUpdateState(null);
9693
+ setCommandNotifyState(null);
9361
9694
  setLocalNotices([]);
9362
9695
  setDismissedNoticeIds(/* @__PURE__ */ new Set());
9363
9696
  }, [activeSessionId]);
9364
- useEffect20(() => {
9697
+ useEffect21(() => {
9365
9698
  const currentSessionId = activeSessionId ?? "__none__";
9366
9699
  if (initialDraftGuard.current.shouldRestore(currentSessionId, initialDraft) && initialDraft !== void 0) {
9367
9700
  setComposerDraft(initialDraft, initialDraft.length > 0);
@@ -9370,7 +9703,7 @@ function PiChatPanel({
9370
9703
  }, [activeSessionId, initialDraft, onDraftRestored, setComposerDraft]);
9371
9704
  const remoteStatus = selectedChatState?.status ?? (sessionsLoading || chatStatePending || selectedSessionPending ? "hydrating" : "idle");
9372
9705
  const status = localSubmittedSessionId === activeSessionId && remoteStatus === "idle" ? "submitted" : remoteStatus;
9373
- useEffect20(() => {
9706
+ useEffect21(() => {
9374
9707
  if (!localSubmittedSessionId) return;
9375
9708
  if (localSubmittedSessionId !== activeSessionId) {
9376
9709
  clearLocalSubmitted();
@@ -9378,7 +9711,7 @@ function PiChatPanel({
9378
9711
  }
9379
9712
  if (remoteStatus !== "idle") clearLocalSubmitted(localSubmittedSessionId);
9380
9713
  }, [activeSessionId, clearLocalSubmitted, localSubmittedSessionId, remoteStatus]);
9381
- useEffect20(() => {
9714
+ useEffect21(() => {
9382
9715
  const previous = prevStatusRef.current;
9383
9716
  statusRef.current = status;
9384
9717
  prevStatusRef.current = status;
@@ -9388,7 +9721,7 @@ function PiChatPanel({
9388
9721
  if (!isPiBusyStatus(previous) && previous !== "submitted") return;
9389
9722
  settlePendingAutoSubmit();
9390
9723
  }, [settlePendingAutoSubmit, status]);
9391
- useEffect20(() => {
9724
+ useEffect21(() => {
9392
9725
  if (!autoSubmitInitialDraft || !policy || !activeSessionId || composerBlocked) return;
9393
9726
  if (!initialDraftGuard.current.claimAutoSubmit(activeSessionId, initialDraft)) return;
9394
9727
  pendingAutoSubmitSettleRef.current = activeSessionId;
@@ -9423,7 +9756,7 @@ function PiChatPanel({
9423
9756
  addLocalNotice({ id: "auto-submit-error", level: "error", text: errorMessage2(error, "Could not auto-submit the initial draft."), dismissible: true });
9424
9757
  });
9425
9758
  }, [activeSessionId, addLocalNotice, autoSubmitInitialDraft, clearLocalSubmitted, composerBlocked, initialDraft, markLocalSubmitted, onAutoSubmitInitialDraftAccepted, policy, selectedPiSession, setComposerDraft, settlePendingAutoSubmit]);
9426
- useEffect20(() => {
9759
+ useEffect21(() => {
9427
9760
  if (workspaceWarmupStatus?.status === "ready") {
9428
9761
  clearLocalNotice("workspace-warmup");
9429
9762
  }
@@ -9434,18 +9767,18 @@ function PiChatPanel({
9434
9767
  const submitDisabled = !policy || sessionsLoading || composerBlocked && !isStreaming;
9435
9768
  const mergedToolRenderers = useMemo12(() => mergeShadcnToolRenderers(toolRenderers), [toolRenderers]);
9436
9769
  const debugMessages = useMemo12(() => messages.map(toDebugUiMessage), [messages]);
9437
- const onTextareaChange = useCallback19((event) => {
9770
+ const onTextareaChange = useCallback18((event) => {
9438
9771
  setModelPickerOpen(false);
9439
9772
  setThinkingPickerOpen(false);
9440
9773
  setDraft(event.currentTarget.value);
9441
9774
  handleComposerChange(event);
9442
9775
  }, [handleComposerChange]);
9443
- useEffect20(() => {
9776
+ useEffect21(() => {
9444
9777
  if (!isStreaming) return;
9445
9778
  setModelPickerOpen(false);
9446
9779
  setThinkingPickerOpen(false);
9447
9780
  }, [isStreaming]);
9448
- useEffect20(() => {
9781
+ useEffect21(() => {
9449
9782
  if (typeof window === "undefined" || !activeChatSessionId) return;
9450
9783
  window.dispatchEvent(new CustomEvent("boring:chat-session-status", {
9451
9784
  detail: { sessionId: activeChatSessionId, working: isStreaming }
@@ -9458,7 +9791,7 @@ function PiChatPanel({
9458
9791
  }));
9459
9792
  };
9460
9793
  }, [activeChatSessionId, isStreaming]);
9461
- const onTextareaKeyDown = useCallback19((event) => {
9794
+ const onTextareaKeyDown = useCallback18((event) => {
9462
9795
  if (event.key === "Escape" && isStreaming) {
9463
9796
  if (event.defaultPrevented || mentionState !== null || slashQuery !== null) {
9464
9797
  handleComposerKeyDown(event);
@@ -9470,7 +9803,7 @@ function PiChatPanel({
9470
9803
  }
9471
9804
  handleComposerKeyDown(event);
9472
9805
  }, [handleComposerKeyDown, interrupt, isStreaming, mentionState, slashQuery]);
9473
- return /* @__PURE__ */ jsx31(ArtifactOpenProvider, { onOpenArtifact, children: /* @__PURE__ */ jsxs29(
9806
+ return /* @__PURE__ */ jsx33(ArtifactOpenProvider, { onOpenArtifact, children: /* @__PURE__ */ jsxs31(
9474
9807
  "div",
9475
9808
  {
9476
9809
  "data-boring-agent": "",
@@ -9487,7 +9820,7 @@ function PiChatPanel({
9487
9820
  role: "region",
9488
9821
  "aria-label": "Agent assistant",
9489
9822
  children: [
9490
- showSessionSidebar ? /* @__PURE__ */ jsx31("aside", { "data-boring-agent-part": "pi-chat-session-sidebar", className: "min-h-0 w-64 shrink-0 border-r border-border/60", children: /* @__PURE__ */ jsx31(
9823
+ 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(
9491
9824
  SessionList,
9492
9825
  {
9493
9826
  sessions: sessionList,
@@ -9501,7 +9834,7 @@ function PiChatPanel({
9501
9834
  loadingMore: sessions.loadingMore
9502
9835
  }
9503
9836
  ) }) : null,
9504
- /* @__PURE__ */ jsx31("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col", children: /* @__PURE__ */ jsxs29(
9837
+ /* @__PURE__ */ jsx33("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col", children: /* @__PURE__ */ jsxs31(
9505
9838
  "div",
9506
9839
  {
9507
9840
  className: cn(
@@ -9510,7 +9843,7 @@ function PiChatPanel({
9510
9843
  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)]"
9511
9844
  ),
9512
9845
  children: [
9513
- /* @__PURE__ */ jsx31(
9846
+ /* @__PURE__ */ jsx33(
9514
9847
  PiConversationSurface,
9515
9848
  {
9516
9849
  chrome,
@@ -9532,7 +9865,7 @@ function PiChatPanel({
9532
9865
  windowResetKey: activeSessionId
9533
9866
  }
9534
9867
  ),
9535
- /* @__PURE__ */ jsx31(
9868
+ /* @__PURE__ */ jsx33(
9536
9869
  PiChatComposerSurface,
9537
9870
  {
9538
9871
  chrome,
@@ -9554,6 +9887,8 @@ function PiChatPanel({
9554
9887
  pluginUpdateState,
9555
9888
  onDismissPluginUpdate: dismissPluginUpdate,
9556
9889
  onRunPluginUpdate: runPluginUpdate,
9890
+ commandNotifyState,
9891
+ onDismissCommandNotify: dismissCommandNotify,
9557
9892
  attachmentNotice,
9558
9893
  onAttachmentNotice: setAttachmentNotice,
9559
9894
  mentionState,
@@ -9592,7 +9927,7 @@ function PiChatPanel({
9592
9927
  ]
9593
9928
  }
9594
9929
  ) }),
9595
- debug ? /* @__PURE__ */ jsx31(Suspense, { fallback: null, children: /* @__PURE__ */ jsx31("div", { "aria-label": "Pi chat debug metadata", className: "contents", role: "region", children: /* @__PURE__ */ jsx31(
9930
+ debug ? /* @__PURE__ */ jsx33(Suspense, { fallback: null, children: /* @__PURE__ */ jsx33("div", { "aria-label": "Pi chat debug metadata", className: "contents", role: "region", children: /* @__PURE__ */ jsx33(
9596
9931
  DebugDrawer2,
9597
9932
  {
9598
9933
  apiBaseUrl,