@hachej/boring-agent 0.1.35 → 0.1.37

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) {
@@ -4863,7 +4931,7 @@ function clearPageUnloading() {
4863
4931
  // src/front/chat/session/usePiSessions.ts
4864
4932
  var DEFAULT_SESSIONS_API_PATH = "/api/v1/agent/pi-chat/sessions";
4865
4933
  var SESSION_PAGE_SIZE = 50;
4866
- var DEFAULT_MAX_RETRIES = 8;
4934
+ var DEFAULT_MAX_RETRIES = 60;
4867
4935
  var DEFAULT_RETRY_BASE_MS = 250;
4868
4936
  var DEFAULT_RETRY_MAX_MS = 2e3;
4869
4937
  var SessionsPreparingError = class extends Error {
@@ -4872,6 +4940,9 @@ var SessionsPreparingError = class extends Error {
4872
4940
  this.name = "SessionsPreparingError";
4873
4941
  }
4874
4942
  };
4943
+ function isNetworkFetchError(error) {
4944
+ return error instanceof TypeError;
4945
+ }
4875
4946
  function usePiSessions(options = {}) {
4876
4947
  const enabled = options.enabled ?? true;
4877
4948
  const apiBaseUrl = options.apiBaseUrl?.replace(/\/$/, "") ?? "";
@@ -4895,20 +4966,20 @@ function usePiSessions(options = {}) {
4895
4966
  const [loadingMore, setLoadingMore] = useState10(false);
4896
4967
  const [hasMore, setHasMore] = useState10(false);
4897
4968
  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);
4969
+ const mountedRef = useRef8(false);
4970
+ const refreshVersionRef = useRef8(0);
4971
+ const retryTimerRef = useRef8(void 0);
4972
+ const sessionsRef = useRef8([]);
4973
+ const activeSessionIdRef = useRef8(activeSessionId);
4974
+ const hasMoreRef = useRef8(hasMore);
4975
+ const canonicalLoadedCountRef = useRef8(0);
4976
+ const loadMoreRequestSeqRef = useRef8(0);
4977
+ const loadMoreInFlightRef = useRef8(false);
4978
+ const pendingCreatedRef = useRef8(/* @__PURE__ */ new Map());
4979
+ const pendingCreatedScopeRef = useRef8(requestScopeKey);
4980
+ const dataStorageScopeRef = useRef8(storageScope);
4981
+ const loadedDataSourceRef = useRef8(dataSourceKey);
4982
+ const requestScopeRef = useRef8(requestScopeKey);
4912
4983
  requestScopeRef.current = requestScopeKey;
4913
4984
  useEffect8(() => {
4914
4985
  sessionsRef.current = sessions;
@@ -5005,7 +5076,8 @@ function usePiSessions(options = {}) {
5005
5076
  data = await fetchSessionList(fetchImpl, sessionsListUrl(0, preferredSessionId()), requestHeaders());
5006
5077
  break;
5007
5078
  } catch (err) {
5008
- const retryable = err instanceof SessionsPreparingError && attempt < retryMaxRetries;
5079
+ const transient = err instanceof SessionsPreparingError || isNetworkFetchError(err);
5080
+ const retryable = transient && attempt < retryMaxRetries;
5009
5081
  if (!retryable) throw err;
5010
5082
  if (!isCurrent()) return;
5011
5083
  await delayWithRef(retryDelayMs(attempt, { baseMs: retryBaseMs, maxMs: retryMaxMs }), retryTimerRef);
@@ -5429,7 +5501,7 @@ function sortByUpdatedDesc(a, b) {
5429
5501
 
5430
5502
  // src/front/chat/components/PiConversationSurface.tsx
5431
5503
  import { Loader2 as Loader22 } from "lucide-react";
5432
- import { useCallback as useCallback14, useEffect as useEffect14, useLayoutEffect, useRef as useRef9, useState as useState15 } from "react";
5504
+ import { useCallback as useCallback14, useEffect as useEffect14, useLayoutEffect, useRef as useRef10, useState as useState15 } from "react";
5433
5505
  import { useStickToBottomContext as useStickToBottomContext2 } from "use-stick-to-bottom";
5434
5506
 
5435
5507
  // src/front/primitives/conversation.tsx
@@ -6125,7 +6197,7 @@ import {
6125
6197
  useContext as useContext5,
6126
6198
  useEffect as useEffect11,
6127
6199
  useMemo as useMemo8,
6128
- useRef as useRef8,
6200
+ useRef as useRef9,
6129
6201
  useState as useState12
6130
6202
  } from "react";
6131
6203
  import { Streamdown as Streamdown2 } from "streamdown";
@@ -6217,9 +6289,9 @@ var Reasoning = memo5(
6217
6289
  defaultProp: void 0,
6218
6290
  prop: durationProp
6219
6291
  });
6220
- const hasEverStreamedRef = useRef8(isStreaming);
6292
+ const hasEverStreamedRef = useRef9(isStreaming);
6221
6293
  const [hasAutoClosed, setHasAutoClosed] = useState12(false);
6222
- const startTimeRef = useRef8(null);
6294
+ const startTimeRef = useRef9(null);
6223
6295
  useEffect11(() => {
6224
6296
  if (isStreaming) {
6225
6297
  hasEverStreamedRef.current = true;
@@ -6947,8 +7019,8 @@ function PiConversationSurface({
6947
7019
  }
6948
7020
  function TranscriptHistoryLoader({ olderCount, onLoadOlder }) {
6949
7021
  const { scrollRef } = useStickToBottomContext2();
6950
- const pendingAnchor = useRef9(null);
6951
- const armed = useRef9(true);
7022
+ const pendingAnchor = useRef10(null);
7023
+ const armed = useRef10(true);
6952
7024
  useEffect14(() => {
6953
7025
  const el = scrollRef.current;
6954
7026
  if (!el) return;
@@ -7009,38 +7081,34 @@ function buildMessageRenderItems(messages) {
7009
7081
  }
7010
7082
 
7011
7083
  // src/front/chat/components/PiChatComposerSurface.tsx
7012
- import { useCallback as useCallback17, useLayoutEffect as useLayoutEffect2 } from "react";
7084
+ import { useCallback as useCallback16, useLayoutEffect as useLayoutEffect2 } from "react";
7013
7085
  import { motion as motion2 } from "motion/react";
7014
7086
 
7015
7087
  // 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";
7088
+ import { useEffect as useEffect15, useRef as useRef11 } from "react";
7089
+
7090
+ // src/front/composer/ComposerStatusBanner.tsx
7018
7091
  import { jsx as jsx24, jsxs as jsxs22 } from "react/jsx-runtime";
7019
- function PluginUpdateStatus({
7020
- state,
7092
+ function ComposerStatusBanner({
7093
+ tone,
7094
+ dataAttribute,
7095
+ runningContent,
7096
+ title,
7097
+ detail,
7098
+ message,
7099
+ children,
7021
7100
  onDismiss,
7022
7101
  onRetry,
7023
- successAutoDismissMs = 1400,
7102
+ retryLabel = "Try again",
7103
+ dismissAriaLabel = "Dismiss status",
7024
7104
  maxWidthClassName = "max-w-3xl"
7025
7105
  }) {
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") {
7106
+ const toneAttr = { [dataAttribute]: tone };
7107
+ if (tone === "running") {
7040
7108
  return /* @__PURE__ */ jsxs22(
7041
7109
  "div",
7042
7110
  {
7043
- "data-boring-plugin-update": "running",
7111
+ ...toneAttr,
7044
7112
  role: "status",
7045
7113
  "aria-live": "polite",
7046
7114
  className: cn(
@@ -7050,22 +7118,16 @@ function PluginUpdateStatus({
7050
7118
  ),
7051
7119
  children: [
7052
7120
  /* @__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" })
7121
+ /* @__PURE__ */ jsx24("span", { children: runningContent })
7054
7122
  ]
7055
7123
  }
7056
7124
  );
7057
7125
  }
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;
7126
+ if (tone === "success") {
7065
7127
  return /* @__PURE__ */ jsxs22(
7066
7128
  "div",
7067
7129
  {
7068
- "data-boring-plugin-update": "success",
7130
+ ...toneAttr,
7069
7131
  role: "status",
7070
7132
  "aria-live": "polite",
7071
7133
  className: cn(
@@ -7080,18 +7142,115 @@ function PluginUpdateStatus({
7080
7142
  /* @__PURE__ */ jsx24("span", { className: "block font-medium leading-5", children: title }),
7081
7143
  detail ? /* @__PURE__ */ jsx24("span", { className: "block leading-4 text-muted-foreground", children: detail }) : null
7082
7144
  ] }),
7083
- /* @__PURE__ */ jsx24(
7145
+ onDismiss ? /* @__PURE__ */ jsx24(
7084
7146
  "button",
7085
7147
  {
7086
7148
  type: "button",
7087
7149
  onClick: onDismiss,
7088
7150
  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",
7151
+ "aria-label": dismissAriaLabel,
7090
7152
  children: "\xD7"
7091
7153
  }
7092
- )
7154
+ ) : null
7093
7155
  ] }),
7094
- diagnostics.length > 0 ? /* @__PURE__ */ jsxs22(
7156
+ children
7157
+ ]
7158
+ }
7159
+ );
7160
+ }
7161
+ return /* @__PURE__ */ jsxs22(
7162
+ "div",
7163
+ {
7164
+ ...toneAttr,
7165
+ role: "status",
7166
+ "aria-live": "polite",
7167
+ className: cn(
7168
+ "mx-auto mb-2 w-full rounded-[var(--radius-md)] border border-destructive/40 bg-destructive/10",
7169
+ "px-3 py-2 text-xs text-foreground",
7170
+ maxWidthClassName
7171
+ ),
7172
+ children: [
7173
+ /* @__PURE__ */ jsxs22("div", { className: "flex items-center gap-2", children: [
7174
+ /* @__PURE__ */ jsx24("span", { className: "text-destructive", "aria-hidden": "true", children: "\u26A0" }),
7175
+ /* @__PURE__ */ jsx24("span", { className: "flex-1 font-medium", children: title }),
7176
+ onRetry ? /* @__PURE__ */ jsx24(
7177
+ "button",
7178
+ {
7179
+ type: "button",
7180
+ onClick: onRetry,
7181
+ className: "rounded border border-destructive/40 px-2 py-0.5 text-[11px] font-medium hover:bg-destructive/10",
7182
+ children: retryLabel
7183
+ }
7184
+ ) : null,
7185
+ onDismiss ? /* @__PURE__ */ jsx24(
7186
+ "button",
7187
+ {
7188
+ type: "button",
7189
+ onClick: onDismiss,
7190
+ className: "rounded border border-transparent px-2 py-0.5 text-[11px] font-medium text-muted-foreground hover:bg-muted hover:text-foreground",
7191
+ "aria-label": dismissAriaLabel,
7192
+ children: "Dismiss"
7193
+ }
7194
+ ) : null
7195
+ ] }),
7196
+ message ? /* @__PURE__ */ jsx24("pre", { className: "mt-2 whitespace-pre-wrap break-words text-[11px] text-destructive/90", children: message }) : null
7197
+ ]
7198
+ }
7199
+ );
7200
+ }
7201
+
7202
+ // src/front/composer/PluginUpdateStatus.tsx
7203
+ import { jsx as jsx25, jsxs as jsxs23 } from "react/jsx-runtime";
7204
+ function PluginUpdateStatus({
7205
+ state,
7206
+ onDismiss,
7207
+ onRetry,
7208
+ successAutoDismissMs = 1400,
7209
+ maxWidthClassName = "max-w-3xl"
7210
+ }) {
7211
+ const onDismissRef = useRef11(onDismiss);
7212
+ useEffect15(() => {
7213
+ onDismissRef.current = onDismiss;
7214
+ }, [onDismiss]);
7215
+ const successDismissKey = state?.kind === "success" ? `${state.reloaded}:${(state.restartWarnings?.length ?? 0) > 0 || (state.diagnostics?.length ?? 0) > 0}` : null;
7216
+ useEffect15(() => {
7217
+ if (!state || state.kind !== "success" || successAutoDismissMs <= 0) return;
7218
+ const hasWarningsOrDiagnostics = (state.restartWarnings?.length ?? 0) > 0 || (state.diagnostics?.length ?? 0) > 0;
7219
+ if (hasWarningsOrDiagnostics) return;
7220
+ const timeout = window.setTimeout(() => onDismissRef.current(), successAutoDismissMs);
7221
+ return () => window.clearTimeout(timeout);
7222
+ }, [state?.kind, successDismissKey, successAutoDismissMs]);
7223
+ if (!state) return null;
7224
+ if (state.kind === "running") {
7225
+ return /* @__PURE__ */ jsx25(
7226
+ ComposerStatusBanner,
7227
+ {
7228
+ tone: "running",
7229
+ dataAttribute: "data-boring-plugin-update",
7230
+ maxWidthClassName,
7231
+ runningContent: "Updating plugins\u2026"
7232
+ }
7233
+ );
7234
+ }
7235
+ if (state.kind === "success") {
7236
+ const warnings = state.restartWarnings ?? [];
7237
+ const diagnostics = state.diagnostics ?? [];
7238
+ const frontEvents = state.frontEvents ?? [];
7239
+ const hasWarningsOrDiagnostics = warnings.length > 0 || diagnostics.length > 0;
7240
+ const title = state.reloaded ? hasWarningsOrDiagnostics ? "Reload finished with warnings" : "Reload complete" : "Reload queued";
7241
+ 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;
7242
+ return /* @__PURE__ */ jsxs23(
7243
+ ComposerStatusBanner,
7244
+ {
7245
+ tone: "success",
7246
+ dataAttribute: "data-boring-plugin-update",
7247
+ maxWidthClassName,
7248
+ title,
7249
+ detail,
7250
+ onDismiss,
7251
+ dismissAriaLabel: "Dismiss plugin update status",
7252
+ children: [
7253
+ diagnostics.length > 0 ? /* @__PURE__ */ jsxs23(
7095
7254
  "div",
7096
7255
  {
7097
7256
  "data-boring-plugin-update-diagnostics": "",
@@ -7100,24 +7259,24 @@ function PluginUpdateStatus({
7100
7259
  "px-2 py-1.5 text-[11px] text-foreground"
7101
7260
  ),
7102
7261
  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: [
7262
+ /* @__PURE__ */ jsxs23("div", { className: "flex items-center gap-1.5 font-medium text-[oklch(0.48_0.15_60)]", children: [
7263
+ /* @__PURE__ */ jsx25("span", { "aria-hidden": "true", children: "\u26A0" }),
7264
+ /* @__PURE__ */ jsxs23("span", { children: [
7106
7265
  "Reload diagnostics for ",
7107
7266
  diagnostics.length,
7108
7267
  " plugin",
7109
7268
  diagnostics.length === 1 ? "" : "s"
7110
7269
  ] })
7111
7270
  ] }),
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" }),
7271
+ /* @__PURE__ */ jsx25("ul", { className: "mt-1 ml-4 list-disc text-foreground/85", children: diagnostics.map((diagnostic, index) => /* @__PURE__ */ jsxs23("li", { children: [
7272
+ /* @__PURE__ */ jsx25("code", { className: "font-mono text-[10.5px]", children: diagnostic.pluginId ?? diagnostic.source ?? "plugin" }),
7114
7273
  " \u2014 ",
7115
7274
  diagnostic.message ?? "reload diagnostic"
7116
7275
  ] }, `${diagnostic.pluginId ?? diagnostic.source ?? "plugin"}-${index}`)) })
7117
7276
  ]
7118
7277
  }
7119
7278
  ) : null,
7120
- warnings.length > 0 ? /* @__PURE__ */ jsxs22(
7279
+ warnings.length > 0 ? /* @__PURE__ */ jsxs23(
7121
7280
  "div",
7122
7281
  {
7123
7282
  "data-boring-plugin-update-restart-warning": "",
@@ -7126,21 +7285,21 @@ function PluginUpdateStatus({
7126
7285
  "px-2 py-1.5 text-[11px] text-foreground"
7127
7286
  ),
7128
7287
  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: [
7288
+ /* @__PURE__ */ jsxs23("div", { className: "flex items-center gap-1.5 font-medium text-[oklch(0.48_0.15_60)]", children: [
7289
+ /* @__PURE__ */ jsx25("span", { "aria-hidden": "true", children: "\u26A0" }),
7290
+ /* @__PURE__ */ jsxs23("span", { children: [
7132
7291
  "Restart needed for ",
7133
7292
  warnings.length,
7134
7293
  " plugin",
7135
7294
  warnings.length === 1 ? "" : "s"
7136
7295
  ] })
7137
7296
  ] }),
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 }),
7297
+ /* @__PURE__ */ jsx25("ul", { className: "mt-1 ml-4 list-disc text-foreground/85", children: warnings.map((w) => /* @__PURE__ */ jsxs23("li", { children: [
7298
+ /* @__PURE__ */ jsx25("code", { className: "font-mono text-[10.5px]", children: w.id }),
7140
7299
  " \u2014 ",
7141
7300
  w.surfaces.join(" + ")
7142
7301
  ] }, 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." })
7302
+ /* @__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
7303
  ]
7145
7304
  }
7146
7305
  ) : null
@@ -7148,45 +7307,104 @@ function PluginUpdateStatus({
7148
7307
  }
7149
7308
  );
7150
7309
  }
7151
- return /* @__PURE__ */ jsxs22(
7152
- "div",
7310
+ return /* @__PURE__ */ jsx25(
7311
+ ComposerStatusBanner,
7153
7312
  {
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
- )
7313
+ tone: "error",
7314
+ dataAttribute: "data-boring-plugin-update",
7315
+ maxWidthClassName,
7316
+ title: "Plugin update failed.",
7317
+ message: state.message,
7318
+ onRetry,
7319
+ onDismiss,
7320
+ retryLabel: "Try again",
7321
+ dismissAriaLabel: "Dismiss plugin update status"
7322
+ }
7323
+ );
7324
+ }
7325
+
7326
+ // src/front/composer/CommandRunStatus.tsx
7327
+ import { useEffect as useEffect16, useRef as useRef12 } from "react";
7328
+ import { Fragment as Fragment7, jsx as jsx26, jsxs as jsxs24 } from "react/jsx-runtime";
7329
+ function CommandRunStatus({
7330
+ state,
7331
+ onDismiss,
7332
+ successAutoDismissMs = 1400,
7333
+ maxWidthClassName = "max-w-3xl"
7334
+ }) {
7335
+ const onDismissRef = useRef12(onDismiss);
7336
+ useEffect16(() => {
7337
+ onDismissRef.current = onDismiss;
7338
+ }, [onDismiss]);
7339
+ const successKey = state?.kind === "success" ? `${state.runId}:${state.command}:${state.detail ?? ""}` : null;
7340
+ useEffect16(() => {
7341
+ if (!state || state.kind !== "success" || successAutoDismissMs <= 0) return;
7342
+ const timeout = window.setTimeout(() => onDismissRef.current(), successAutoDismissMs);
7343
+ return () => window.clearTimeout(timeout);
7344
+ }, [state?.kind, successKey, successAutoDismissMs]);
7345
+ if (!state) return null;
7346
+ if (state.kind === "running") {
7347
+ return /* @__PURE__ */ jsx26(
7348
+ ComposerStatusBanner,
7349
+ {
7350
+ tone: "running",
7351
+ dataAttribute: "data-boring-command-run",
7352
+ maxWidthClassName,
7353
+ runningContent: state.command ? /* @__PURE__ */ jsxs24(Fragment7, { children: [
7354
+ " Running ",
7355
+ /* @__PURE__ */ jsxs24("code", { className: "font-mono", children: [
7356
+ "/",
7357
+ state.command
7358
+ ] }),
7359
+ "\u2026 "
7360
+ ] }) : "Running command\u2026"
7361
+ }
7362
+ );
7363
+ }
7364
+ if (state.kind === "success") {
7365
+ return /* @__PURE__ */ jsx26(
7366
+ ComposerStatusBanner,
7367
+ {
7368
+ tone: "success",
7369
+ dataAttribute: "data-boring-command-run",
7370
+ maxWidthClassName,
7371
+ title: state.command ? /* @__PURE__ */ jsxs24(Fragment7, { children: [
7372
+ " Ran ",
7373
+ /* @__PURE__ */ jsxs24("code", { className: "font-mono", children: [
7374
+ "/",
7375
+ state.command
7376
+ ] }),
7377
+ " "
7378
+ ] }) : "Command ran",
7379
+ detail: state.detail,
7380
+ onDismiss,
7381
+ dismissAriaLabel: "Dismiss command status"
7382
+ }
7383
+ );
7384
+ }
7385
+ return /* @__PURE__ */ jsx26(
7386
+ ComposerStatusBanner,
7387
+ {
7388
+ tone: "error",
7389
+ dataAttribute: "data-boring-command-run",
7390
+ maxWidthClassName,
7391
+ title: state.command ? /* @__PURE__ */ jsxs24(Fragment7, { children: [
7392
+ " ",
7393
+ /* @__PURE__ */ jsxs24("code", { className: "font-mono", children: [
7394
+ "/",
7395
+ state.command
7181
7396
  ] }),
7182
- /* @__PURE__ */ jsx24("pre", { className: noticeTextClass("mt-2 text-[11px] text-muted-foreground"), children: state.message })
7183
- ]
7397
+ " failed. "
7398
+ ] }) : "Command failed.",
7399
+ message: state.message,
7400
+ onDismiss,
7401
+ dismissAriaLabel: "Dismiss command status"
7184
7402
  }
7185
7403
  );
7186
7404
  }
7187
7405
 
7188
7406
  // src/front/chatPanelComposerControls.tsx
7189
- import { forwardRef, useEffect as useEffect16, useRef as useRef11, useState as useState16 } from "react";
7407
+ import { forwardRef, useEffect as useEffect17, useRef as useRef13, useState as useState16 } from "react";
7190
7408
  import { CheckIcon as CheckIcon3, ChevronDownIcon as ChevronDownIcon5 } from "lucide-react";
7191
7409
  import {
7192
7410
  Command,
@@ -7220,7 +7438,7 @@ function displayProviderLabel(provider) {
7220
7438
  }
7221
7439
 
7222
7440
  // src/front/chatPanelComposerControls.tsx
7223
- import { Fragment as Fragment7, jsx as jsx25, jsxs as jsxs23 } from "react/jsx-runtime";
7441
+ import { Fragment as Fragment8, jsx as jsx27, jsxs as jsxs25 } from "react/jsx-runtime";
7224
7442
  var composerActionClass = cn(
7225
7443
  "inline-flex h-8 items-center justify-center gap-1.5 rounded-[var(--radius-md)] border-0 bg-transparent",
7226
7444
  "text-muted-foreground shadow-none transition",
@@ -7270,7 +7488,7 @@ function isTextInputTarget(target) {
7270
7488
  return target instanceof HTMLInputElement;
7271
7489
  }
7272
7490
  function useDismissOnOutsidePointer(ref, onClose) {
7273
- useEffect16(() => {
7491
+ useEffect17(() => {
7274
7492
  if (!onClose) return;
7275
7493
  const handler = (event) => {
7276
7494
  const target = event.target;
@@ -7288,7 +7506,7 @@ function useDismissOnOutsidePointer(ref, onClose) {
7288
7506
  }
7289
7507
  function ThinkingLevelGlyph({ level }) {
7290
7508
  const lit = level === "off" ? 0 : level === "low" ? 1 : level === "medium" ? 2 : 3;
7291
- return /* @__PURE__ */ jsx25(
7509
+ return /* @__PURE__ */ jsx27(
7292
7510
  "svg",
7293
7511
  {
7294
7512
  "aria-hidden": "true",
@@ -7297,7 +7515,7 @@ function ThinkingLevelGlyph({ level }) {
7297
7515
  viewBox: "0 0 14 14",
7298
7516
  fill: "none",
7299
7517
  className: "shrink-0",
7300
- children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx25(
7518
+ children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx27(
7301
7519
  "rect",
7302
7520
  {
7303
7521
  x: 2 + i * 4,
@@ -7354,7 +7572,7 @@ var ModelSelectTrigger = forwardRef(function ModelSelectTrigger2({
7354
7572
  }, ref) {
7355
7573
  const triggerLabel = modelTriggerLabel(value, options);
7356
7574
  const triggerDisplay = value ? `${triggerLabel} (${displayProviderLabel(value.provider)})` : triggerLabel;
7357
- return /* @__PURE__ */ jsx25(
7575
+ return /* @__PURE__ */ jsx27(
7358
7576
  "button",
7359
7577
  {
7360
7578
  ref,
@@ -7372,12 +7590,12 @@ var ModelSelectTrigger = forwardRef(function ModelSelectTrigger2({
7372
7590
  className
7373
7591
  ),
7374
7592
  ...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" })
7593
+ children: trigger === "slash" ? /* @__PURE__ */ jsxs25(Fragment8, { children: [
7594
+ /* @__PURE__ */ jsx27("span", { className: "text-muted-foreground", children: "/model: " }),
7595
+ /* @__PURE__ */ jsx27("span", { className: "text-foreground", children: triggerDisplay })
7596
+ ] }) : /* @__PURE__ */ jsxs25(Fragment8, { children: [
7597
+ /* @__PURE__ */ jsx27("span", { className: "min-w-0 truncate", children: triggerDisplay }),
7598
+ /* @__PURE__ */ jsx27(ChevronDownIcon5, { className: "h-3 w-3 shrink-0 text-muted-foreground/50", "aria-hidden": "true" })
7381
7599
  ] })
7382
7600
  }
7383
7601
  );
@@ -7397,18 +7615,18 @@ function ModelPickerMenu({
7397
7615
  const keyboardOptions = [null, ...groupedOptions];
7398
7616
  const selectedIndex = currentKey ? Math.max(0, keyboardOptions.findIndex((option) => option && encodeModelKey(option) === currentKey)) : 0;
7399
7617
  const [activeIndex, setActiveIndex] = useState16(selectedIndex);
7400
- const activeIndexRef = useRef11(selectedIndex);
7401
- const menuRef = useRef11(null);
7618
+ const activeIndexRef = useRef13(selectedIndex);
7619
+ const menuRef = useRef13(null);
7402
7620
  useDismissOnOutsidePointer(menuRef, onClose);
7403
7621
  const setKeyboardActiveIndex = (next) => {
7404
7622
  const resolved = typeof next === "function" ? next(activeIndexRef.current) : next;
7405
7623
  activeIndexRef.current = resolved;
7406
7624
  setActiveIndex(resolved);
7407
7625
  };
7408
- useEffect16(() => {
7626
+ useEffect17(() => {
7409
7627
  setKeyboardActiveIndex(selectedIndex);
7410
7628
  }, [selectedIndex]);
7411
- useEffect16(() => {
7629
+ useEffect17(() => {
7412
7630
  const handler = (event) => {
7413
7631
  if (event.key === "Escape") {
7414
7632
  event.preventDefault();
@@ -7432,8 +7650,8 @@ function ModelPickerMenu({
7432
7650
  return () => window.removeEventListener("keydown", handler, { capture: true });
7433
7651
  }, [activeIndex, disabled, keyboardOptions, onChange, onClose]);
7434
7652
  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(
7653
+ 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: [
7654
+ menuOptions.length > 8 && /* @__PURE__ */ jsx27("div", { className: "border-b border-border/60 px-2", children: /* @__PURE__ */ jsx27(
7437
7655
  CommandInput,
7438
7656
  {
7439
7657
  placeholder: "Search models\u2026",
@@ -7441,9 +7659,9 @@ function ModelPickerMenu({
7441
7659
  className: "h-8 w-full border-0 bg-transparent text-[13px] outline-none focus:ring-0"
7442
7660
  }
7443
7661
  ) }),
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(
7662
+ /* @__PURE__ */ jsxs25(CommandList, { className: "max-h-[300px] p-0.5", children: [
7663
+ /* @__PURE__ */ jsx27(CommandEmpty, { className: "py-4 text-center text-[13px] text-muted-foreground", children: "No models found" }),
7664
+ /* @__PURE__ */ jsx27(CommandGroup, { children: /* @__PURE__ */ jsxs25(
7447
7665
  CommandItem,
7448
7666
  {
7449
7667
  value: "Pi default automatic auto",
@@ -7454,7 +7672,7 @@ function ModelPickerMenu({
7454
7672
  },
7455
7673
  className: selectorItemClass(!value || activeIndex === 0),
7456
7674
  children: [
7457
- /* @__PURE__ */ jsx25(
7675
+ /* @__PURE__ */ jsx27(
7458
7676
  CheckIcon3,
7459
7677
  {
7460
7678
  className: cn(
@@ -7463,12 +7681,12 @@ function ModelPickerMenu({
7463
7681
  )
7464
7682
  }
7465
7683
  ),
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" })
7684
+ /* @__PURE__ */ jsx27("span", { className: "truncate", children: "Pi default" }),
7685
+ /* @__PURE__ */ jsx27("span", { className: "ml-auto shrink-0 text-[10px] text-muted-foreground/60", children: "auto" })
7468
7686
  ]
7469
7687
  }
7470
7688
  ) }),
7471
- [...groups.entries()].map(([provider, list]) => /* @__PURE__ */ jsx25(
7689
+ [...groups.entries()].map(([provider, list]) => /* @__PURE__ */ jsx27(
7472
7690
  CommandGroup,
7473
7691
  {
7474
7692
  heading: displayProviderLabel(provider),
@@ -7477,7 +7695,7 @@ function ModelPickerMenu({
7477
7695
  const key = encodeModelKey(m);
7478
7696
  const label = m.label || displayModelLabel(m.id);
7479
7697
  const itemIndex = optionIndexes.get(key) ?? 0;
7480
- return /* @__PURE__ */ jsxs23(
7698
+ return /* @__PURE__ */ jsxs25(
7481
7699
  CommandItem,
7482
7700
  {
7483
7701
  value: `${label} ${m.id} ${displayProviderLabel(m.provider)}`,
@@ -7488,7 +7706,7 @@ function ModelPickerMenu({
7488
7706
  },
7489
7707
  className: cn(selectorItemClass(key === currentKey || activeIndex === itemIndex)),
7490
7708
  children: [
7491
- /* @__PURE__ */ jsx25(
7709
+ /* @__PURE__ */ jsx27(
7492
7710
  CheckIcon3,
7493
7711
  {
7494
7712
  className: cn(
@@ -7497,8 +7715,8 @@ function ModelPickerMenu({
7497
7715
  )
7498
7716
  }
7499
7717
  ),
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 })
7718
+ /* @__PURE__ */ jsx27("span", { className: "min-w-0 flex-1 truncate whitespace-nowrap", children: label }),
7719
+ /* @__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
7720
  ]
7503
7721
  },
7504
7722
  key
@@ -7519,7 +7737,7 @@ var ThinkingSelectTrigger = forwardRef(function ThinkingSelectTrigger2({
7519
7737
  className,
7520
7738
  ...props
7521
7739
  }, ref) {
7522
- return /* @__PURE__ */ jsxs23(
7740
+ return /* @__PURE__ */ jsxs25(
7523
7741
  "button",
7524
7742
  {
7525
7743
  ref,
@@ -7540,13 +7758,13 @@ var ThinkingSelectTrigger = forwardRef(function ThinkingSelectTrigger2({
7540
7758
  onClick,
7541
7759
  ...props,
7542
7760
  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" })
7761
+ trigger === "button" ? /* @__PURE__ */ jsx27(ThinkingLevelGlyph, { level: value }) : null,
7762
+ trigger === "slash" ? /* @__PURE__ */ jsxs25(Fragment8, { children: [
7763
+ /* @__PURE__ */ jsx27("span", { className: "text-muted-foreground", children: "/thinking: " }),
7764
+ /* @__PURE__ */ jsx27("span", { className: "text-foreground", children: THINKING_LEVEL_STATUS_LABELS[value] })
7765
+ ] }) : /* @__PURE__ */ jsxs25(Fragment8, { children: [
7766
+ /* @__PURE__ */ jsx27("span", { children: THINKING_LEVEL_LABELS[value] }),
7767
+ /* @__PURE__ */ jsx27(ChevronDownIcon5, { className: "h-3 w-3 shrink-0 text-muted-foreground/50", "aria-hidden": "true" })
7550
7768
  ] })
7551
7769
  ]
7552
7770
  }
@@ -7561,18 +7779,18 @@ function ThinkingPickerMenu({
7561
7779
  }) {
7562
7780
  const selectedIndex = Math.max(0, THINKING_LEVELS.indexOf(value));
7563
7781
  const [activeIndex, setActiveIndex] = useState16(selectedIndex);
7564
- const activeIndexRef = useRef11(selectedIndex);
7565
- const menuRef = useRef11(null);
7782
+ const activeIndexRef = useRef13(selectedIndex);
7783
+ const menuRef = useRef13(null);
7566
7784
  useDismissOnOutsidePointer(menuRef, onClose);
7567
7785
  const setKeyboardActiveIndex = (next) => {
7568
7786
  const resolved = typeof next === "function" ? next(activeIndexRef.current) : next;
7569
7787
  activeIndexRef.current = resolved;
7570
7788
  setActiveIndex(resolved);
7571
7789
  };
7572
- useEffect16(() => {
7790
+ useEffect17(() => {
7573
7791
  setKeyboardActiveIndex(selectedIndex);
7574
7792
  }, [selectedIndex]);
7575
- useEffect16(() => {
7793
+ useEffect17(() => {
7576
7794
  const handler = (event) => {
7577
7795
  if (event.key === "Escape") {
7578
7796
  event.preventDefault();
@@ -7595,7 +7813,7 @@ function ThinkingPickerMenu({
7595
7813
  window.addEventListener("keydown", handler, { capture: true });
7596
7814
  return () => window.removeEventListener("keydown", handler, { capture: true });
7597
7815
  }, [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(
7816
+ 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
7817
  CommandItem,
7600
7818
  {
7601
7819
  value: `Thinking ${THINKING_LEVEL_LABELS[level]} ${THINKING_LEVEL_DETAILS[level]}`,
@@ -7606,7 +7824,7 @@ function ThinkingPickerMenu({
7606
7824
  },
7607
7825
  className: selectorItemClass(level === value || index === activeIndex),
7608
7826
  children: [
7609
- /* @__PURE__ */ jsx25(
7827
+ /* @__PURE__ */ jsx27(
7610
7828
  CheckIcon3,
7611
7829
  {
7612
7830
  className: cn(
@@ -7615,9 +7833,9 @@ function ThinkingPickerMenu({
7615
7833
  )
7616
7834
  }
7617
7835
  ),
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] })
7836
+ /* @__PURE__ */ jsx27(ThinkingLevelGlyph, { level }),
7837
+ /* @__PURE__ */ jsx27("span", { className: "font-medium", children: THINKING_LEVEL_LABELS[level] }),
7838
+ /* @__PURE__ */ jsx27("span", { className: "ml-auto text-[11px] text-muted-foreground/65", children: THINKING_LEVEL_DETAILS[level] })
7621
7839
  ]
7622
7840
  },
7623
7841
  level
@@ -7655,10 +7873,10 @@ import {
7655
7873
  Monitor,
7656
7874
  PlusIcon as PlusIcon2,
7657
7875
  SquareIcon,
7658
- XIcon as XIcon5
7876
+ XIcon as XIcon4
7659
7877
  } from "lucide-react";
7660
7878
  import { nanoid } from "nanoid";
7661
- import { useCallback as useCallback15, useEffect as useEffect17, useMemo as useMemo10, useRef as useRef12, useState as useState17 } from "react";
7879
+ import { useCallback as useCallback15, useEffect as useEffect18, useMemo as useMemo10, useRef as useRef14, useState as useState17 } from "react";
7662
7880
 
7663
7881
  // src/front/primitives/prompt-input-context.ts
7664
7882
  import { createContext as createContext6, useContext as useContext6 } from "react";
@@ -7693,12 +7911,12 @@ import {
7693
7911
  TooltipTrigger as TooltipTrigger3
7694
7912
  } from "@hachej/boring-ui-kit";
7695
7913
  import { Children } from "react";
7696
- import { jsx as jsx26, jsxs as jsxs24 } from "react/jsx-runtime";
7914
+ import { jsx as jsx28, jsxs as jsxs26 } from "react/jsx-runtime";
7697
7915
  var PromptInputFooter = ({
7698
7916
  align = "block-end",
7699
7917
  className,
7700
7918
  ...props
7701
- }) => /* @__PURE__ */ jsx26(
7919
+ }) => /* @__PURE__ */ jsx28(
7702
7920
  InputGroupAddon,
7703
7921
  {
7704
7922
  align,
@@ -7708,7 +7926,7 @@ var PromptInputFooter = ({
7708
7926
  );
7709
7927
 
7710
7928
  // src/front/primitives/prompt-input.tsx
7711
- import { Fragment as Fragment8, jsx as jsx27, jsxs as jsxs25 } from "react/jsx-runtime";
7929
+ import { Fragment as Fragment9, jsx as jsx29, jsxs as jsxs27 } from "react/jsx-runtime";
7712
7930
  var PromptInput = ({
7713
7931
  className,
7714
7932
  accept,
@@ -7725,12 +7943,12 @@ var PromptInput = ({
7725
7943
  }) => {
7726
7944
  const controller = useOptionalPromptInputController();
7727
7945
  const usingProvider = !!controller;
7728
- const providerTextValueRef = useRef12("");
7946
+ const providerTextValueRef = useRef14("");
7729
7947
  if (usingProvider) {
7730
7948
  providerTextValueRef.current = controller.textInput.value;
7731
7949
  }
7732
- const inputRef = useRef12(null);
7733
- const formRef = useRef12(null);
7950
+ const inputRef = useRef14(null);
7951
+ const formRef = useRef14(null);
7734
7952
  const [items, setItems] = useState17([]);
7735
7953
  const setFileUrlLocal = useCallback15((id, url, status) => {
7736
7954
  setItems((prev) => prev.map((f) => {
@@ -7741,8 +7959,8 @@ var PromptInput = ({
7741
7959
  }, []);
7742
7960
  const files = usingProvider ? controller.attachments.files : items;
7743
7961
  const [referencedSources, setReferencedSources] = useState17([]);
7744
- const filesRef = useRef12(files);
7745
- useEffect17(() => {
7962
+ const filesRef = useRef14(files);
7963
+ useEffect18(() => {
7746
7964
  filesRef.current = files;
7747
7965
  }, [files]);
7748
7966
  const openFileDialogLocal = useCallback15(() => {
@@ -7878,18 +8096,18 @@ var PromptInput = ({
7878
8096
  clearAttachments();
7879
8097
  clearReferencedSources();
7880
8098
  }, [clearAttachments, clearReferencedSources]);
7881
- useEffect17(() => {
8099
+ useEffect18(() => {
7882
8100
  if (!usingProvider) {
7883
8101
  return;
7884
8102
  }
7885
8103
  controller.__registerFileInput(inputRef, () => inputRef.current?.click());
7886
8104
  }, [usingProvider, controller]);
7887
- useEffect17(() => {
8105
+ useEffect18(() => {
7888
8106
  if (syncHiddenInput && inputRef.current && files.length === 0) {
7889
8107
  inputRef.current.value = "";
7890
8108
  }
7891
8109
  }, [files, syncHiddenInput]);
7892
- useEffect17(() => {
8110
+ useEffect18(() => {
7893
8111
  const form = formRef.current;
7894
8112
  if (!form) {
7895
8113
  return;
@@ -7917,7 +8135,7 @@ var PromptInput = ({
7917
8135
  form.removeEventListener("drop", onDrop);
7918
8136
  };
7919
8137
  }, [add, globalDrop]);
7920
- useEffect17(() => {
8138
+ useEffect18(() => {
7921
8139
  if (!globalDrop) {
7922
8140
  return;
7923
8141
  }
@@ -7941,7 +8159,7 @@ var PromptInput = ({
7941
8159
  document.removeEventListener("drop", onDrop);
7942
8160
  };
7943
8161
  }, [add, globalDrop]);
7944
- useEffect17(
8162
+ useEffect18(
7945
8163
  () => () => {
7946
8164
  if (!usingProvider) {
7947
8165
  for (const f of filesRef.current) {
@@ -8051,8 +8269,8 @@ var PromptInput = ({
8051
8269
  },
8052
8270
  [usingProvider, controller, files, onSubmit, clear]
8053
8271
  );
8054
- const inner = /* @__PURE__ */ jsxs25(Fragment8, { children: [
8055
- /* @__PURE__ */ jsx27(
8272
+ const inner = /* @__PURE__ */ jsxs27(Fragment9, { children: [
8273
+ /* @__PURE__ */ jsx29(
8056
8274
  Input,
8057
8275
  {
8058
8276
  accept,
@@ -8065,7 +8283,7 @@ var PromptInput = ({
8065
8283
  type: "file"
8066
8284
  }
8067
8285
  ),
8068
- /* @__PURE__ */ jsx27(
8286
+ /* @__PURE__ */ jsx29(
8069
8287
  "form",
8070
8288
  {
8071
8289
  "data-boring-agent-part": "composer",
@@ -8073,12 +8291,12 @@ var PromptInput = ({
8073
8291
  onSubmit: handleSubmit,
8074
8292
  ref: formRef,
8075
8293
  ...props,
8076
- children: /* @__PURE__ */ jsx27(InputGroup, { className: "overflow-hidden", children })
8294
+ children: /* @__PURE__ */ jsx29(InputGroup, { className: "overflow-hidden", children })
8077
8295
  }
8078
8296
  )
8079
8297
  ] });
8080
- const withReferencedSources = /* @__PURE__ */ jsx27(LocalReferencedSourcesContext.Provider, { value: refsCtx, children: inner });
8081
- return /* @__PURE__ */ jsx27(LocalAttachmentsContext.Provider, { value: attachmentsCtx, children: withReferencedSources });
8298
+ const withReferencedSources = /* @__PURE__ */ jsx29(LocalReferencedSourcesContext.Provider, { value: refsCtx, children: inner });
8299
+ return /* @__PURE__ */ jsx29(LocalAttachmentsContext.Provider, { value: attachmentsCtx, children: withReferencedSources });
8082
8300
  };
8083
8301
  var PromptInputTextarea = ({
8084
8302
  onChange,
@@ -8156,7 +8374,7 @@ var PromptInputTextarea = ({
8156
8374
  } : {
8157
8375
  onChange
8158
8376
  };
8159
- return /* @__PURE__ */ jsx27(
8377
+ return /* @__PURE__ */ jsx29(
8160
8378
  InputGroupTextarea,
8161
8379
  {
8162
8380
  "data-boring-agent-part": "composer-input",
@@ -8183,13 +8401,13 @@ var PromptInputSubmit = ({
8183
8401
  ...props
8184
8402
  }) => {
8185
8403
  const isGenerating = status === "submitted" || status === "streaming";
8186
- let Icon = /* @__PURE__ */ jsx27(CornerDownLeftIcon, { className: "size-4" });
8404
+ let Icon = /* @__PURE__ */ jsx29(CornerDownLeftIcon, { className: "size-4" });
8187
8405
  if (status === "submitted") {
8188
- Icon = /* @__PURE__ */ jsx27(Spinner, {});
8406
+ Icon = /* @__PURE__ */ jsx29(Spinner, {});
8189
8407
  } else if (status === "streaming") {
8190
- Icon = /* @__PURE__ */ jsx27(SquareIcon, { className: "size-4" });
8408
+ Icon = /* @__PURE__ */ jsx29(SquareIcon, { className: "size-4" });
8191
8409
  } else if (status === "error") {
8192
- Icon = /* @__PURE__ */ jsx27(XIcon5, { className: "size-4" });
8410
+ Icon = /* @__PURE__ */ jsx29(XIcon4, { className: "size-4" });
8193
8411
  }
8194
8412
  const handleClick = useCallback15(
8195
8413
  (e) => {
@@ -8202,7 +8420,7 @@ var PromptInputSubmit = ({
8202
8420
  },
8203
8421
  [isGenerating, onStop, onClick]
8204
8422
  );
8205
- return /* @__PURE__ */ jsx27(
8423
+ return /* @__PURE__ */ jsx29(
8206
8424
  InputGroupButton2,
8207
8425
  {
8208
8426
  "aria-label": isGenerating ? "Stop" : "Submit",
@@ -8219,54 +8437,142 @@ var PromptInputSubmit = ({
8219
8437
  };
8220
8438
 
8221
8439
  // 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";
8440
+ import { useEffect as useEffect19, useMemo as useMemo11, useRef as useRef15, useState as useState18 } from "react";
8441
+ import { Fragment as Fragment10, jsx as jsx30, jsxs as jsxs28 } from "react/jsx-runtime";
8442
+ var ALL_PLUGINS = "__all__";
8443
+ function commandGroup(cmd) {
8444
+ if (cmd.source === "skill") return "skills";
8445
+ if (cmd.sourcePlugin) return cmd.sourcePlugin;
8446
+ return "built-in";
8447
+ }
8224
8448
  function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
8225
- const filtered = useMemo11(
8226
- () => commands.filter((c) => c.name.toLowerCase().startsWith(query.toLowerCase())),
8227
- [commands, query]
8228
- );
8449
+ const [plugin, setPlugin] = useState18(ALL_PLUGINS);
8450
+ const [searchQuery, setSearchQuery] = useState18(query);
8229
8451
  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
- ]
8452
+ const containerRef = useRef15(null);
8453
+ const listRef = useRef15(null);
8454
+ const inputRef = useRef15(null);
8455
+ useEffect19(() => {
8456
+ setSearchQuery(query);
8457
+ }, [query]);
8458
+ const groups = useMemo11(() => {
8459
+ const set = new Set(commands.map(commandGroup));
8460
+ return Array.from(set).sort((a, b) => a.localeCompare(b));
8461
+ }, [commands]);
8462
+ const filtered = useMemo11(() => {
8463
+ const q = searchQuery.trim().toLowerCase();
8464
+ return commands.filter((c) => {
8465
+ if (plugin !== ALL_PLUGINS && commandGroup(c) !== plugin) return false;
8466
+ if (!q) return true;
8467
+ return c.name.toLowerCase().includes(q) || c.description.toLowerCase().includes(q);
8468
+ });
8469
+ }, [commands, searchQuery, plugin]);
8470
+ useEffect19(() => {
8471
+ setActiveIdx((i) => filtered.length === 0 ? 0 : Math.min(i, filtered.length - 1));
8472
+ }, [filtered.length]);
8473
+ useEffect19(() => {
8474
+ const handler = (e) => {
8475
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
8476
+ onDismiss();
8477
+ }
8478
+ };
8479
+ document.addEventListener("mousedown", handler);
8480
+ return () => document.removeEventListener("mousedown", handler);
8481
+ }, [onDismiss]);
8482
+ usePickerKeyboard({
8483
+ count: filtered.length,
8484
+ activeIdx,
8485
+ setActiveIdx,
8486
+ listRef,
8487
+ onSelect: (idx) => {
8488
+ if (filtered[idx]) onSelect(filtered[idx].name);
8258
8489
  },
8259
- cmd.name
8260
- )) }) });
8490
+ onDismiss
8491
+ });
8492
+ return /* @__PURE__ */ jsxs28("div", { ref: containerRef, className: "mb-1 w-full overflow-hidden rounded-lg border border-border/60 bg-popover shadow-lg", children: [
8493
+ /* @__PURE__ */ jsx30(
8494
+ "input",
8495
+ {
8496
+ ref: inputRef,
8497
+ "aria-label": "Search commands",
8498
+ type: "text",
8499
+ value: searchQuery,
8500
+ onChange: (e) => {
8501
+ setSearchQuery(e.target.value);
8502
+ setActiveIdx(0);
8503
+ },
8504
+ className: "w-full border-b border-border/50 bg-transparent px-3 py-1.5 text-[12px] outline-none",
8505
+ autoComplete: "off"
8506
+ }
8507
+ ),
8508
+ 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) => {
8509
+ const selected = plugin === g;
8510
+ return /* @__PURE__ */ jsx30(
8511
+ "button",
8512
+ {
8513
+ type: "button",
8514
+ role: "tab",
8515
+ "aria-selected": selected,
8516
+ onMouseDown: (e) => {
8517
+ e.preventDefault();
8518
+ setPlugin(g);
8519
+ setActiveIdx(0);
8520
+ },
8521
+ className: cn(
8522
+ "rounded-full px-2 py-px text-[10px] font-medium transition-colors",
8523
+ selected ? "bg-accent/15 text-accent-foreground" : "bg-muted/60 text-muted-foreground hover:bg-muted"
8524
+ ),
8525
+ children: g === ALL_PLUGINS ? "All" : g
8526
+ },
8527
+ g
8528
+ );
8529
+ }) }),
8530
+ 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(
8531
+ "li",
8532
+ {
8533
+ role: "option",
8534
+ "aria-selected": i === activeIdx,
8535
+ title: cmd.description || void 0,
8536
+ className: cn(
8537
+ "flex cursor-pointer flex-col gap-0.5 px-3 py-1.5 text-[12px]",
8538
+ // Single highlight: hovering moves the active row (onMouseEnter),
8539
+ // so the active style is the only highlight — no separate hover bg.
8540
+ i === activeIdx ? "bg-accent/10 text-foreground" : "text-muted-foreground"
8541
+ ),
8542
+ onMouseEnter: () => setActiveIdx(i),
8543
+ onMouseDown: (e) => {
8544
+ e.preventDefault();
8545
+ onSelect(cmd.name);
8546
+ },
8547
+ children: [
8548
+ /* @__PURE__ */ jsxs28("span", { className: "flex items-center gap-1.5", children: [
8549
+ /* @__PURE__ */ jsxs28("span", { className: "font-medium text-foreground/80", children: [
8550
+ "/",
8551
+ cmd.name
8552
+ ] }),
8553
+ cmd.source === "skill" ? /* @__PURE__ */ jsxs28(Fragment10, { children: [
8554
+ /* @__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" }),
8555
+ 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
8556
+ ] }) : cmd.sourcePlugin ? (
8557
+ // Originating plugin/package for non-skill commands.
8558
+ /* @__PURE__ */ jsx30("span", { className: "rounded-sm bg-muted px-1 py-px text-[9px] font-medium text-muted-foreground", children: cmd.sourcePlugin })
8559
+ ) : null
8560
+ ] }),
8561
+ /* @__PURE__ */ jsx30("span", { className: "truncate text-[11px] opacity-50", children: cmd.description })
8562
+ ]
8563
+ },
8564
+ cmd.name
8565
+ )) })
8566
+ ] });
8261
8567
  }
8262
8568
 
8263
8569
  // src/front/chat/components/ComposerAttachments.tsx
8264
- import { AlertCircleIcon as AlertCircleIcon3, Loader2 as Loader23, PaperclipIcon as PaperclipIcon2 } from "lucide-react";
8570
+ import { AlertCircleIcon as AlertCircleIcon2, Loader2 as Loader23, PaperclipIcon as PaperclipIcon2 } from "lucide-react";
8265
8571
  import { IconButton as IconButton3 } from "@hachej/boring-ui-kit";
8266
- import { jsx as jsx29, jsxs as jsxs27 } from "react/jsx-runtime";
8572
+ import { jsx as jsx31, jsxs as jsxs29 } from "react/jsx-runtime";
8267
8573
  function AttachmentButton({ disabled, className }) {
8268
8574
  const attachments = usePromptInputAttachments();
8269
- return /* @__PURE__ */ jsx29(
8575
+ return /* @__PURE__ */ jsx31(
8270
8576
  IconButton3,
8271
8577
  {
8272
8578
  type: "button",
@@ -8279,21 +8585,21 @@ function AttachmentButton({ disabled, className }) {
8279
8585
  className: cn(composerActionClass, "w-8", className),
8280
8586
  "aria-label": "Attach files",
8281
8587
  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 })
8588
+ children: /* @__PURE__ */ jsx31(PaperclipIcon2, { className: "h-3.5 w-3.5", strokeWidth: 1.75 })
8283
8589
  }
8284
8590
  );
8285
8591
  }
8286
8592
  function AttachmentsList() {
8287
8593
  const attachments = usePromptInputAttachments();
8288
8594
  if (attachments.files.length === 0) return null;
8289
- return /* @__PURE__ */ jsx29(
8595
+ return /* @__PURE__ */ jsx31(
8290
8596
  Attachments,
8291
8597
  {
8292
8598
  "data-boring-agent-part": "composer-attachment-row",
8293
8599
  "data-align": "block-start",
8294
8600
  variant: "inline",
8295
8601
  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(
8602
+ children: attachments.files.map((file) => /* @__PURE__ */ jsxs29(
8297
8603
  Attachment,
8298
8604
  {
8299
8605
  data: file,
@@ -8304,13 +8610,13 @@ function AttachmentsList() {
8304
8610
  file.status === "error" && "!border-destructive/50 !bg-destructive/10"
8305
8611
  ),
8306
8612
  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
8613
+ /* @__PURE__ */ jsxs29("div", { className: "relative shrink-0", children: [
8614
+ /* @__PURE__ */ jsx31(AttachmentPreview, { className: "!size-7 overflow-hidden !rounded-full bg-background/60" }),
8615
+ 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,
8616
+ 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
8617
  ] }),
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" })
8618
+ /* @__PURE__ */ jsx31(AttachmentInfo, { className: "min-w-0 !max-w-[180px] truncate text-[13px] font-medium" }),
8619
+ /* @__PURE__ */ jsx31(AttachmentRemove, { className: "!size-5 !rounded-full !opacity-100 text-muted-foreground/80 hover:text-foreground" })
8314
8620
  ]
8315
8621
  },
8316
8622
  file.id
@@ -8320,7 +8626,7 @@ function AttachmentsList() {
8320
8626
  }
8321
8627
 
8322
8628
  // src/front/chat/components/PiChatComposerSurface.tsx
8323
- import { jsx as jsx30, jsxs as jsxs28 } from "react/jsx-runtime";
8629
+ import { jsx as jsx32, jsxs as jsxs30 } from "react/jsx-runtime";
8324
8630
  var MAX_PROMPT_ATTACHMENTS = 2;
8325
8631
  var MAX_PROMPT_ATTACHMENT_BYTES = 4 * 1024 * 1024;
8326
8632
  var COMPOSER_INPUT_GROUP_MIN_HEIGHT = 56;
@@ -8362,6 +8668,8 @@ function PiChatComposerSurface({
8362
8668
  pluginUpdateState,
8363
8669
  onDismissPluginUpdate,
8364
8670
  onRunPluginUpdate,
8671
+ commandNotifyState,
8672
+ onDismissCommandNotify,
8365
8673
  attachmentNotice,
8366
8674
  onAttachmentNotice,
8367
8675
  mentionState,
@@ -8396,7 +8704,7 @@ function PiChatComposerSurface({
8396
8704
  onSubmitMessage,
8397
8705
  onStop
8398
8706
  }) {
8399
- const resizeTextarea = useCallback17((node) => {
8707
+ const resizeTextarea = useCallback16((node) => {
8400
8708
  if (!node) return;
8401
8709
  node.style.height = "auto";
8402
8710
  const style = window.getComputedStyle(node);
@@ -8420,8 +8728,8 @@ function PiChatComposerSurface({
8420
8728
  useLayoutEffect2(() => {
8421
8729
  resizeTextarea(textareaRef.current);
8422
8730
  }, [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(
8731
+ 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: [
8732
+ /* @__PURE__ */ jsx32(
8425
8733
  "div",
8426
8734
  {
8427
8735
  "data-boring-agent-part": "chat-working-slot",
@@ -8431,7 +8739,7 @@ function PiChatComposerSurface({
8431
8739
  isStreaming ? "mb-2 max-h-8 opacity-100" : "mb-0 max-h-0 opacity-0"
8432
8740
  ),
8433
8741
  "aria-hidden": !isStreaming,
8434
- children: /* @__PURE__ */ jsxs28(
8742
+ children: /* @__PURE__ */ jsxs30(
8435
8743
  "div",
8436
8744
  {
8437
8745
  "data-testid": isStreaming ? "chat-working" : void 0,
@@ -8439,7 +8747,7 @@ function PiChatComposerSurface({
8439
8747
  "aria-live": isStreaming ? "polite" : void 0,
8440
8748
  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
8749
  children: [
8442
- /* @__PURE__ */ jsx30(
8750
+ /* @__PURE__ */ jsx32(
8443
8751
  motion2.span,
8444
8752
  {
8445
8753
  "aria-hidden": "true",
@@ -8448,14 +8756,14 @@ function PiChatComposerSurface({
8448
8756
  transition: { duration: 1.2, repeat: Infinity, ease: "easeInOut" }
8449
8757
  }
8450
8758
  ),
8451
- /* @__PURE__ */ jsx30("span", { children: "Working\u2026" })
8759
+ /* @__PURE__ */ jsx32("span", { children: "Working\u2026" })
8452
8760
  ]
8453
8761
  }
8454
8762
  )
8455
8763
  }
8456
8764
  ),
8457
- composerStatusNotice ? /* @__PURE__ */ jsx30(ComposerRuntimeNotice, { notice: composerStatusNotice }) : null,
8458
- composerBlocked && !workspaceWarmupBlocked ? /* @__PURE__ */ jsx30(
8765
+ composerStatusNotice ? /* @__PURE__ */ jsx32(ComposerRuntimeNotice, { notice: composerStatusNotice }) : null,
8766
+ composerBlocked && !workspaceWarmupBlocked ? /* @__PURE__ */ jsx32(
8459
8767
  ComposerBlockerNotice,
8460
8768
  {
8461
8769
  blocker: primaryComposerBlocker,
@@ -8463,8 +8771,8 @@ function PiChatComposerSurface({
8463
8771
  onAction: onComposerBlockerAction
8464
8772
  }
8465
8773
  ) : null,
8466
- queuePreview.length > 0 ? /* @__PURE__ */ jsx30(QueuedComposerNotice, { followUps: queuePreview, onEdit: onEditQueued }) : null,
8467
- hotReloadEnabled ? /* @__PURE__ */ jsx30(
8774
+ queuePreview.length > 0 ? /* @__PURE__ */ jsx32(QueuedComposerNotice, { followUps: queuePreview, onEdit: onEditQueued }) : null,
8775
+ hotReloadEnabled ? /* @__PURE__ */ jsx32(
8468
8776
  PluginUpdateStatus,
8469
8777
  {
8470
8778
  state: pluginUpdateState,
@@ -8472,7 +8780,14 @@ function PiChatComposerSurface({
8472
8780
  onRetry: onRunPluginUpdate
8473
8781
  }
8474
8782
  ) : null,
8475
- attachmentNotice ? /* @__PURE__ */ jsx30(
8783
+ /* @__PURE__ */ jsx32(
8784
+ CommandRunStatus,
8785
+ {
8786
+ state: commandNotifyState,
8787
+ onDismiss: onDismissCommandNotify
8788
+ }
8789
+ ),
8790
+ attachmentNotice ? /* @__PURE__ */ jsx32(
8476
8791
  "div",
8477
8792
  {
8478
8793
  role: "status",
@@ -8481,8 +8796,8 @@ function PiChatComposerSurface({
8481
8796
  children: attachmentNotice
8482
8797
  }
8483
8798
  ) : null,
8484
- /* @__PURE__ */ jsxs28("div", { className: cn("mx-auto w-full", chrome ? "max-w-3xl" : "max-w-[680px]"), children: [
8485
- mentionState ? /* @__PURE__ */ jsx30(
8799
+ /* @__PURE__ */ jsxs30("div", { className: cn("mx-auto w-full", chrome ? "max-w-3xl" : "max-w-[680px]"), children: [
8800
+ mentionState ? /* @__PURE__ */ jsx32(
8486
8801
  MentionPicker,
8487
8802
  {
8488
8803
  mention: mentionState,
@@ -8494,7 +8809,7 @@ function PiChatComposerSurface({
8494
8809
  onDismiss: onDismissMention
8495
8810
  }
8496
8811
  ) : null,
8497
- slashQuery !== null ? /* @__PURE__ */ jsx30(
8812
+ slashQuery !== null ? /* @__PURE__ */ jsx32(
8498
8813
  SlashCommandPicker,
8499
8814
  {
8500
8815
  query: slashQuery,
@@ -8503,7 +8818,7 @@ function PiChatComposerSurface({
8503
8818
  onDismiss: onDismissSlash
8504
8819
  }
8505
8820
  ) : null,
8506
- mentionState === null && slashQuery === null && modelPickerOpen ? /* @__PURE__ */ jsx30(
8821
+ mentionState === null && slashQuery === null && modelPickerOpen ? /* @__PURE__ */ jsx32(
8507
8822
  ModelPickerMenu,
8508
8823
  {
8509
8824
  value: selectedModel,
@@ -8513,7 +8828,7 @@ function PiChatComposerSurface({
8513
8828
  onClose: () => onSetModelPickerOpen(false)
8514
8829
  }
8515
8830
  ) : null,
8516
- mentionState === null && slashQuery === null && thinkingPickerOpen ? /* @__PURE__ */ jsx30(
8831
+ mentionState === null && slashQuery === null && thinkingPickerOpen ? /* @__PURE__ */ jsx32(
8517
8832
  ThinkingPickerMenu,
8518
8833
  {
8519
8834
  value: selectedThinking,
@@ -8523,7 +8838,7 @@ function PiChatComposerSurface({
8523
8838
  }
8524
8839
  ) : null
8525
8840
  ] }),
8526
- /* @__PURE__ */ jsx30(
8841
+ /* @__PURE__ */ jsx32(
8527
8842
  "div",
8528
8843
  {
8529
8844
  "data-boring-agent-part": "composer-rail",
@@ -8542,7 +8857,7 @@ function PiChatComposerSurface({
8542
8857
  "[&[data-composer-multiline=true]_[data-boring-agent-part=composer-submit-addon]]:self-end",
8543
8858
  "[&[data-composer-multiline=true]_[data-boring-agent-part=composer-submit-addon]]:mb-2"
8544
8859
  ),
8545
- children: /* @__PURE__ */ jsxs28(
8860
+ children: /* @__PURE__ */ jsxs30(
8546
8861
  PromptInput,
8547
8862
  {
8548
8863
  "data-boring-state": status,
@@ -8557,15 +8872,15 @@ function PiChatComposerSurface({
8557
8872
  else onAttachmentNotice(err.message || "Attachment rejected.");
8558
8873
  },
8559
8874
  children: [
8560
- /* @__PURE__ */ jsx30(AttachmentsList, {}),
8561
- /* @__PURE__ */ jsxs28(
8875
+ /* @__PURE__ */ jsx32(AttachmentsList, {}),
8876
+ /* @__PURE__ */ jsxs30(
8562
8877
  "div",
8563
8878
  {
8564
8879
  "data-boring-agent-part": "composer-input-row",
8565
8880
  className: "flex min-h-[var(--composer-input-group-height)] w-full items-center",
8566
8881
  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(
8882
+ /* @__PURE__ */ jsx32("div", { className: "flex h-14 shrink-0 items-center pl-2", children: /* @__PURE__ */ jsx32(AttachmentButton, { disabled: disabled || isStreaming }) }),
8883
+ /* @__PURE__ */ jsx32(
8569
8884
  PromptInputTextarea,
8570
8885
  {
8571
8886
  value: draft,
@@ -8589,13 +8904,13 @@ function PiChatComposerSurface({
8589
8904
  )
8590
8905
  }
8591
8906
  ),
8592
- /* @__PURE__ */ jsx30(
8907
+ /* @__PURE__ */ jsx32(
8593
8908
  PromptInputFooter,
8594
8909
  {
8595
8910
  align: "inline-end",
8596
8911
  "data-boring-agent-part": "composer-submit-addon",
8597
8912
  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(
8913
+ children: /* @__PURE__ */ jsx32("div", { className: "ml-auto flex items-center gap-1.5", children: /* @__PURE__ */ jsx32(
8599
8914
  PromptInputSubmit,
8600
8915
  {
8601
8916
  "data-boring-agent-part": "composer-submit",
@@ -8625,7 +8940,7 @@ function PiChatComposerSurface({
8625
8940
  )
8626
8941
  }
8627
8942
  ),
8628
- /* @__PURE__ */ jsxs28(
8943
+ /* @__PURE__ */ jsxs30(
8629
8944
  "div",
8630
8945
  {
8631
8946
  "data-boring-agent-part": "composer-settings-row",
@@ -8634,7 +8949,7 @@ function PiChatComposerSurface({
8634
8949
  chrome ? "max-w-3xl" : "max-w-[680px]"
8635
8950
  ),
8636
8951
  children: [
8637
- /* @__PURE__ */ jsx30(
8952
+ /* @__PURE__ */ jsx32(
8638
8953
  ModelSelectTrigger,
8639
8954
  {
8640
8955
  value: selectedModel,
@@ -8651,7 +8966,7 @@ function PiChatComposerSurface({
8651
8966
  }
8652
8967
  }
8653
8968
  ),
8654
- thinkingControl ? /* @__PURE__ */ jsx30(
8969
+ thinkingControl ? /* @__PURE__ */ jsx32(
8655
8970
  ThinkingSelectTrigger,
8656
8971
  {
8657
8972
  value: selectedThinking,
@@ -8674,7 +8989,7 @@ function PiChatComposerSurface({
8674
8989
  }
8675
8990
 
8676
8991
  // src/front/chat/piChatPanelHooks.ts
8677
- import { useCallback as useCallback18, useEffect as useEffect19, useState as useState19, useSyncExternalStore } from "react";
8992
+ import { useCallback as useCallback17, useEffect as useEffect20, useState as useState19, useSyncExternalStore } from "react";
8678
8993
  function useExternalRemotePiSession({
8679
8994
  sessionId,
8680
8995
  workspaceId,
@@ -8686,7 +9001,7 @@ function useExternalRemotePiSession({
8686
9001
  remoteSessionOptions
8687
9002
  }) {
8688
9003
  const [session, setSession] = useState19();
8689
- useEffect19(() => {
9004
+ useEffect20(() => {
8690
9005
  if (!sessionId) {
8691
9006
  setSession(void 0);
8692
9007
  return;
@@ -8707,10 +9022,10 @@ function useExternalRemotePiSession({
8707
9022
  }
8708
9023
  function useRemotePiSessionState(session) {
8709
9024
  return useSyncExternalStore(
8710
- useCallback18((listener) => session?.subscribe(listener) ?? (() => {
9025
+ useCallback17((listener) => session?.subscribe(listener) ?? (() => {
8711
9026
  }), [session]),
8712
- useCallback18(() => session?.getState(), [session]),
8713
- useCallback18(() => session?.getState(), [session])
9027
+ useCallback17(() => session?.getState(), [session]),
9028
+ useCallback17(() => session?.getState(), [session])
8714
9029
  );
8715
9030
  }
8716
9031
 
@@ -8826,7 +9141,7 @@ function errorMessage2(error, fallback) {
8826
9141
  }
8827
9142
 
8828
9143
  // src/front/chat/PiChatPanel.tsx
8829
- import { jsx as jsx31, jsxs as jsxs29 } from "react/jsx-runtime";
9144
+ import { jsx as jsx33, jsxs as jsxs31 } from "react/jsx-runtime";
8830
9145
  var DebugDrawer2 = lazy(() => import("../DebugDrawer-2PUDZ2RL.js").then((m) => ({ default: m.DebugDrawer })));
8831
9146
  var EMPTY_COMMANDS = [];
8832
9147
  var EMPTY_BLOCKERS = [];
@@ -8880,9 +9195,9 @@ function PiChatPanel({
8880
9195
  }) {
8881
9196
  const externalSessionId = sessionId?.trim() || void 0;
8882
9197
  const showSessionSidebar = showSessions ?? externalSessionId === void 0;
8883
- const onDataRef = useRef14(onData);
9198
+ const onDataRef = useRef16(onData);
8884
9199
  onDataRef.current = onData;
8885
- const sessionListRefreshRef = useRef14(void 0);
9200
+ const sessionListRefreshRef = useRef16(void 0);
8886
9201
  const requestHeadersKey = useMemo12(() => headersContentKey(requestHeaders), [requestHeaders]);
8887
9202
  const normalizedRequestHeaders = useMemo12(() => normalizedHeadersFromContentKey(requestHeadersKey), [requestHeadersKey]);
8888
9203
  const remoteSessionOptionsWithEvents = useMemo12(() => ({
@@ -8905,7 +9220,7 @@ function PiChatPanel({
8905
9220
  remoteSessionOptions: remoteSessionOptionsWithEvents,
8906
9221
  enabled: externalSessionId === void 0
8907
9222
  });
8908
- useEffect20(() => {
9223
+ useEffect21(() => {
8909
9224
  if (externalSessionId) {
8910
9225
  sessionListRefreshRef.current = void 0;
8911
9226
  return;
@@ -8959,19 +9274,19 @@ function PiChatPanel({
8959
9274
  () => readPiComposerSettings({ storageScope, storage }).showThoughts
8960
9275
  );
8961
9276
  const [composerSettingsOwner, setComposerSettingsOwner] = useState20(() => ({ storageScope, storage }));
8962
- useEffect20(() => {
9277
+ useEffect21(() => {
8963
9278
  const settings = readPiComposerSettings({ storageScope, storage });
8964
9279
  setStoredThinkingLevel(thinkingControl ? settings.thinkingLevel : DEFAULT_THINKING);
8965
9280
  setShowThoughts(settings.showThoughts);
8966
9281
  setComposerSettingsOwner({ storageScope, storage });
8967
9282
  }, [storage, storageScope, thinkingControl]);
8968
9283
  const composerSettingsLoaded = composerSettingsOwner.storageScope === storageScope && composerSettingsOwner.storage === storage;
8969
- useEffect20(() => {
9284
+ useEffect21(() => {
8970
9285
  if (!thinkingControl) return;
8971
9286
  if (!composerSettingsLoaded) return;
8972
9287
  writePiComposerThinking(storedThinkingLevel, { storageScope, storage });
8973
9288
  }, [composerSettingsLoaded, storage, storageScope, storedThinkingLevel, thinkingControl]);
8974
- useEffect20(() => {
9289
+ useEffect21(() => {
8975
9290
  if (!composerSettingsLoaded) return;
8976
9291
  writePiComposerShowThoughts(showThoughts, { storageScope, storage });
8977
9292
  }, [composerSettingsLoaded, showThoughts, storage, storageScope]);
@@ -8980,14 +9295,14 @@ function PiChatPanel({
8980
9295
  const [modelPickerOpen, setModelPickerOpen] = useState20(false);
8981
9296
  const [thinkingPickerOpen, setThinkingPickerOpen] = useState20(false);
8982
9297
  const [draft, setDraft] = useState20(() => initialDraft ?? "");
8983
- const draftRef = useRef14(draft);
9298
+ const draftRef = useRef16(draft);
8984
9299
  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) => {
9300
+ const initialDraftGuard = useRef16(new InitialDraftAutoSubmitGuard());
9301
+ const pendingAutoSubmitSettleRef = useRef16(void 0);
9302
+ const acceptedAutoSubmitSettleRef = useRef16(void 0);
9303
+ const resetInProgressRef = useRef16(false);
9304
+ const autoCreateInFlightRef = useRef16(false);
9305
+ const settlePendingAutoSubmit = useCallback18((sessionId2) => {
8991
9306
  const pendingSessionId = pendingAutoSubmitSettleRef.current;
8992
9307
  if (!pendingSessionId || sessionId2 && pendingSessionId !== sessionId2) return false;
8993
9308
  pendingAutoSubmitSettleRef.current = void 0;
@@ -8995,23 +9310,25 @@ function PiChatPanel({
8995
9310
  onAutoSubmitInitialDraftSettled?.();
8996
9311
  return true;
8997
9312
  }, [onAutoSubmitInitialDraftSettled]);
8998
- const prevStatusRef = useRef14("idle");
8999
- const statusRef = useRef14("idle");
9000
- const scrollToBottomRef = useRef14(() => {
9313
+ const prevStatusRef = useRef16("idle");
9314
+ const statusRef = useRef16("idle");
9315
+ const scrollToBottomRef = useRef16(() => {
9001
9316
  });
9002
- const textareaRef = useRef14(null);
9317
+ const textareaRef = useRef16(null);
9003
9318
  const [localNotices, setLocalNotices] = useState20([]);
9004
9319
  const [dismissedNoticeIds, setDismissedNoticeIds] = useState20(() => /* @__PURE__ */ new Set());
9005
9320
  const [pluginUpdateState, setPluginUpdateState] = useState20(null);
9321
+ const [commandNotifyState, setCommandNotifyState] = useState20(null);
9322
+ const commandRunIdRef = useRef16(0);
9006
9323
  const [serverSkillsRefreshKey, setServerSkillsRefreshKey] = useState20(0);
9007
9324
  const [localSubmittedSessionId, setLocalSubmittedSessionId] = useState20();
9008
- const localSubmittedSessionRef = useRef14(void 0);
9325
+ const localSubmittedSessionRef = useRef16(void 0);
9009
9326
  const { attachmentNotice, setAttachmentNotice } = useAttachmentNotice();
9010
- const markLocalSubmitted = useCallback19((sessionId2) => {
9327
+ const markLocalSubmitted = useCallback18((sessionId2) => {
9011
9328
  localSubmittedSessionRef.current = sessionId2;
9012
9329
  setLocalSubmittedSessionId(sessionId2);
9013
9330
  }, []);
9014
- const clearLocalSubmitted = useCallback19((sessionId2) => {
9331
+ const clearLocalSubmitted = useCallback18((sessionId2) => {
9015
9332
  if (sessionId2 && localSubmittedSessionRef.current !== sessionId2) return;
9016
9333
  localSubmittedSessionRef.current = void 0;
9017
9334
  setLocalSubmittedSessionId(void 0);
@@ -9023,16 +9340,17 @@ function PiChatPanel({
9023
9340
  for (const command of commands) next.register(command);
9024
9341
  return next;
9025
9342
  }, [apiBaseUrl, commands, extraCommands, hotReloadEnabled, normalizedRequestHeaders, serverResourcesEnabled, serverSkillsRefreshKey, storageScope]);
9026
- const skillsStamp = useServerSkills({
9027
- apiBaseUrl,
9028
- fetch: fetch2,
9343
+ const commandsStamp = useServerCommands({
9029
9344
  registry,
9030
- refreshKey: serverSkillsRefreshKey,
9031
9345
  requestHeaders: normalizedRequestHeaders,
9346
+ sessionId: activeSessionId ?? "default",
9347
+ apiBaseUrl,
9348
+ fetch: fetch2,
9032
9349
  storageScope,
9350
+ refreshKey: serverSkillsRefreshKey,
9033
9351
  enabled: serverResourcesEnabled
9034
9352
  });
9035
- const allCommands = useMemo12(() => registry.list(), [registry, skillsStamp]);
9353
+ const allCommands = useMemo12(() => registry.list(), [registry, commandsStamp]);
9036
9354
  const activeChatSessionId = selectedChatState?.sessionId;
9037
9355
  const warmupNotice = composerNoticeForWarmup(workspaceWarmupStatus);
9038
9356
  const runtimeDependenciesNotice = composerNoticeForRuntimeDependencies(workspaceWarmupStatus);
@@ -9063,23 +9381,23 @@ function PiChatPanel({
9063
9381
  }] : [];
9064
9382
  return [...fromState, ...sessionNotice, ...largeStateNotice, ...localNotices].filter((notice) => !dismissedNoticeIds.has(notice.id));
9065
9383
  }, [debug, debugState?.largeStateWarning, dismissedNoticeIds, localNotices, selectedChatState, sessionsError]);
9066
- const addLocalNotice = useCallback19((notice) => {
9384
+ const addLocalNotice = useCallback18((notice) => {
9067
9385
  setLocalNotices((previous) => {
9068
9386
  const next = previous.filter((candidate) => candidate.id !== notice.id);
9069
9387
  return [...next, notice];
9070
9388
  });
9071
9389
  }, []);
9072
- const clearLocalNotice = useCallback19((id) => {
9390
+ const clearLocalNotice = useCallback18((id) => {
9073
9391
  setDismissedNoticeIds((previous) => new Set(previous).add(id));
9074
9392
  setLocalNotices((previous) => previous.filter((notice) => notice.id !== id));
9075
9393
  }, []);
9076
- const createSession = useCallback19(() => {
9394
+ const createSession = useCallback18(() => {
9077
9395
  if (externalSessionId) return;
9078
9396
  void sessions.create().catch((error) => {
9079
9397
  addLocalNotice({ id: "session-create-error", level: "error", text: errorMessage2(error, "Could not create a Pi session."), dismissible: true });
9080
9398
  });
9081
9399
  }, [addLocalNotice, externalSessionId, sessions.create]);
9082
- useEffect20(() => {
9400
+ useEffect21(() => {
9083
9401
  if (externalSessionId || sessionsLoading || sessionsError || activeSessionId || sessionList.length > 0) return;
9084
9402
  if (resetInProgressRef.current) return;
9085
9403
  if (autoCreateInFlightRef.current) return;
@@ -9089,18 +9407,18 @@ function PiChatPanel({
9089
9407
  addLocalNotice({ id: "session-auto-create-error", level: "error", text: errorMessage2(error, "Could not create a Pi session."), dismissible: true });
9090
9408
  });
9091
9409
  }, [activeSessionId, addLocalNotice, externalSessionId, sessionList.length, sessions.create, sessionsError, sessionsLoading]);
9092
- useEffect20(() => {
9410
+ useEffect21(() => {
9093
9411
  if (externalSessionId || sessionsError || activeSessionId || sessionList.length > 0) {
9094
9412
  autoCreateInFlightRef.current = false;
9095
9413
  }
9096
9414
  }, [activeSessionId, externalSessionId, sessionList.length, sessionsError]);
9097
- const deleteSession = useCallback19((sessionId2) => {
9415
+ const deleteSession = useCallback18((sessionId2) => {
9098
9416
  if (externalSessionId) return;
9099
9417
  void sessions.delete(sessionId2).catch((error) => {
9100
9418
  addLocalNotice({ id: `session-delete-error:${sessionId2}`, level: "error", text: errorMessage2(error, "Could not delete the Pi session."), dismissible: true });
9101
9419
  });
9102
9420
  }, [addLocalNotice, externalSessionId, sessions.delete]);
9103
- const resetSession = useCallback19(() => {
9421
+ const resetSession = useCallback18(() => {
9104
9422
  const currentSessionId = activeSessionId;
9105
9423
  if (externalSessionId) {
9106
9424
  void onSessionReset?.();
@@ -9118,11 +9436,11 @@ function PiChatPanel({
9118
9436
  resetInProgressRef.current = false;
9119
9437
  });
9120
9438
  }, [activeSessionId, addLocalNotice, externalSessionId, onSessionReset, sessions.create, sessions.delete]);
9121
- const reloadAgentPlugins = useCallback19(async () => {
9439
+ const reloadAgentPlugins = useCallback18(async () => {
9122
9440
  if (!onReloadAgentPlugins) throw new Error("Agent plugin reload is not configured.");
9123
9441
  return await onReloadAgentPlugins();
9124
9442
  }, [onReloadAgentPlugins]);
9125
- const runPluginUpdate = useCallback19(async () => {
9443
+ const runPluginUpdate = useCallback18(async () => {
9126
9444
  setPluginUpdateState({ kind: "running" });
9127
9445
  try {
9128
9446
  const message = await reloadAgentPlugins();
@@ -9140,8 +9458,26 @@ function PiChatPanel({
9140
9458
  return `Plugin update failed: ${message}`;
9141
9459
  }
9142
9460
  }, [reloadAgentPlugins]);
9143
- const dismissPluginUpdate = useCallback19(() => setPluginUpdateState(null), []);
9144
- useEffect20(() => {
9461
+ const dismissPluginUpdate = useCallback18(() => setPluginUpdateState(null), []);
9462
+ const dismissCommandNotify = useCallback18(() => setCommandNotifyState(null), []);
9463
+ useEffect21(() => {
9464
+ if (typeof window === "undefined") return;
9465
+ const onCommandNotify = (event) => {
9466
+ const payload = event.detail;
9467
+ if (!payload || typeof payload.message !== "string") return;
9468
+ const tone = payload.tone;
9469
+ const command = payload.command ?? "";
9470
+ if (tone === "error") {
9471
+ setCommandNotifyState({ kind: "error", command, message: payload.message });
9472
+ } else {
9473
+ const runId = ++commandRunIdRef.current;
9474
+ setCommandNotifyState({ kind: "success", command, detail: payload.message, runId });
9475
+ }
9476
+ };
9477
+ window.addEventListener(WORKSPACE_COMMAND_NOTIFY_EVENT, onCommandNotify);
9478
+ return () => window.removeEventListener(WORKSPACE_COMMAND_NOTIFY_EVENT, onCommandNotify);
9479
+ }, []);
9480
+ useEffect21(() => {
9145
9481
  if (!hotReloadEnabled || typeof window === "undefined") return;
9146
9482
  const onBrowserPluginReload = (event) => {
9147
9483
  const parsed = parseBrowserPluginReloadDetail(event.detail);
@@ -9179,7 +9515,7 @@ function PiChatPanel({
9179
9515
  textareaRef,
9180
9516
  disabled: mentionState !== null || slashQuery !== null || modelPickerOpen || thinkingPickerOpen
9181
9517
  });
9182
- const setComposerDraft = useCallback19((next, focus = true) => {
9518
+ const setComposerDraft = useCallback18((next, focus = true) => {
9183
9519
  draftRef.current = next;
9184
9520
  setDraft(next);
9185
9521
  if (textareaRef.current) {
@@ -9187,11 +9523,11 @@ function PiChatPanel({
9187
9523
  if (focus) textareaRef.current.focus();
9188
9524
  }
9189
9525
  }, []);
9190
- const warnComposer = useCallback19((message) => {
9526
+ const warnComposer = useCallback18((message) => {
9191
9527
  onComposerWarning?.(message);
9192
9528
  addLocalNotice({ id: `composer-warning:${Date.now()}`, level: "warning", text: message, dismissible: true });
9193
9529
  }, [addLocalNotice, onComposerWarning]);
9194
- const openModelPicker = useCallback19(() => {
9530
+ const openModelPicker = useCallback18(() => {
9195
9531
  if (isPiBusyStatus(statusRef.current)) {
9196
9532
  warnComposer("Model picker is unavailable while the agent is running.");
9197
9533
  return false;
@@ -9204,7 +9540,7 @@ function PiChatPanel({
9204
9540
  setModelPickerOpen(true);
9205
9541
  return true;
9206
9542
  }, [model, warnComposer]);
9207
- const openThinkingPicker = useCallback19(() => {
9543
+ const openThinkingPicker = useCallback18(() => {
9208
9544
  if (isPiBusyStatus(statusRef.current)) {
9209
9545
  warnComposer("Thinking picker is unavailable while the agent is running.");
9210
9546
  return false;
@@ -9217,21 +9553,21 @@ function PiChatPanel({
9217
9553
  setThinkingPickerOpen(true);
9218
9554
  return true;
9219
9555
  }, [thinkingControl, thinkingLevel, warnComposer]);
9220
- const selectComposerModel = useCallback19((query) => {
9556
+ const selectComposerModel = useCallback18((query) => {
9221
9557
  if (model !== void 0) return "Model selection is controlled by the host.";
9222
9558
  const match = resolveModelSlashSelection(query, modelOptions);
9223
9559
  if (!match) return `No model matched "${query}".`;
9224
9560
  modelDiscovery.setModel(match);
9225
9561
  return `Model set to ${match.label ?? match.id}.`;
9226
9562
  }, [model, modelDiscovery, modelOptions]);
9227
- const selectComposerThinking = useCallback19((query) => {
9563
+ const selectComposerThinking = useCallback18((query) => {
9228
9564
  if (!thinkingControl || thinkingLevel !== void 0) return "Thinking level is controlled by the host.";
9229
9565
  const match = resolveThinkingSlashSelection(query);
9230
9566
  if (!match) return `No thinking level matched "${query}".`;
9231
9567
  setStoredThinkingLevel(match);
9232
9568
  return `Thinking set to ${thinkingLabel(match)}.`;
9233
9569
  }, [thinkingControl, thinkingLevel]);
9234
- const selectSlashCommand = useCallback19((name) => {
9570
+ const selectSlashCommand = useCallback18((name) => {
9235
9571
  if (name === "model") {
9236
9572
  dismissSlash();
9237
9573
  if (openModelPicker()) setComposerDraft("");
@@ -9309,7 +9645,7 @@ function PiChatPanel({
9309
9645
  }
9310
9646
  });
9311
9647
  }, [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" }) => {
9648
+ const sendComposerMessage = useCallback18(async ({ text, files, source = "composer" }) => {
9313
9649
  if (!policy) {
9314
9650
  addLocalNotice({ id: "composer-no-session", level: "warning", text: "Create or select a Pi session before sending.", dismissible: true });
9315
9651
  return false;
@@ -9337,7 +9673,7 @@ function PiChatPanel({
9337
9673
  throw error;
9338
9674
  }
9339
9675
  }, [activeChatSessionId, addLocalNotice, clearLocalSubmitted, markLocalSubmitted, policy, selectedPiSession, setComposerDraft]);
9340
- const editQueued = useCallback19(() => {
9676
+ const editQueued = useCallback18(() => {
9341
9677
  if (!policy) return;
9342
9678
  void policy.editQueued().then((result) => {
9343
9679
  if (result.type === "clear-failed") {
@@ -9345,23 +9681,24 @@ function PiChatPanel({
9345
9681
  }
9346
9682
  });
9347
9683
  }, [addLocalNotice, policy]);
9348
- const stop = useCallback19(() => {
9684
+ const stop = useCallback18(() => {
9349
9685
  onComposerStop?.();
9350
9686
  void policy?.stop().catch((error) => {
9351
9687
  addLocalNotice({ id: "stop-error", level: "error", text: errorMessage2(error, "Could not stop the Pi session."), dismissible: true });
9352
9688
  });
9353
9689
  }, [addLocalNotice, onComposerStop, policy]);
9354
- const interrupt = useCallback19(() => {
9690
+ const interrupt = useCallback18(() => {
9355
9691
  void policy?.interrupt().catch((error) => {
9356
9692
  addLocalNotice({ id: "interrupt-error", level: "error", text: errorMessage2(error, "Could not interrupt the Pi session."), dismissible: true });
9357
9693
  });
9358
9694
  }, [addLocalNotice, policy]);
9359
- useEffect20(() => {
9695
+ useEffect21(() => {
9360
9696
  setPluginUpdateState(null);
9697
+ setCommandNotifyState(null);
9361
9698
  setLocalNotices([]);
9362
9699
  setDismissedNoticeIds(/* @__PURE__ */ new Set());
9363
9700
  }, [activeSessionId]);
9364
- useEffect20(() => {
9701
+ useEffect21(() => {
9365
9702
  const currentSessionId = activeSessionId ?? "__none__";
9366
9703
  if (initialDraftGuard.current.shouldRestore(currentSessionId, initialDraft) && initialDraft !== void 0) {
9367
9704
  setComposerDraft(initialDraft, initialDraft.length > 0);
@@ -9370,7 +9707,7 @@ function PiChatPanel({
9370
9707
  }, [activeSessionId, initialDraft, onDraftRestored, setComposerDraft]);
9371
9708
  const remoteStatus = selectedChatState?.status ?? (sessionsLoading || chatStatePending || selectedSessionPending ? "hydrating" : "idle");
9372
9709
  const status = localSubmittedSessionId === activeSessionId && remoteStatus === "idle" ? "submitted" : remoteStatus;
9373
- useEffect20(() => {
9710
+ useEffect21(() => {
9374
9711
  if (!localSubmittedSessionId) return;
9375
9712
  if (localSubmittedSessionId !== activeSessionId) {
9376
9713
  clearLocalSubmitted();
@@ -9378,7 +9715,7 @@ function PiChatPanel({
9378
9715
  }
9379
9716
  if (remoteStatus !== "idle") clearLocalSubmitted(localSubmittedSessionId);
9380
9717
  }, [activeSessionId, clearLocalSubmitted, localSubmittedSessionId, remoteStatus]);
9381
- useEffect20(() => {
9718
+ useEffect21(() => {
9382
9719
  const previous = prevStatusRef.current;
9383
9720
  statusRef.current = status;
9384
9721
  prevStatusRef.current = status;
@@ -9388,7 +9725,7 @@ function PiChatPanel({
9388
9725
  if (!isPiBusyStatus(previous) && previous !== "submitted") return;
9389
9726
  settlePendingAutoSubmit();
9390
9727
  }, [settlePendingAutoSubmit, status]);
9391
- useEffect20(() => {
9728
+ useEffect21(() => {
9392
9729
  if (!autoSubmitInitialDraft || !policy || !activeSessionId || composerBlocked) return;
9393
9730
  if (!initialDraftGuard.current.claimAutoSubmit(activeSessionId, initialDraft)) return;
9394
9731
  pendingAutoSubmitSettleRef.current = activeSessionId;
@@ -9423,7 +9760,7 @@ function PiChatPanel({
9423
9760
  addLocalNotice({ id: "auto-submit-error", level: "error", text: errorMessage2(error, "Could not auto-submit the initial draft."), dismissible: true });
9424
9761
  });
9425
9762
  }, [activeSessionId, addLocalNotice, autoSubmitInitialDraft, clearLocalSubmitted, composerBlocked, initialDraft, markLocalSubmitted, onAutoSubmitInitialDraftAccepted, policy, selectedPiSession, setComposerDraft, settlePendingAutoSubmit]);
9426
- useEffect20(() => {
9763
+ useEffect21(() => {
9427
9764
  if (workspaceWarmupStatus?.status === "ready") {
9428
9765
  clearLocalNotice("workspace-warmup");
9429
9766
  }
@@ -9434,18 +9771,18 @@ function PiChatPanel({
9434
9771
  const submitDisabled = !policy || sessionsLoading || composerBlocked && !isStreaming;
9435
9772
  const mergedToolRenderers = useMemo12(() => mergeShadcnToolRenderers(toolRenderers), [toolRenderers]);
9436
9773
  const debugMessages = useMemo12(() => messages.map(toDebugUiMessage), [messages]);
9437
- const onTextareaChange = useCallback19((event) => {
9774
+ const onTextareaChange = useCallback18((event) => {
9438
9775
  setModelPickerOpen(false);
9439
9776
  setThinkingPickerOpen(false);
9440
9777
  setDraft(event.currentTarget.value);
9441
9778
  handleComposerChange(event);
9442
9779
  }, [handleComposerChange]);
9443
- useEffect20(() => {
9780
+ useEffect21(() => {
9444
9781
  if (!isStreaming) return;
9445
9782
  setModelPickerOpen(false);
9446
9783
  setThinkingPickerOpen(false);
9447
9784
  }, [isStreaming]);
9448
- useEffect20(() => {
9785
+ useEffect21(() => {
9449
9786
  if (typeof window === "undefined" || !activeChatSessionId) return;
9450
9787
  window.dispatchEvent(new CustomEvent("boring:chat-session-status", {
9451
9788
  detail: { sessionId: activeChatSessionId, working: isStreaming }
@@ -9458,7 +9795,7 @@ function PiChatPanel({
9458
9795
  }));
9459
9796
  };
9460
9797
  }, [activeChatSessionId, isStreaming]);
9461
- const onTextareaKeyDown = useCallback19((event) => {
9798
+ const onTextareaKeyDown = useCallback18((event) => {
9462
9799
  if (event.key === "Escape" && isStreaming) {
9463
9800
  if (event.defaultPrevented || mentionState !== null || slashQuery !== null) {
9464
9801
  handleComposerKeyDown(event);
@@ -9470,7 +9807,7 @@ function PiChatPanel({
9470
9807
  }
9471
9808
  handleComposerKeyDown(event);
9472
9809
  }, [handleComposerKeyDown, interrupt, isStreaming, mentionState, slashQuery]);
9473
- return /* @__PURE__ */ jsx31(ArtifactOpenProvider, { onOpenArtifact, children: /* @__PURE__ */ jsxs29(
9810
+ return /* @__PURE__ */ jsx33(ArtifactOpenProvider, { onOpenArtifact, children: /* @__PURE__ */ jsxs31(
9474
9811
  "div",
9475
9812
  {
9476
9813
  "data-boring-agent": "",
@@ -9487,7 +9824,7 @@ function PiChatPanel({
9487
9824
  role: "region",
9488
9825
  "aria-label": "Agent assistant",
9489
9826
  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(
9827
+ 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
9828
  SessionList,
9492
9829
  {
9493
9830
  sessions: sessionList,
@@ -9501,7 +9838,7 @@ function PiChatPanel({
9501
9838
  loadingMore: sessions.loadingMore
9502
9839
  }
9503
9840
  ) }) : null,
9504
- /* @__PURE__ */ jsx31("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col", children: /* @__PURE__ */ jsxs29(
9841
+ /* @__PURE__ */ jsx33("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col", children: /* @__PURE__ */ jsxs31(
9505
9842
  "div",
9506
9843
  {
9507
9844
  className: cn(
@@ -9510,7 +9847,7 @@ function PiChatPanel({
9510
9847
  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
9848
  ),
9512
9849
  children: [
9513
- /* @__PURE__ */ jsx31(
9850
+ /* @__PURE__ */ jsx33(
9514
9851
  PiConversationSurface,
9515
9852
  {
9516
9853
  chrome,
@@ -9532,7 +9869,7 @@ function PiChatPanel({
9532
9869
  windowResetKey: activeSessionId
9533
9870
  }
9534
9871
  ),
9535
- /* @__PURE__ */ jsx31(
9872
+ /* @__PURE__ */ jsx33(
9536
9873
  PiChatComposerSurface,
9537
9874
  {
9538
9875
  chrome,
@@ -9554,6 +9891,8 @@ function PiChatPanel({
9554
9891
  pluginUpdateState,
9555
9892
  onDismissPluginUpdate: dismissPluginUpdate,
9556
9893
  onRunPluginUpdate: runPluginUpdate,
9894
+ commandNotifyState,
9895
+ onDismissCommandNotify: dismissCommandNotify,
9557
9896
  attachmentNotice,
9558
9897
  onAttachmentNotice: setAttachmentNotice,
9559
9898
  mentionState,
@@ -9592,7 +9931,7 @@ function PiChatPanel({
9592
9931
  ]
9593
9932
  }
9594
9933
  ) }),
9595
- debug ? /* @__PURE__ */ jsx31(Suspense, { fallback: null, children: /* @__PURE__ */ jsx31("div", { "aria-label": "Pi chat debug metadata", className: "contents", role: "region", children: /* @__PURE__ */ jsx31(
9934
+ debug ? /* @__PURE__ */ jsx33(Suspense, { fallback: null, children: /* @__PURE__ */ jsx33("div", { "aria-label": "Pi chat debug metadata", className: "contents", role: "region", children: /* @__PURE__ */ jsx33(
9596
9935
  DebugDrawer2,
9597
9936
  {
9598
9937
  apiBaseUrl,