@hachej/boring-agent 0.1.104 → 0.1.107

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.
Files changed (34) hide show
  1. package/dist/{chunk-K25PK5EC.js → chunk-3JFPS2O6.js} +1 -0
  2. package/dist/{chunk-JNEKOVMJ.js → chunk-6OANSUEI.js} +1 -1
  3. package/dist/{chunk-4IQ4HHBG.js → chunk-7XAOVFSN.js} +7 -0
  4. package/dist/{chunk-I7KFNGB4.js → chunk-GS4ADE34.js} +877 -349
  5. package/dist/{chunk-BTYOMR7W.js → chunk-KDXIXVF6.js} +5179 -3668
  6. package/dist/{chunk-6DRHERVK.js → chunk-MVKXLMH3.js} +44 -2
  7. package/dist/chunk-YTUZFKB3.js +78 -0
  8. package/dist/core/index.d.ts +5 -5
  9. package/dist/core/index.js +2 -2
  10. package/dist/{createHarness-POZ4AXCZ.js → createHarness-AHWSC36D.js} +3 -3
  11. package/dist/{errors-DH7C01Pm.d.ts → errors-CEU4iujR.d.ts} +59 -4
  12. package/dist/{file-search-qcD5zItr.d.ts → file-search-BGUHnW9o.d.ts} +3 -3
  13. package/dist/front/index.d.ts +43 -7
  14. package/dist/front/index.js +1505 -729
  15. package/dist/front/styles.css +109 -15
  16. package/dist/{harness-Dcd08yKr.d.ts → harness-Ct0e4RWc.d.ts} +6 -3
  17. package/dist/{lease-DffrPrQn.d.ts → lease-nASWv2tU.d.ts} +1 -1
  18. package/dist/{piChatEvent-iNSXiFQq.d.ts → piChatEvent-BUWIzw5l.d.ts} +67 -36
  19. package/dist/{piChatSessionService-DQSQ_kWZ.d.ts → piChatSessionService-nvroRAVk.d.ts} +13 -11
  20. package/dist/server/agent-host/testing/gatewayConformance.d.ts +6 -5
  21. package/dist/server/agent-host/testing/gatewayConformance.js +57 -8
  22. package/dist/server/index.d.ts +207 -243
  23. package/dist/server/index.js +15 -9
  24. package/dist/server/worker/index.js +7 -7
  25. package/dist/shared/index.d.ts +34 -28
  26. package/dist/shared/index.js +8 -8
  27. package/dist/{types-B1xDlBSo.d.ts → types-B7-PN5jV.d.ts} +75 -23
  28. package/docs/AGENT_GATEWAY_V0.md +40 -2
  29. package/docs/ERROR_CODES.md +2 -1
  30. package/docs/README.md +1 -0
  31. package/docs/SESSION_IDENTITY.md +91 -0
  32. package/docs/tools.md +4 -0
  33. package/package.json +11 -10
  34. package/dist/chunk-WSQ5QNIY.js +0 -18
@@ -20,6 +20,7 @@ import {
20
20
  } from "../chunk-2BGLZZ7T.js";
21
21
  import {
22
22
  AGENT_GATEWAY_ERROR_CODES,
23
+ AgentGatewayErrorCode,
23
24
  CommandReceiptSchema,
24
25
  FollowUpReceiptSchema,
25
26
  PiChatSnapshotSchema,
@@ -31,11 +32,13 @@ import {
31
32
  WORKSPACE_COMMAND_NOTIFY_EVENT,
32
33
  extractToolUiMetadata,
33
34
  sanitizeToolUiMetadata
34
- } from "../chunk-6DRHERVK.js";
35
- import "../chunk-YGRQ4Q5P.js";
35
+ } from "../chunk-MVKXLMH3.js";
36
+ import {
37
+ safeRandomUUID
38
+ } from "../chunk-YGRQ4Q5P.js";
36
39
  import {
37
40
  ErrorCode
38
- } from "../chunk-K25PK5EC.js";
41
+ } from "../chunk-3JFPS2O6.js";
39
42
 
40
43
  // src/front/upload/uploadFile.ts
41
44
  function readAsDataUrl(file) {
@@ -47,12 +50,16 @@ function readAsDataUrl(file) {
47
50
  });
48
51
  }
49
52
  async function uploadFile(file, opts = {}) {
50
- const { apiBaseUrl = "", workspaceRequestId, directory, sourcePath, responseUrl = "markdown", fetch: fetchImpl = globalThis.fetch } = opts;
53
+ const { apiBaseUrl = "", workspaceRequestId, requestHeaders, directory, sourcePath, responseUrl = "markdown", fetch: fetchImpl = globalThis.fetch } = opts;
51
54
  const dataUrl = await readAsDataUrl(file);
52
55
  const comma = dataUrl.indexOf(",");
53
56
  const contentBase64 = comma >= 0 ? dataUrl.slice(comma + 1) : "";
54
- const headers = { "Content-Type": "application/json" };
55
- if (workspaceRequestId) headers["x-boring-workspace-id"] = workspaceRequestId;
57
+ const headers = new Headers();
58
+ for (const [name, value] of Object.entries(requestHeaders ?? {})) {
59
+ if (value !== void 0) headers.set(name, value);
60
+ }
61
+ headers.set("content-type", "application/json");
62
+ if (workspaceRequestId) headers.set("x-boring-workspace-id", workspaceRequestId);
56
63
  const base = apiBaseUrl.replace(/\/$/, "");
57
64
  const res = await fetchImpl(`${base}/api/v1/files/upload`, {
58
65
  method: "POST",
@@ -85,7 +92,7 @@ function rawWorkspaceFileUrl(path, opts) {
85
92
  }
86
93
 
87
94
  // src/front/chat/PiChatPanel.tsx
88
- import { lazy, Suspense, useCallback as useCallback18, useEffect as useEffect21, useMemo as useMemo16, useRef as useRef17, useState as useState20 } from "react";
95
+ import { lazy, Suspense, useCallback as useCallback19, useEffect as useEffect23, useMemo as useMemo17, useRef as useRef18, useState as useState22 } from "react";
89
96
 
90
97
  // src/front/chatPanelSettings.ts
91
98
  var DEFAULT_THINKING = "medium";
@@ -280,6 +287,14 @@ function createCommandRegistry(initial) {
280
287
  }
281
288
 
282
289
  // src/front/slashCommands/builtins.ts
290
+ var MAX_RELOAD_MODEL_MESSAGE = 2e3;
291
+ function reloadResultForModel(message) {
292
+ const body = message.trim() || "No result reported.";
293
+ const bounded = body.length > MAX_RELOAD_MODEL_MESSAGE ? `${body.slice(0, MAX_RELOAD_MODEL_MESSAGE)}
294
+ \u2026 (truncated)` : body;
295
+ return `/reload result:
296
+ ${bounded}`;
297
+ }
283
298
  var builtinCommands = [
284
299
  {
285
300
  name: "reset",
@@ -301,9 +316,9 @@ var builtinCommands = [
301
316
  name: "reload",
302
317
  description: "Reload agent plugins",
303
318
  clickBehavior: "execute",
304
- handler(_, ctx) {
305
- if (ctx.pluginUpdate) return ctx.pluginUpdate.run();
306
- return ctx.reloadAgentPlugins();
319
+ async handler(_, ctx) {
320
+ const message = ctx.pluginUpdate ? await ctx.pluginUpdate.run() : await ctx.reloadAgentPlugins();
321
+ return { message, modelMessage: reloadResultForModel(message) };
307
322
  }
308
323
  },
309
324
  {
@@ -542,7 +557,7 @@ import { useCallback, useState as useState3 } from "react";
542
557
  import { Button as Button4 } from "@hachej/boring-ui-kit";
543
558
  import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
544
559
  function CodeBlock({
545
- code: code3,
560
+ code,
546
561
  language,
547
562
  filename,
548
563
  className,
@@ -551,13 +566,13 @@ function CodeBlock({
551
566
  }) {
552
567
  const [copied, setCopied] = useState3(false);
553
568
  const handleCopy = useCallback(() => {
554
- void copyTextToClipboard(code3).then((ok) => {
569
+ void copyTextToClipboard(code).then((ok) => {
555
570
  if (!ok) return;
556
571
  setCopied(true);
557
572
  setTimeout(() => setCopied(false), 2e3);
558
573
  });
559
- }, [code3]);
560
- const lines = code3.split("\n");
574
+ }, [code]);
575
+ const lines = code.split("\n");
561
576
  const lineNumberWidth = `${String(lines.length).length + 1}ch`;
562
577
  return /* @__PURE__ */ jsxs5(
563
578
  "div",
@@ -574,7 +589,7 @@ function CodeBlock({
574
589
  /* @__PURE__ */ jsx5("pre", { className: "m-0 overflow-auto p-3", children: /* @__PURE__ */ jsx5("code", { "data-language": language, children: showLineNumbers ? lines.map((line, i) => /* @__PURE__ */ jsxs5("span", { className: "block", children: [
575
590
  /* @__PURE__ */ jsx5("span", { className: "mr-4 inline-block select-none text-right opacity-40", style: { width: lineNumberWidth }, children: i + 1 }),
576
591
  line
577
- ] }, i)) : code3 }) }),
592
+ ] }, i)) : code }) }),
578
593
  copyable && !filename && !language && /* @__PURE__ */ jsx5(
579
594
  Button4,
580
595
  {
@@ -921,7 +936,6 @@ import {
921
936
  useRef as useRef2,
922
937
  useState as useState4
923
938
  } from "react";
924
- import { createHighlighter } from "shiki";
925
939
  import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
926
940
  var isItalic = (fontStyle) => fontStyle && fontStyle & 1;
927
941
  var isBold = (fontStyle) => fontStyle && fontStyle & 2;
@@ -973,27 +987,27 @@ var CodeBlockContext = createContext({
973
987
  var highlighterCache = /* @__PURE__ */ new Map();
974
988
  var tokensCache = /* @__PURE__ */ new Map();
975
989
  var subscribers = /* @__PURE__ */ new Map();
976
- var getTokensCacheKey = (code3, language) => {
977
- const start = code3.slice(0, 100);
978
- const end = code3.length > 100 ? code3.slice(-100) : "";
979
- return `${language}:${code3.length}:${start}:${end}`;
990
+ var getTokensCacheKey = (code, language) => {
991
+ const start = code.slice(0, 100);
992
+ const end = code.length > 100 ? code.slice(-100) : "";
993
+ return `${language}:${code.length}:${start}:${end}`;
980
994
  };
981
995
  var getHighlighter = (language) => {
982
996
  const cached = highlighterCache.get(language);
983
997
  if (cached) {
984
998
  return cached;
985
999
  }
986
- const highlighterPromise = createHighlighter({
1000
+ const highlighterPromise = import("shiki").then(({ createHighlighter }) => createHighlighter({
987
1001
  langs: [language],
988
1002
  themes: ["github-light", "github-dark"]
989
- });
1003
+ }));
990
1004
  highlighterCache.set(language, highlighterPromise);
991
1005
  return highlighterPromise;
992
1006
  };
993
- var createRawTokens = (code3) => ({
1007
+ var createRawTokens = (code) => ({
994
1008
  bg: "transparent",
995
1009
  fg: "inherit",
996
- tokens: code3.split("\n").map(
1010
+ tokens: code.split("\n").map(
997
1011
  (line) => line === "" ? [] : [
998
1012
  {
999
1013
  color: "inherit",
@@ -1002,8 +1016,8 @@ var createRawTokens = (code3) => ({
1002
1016
  ]
1003
1017
  )
1004
1018
  });
1005
- var highlightCode = (code3, language, callback) => {
1006
- const tokensCacheKey = getTokensCacheKey(code3, language);
1019
+ var highlightCode = (code, language, callback) => {
1020
+ const tokensCacheKey = getTokensCacheKey(code, language);
1007
1021
  const cached = tokensCache.get(tokensCacheKey);
1008
1022
  if (cached) {
1009
1023
  return cached;
@@ -1017,7 +1031,7 @@ var highlightCode = (code3, language, callback) => {
1017
1031
  getHighlighter(language).then((highlighter) => {
1018
1032
  const availableLangs = highlighter.getLoadedLanguages();
1019
1033
  const langToUse = availableLangs.includes(language) ? language : "text";
1020
- const result = highlighter.codeToTokens(code3, {
1034
+ const result = highlighter.codeToTokens(code, {
1021
1035
  lang: langToUse,
1022
1036
  themes: {
1023
1037
  dark: "github-dark",
@@ -1147,25 +1161,25 @@ var CodeBlockFilename = ({
1147
1161
  }
1148
1162
  );
1149
1163
  var CodeBlockContent = ({
1150
- code: code3,
1164
+ code,
1151
1165
  language,
1152
1166
  showLineNumbers = false
1153
1167
  }) => {
1154
- const rawTokens = useMemo(() => createRawTokens(code3), [code3]);
1168
+ const rawTokens = useMemo(() => createRawTokens(code), [code]);
1155
1169
  const lang = language;
1156
1170
  const syncTokens = useMemo(
1157
- () => highlightCode(code3, lang) ?? rawTokens,
1158
- [code3, lang, rawTokens]
1171
+ () => highlightCode(code, lang) ?? rawTokens,
1172
+ [code, lang, rawTokens]
1159
1173
  );
1160
1174
  const [asyncTokens, setAsyncTokens] = useState4(null);
1161
- const asyncKeyRef = useRef2({ code: code3, language });
1162
- if (asyncKeyRef.current.code !== code3 || asyncKeyRef.current.language !== language) {
1163
- asyncKeyRef.current = { code: code3, language };
1175
+ const asyncKeyRef = useRef2({ code, language });
1176
+ if (asyncKeyRef.current.code !== code || asyncKeyRef.current.language !== language) {
1177
+ asyncKeyRef.current = { code, language };
1164
1178
  setAsyncTokens(null);
1165
1179
  }
1166
1180
  useEffect2(() => {
1167
1181
  let cancelled = false;
1168
- highlightCode(code3, lang, (result) => {
1182
+ highlightCode(code, lang, (result) => {
1169
1183
  if (!cancelled) {
1170
1184
  setAsyncTokens(result);
1171
1185
  }
@@ -1173,25 +1187,25 @@ var CodeBlockContent = ({
1173
1187
  return () => {
1174
1188
  cancelled = true;
1175
1189
  };
1176
- }, [code3, language]);
1190
+ }, [code, language]);
1177
1191
  const tokenized = asyncTokens ?? syncTokens;
1178
1192
  return /* @__PURE__ */ jsx7("div", { className: "relative overflow-auto", children: /* @__PURE__ */ jsx7(CodeBlockBody, { showLineNumbers, tokenized }) });
1179
1193
  };
1180
1194
  var CodeBlock2 = ({
1181
- code: code3,
1195
+ code,
1182
1196
  language,
1183
1197
  showLineNumbers = false,
1184
1198
  className,
1185
1199
  children,
1186
1200
  ...props
1187
1201
  }) => {
1188
- const contextValue = useMemo(() => ({ code: code3 }), [code3]);
1202
+ const contextValue = useMemo(() => ({ code }), [code]);
1189
1203
  return /* @__PURE__ */ jsx7(CodeBlockContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs7(CodeBlockContainer, { className, language, ...props, children: [
1190
1204
  children,
1191
1205
  /* @__PURE__ */ jsx7(
1192
1206
  CodeBlockContent,
1193
1207
  {
1194
- code: code3,
1208
+ code,
1195
1209
  language,
1196
1210
  showLineNumbers
1197
1211
  }
@@ -1208,7 +1222,7 @@ var CodeBlockCopyButton = ({
1208
1222
  }) => {
1209
1223
  const [isCopied, setIsCopied] = useState4(false);
1210
1224
  const timeoutRef = useRef2(0);
1211
- const { code: code3 } = useContext(CodeBlockContext);
1225
+ const { code } = useContext(CodeBlockContext);
1212
1226
  const copyToClipboard = useCallback2(async () => {
1213
1227
  if (typeof window === "undefined") {
1214
1228
  onError?.(new Error("Clipboard API not available"));
@@ -1217,7 +1231,7 @@ var CodeBlockCopyButton = ({
1217
1231
  if (isCopied) {
1218
1232
  return;
1219
1233
  }
1220
- const copied = await copyTextToClipboard(code3);
1234
+ const copied = await copyTextToClipboard(code);
1221
1235
  if (!copied) {
1222
1236
  onError?.(new Error("Clipboard API not available"));
1223
1237
  return;
@@ -1228,7 +1242,7 @@ var CodeBlockCopyButton = ({
1228
1242
  () => setIsCopied(false),
1229
1243
  timeout
1230
1244
  );
1231
- }, [code3, onCopy, onError, timeout, isCopied]);
1245
+ }, [code, onCopy, onError, timeout, isCopied]);
1232
1246
  useEffect2(
1233
1247
  () => () => {
1234
1248
  window.clearTimeout(timeoutRef.current);
@@ -2220,6 +2234,7 @@ var PiFollowUpQueueController = class {
2220
2234
  session;
2221
2235
  options;
2222
2236
  nextClientSeqFloor;
2237
+ queueMutationPending = false;
2223
2238
  async submit(input) {
2224
2239
  const text = input.text.trim();
2225
2240
  const attachments = input.attachments ?? [];
@@ -2257,30 +2272,65 @@ var PiFollowUpQueueController = class {
2257
2272
  });
2258
2273
  return { type: "prompt", clientNonce, cursor: receipt.cursor };
2259
2274
  }
2260
- async editQueued() {
2261
- const followUps = this.session.getState().queue.followUps;
2262
- if (followUps.length === 0) {
2263
- const message = "No queued messages to edit.";
2264
- this.options.onWarning?.(message);
2265
- return { type: "empty", message };
2266
- }
2267
- const draft = buildEditedQueuedDraft(followUps, this.options.getDraft?.() ?? "");
2268
- this.options.onDraftChange?.(draft);
2269
- try {
2270
- await this.session.clearQueue();
2271
- return { type: "cleared", draft };
2272
- } catch (error) {
2273
- const message = "Queued messages were copied into the composer, but the server queue was not cleared. They may still send unless you retry Edit queued or Stop.";
2274
- this.options.onWarning?.(message);
2275
- return { type: "clear-failed", draft, error, message };
2276
- }
2275
+ editQueued() {
2276
+ return this.runQueueMutation(
2277
+ { type: "busy", message: "Another queue action is already in progress." },
2278
+ async () => {
2279
+ const followUps = this.session.getState().queue.followUps;
2280
+ if (followUps.length === 0) {
2281
+ const message = "No queued messages to edit.";
2282
+ this.options.onWarning?.(message);
2283
+ return { type: "empty", message };
2284
+ }
2285
+ const currentDraft = this.options.getDraft?.() ?? "";
2286
+ const draft = buildEditedQueuedDraft(followUps, currentDraft);
2287
+ if (draft !== currentDraft) this.options.onDraftChange?.(draft);
2288
+ try {
2289
+ await this.session.clearQueue();
2290
+ return { type: "cleared", draft };
2291
+ } catch (error) {
2292
+ const message = "Queued messages were copied into the composer, but the server queue was not cleared. They may still send unless you retry Edit or Remove.";
2293
+ this.options.onWarning?.(message);
2294
+ return { type: "clear-failed", draft, error, message };
2295
+ }
2296
+ }
2297
+ );
2298
+ }
2299
+ removeAllQueued() {
2300
+ return this.runQueueMutation(
2301
+ { ok: false, message: "Another queue action is already in progress." },
2302
+ async () => {
2303
+ try {
2304
+ await this.session.clearQueue();
2305
+ return { ok: true };
2306
+ } catch {
2307
+ const message = "Could not remove the queued messages. They may still send unless you retry or use Edit.";
2308
+ this.options.onWarning?.(message);
2309
+ return { ok: false, message };
2310
+ }
2311
+ }
2312
+ );
2277
2313
  }
2278
- interrupt() {
2279
- return this.session.interrupt();
2314
+ interrupt(payload = {}) {
2315
+ return this.session.interrupt(payload);
2316
+ }
2317
+ resumeQueued() {
2318
+ return this.session.interrupt({ queueAction: "resume" });
2280
2319
  }
2281
2320
  stop() {
2282
2321
  return this.session.stop();
2283
2322
  }
2323
+ async runQueueMutation(busyResult, action) {
2324
+ if (this.queueMutationPending) return busyResult;
2325
+ this.queueMutationPending = true;
2326
+ this.options.onQueueMutationPending?.(true);
2327
+ try {
2328
+ return await action();
2329
+ } finally {
2330
+ this.queueMutationPending = false;
2331
+ this.options.onQueueMutationPending?.(false);
2332
+ }
2333
+ }
2284
2334
  nextFollowUpClientSeq() {
2285
2335
  const next = nextFollowUpClientSeq(this.session.getState(), this.nextClientSeqFloor);
2286
2336
  this.nextClientSeqFloor = next + 1;
@@ -2321,6 +2371,9 @@ function buildEditedQueuedDraft(followUps, existingDraft = "") {
2321
2371
  const draft = existingDraft.trim();
2322
2372
  if (!queuedText) return draft;
2323
2373
  if (!draft) return queuedText;
2374
+ if (draft === queuedText || draft.startsWith(`${queuedText}
2375
+
2376
+ `)) return draft;
2324
2377
  return `${queuedText}
2325
2378
 
2326
2379
  ${draft}`;
@@ -2339,76 +2392,148 @@ function defaultClientNonce() {
2339
2392
  // src/front/chat/session/composerPolicy.ts
2340
2393
  var COMPOSER_SETTINGS_PREFIX = "boring-agent:v2";
2341
2394
  var DEFAULT_STORAGE_SCOPE = "default";
2395
+ var PiComposerSubmissionCoordinator = class {
2396
+ tails = /* @__PURE__ */ new Map();
2397
+ run(identity, task) {
2398
+ const previous = this.tails.get(identity) ?? Promise.resolve();
2399
+ let releaseGate;
2400
+ const gate = new Promise((resolve) => {
2401
+ releaseGate = resolve;
2402
+ });
2403
+ let released = false;
2404
+ const release = () => {
2405
+ if (released) return;
2406
+ released = true;
2407
+ releaseGate();
2408
+ };
2409
+ const pending = previous.then(async () => {
2410
+ try {
2411
+ return await task(release);
2412
+ } finally {
2413
+ release();
2414
+ }
2415
+ });
2416
+ const tail = previous.then(async () => await gate);
2417
+ this.tails.set(identity, tail);
2418
+ void tail.finally(() => {
2419
+ if (this.tails.get(identity) === tail) this.tails.delete(identity);
2420
+ });
2421
+ return pending;
2422
+ }
2423
+ };
2342
2424
  var PiComposerPolicyController = class {
2343
2425
  constructor(options) {
2344
2426
  this.options = options;
2345
2427
  this.queueController = createPiFollowUpQueueController(options.session, options);
2428
+ this.submissionCoordinator = options.submissionCoordinator ?? new PiComposerSubmissionCoordinator();
2346
2429
  }
2347
2430
  options;
2348
2431
  queueController;
2349
- async submit(input) {
2350
- const text = input.text.trim();
2432
+ submissionCoordinator;
2433
+ submit(input) {
2434
+ return this.submissionCoordinator.run(
2435
+ this.options.submissionIdentity ?? "default",
2436
+ async (release) => await this.submitInOrder(input, release)
2437
+ );
2438
+ }
2439
+ async submitInOrder(input, release) {
2351
2440
  const files = input.files ?? [];
2352
2441
  const source = input.source ?? "composer";
2353
2442
  if (this.options.composerBlocked) {
2354
2443
  return this.block("composer-blocked", this.options.blockerMessage ?? "Composer is not ready yet.");
2355
2444
  }
2356
- if (!await this.runBeforeSubmit(input.text, files, source)) {
2357
- return this.block("pre-submit-cancelled", "Submit was cancelled before sending.");
2445
+ let beforeSubmit;
2446
+ try {
2447
+ beforeSubmit = await this.runBeforeSubmit(input.text, files, source);
2448
+ } catch (error) {
2449
+ if (this.options.isActiveSession && !this.options.isActiveSession()) return this.stale();
2450
+ throw error;
2358
2451
  }
2359
2452
  if (this.options.isActiveSession && !this.options.isActiveSession()) {
2360
- return this.block("inactive-session", "The active session changed before the message was sent.");
2453
+ return this.stale();
2454
+ }
2455
+ if (beforeSubmit === false) {
2456
+ return this.block("pre-submit-cancelled", "Submit was cancelled before sending.");
2457
+ }
2458
+ if (typeof beforeSubmit === "object" && "handled" in beforeSubmit && beforeSubmit.handled) {
2459
+ return { type: "handled", ...beforeSubmit.message ? { message: beforeSubmit.message } : {}, preserveDraft: false };
2361
2460
  }
2362
- const parsed = parseSlashCommand(text);
2461
+ const replacement = typeof beforeSubmit === "object" && "replacement" in beforeSubmit ? beforeSubmit.replacement : void 0;
2462
+ const text = (replacement?.text ?? input.text).trim();
2463
+ const displayText = replacement?.displayText ?? text;
2464
+ const parsed = replacement ? null : parseSlashCommand(text);
2363
2465
  if (parsed) {
2364
2466
  const command = this.options.registry.get(parsed.name);
2365
2467
  if (command?.kind === "skill") {
2366
- return this.submitExpandedText(skillCommandText(parsed.name, parsed.args), source, false);
2468
+ return this.submitExpandedText(skillCommandText(parsed.name, parsed.args), files, source, release);
2367
2469
  }
2368
- if (command) return this.runLocalCommand(parsed.name, parsed.args);
2470
+ if (command) return this.runLocalCommand(parsed.name, parsed.args, source, release);
2369
2471
  }
2370
2472
  if (isPiChatBusy(this.options.session.getState().status) && files.length > 0) {
2371
2473
  return this.fromQueueResult(await this.queueController.submit({ text, attachments: this.toAttachmentPayloads(files) }));
2372
2474
  }
2475
+ const transformed = await this.runPromptTransform(text, files, source);
2476
+ if (this.options.isActiveSession && !this.options.isActiveSession()) return this.stale();
2477
+ const finalText = transformed?.replacement.text ?? text;
2478
+ const finalDisplayText = transformed?.replacement.displayText ?? displayText;
2373
2479
  const { serverMessage, attachments } = await createEnrichedSubmitPayload({
2374
- text,
2480
+ text: finalText,
2375
2481
  files,
2376
2482
  mentionedFiles: this.getMentionedFiles()
2377
2483
  });
2378
2484
  if (this.options.isActiveSession && !this.options.isActiveSession()) {
2379
- return this.block("inactive-session", "The active session changed before the message was sent.");
2485
+ return this.stale();
2380
2486
  }
2381
- const result = await this.queueController.submit({
2487
+ const admission = this.queueController.submit({
2382
2488
  text: serverMessage,
2383
- displayText: text,
2489
+ displayText: finalDisplayText,
2384
2490
  attachments,
2385
2491
  model: this.options.model ?? void 0,
2386
2492
  ...this.options.thinkingControl ? { thinkingLevel: this.options.thinkingLevel ?? DEFAULT_THINKING } : {}
2387
2493
  });
2494
+ release();
2495
+ const result = await admission;
2388
2496
  if (result.type !== "blocked") this.options.onMentionedFilesConsumed?.();
2389
2497
  return this.fromQueueResult(result);
2390
2498
  }
2391
2499
  async editQueued() {
2392
2500
  return this.queueController.editQueued();
2393
2501
  }
2394
- interrupt() {
2395
- return this.queueController.interrupt();
2502
+ removeAllQueued() {
2503
+ return this.queueController.removeAllQueued();
2504
+ }
2505
+ interrupt(payload = {}) {
2506
+ return this.queueController.interrupt(payload);
2507
+ }
2508
+ resumeQueued() {
2509
+ return this.queueController.resumeQueued();
2396
2510
  }
2397
2511
  stop() {
2398
2512
  return this.queueController.stop();
2399
2513
  }
2400
- async submitExpandedText(text, source, runBeforeSubmit = true) {
2401
- if (runBeforeSubmit && !await this.runBeforeSubmit(text, [], source)) {
2402
- return this.block("pre-submit-cancelled", "Submit was cancelled before sending.");
2403
- }
2404
- return this.fromQueueResult(await this.queueController.submit({
2405
- text,
2514
+ async submitExpandedText(text, files, source, release) {
2515
+ const transformed = await this.runPromptTransform(text, files, source);
2516
+ if (this.options.isActiveSession && !this.options.isActiveSession()) return this.stale();
2517
+ const admission = this.queueController.submit({
2518
+ text: transformed?.replacement.text ?? text,
2519
+ ...transformed?.replacement.displayText ? { displayText: transformed.replacement.displayText } : {},
2406
2520
  kind: "expanded-text",
2407
2521
  model: this.options.model ?? void 0,
2408
2522
  ...this.options.thinkingControl ? { thinkingLevel: this.options.thinkingLevel ?? DEFAULT_THINKING } : {}
2409
- }));
2523
+ });
2524
+ release();
2525
+ return this.fromQueueResult(await admission);
2410
2526
  }
2411
- async runLocalCommand(commandName, args) {
2527
+ async runPromptTransform(text, files, source) {
2528
+ try {
2529
+ const transformed = await this.options.onTransformPrompt?.(text, { files, source });
2530
+ return transformed || void 0;
2531
+ } catch (error) {
2532
+ if (this.options.isActiveSession && !this.options.isActiveSession()) return void 0;
2533
+ throw error;
2534
+ }
2535
+ }
2536
+ async runLocalCommand(commandName, args, source, release) {
2412
2537
  const command = this.options.registry.get(commandName);
2413
2538
  if (isPiChatBusy(this.options.session.getState().status) && command?.allowWhileBusy?.(args) !== true) {
2414
2539
  return this.block("busy-slash-command", "Slash commands are not queued while the agent is responding.");
@@ -2417,16 +2542,23 @@ var PiComposerPolicyController = class {
2417
2542
  const message = typeof result === "string" ? result : result && typeof result === "object" ? result.message : void 0;
2418
2543
  const preserveDraft = Boolean(result && typeof result === "object" && result.preserveDraft === true);
2419
2544
  if (message) this.options.onCommandResult?.(message);
2545
+ const modelMessage = result && typeof result === "object" ? result.modelMessage?.trim() : void 0;
2546
+ if (modelMessage) {
2547
+ return await this.submitExpandedText(modelMessage, [], source, release);
2548
+ }
2549
+ release();
2420
2550
  return { type: "command", command: commandName, ...message ? { result: message } : {}, preserveDraft };
2421
2551
  }
2422
2552
  async runBeforeSubmit(draft, files, source) {
2423
- const result = await this.options.onBeforeSubmit?.(draft, { files, source });
2424
- return result !== false;
2553
+ return await this.options.onBeforeSubmit?.(draft, { files, source });
2425
2554
  }
2426
2555
  fromQueueResult(result) {
2427
2556
  if (result.type === "blocked") return { ...result, preserveDraft: true };
2428
2557
  return { ...result, preserveDraft: false };
2429
2558
  }
2559
+ stale() {
2560
+ return { type: "stale", reason: "inactive-session", preserveDraft: false };
2561
+ }
2430
2562
  block(reason, message) {
2431
2563
  this.options.onWarning?.(message);
2432
2564
  return { type: "blocked", reason, message, preserveDraft: true };
@@ -2640,15 +2772,17 @@ function useChatModelSelection({
2640
2772
  setAvailableModels(models);
2641
2773
  setSelection((current) => {
2642
2774
  const selected = current.pendingOverride ?? sessionModel ?? current.localDefault;
2643
- if (availableModel(models, selected)) return current;
2644
- if (sessionModel) return { ...current, pendingOverride: void 0 };
2645
- if (sessionId && !sessionIsNew) return { ...current, pendingOverride: void 0, localDefault: null };
2775
+ if (sessionModel) {
2776
+ if (availableModel(models, selected)) return current;
2777
+ return { ...current, pendingOverride: void 0 };
2778
+ }
2646
2779
  const firstAvailable = models.find((candidate) => candidate.available);
2647
- return {
2648
- ...current,
2649
- pendingOverride: void 0,
2650
- localDefault: payload.defaultModel ?? (firstAvailable ? { provider: firstAvailable.provider, id: firstAvailable.id } : null)
2651
- };
2780
+ const serverDefault = payload.defaultModel ?? (firstAvailable ? { provider: firstAvailable.provider, id: firstAvailable.id } : null);
2781
+ if (sessionId && !sessionIsNew) {
2782
+ return { ...current, pendingOverride: void 0, localDefault: serverDefault };
2783
+ }
2784
+ if (availableModel(models, selected)) return current;
2785
+ return { ...current, pendingOverride: void 0, localDefault: serverDefault };
2652
2786
  });
2653
2787
  setLoadedDiscoveryKey(discoveryKey);
2654
2788
  setLoaded(true);
@@ -2676,7 +2810,8 @@ function useChatModelSelection({
2676
2810
  const selectionBelongsToSession = selection.sessionId === sessionId && selection.storage === storage && selection.storageScope === storageScope;
2677
2811
  const pendingOverride = selectionBelongsToSession ? selection.pendingOverride : void 0;
2678
2812
  const isOverride = Boolean(pendingOverride && sessionModel && !sameModel(pendingOverride, sessionModel));
2679
- const sessionAuthorityReady = !sessionId || sessionHydrated && (sessionModel !== void 0 || sessionIsNew);
2813
+ const discoveryVetted = enabled && currentDiscoveryLoaded;
2814
+ const sessionAuthorityReady = !sessionId || sessionHydrated && (sessionModel !== void 0 || sessionIsNew || discoveryVetted);
2680
2815
  const model = currentDiscoveryLoaded && sessionAuthorityReady ? pendingOverride ?? sessionModel ?? selection.localDefault : null;
2681
2816
  return {
2682
2817
  availableModels: currentDiscoveryLoaded ? availableModels : [],
@@ -3383,15 +3518,15 @@ function messageCreatedAtMs(message) {
3383
3518
 
3384
3519
  // src/front/chat/gatewayResponseError.ts
3385
3520
  var GatewayResponseError = class extends Error {
3386
- constructor(status, message, code3, details, body, operation) {
3521
+ constructor(status, message, code, details, body, operation) {
3387
3522
  super(message);
3388
3523
  this.status = status;
3389
- this.code = code3;
3524
+ this.code = code;
3390
3525
  this.details = details;
3391
3526
  this.body = body;
3392
3527
  this.operation = operation;
3393
3528
  this.name = "GatewayResponseError";
3394
- this.errorCode = code3;
3529
+ this.errorCode = code;
3395
3530
  }
3396
3531
  status;
3397
3532
  code;
@@ -3406,11 +3541,11 @@ async function gatewayResponseError(response, fallbackMessage, operation) {
3406
3541
  }
3407
3542
  function gatewayResponseErrorFromBody(status, body, fallbackMessage, operation) {
3408
3543
  const payload = errorPayload(body);
3409
- const code3 = gatewayResponseErrorCode(payload?.code);
3544
+ const code = gatewayResponseErrorCode(payload?.code);
3410
3545
  const serverMessage = typeof payload?.message === "string" && payload.message.trim().length > 0 ? payload.message : fallbackMessage;
3411
3546
  const message = serverMessage;
3412
3547
  const details = isJsonValue(payload?.details) ? payload.details : void 0;
3413
- return new GatewayResponseError(status, message, code3, details, body, operation);
3548
+ return new GatewayResponseError(status, message, code, details, body, operation);
3414
3549
  }
3415
3550
  function gatewayResponseErrorCode(value) {
3416
3551
  const sharedCode = ErrorCode.safeParse(value);
@@ -4683,12 +4818,30 @@ var RemotePiSession = class {
4683
4818
  }
4684
4819
  }
4685
4820
  async clearQueue(payload = {}) {
4686
- const receipt = await this.postCommand("/queue/clear", payload, QueueClearReceiptSchema);
4821
+ const receipt = await this.postQueueClear({ ...payload, requestId: `queue-clear:${safeRandomUUID()}` });
4687
4822
  if (!this.disposed && receipt.cleared > 0) {
4688
4823
  this.store.dispatch({ type: "clear-optimistic-followups", ...payload }, { flush: true });
4689
4824
  }
4690
4825
  return receipt;
4691
4826
  }
4827
+ async postQueueClear(commandPayload) {
4828
+ let transportRetried = false;
4829
+ for (let attempt = 0; ; attempt += 1) {
4830
+ try {
4831
+ return await this.postCommand("/queue/clear", commandPayload, QueueClearReceiptSchema);
4832
+ } catch (error) {
4833
+ if (!(error instanceof GatewayResponseError)) {
4834
+ if (transportRetried) throw error;
4835
+ transportRetried = true;
4836
+ continue;
4837
+ }
4838
+ if (errorResponseCode(error) !== AgentGatewayErrorCode.AGENT_REQUEST_IN_PROGRESS || attempt >= 40) throw error;
4839
+ await new Promise((resolve) => {
4840
+ this.setTimeoutFn(resolve, 50);
4841
+ });
4842
+ }
4843
+ }
4844
+ }
4692
4845
  async interrupt(payload = {}) {
4693
4846
  return this.postCommand("/interrupt", payload, CommandReceiptSchema);
4694
4847
  }
@@ -5298,17 +5451,27 @@ function usePiSessions(options) {
5298
5451
  const [loading, setLoading] = useState10(enabled);
5299
5452
  const [loadingMore, setLoadingMore] = useState10(false);
5300
5453
  const [hasMore, setHasMore] = useState10(false);
5454
+ const [archivedLoaded, setArchivedLoaded] = useState10(false);
5455
+ const [archivedLoading, setArchivedLoading] = useState10(false);
5456
+ const [hasMoreArchived, setHasMoreArchived] = useState10(false);
5301
5457
  const [error, setError] = useState10(void 0);
5302
5458
  const mountedRef = useRef7(false);
5303
5459
  const refreshVersionRef = useRef7(0);
5304
5460
  const retryTimerRef = useRef7(void 0);
5305
5461
  const sessionsRef = useRef7([]);
5462
+ const filterPagersRef = useRef7({
5463
+ active: { sessions: [], loaded: false },
5464
+ archived: { sessions: [], loaded: false }
5465
+ });
5466
+ const archiveMutationVersionRef = useRef7(0);
5306
5467
  const activeSessionIdRef = useRef7(activeSessionId);
5307
- const hasMoreRef = useRef7(hasMore);
5308
5468
  const canonicalLoadedCountRef = useRef7(0);
5309
5469
  const nextCursorRef = useRef7(void 0);
5310
- const loadMoreRequestSeqRef = useRef7(0);
5311
- const loadMoreInFlightRef = useRef7(false);
5470
+ const archivedCursorRef = useRef7(void 0);
5471
+ const archivedLoadedRef = useRef7(false);
5472
+ const archiveRequestSeqRef = useRef7(0);
5473
+ const archiveInFlightRef = useRef7(false);
5474
+ const activeRequestOwnerRef = useRef7(void 0);
5312
5475
  const pendingCreatedRef = useRef7(/* @__PURE__ */ new Map());
5313
5476
  const pendingDeletedRef = useRef7(/* @__PURE__ */ new Set());
5314
5477
  const pendingCreatedScopeRef = useRef7(requestScopeKey);
@@ -5340,14 +5503,11 @@ function usePiSessions(options) {
5340
5503
  useEffect8(() => {
5341
5504
  activeSessionIdRef.current = activeSessionId;
5342
5505
  }, [activeSessionId]);
5343
- useEffect8(() => {
5344
- hasMoreRef.current = hasMore;
5345
- }, [hasMore]);
5346
5506
  const activeSessionKnown = Boolean(activeSessionId && sessions.some((session) => session.id === activeSessionId));
5347
5507
  const requestHeaders = useCallback7(() => normalizedHeaders, [normalizedHeaders]);
5348
5508
  const sessionsUrl = useCallback7((suffix = "") => `${apiBaseUrl}${sessionsApiPath}${suffix}`, [apiBaseUrl, sessionsApiPath]);
5349
- const sessionsListUrl = useCallback7((cursor) => {
5350
- const query = new URLSearchParams({ limit: String(SESSION_PAGE_SIZE) });
5509
+ const sessionsListUrl = useCallback7((cursor, archived = "active") => {
5510
+ const query = new URLSearchParams({ limit: String(SESSION_PAGE_SIZE), archived });
5351
5511
  if (cursor) query.set("cursor", cursor);
5352
5512
  return sessionsUrl(`?${query.toString()}`);
5353
5513
  }, [sessionsUrl]);
@@ -5368,6 +5528,27 @@ function usePiSessions(options) {
5368
5528
  pendingCreatedRef.current.clear();
5369
5529
  pendingDeletedRef.current.clear();
5370
5530
  }, [requestScopeKey]);
5531
+ const publishFilterPagers = useCallback7(() => {
5532
+ const deleted = pendingDeletedRef.current;
5533
+ const merged = mergeSessions(
5534
+ Array.from(pendingCreatedRef.current.values()),
5535
+ filterPagersRef.current.active.sessions,
5536
+ filterPagersRef.current.archived.sessions
5537
+ ).filter((session) => !deleted.has(session.id));
5538
+ sessionsRef.current = merged;
5539
+ setSessions(merged);
5540
+ return merged;
5541
+ }, []);
5542
+ const resetArchivePager = useCallback7(() => {
5543
+ filterPagersRef.current.archived = { sessions: [], loaded: false };
5544
+ archivedCursorRef.current = void 0;
5545
+ archivedLoadedRef.current = false;
5546
+ archiveRequestSeqRef.current += 1;
5547
+ archiveInFlightRef.current = false;
5548
+ setArchivedLoaded(false);
5549
+ setArchivedLoading(false);
5550
+ setHasMoreArchived(false);
5551
+ }, []);
5371
5552
  const preferredSessionId = useCallback7(() => {
5372
5553
  const persisted = options.initialActiveSessionId ?? readActiveSessionId({
5373
5554
  storageScope: activeSessionStorageScope,
@@ -5394,11 +5575,23 @@ function usePiSessions(options) {
5394
5575
  }
5395
5576
  }
5396
5577
  const canonicalCount = canonicalPageCount(filteredData);
5397
- const pageMayHaveMore = applyOptions.nextCursor !== void 0;
5398
- const wasExhaustedBeyondFirstPage = applyOptions.background && !hasMoreRef.current && canonicalLoadedCountRef.current >= canonicalCount;
5399
- const requestedActiveReturned = Boolean(requestedActiveId && filteredData.some((session) => session.id === requestedActiveId));
5400
- const current = applyOptions.background && pageMayHaveMore ? sessionsRef.current.filter((session) => !requestedActiveId || requestedActiveReturned || session.id !== requestedActiveId) : [];
5401
- const merged = mergeSessions(Array.from(pendingCreated.values()), filteredData, current.filter((session) => !deletedIds.has(session.id)));
5578
+ filterPagersRef.current.active = {
5579
+ sessions: mergeSessions(filteredData),
5580
+ nextCursor: applyOptions.nextCursor,
5581
+ loaded: true
5582
+ };
5583
+ if (applyOptions.archivedPage) {
5584
+ const archived = applyOptions.archivedPage.sessions.filter((session) => !deletedIds.has(session.id));
5585
+ filterPagersRef.current.archived = {
5586
+ sessions: archived,
5587
+ nextCursor: applyOptions.archivedPage.nextCursor,
5588
+ loaded: true
5589
+ };
5590
+ archivedCursorRef.current = applyOptions.archivedPage.nextCursor;
5591
+ archivedLoadedRef.current = true;
5592
+ setArchivedLoaded(true);
5593
+ setHasMoreArchived(applyOptions.archivedPage.nextCursor !== void 0);
5594
+ }
5402
5595
  const rememberedEmptyId = readBootResumeSessionId({
5403
5596
  bootResumeSource,
5404
5597
  storage: options.bootResumeStorage
@@ -5406,12 +5599,13 @@ function usePiSessions(options) {
5406
5599
  if (rememberedEmptyId && filteredData.some((session) => session.id === rememberedEmptyId)) {
5407
5600
  persistBootResume(void 0);
5408
5601
  }
5409
- const nextHasMore = pageMayHaveMore && !wasExhaustedBeyondFirstPage;
5602
+ const nextHasMore = applyOptions.nextCursor !== void 0;
5410
5603
  canonicalLoadedCountRef.current = applyOptions.background ? Math.max(canonicalLoadedCountRef.current, canonicalCount) : canonicalCount;
5604
+ if (replacingScope && !applyOptions.archivedPage) resetArchivePager();
5411
5605
  loadedDataSourceRef.current = dataSourceKey;
5412
5606
  dataStorageScopeRef.current = storageScope;
5413
5607
  setDataStorageScope(storageScope);
5414
- setSessions(merged);
5608
+ const merged = publishFilterPagers();
5415
5609
  nextCursorRef.current = applyOptions.nextCursor;
5416
5610
  setHasMore(nextHasMore);
5417
5611
  setActiveSessionId((previous) => {
@@ -5420,21 +5614,26 @@ function usePiSessions(options) {
5420
5614
  persistActive(next);
5421
5615
  return next;
5422
5616
  });
5423
- }, [bootResumeSource, dataSourceKey, ensurePendingScope, options.bootResumeStorage, persistActive, persistBootResume, preferredSessionId, storageScope]);
5617
+ }, [bootResumeSource, dataSourceKey, ensurePendingScope, options.bootResumeStorage, persistActive, persistBootResume, preferredSessionId, publishFilterPagers, resetArchivePager, storageScope]);
5424
5618
  const refresh = useCallback7(async (refreshOptions = {}) => {
5425
5619
  const scope = requestScopeKey;
5426
5620
  if (mountedRef.current && !sourceIsCurrent(scope)) throw new StaleSessionsSourceError();
5621
+ const requestOwner = {};
5622
+ activeRequestOwnerRef.current = requestOwner;
5427
5623
  const version = ++refreshVersionRef.current;
5428
- const isCurrent = () => sourceIsCurrent(scope) && version === refreshVersionRef.current;
5624
+ const mutationVersion = archiveMutationVersionRef.current;
5625
+ const isCurrent = () => sourceIsCurrent(scope) && version === refreshVersionRef.current && activeRequestOwnerRef.current === requestOwner;
5429
5626
  clearRetryTimer(retryTimerRef);
5430
5627
  const background = refreshOptions.background === true;
5628
+ let archivedRequestSeq;
5431
5629
  if (!enabled) {
5432
- loadMoreRequestSeqRef.current += 1;
5433
- loadMoreInFlightRef.current = false;
5434
5630
  canonicalLoadedCountRef.current = 0;
5435
5631
  loadedDataSourceRef.current = dataSourceKey;
5436
5632
  dataStorageScopeRef.current = storageScope;
5437
5633
  setDataStorageScope(storageScope);
5634
+ filterPagersRef.current.active = { sessions: [], loaded: true };
5635
+ resetArchivePager();
5636
+ sessionsRef.current = [];
5438
5637
  setSessions([]);
5439
5638
  setActiveSessionId(void 0);
5440
5639
  setError(void 0);
@@ -5442,17 +5641,22 @@ function usePiSessions(options) {
5442
5641
  setLoadingMore(false);
5443
5642
  setHasMore(false);
5444
5643
  persistActive(void 0);
5644
+ if (activeRequestOwnerRef.current === requestOwner) activeRequestOwnerRef.current = void 0;
5445
5645
  return;
5446
5646
  }
5447
- loadMoreRequestSeqRef.current += 1;
5448
- loadMoreInFlightRef.current = false;
5449
5647
  setLoadingMore(false);
5450
5648
  if (!background) setLoading(true);
5451
5649
  try {
5452
5650
  let page;
5453
5651
  for (let attempt = 0; ; attempt += 1) {
5454
5652
  try {
5455
- page = await fetchSessionList(fetchImpl, sessionsListUrl(), requestHeaders());
5653
+ page = await fetchSessionPrefix({
5654
+ fetchImpl,
5655
+ firstUrl: sessionsListUrl(),
5656
+ nextUrl: (cursor) => sessionsListUrl(cursor),
5657
+ headers: requestHeaders(),
5658
+ minimumRows: filterPagersRef.current.active.sessions.length
5659
+ });
5456
5660
  break;
5457
5661
  } catch (err) {
5458
5662
  const transient = err instanceof SessionsPreparingError || isNetworkFetchError(err);
@@ -5464,17 +5668,41 @@ function usePiSessions(options) {
5464
5668
  }
5465
5669
  }
5466
5670
  if (!isCurrent() || !page) return;
5467
- applySessions(page.sessions, { background, nextCursor: page.nextCursor });
5671
+ let archivedPage;
5672
+ if (filterPagersRef.current.archived.loaded) {
5673
+ archivedRequestSeq = ++archiveRequestSeqRef.current;
5674
+ archiveInFlightRef.current = true;
5675
+ setArchivedLoading(true);
5676
+ archivedPage = await fetchSessionPrefix({
5677
+ fetchImpl,
5678
+ firstUrl: sessionsListUrl(void 0, "archived"),
5679
+ nextUrl: (cursor) => sessionsListUrl(cursor, "archived"),
5680
+ headers: requestHeaders(),
5681
+ minimumRows: filterPagersRef.current.archived.sessions.length
5682
+ });
5683
+ }
5684
+ if (!isCurrent() || archivedRequestSeq !== void 0 && archivedRequestSeq !== archiveRequestSeqRef.current) return;
5685
+ if (mutationVersion !== archiveMutationVersionRef.current) {
5686
+ setLoading(false);
5687
+ return;
5688
+ }
5689
+ applySessions(page.sessions, { background, nextCursor: page.nextCursor, archivedPage });
5468
5690
  setError(void 0);
5469
5691
  setLoading(false);
5470
5692
  } catch (err) {
5471
- if (!isCurrent()) return;
5693
+ if (!isCurrent() || archivedRequestSeq !== void 0 && archivedRequestSeq !== archiveRequestSeqRef.current) return;
5472
5694
  const error2 = err instanceof Error ? err : new Error(String(err));
5473
5695
  if (!background) setError(error2);
5474
5696
  setLoading(false);
5475
5697
  if (refreshOptions.throwOnError) throw error2;
5698
+ } finally {
5699
+ if (activeRequestOwnerRef.current === requestOwner) activeRequestOwnerRef.current = void 0;
5700
+ if (archivedRequestSeq !== void 0 && archivedRequestSeq === archiveRequestSeqRef.current) {
5701
+ archiveInFlightRef.current = false;
5702
+ if (sourceIsCurrent(scope)) setArchivedLoading(false);
5703
+ }
5476
5704
  }
5477
- }, [applySessions, enabled, fetchImpl, persistActive, preferredSessionId, requestHeaders, requestScopeKey, retryBaseMs, retryMaxMs, retryMaxRetries, sessionsListUrl, sourceIsCurrent]);
5705
+ }, [applySessions, enabled, fetchImpl, persistActive, preferredSessionId, requestHeaders, requestScopeKey, resetArchivePager, retryBaseMs, retryMaxMs, retryMaxRetries, sessionsListUrl, sourceIsCurrent]);
5478
5706
  useEffect8(() => {
5479
5707
  mountedRef.current = true;
5480
5708
  void refresh();
@@ -5485,21 +5713,26 @@ function usePiSessions(options) {
5485
5713
  };
5486
5714
  }, [refresh, options.refreshKey]);
5487
5715
  const loadMore = useCallback7(async () => {
5488
- if (!enabled || loading || loadingMore || loadMoreInFlightRef.current || !hasMore) return;
5489
- const requestSeq = ++loadMoreRequestSeqRef.current;
5490
- loadMoreInFlightRef.current = true;
5716
+ if (!enabled || loading || loadingMore || activeRequestOwnerRef.current !== void 0 || !hasMore) return;
5717
+ const requestOwner = {};
5718
+ activeRequestOwnerRef.current = requestOwner;
5491
5719
  const version = refreshVersionRef.current;
5720
+ const mutationVersion = archiveMutationVersionRef.current;
5492
5721
  const scope = requestScopeKey;
5493
5722
  setLoadingMore(true);
5494
5723
  try {
5495
5724
  const page = await fetchSessionList(fetchImpl, sessionsListUrl(nextCursorRef.current), requestHeaders());
5496
5725
  const data = page.sessions.filter((session) => !pendingDeletedRef.current.has(session.id));
5497
- if (requestSeq !== loadMoreRequestSeqRef.current || version !== refreshVersionRef.current || !sourceIsCurrent(scope)) return;
5498
- const merged = mergeSessions(sessionsRef.current.filter((session) => !pendingDeletedRef.current.has(session.id)), data);
5726
+ if (activeRequestOwnerRef.current !== requestOwner || version !== refreshVersionRef.current || mutationVersion !== archiveMutationVersionRef.current || !sourceIsCurrent(scope)) return;
5727
+ filterPagersRef.current.active = {
5728
+ sessions: mergeSessions(filterPagersRef.current.active.sessions, data),
5729
+ nextCursor: page.nextCursor,
5730
+ loaded: true
5731
+ };
5732
+ const merged = publishFilterPagers();
5499
5733
  const nextHasMore = page.nextCursor !== void 0;
5500
5734
  canonicalLoadedCountRef.current += data.length;
5501
5735
  nextCursorRef.current = page.nextCursor;
5502
- setSessions(merged);
5503
5736
  setHasMore(nextHasMore);
5504
5737
  setError(void 0);
5505
5738
  setActiveSessionId((previous) => {
@@ -5509,16 +5742,52 @@ function usePiSessions(options) {
5509
5742
  return next;
5510
5743
  });
5511
5744
  } catch (err) {
5512
- if (requestSeq === loadMoreRequestSeqRef.current && version === refreshVersionRef.current && sourceIsCurrent(scope)) {
5745
+ if (activeRequestOwnerRef.current === requestOwner && version === refreshVersionRef.current && sourceIsCurrent(scope)) {
5513
5746
  setError(err instanceof Error ? err : new Error(String(err)));
5514
5747
  }
5515
5748
  } finally {
5516
- if (requestSeq === loadMoreRequestSeqRef.current) loadMoreInFlightRef.current = false;
5517
- if (requestSeq === loadMoreRequestSeqRef.current && version === refreshVersionRef.current && sourceIsCurrent(scope)) {
5518
- setLoadingMore(false);
5749
+ if (activeRequestOwnerRef.current === requestOwner) {
5750
+ activeRequestOwnerRef.current = void 0;
5751
+ if (version === refreshVersionRef.current && sourceIsCurrent(scope)) setLoadingMore(false);
5519
5752
  }
5520
5753
  }
5521
- }, [enabled, fetchImpl, hasMore, loading, loadingMore, persistActive, requestHeaders, requestScopeKey, sessionsListUrl, sourceIsCurrent]);
5754
+ }, [enabled, fetchImpl, hasMore, loading, loadingMore, persistActive, publishFilterPagers, requestHeaders, requestScopeKey, sessionsListUrl, sourceIsCurrent]);
5755
+ const loadArchived = useCallback7(async () => {
5756
+ if (!enabled || archivedLoading || archiveInFlightRef.current) return;
5757
+ if (archivedLoadedRef.current && !hasMoreArchived) return;
5758
+ const requestSeq = ++archiveRequestSeqRef.current;
5759
+ const mutationVersion = archiveMutationVersionRef.current;
5760
+ const scope = requestScopeKey;
5761
+ archiveInFlightRef.current = true;
5762
+ setArchivedLoading(true);
5763
+ try {
5764
+ const page = await fetchSessionList(
5765
+ fetchImpl,
5766
+ sessionsListUrl(archivedCursorRef.current, "archived"),
5767
+ requestHeaders()
5768
+ );
5769
+ if (requestSeq !== archiveRequestSeqRef.current || mutationVersion !== archiveMutationVersionRef.current || !sourceIsCurrent(scope)) return;
5770
+ const archived = page.sessions.filter((session) => !pendingDeletedRef.current.has(session.id));
5771
+ filterPagersRef.current.archived = {
5772
+ sessions: mergeSessions(filterPagersRef.current.archived.sessions, archived),
5773
+ nextCursor: page.nextCursor,
5774
+ loaded: true
5775
+ };
5776
+ archivedCursorRef.current = page.nextCursor;
5777
+ archivedLoadedRef.current = true;
5778
+ publishFilterPagers();
5779
+ setArchivedLoaded(true);
5780
+ setHasMoreArchived(page.nextCursor !== void 0);
5781
+ setError(void 0);
5782
+ } catch (err) {
5783
+ if (requestSeq === archiveRequestSeqRef.current && sourceIsCurrent(scope)) {
5784
+ setError(err instanceof Error ? err : new Error(String(err)));
5785
+ }
5786
+ } finally {
5787
+ if (requestSeq === archiveRequestSeqRef.current) archiveInFlightRef.current = false;
5788
+ if (requestSeq === archiveRequestSeqRef.current && sourceIsCurrent(scope)) setArchivedLoading(false);
5789
+ }
5790
+ }, [archivedLoading, enabled, fetchImpl, hasMoreArchived, publishFilterPagers, requestHeaders, requestScopeKey, sessionsListUrl, sourceIsCurrent]);
5522
5791
  useEffect8(() => {
5523
5792
  if (!enabled || !connectActiveSession || !activeSessionId || !activeSessionKnown) {
5524
5793
  setActivePiSession(void 0);
@@ -5560,12 +5829,12 @@ function usePiSessions(options) {
5560
5829
  rememberConfirmedEmptySession(session.id);
5561
5830
  pendingCreatedRef.current.set(session.id, session);
5562
5831
  setDataStorageScope(storageScope);
5563
- setSessions((previous) => mergeSessions([session], previous));
5832
+ publishFilterPagers();
5564
5833
  setActiveSessionId(session.id);
5565
5834
  persistActive(session.id);
5566
5835
  void refresh();
5567
5836
  return session;
5568
- }, [enabled, ensurePendingScope, fetchImpl, persistActive, refresh, rememberConfirmedEmptySession, requestHeaders, requestScopeKey, sessionsUrl, sourceIsCurrent, storageScope]);
5837
+ }, [enabled, ensurePendingScope, fetchImpl, persistActive, publishFilterPagers, refresh, rememberConfirmedEmptySession, requestHeaders, requestScopeKey, sessionsUrl, sourceIsCurrent, storageScope]);
5569
5838
  const switchSession = useCallback7((id) => {
5570
5839
  if (!sourceIsCurrent(requestScopeKey)) return;
5571
5840
  const known = sessionsRef.current.some((session) => session.id === id);
@@ -5587,9 +5856,34 @@ function usePiSessions(options) {
5587
5856
  if (!sourceIsCurrent(scope)) throw new StaleSessionsSourceError();
5588
5857
  ensurePendingScope();
5589
5858
  if (pendingCreatedRef.current.has(id)) pendingCreatedRef.current.set(id, renamed);
5590
- setSessions((current) => current.map((session) => session.id === id ? renamed : session));
5859
+ for (const pager of Object.values(filterPagersRef.current)) {
5860
+ pager.sessions = pager.sessions.map((session) => session.id === id ? renamed : session);
5861
+ }
5862
+ publishFilterPagers();
5591
5863
  return renamed;
5592
- }, [enabled, ensurePendingScope, fetchImpl, requestHeaders, requestScopeKey, sessionsUrl, sourceIsCurrent]);
5864
+ }, [enabled, ensurePendingScope, fetchImpl, publishFilterPagers, requestHeaders, requestScopeKey, sessionsUrl, sourceIsCurrent]);
5865
+ const setArchived = useCallback7(async (id, archived) => {
5866
+ const scope = requestScopeKey;
5867
+ if (!sourceIsCurrent(scope)) throw new StaleSessionsSourceError();
5868
+ if (!enabled) throw new Error("Pi sessions are disabled");
5869
+ const response = await fetchImpl(sessionsUrl(`/${encodeURIComponent(id)}/archive`), {
5870
+ method: "POST",
5871
+ headers: { ...requestHeaders(), "Content-Type": "application/json" },
5872
+ body: JSON.stringify({ requestId: createRequestId("archive"), archived })
5873
+ });
5874
+ if (!response.ok) throw new Error(`Failed to ${archived ? "archive" : "unarchive"} session: ${response.status}`);
5875
+ const updated = toAddressedSessionSummary(await response.json());
5876
+ if (!sourceIsCurrent(scope)) throw new StaleSessionsSourceError();
5877
+ archiveMutationVersionRef.current += 1;
5878
+ pendingCreatedRef.current.delete(id);
5879
+ for (const pager of Object.values(filterPagersRef.current)) {
5880
+ pager.sessions = pager.sessions.filter((session) => session.id !== id);
5881
+ }
5882
+ const target = archived ? filterPagersRef.current.archived : filterPagersRef.current.active;
5883
+ target.sessions = sortSessionSummaries([...target.sessions, updated]);
5884
+ publishFilterPagers();
5885
+ return updated;
5886
+ }, [enabled, fetchImpl, publishFilterPagers, requestHeaders, requestScopeKey, sessionsUrl, sourceIsCurrent]);
5593
5887
  const deleteSession = useCallback7(async (id) => {
5594
5888
  const scope = requestScopeKey;
5595
5889
  if (!sourceIsCurrent(scope)) throw new StaleSessionsSourceError();
@@ -5602,8 +5896,11 @@ function usePiSessions(options) {
5602
5896
  const deletedSessionWasActive = activeSessionIdRef.current === id;
5603
5897
  pendingCreatedRef.current.delete(id);
5604
5898
  pendingDeletedRef.current.add(id);
5899
+ for (const pager of Object.values(filterPagersRef.current)) {
5900
+ pager.sessions = pager.sessions.filter((session) => session.id !== id);
5901
+ }
5605
5902
  setDataStorageScope(storageScope);
5606
- setSessions((previous) => previous.filter((session) => session.id !== id));
5903
+ publishFilterPagers();
5607
5904
  setActiveSessionId((previous) => {
5608
5905
  if (previous !== id) return previous;
5609
5906
  const next = sessionsRef.current.find((session) => session.id !== id)?.id;
@@ -5621,7 +5918,9 @@ function usePiSessions(options) {
5621
5918
  if (sourceIsCurrent(scope)) {
5622
5919
  pendingDeletedRef.current.delete(id);
5623
5920
  if (deletedSession) {
5624
- setSessions((previous) => mergeSessions([deletedSession], previous));
5921
+ const target = deletedSession.archived === true ? filterPagersRef.current.archived : filterPagersRef.current.active;
5922
+ target.sessions = sortSessionSummaries([...target.sessions, deletedSession]);
5923
+ publishFilterPagers();
5625
5924
  if (deletedSessionWasActive) {
5626
5925
  setActiveSessionId(id);
5627
5926
  persistActive(id);
@@ -5633,14 +5932,15 @@ function usePiSessions(options) {
5633
5932
  throw error2;
5634
5933
  }
5635
5934
  if (sourceIsCurrent(scope)) void refresh();
5636
- }, [bootResumeSource, enabled, ensurePendingScope, fetchImpl, options.bootResumeStorage, persistActive, persistBootResume, refresh, requestHeaders, requestScopeKey, sessionsUrl, sourceIsCurrent, storageScope]);
5935
+ }, [bootResumeSource, enabled, ensurePendingScope, fetchImpl, options.bootResumeStorage, persistActive, persistBootResume, publishFilterPagers, refresh, requestHeaders, requestScopeKey, sessionsUrl, sourceIsCurrent, storageScope]);
5637
5936
  const reset = useCallback7(() => {
5638
5937
  if (!sourceIsCurrent(requestScopeKey)) return;
5639
5938
  pendingCreatedRef.current.clear();
5640
5939
  pendingDeletedRef.current.clear();
5641
- loadMoreRequestSeqRef.current += 1;
5642
- loadMoreInFlightRef.current = false;
5643
- canonicalLoadedCountRef.current = canonicalPageCount(sessionsRef.current);
5940
+ activeRequestOwnerRef.current = void 0;
5941
+ resetArchivePager();
5942
+ publishFilterPagers();
5943
+ canonicalLoadedCountRef.current = canonicalPageCount(filterPagersRef.current.active.sessions);
5644
5944
  loadedDataSourceRef.current = dataSourceKey;
5645
5945
  dataStorageScopeRef.current = storageScope;
5646
5946
  setDataStorageScope(storageScope);
@@ -5648,7 +5948,7 @@ function usePiSessions(options) {
5648
5948
  setActivePiSession(void 0);
5649
5949
  setLoadingMore(false);
5650
5950
  persistActive(void 0);
5651
- }, [dataSourceKey, persistActive, requestScopeKey, sourceIsCurrent, storageScope]);
5951
+ }, [dataSourceKey, persistActive, publishFilterPagers, requestScopeKey, resetArchivePager, sourceIsCurrent, storageScope]);
5652
5952
  const visibleActiveSessionId = enabled ? activeSessionId : void 0;
5653
5953
  const activeSession = enabled ? sessions.find((session) => session.id === visibleActiveSessionId) : void 0;
5654
5954
  return {
@@ -5662,10 +5962,15 @@ function usePiSessions(options) {
5662
5962
  loading: enabled ? loading : false,
5663
5963
  loadingMore,
5664
5964
  hasMore: enabled ? hasMore : false,
5965
+ archivedLoaded,
5966
+ archivedLoading,
5967
+ hasMoreArchived: enabled ? hasMoreArchived : false,
5665
5968
  error,
5666
5969
  refresh,
5667
5970
  create,
5668
5971
  rename,
5972
+ setArchived,
5973
+ loadArchived,
5669
5974
  switch: switchSession,
5670
5975
  delete: deleteSession,
5671
5976
  loadMore,
@@ -5686,6 +5991,19 @@ async function fetchSessionList(fetchImpl, url, headers) {
5686
5991
  ...typeof page.nextCursor === "string" ? { nextCursor: page.nextCursor } : {}
5687
5992
  };
5688
5993
  }
5994
+ async function fetchSessionPrefix(input) {
5995
+ const sessions = [];
5996
+ let page = await fetchSessionList(input.fetchImpl, input.firstUrl, input.headers);
5997
+ sessions.push(...page.sessions);
5998
+ while (page.nextCursor && sessions.length < input.minimumRows) {
5999
+ page = await fetchSessionList(input.fetchImpl, input.nextUrl(page.nextCursor), input.headers);
6000
+ sessions.push(...page.sessions);
6001
+ }
6002
+ return {
6003
+ sessions: mergeSessions(sessions),
6004
+ ...page.nextCursor ? { nextCursor: page.nextCursor } : {}
6005
+ };
6006
+ }
5689
6007
  function toAddressedSessionSummary(value) {
5690
6008
  if (typeof value !== "object" || value === null) throw new Error("invalid addressed session summary");
5691
6009
  const record = value;
@@ -5707,7 +6025,8 @@ function toAddressedSessionSummary(value) {
5707
6025
  ...typeof agentTypeId === "string" ? { agentTypeId } : {},
5708
6026
  ...typeof record.nativeSessionId === "string" ? { nativeSessionId: record.nativeSessionId } : {},
5709
6027
  ...typeof record.hasAssistantReply === "boolean" ? { hasAssistantReply: record.hasAssistantReply } : {},
5710
- ...addressedStatus ? { status: addressedStatus } : {}
6028
+ ...addressedStatus ? { status: addressedStatus } : {},
6029
+ ...record.archived === true ? { archived: true } : {}
5711
6030
  };
5712
6031
  }
5713
6032
  function addressedCreatedSession(value, expectedAgentTypeId, title) {
@@ -5761,6 +6080,13 @@ function remoteSessionOptionsIdentity(options) {
5761
6080
  } : void 0
5762
6081
  });
5763
6082
  }
6083
+ function sortSessionSummaries(sessions) {
6084
+ return sessions.sort((left, right) => {
6085
+ const time = Date.parse(right.updatedAt) - Date.parse(left.updatedAt);
6086
+ if (time !== 0) return time;
6087
+ return left.id < right.id ? -1 : left.id > right.id ? 1 : 0;
6088
+ });
6089
+ }
5764
6090
  function mergeSessions(...lists) {
5765
6091
  const seen = /* @__PURE__ */ new Set();
5766
6092
  const merged = [];
@@ -5987,7 +6313,7 @@ function sortByUpdatedDesc(a, b) {
5987
6313
  return a.id.localeCompare(b.id);
5988
6314
  }
5989
6315
 
5990
- // ../../node_modules/.pnpm/@earendil-works+pi-tui@0.80.7/node_modules/@earendil-works/pi-tui/dist/fuzzy.js
6316
+ // ../../node_modules/.pnpm/@earendil-works+pi-tui@0.84.3/node_modules/@earendil-works/pi-tui/dist/fuzzy.js
5991
6317
  function fuzzyMatch(query, text) {
5992
6318
  const queryLower = query.toLowerCase();
5993
6319
  const textLower = text.toLowerCase();
@@ -6173,7 +6499,7 @@ function searchPiSessions(sessions, query, options = {}) {
6173
6499
 
6174
6500
  // src/front/chat/components/PiConversationSurface.tsx
6175
6501
  import { Skeleton } from "@hachej/boring-ui-kit";
6176
- import { useCallback as useCallback14, useEffect as useEffect14, useLayoutEffect as useLayoutEffect2, useRef as useRef10, useState as useState15 } from "react";
6502
+ import { useCallback as useCallback15, useEffect as useEffect16, useLayoutEffect as useLayoutEffect2, useRef as useRef11, useState as useState17 } from "react";
6177
6503
  import { useStickToBottomContext as useStickToBottomContext2 } from "use-stick-to-bottom";
6178
6504
 
6179
6505
  // src/front/primitives/conversation.tsx
@@ -6249,8 +6575,8 @@ var ConversationScrollButton = ({
6249
6575
  };
6250
6576
 
6251
6577
  // src/front/chat/components/ChatNotices.tsx
6252
- import { AlertCircleIcon, ExternalLinkIcon as ExternalLinkIcon2, ListRestartIcon, Loader2, XIcon as XIcon2 } from "lucide-react";
6253
- import { IconButton as IconButton2 } from "@hachej/boring-ui-kit";
6578
+ import { AlertCircleIcon, ExternalLinkIcon as ExternalLinkIcon2, Loader2, PencilIcon, SendHorizontalIcon, XIcon as XIcon2 } from "lucide-react";
6579
+ import { Button as Button8, IconButton as IconButton2 } from "@hachej/boring-ui-kit";
6254
6580
 
6255
6581
  // src/front/chat/components/noticeStyles.ts
6256
6582
  function noticeSurfaceClass(level, className) {
@@ -6484,7 +6810,15 @@ function composerBlockerActionIcon(actionId) {
6484
6810
  if (actionId === "cancel") return /* @__PURE__ */ jsx15(XIcon2, { className: "size-3.5", "aria-hidden": "true" });
6485
6811
  return null;
6486
6812
  }
6487
- function QueuedComposerNotice({ followUps, onEdit }) {
6813
+ function QueuedComposerNotice({
6814
+ followUps,
6815
+ onEdit,
6816
+ onResume,
6817
+ resumePending = false,
6818
+ onRemove,
6819
+ actionPending = false
6820
+ }) {
6821
+ const toolbarPending = actionPending || resumePending;
6488
6822
  return /* @__PURE__ */ jsxs14(
6489
6823
  "div",
6490
6824
  {
@@ -6494,7 +6828,7 @@ function QueuedComposerNotice({ followUps, onEdit }) {
6494
6828
  "border border-dashed border-[color:var(--border)] bg-[color:oklch(from_var(--muted)_l_c_h/0.45)] px-3 py-2 text-xs text-[color:var(--foreground)]"
6495
6829
  ),
6496
6830
  children: [
6497
- /* @__PURE__ */ jsxs14("div", { className: "min-w-0 text-[color:var(--muted-foreground)]", children: [
6831
+ /* @__PURE__ */ jsxs14("div", { className: "min-w-0 flex-1 text-[color:var(--muted-foreground)]", children: [
6498
6832
  /* @__PURE__ */ jsxs14("div", { className: "font-medium text-[color:var(--foreground)]", children: [
6499
6833
  followUps.length,
6500
6834
  " queued follow-up",
@@ -6502,31 +6836,73 @@ function QueuedComposerNotice({ followUps, onEdit }) {
6502
6836
  ] }),
6503
6837
  /* @__PURE__ */ jsx15("div", { className: "truncate text-[color:var(--muted-foreground)]", "data-boring-agent-part": "composer-queue-preview-text", children: followUps.map((followUp) => followUp.displayText).join(" - ") })
6504
6838
  ] }),
6505
- /* @__PURE__ */ jsx15(
6506
- IconButton2,
6507
- {
6508
- type: "button",
6509
- variant: "ghost",
6510
- size: "icon-sm",
6511
- onClick: onEdit,
6512
- className: "shrink-0 text-[color:var(--muted-foreground)] hover:bg-[color:var(--muted)] hover:text-[color:var(--foreground)]",
6513
- "aria-label": "Edit queued follow-ups",
6514
- title: "Edit queued follow-ups",
6515
- children: /* @__PURE__ */ jsx15(ListRestartIcon, { className: "size-3.5", "aria-hidden": "true" })
6516
- }
6517
- )
6839
+ /* @__PURE__ */ jsxs14("div", { className: "flex shrink-0 self-center items-center gap-1", children: [
6840
+ onRemove ? /* @__PURE__ */ jsx15(
6841
+ IconButton2,
6842
+ {
6843
+ type: "button",
6844
+ variant: "ghost",
6845
+ size: "icon-xs",
6846
+ onClick: onRemove,
6847
+ disabled: toolbarPending,
6848
+ "aria-busy": toolbarPending,
6849
+ className: "text-[color:var(--muted-foreground)] hover:bg-[color:var(--muted)] hover:text-[color:var(--foreground)]",
6850
+ "aria-label": "Remove all queued messages",
6851
+ title: "Remove all queued messages",
6852
+ "data-boring-agent-part": "composer-queue-remove",
6853
+ children: /* @__PURE__ */ jsx15(XIcon2, { className: "size-3", "aria-hidden": "true" })
6854
+ }
6855
+ ) : null,
6856
+ onResume ? /* @__PURE__ */ jsxs14(
6857
+ Button8,
6858
+ {
6859
+ type: "button",
6860
+ variant: "ghost",
6861
+ size: "xs",
6862
+ onClick: onResume,
6863
+ disabled: toolbarPending,
6864
+ "aria-busy": toolbarPending,
6865
+ className: "text-[color:var(--muted-foreground)] hover:bg-[color:var(--muted)] hover:text-[color:var(--foreground)]",
6866
+ "aria-label": "Nudge agent: stop the current run and send queued messages now",
6867
+ title: "Send queued messages now and stop the current run",
6868
+ "data-boring-agent-part": "composer-queue-nudge",
6869
+ children: [
6870
+ /* @__PURE__ */ jsx15(SendHorizontalIcon, { className: "size-3", "aria-hidden": "true" }),
6871
+ "Send now"
6872
+ ]
6873
+ }
6874
+ ) : null,
6875
+ /* @__PURE__ */ jsxs14(
6876
+ Button8,
6877
+ {
6878
+ type: "button",
6879
+ variant: "ghost",
6880
+ size: "xs",
6881
+ onClick: onEdit,
6882
+ disabled: toolbarPending,
6883
+ "aria-busy": toolbarPending,
6884
+ className: "text-[color:var(--muted-foreground)] hover:bg-[color:var(--muted)] hover:text-[color:var(--foreground)]",
6885
+ "aria-label": "Edit queued follow-ups",
6886
+ title: "Move queued messages back to the composer",
6887
+ children: [
6888
+ /* @__PURE__ */ jsx15(PencilIcon, { className: "size-3", "aria-hidden": "true" }),
6889
+ "Edit"
6890
+ ]
6891
+ }
6892
+ )
6893
+ ] })
6518
6894
  ]
6519
6895
  }
6520
6896
  );
6521
6897
  }
6522
6898
 
6523
6899
  // src/front/chat/components/PiTimelineMessage.tsx
6524
- import { useCallback as useCallback13, useEffect as useEffect13, useMemo as useMemo12, useRef as useRef9, useState as useState14 } from "react";
6525
- import { CheckIcon as CheckIcon2, CopyIcon as CopyIcon3 } from "lucide-react";
6526
- import { Button as Button10 } from "@hachej/boring-ui-kit";
6900
+ import { useCallback as useCallback14, useEffect as useEffect15, useMemo as useMemo13, useRef as useRef10, useState as useState16 } from "react";
6901
+ import { CheckIcon as CheckIcon3, CopyIcon as CopyIcon4 } from "lucide-react";
6902
+ import { Button as Button11 } from "@hachej/boring-ui-kit";
6527
6903
 
6528
6904
  // src/front/primitives/attachments.tsx
6529
- import { Button as Button8, HoverCard, HoverCardContent, HoverCardTrigger } from "@hachej/boring-ui-kit";
6905
+ import { Button as Button9, HoverCard, HoverCardContent, HoverCardTrigger } from "@hachej/boring-ui-kit";
6530
6906
  import {
6531
6907
  FileTextIcon as FileTextIcon2,
6532
6908
  GlobeIcon,
@@ -6724,7 +7100,7 @@ var AttachmentRemove = ({
6724
7100
  return null;
6725
7101
  }
6726
7102
  return /* @__PURE__ */ jsxs15(
6727
- Button8,
7103
+ Button9,
6728
7104
  {
6729
7105
  "aria-label": label,
6730
7106
  className: cn(
@@ -6756,7 +7132,7 @@ var AttachmentRemove = ({
6756
7132
  };
6757
7133
 
6758
7134
  // src/front/primitives/message.tsx
6759
- import { Button as Button9, ButtonGroup, ButtonGroupText } from "@hachej/boring-ui-kit";
7135
+ import { Button as Button10, ButtonGroup, ButtonGroupText } from "@hachej/boring-ui-kit";
6760
7136
  import {
6761
7137
  Tooltip,
6762
7138
  TooltipContent,
@@ -6768,23 +7144,228 @@ import {
6768
7144
  var INLINE_CODE_CLASS_NAME = "rounded-[0.3em] bg-muted/55 px-[0.32em] py-[0.08em] font-mono text-[0.9em] font-medium text-foreground/90";
6769
7145
 
6770
7146
  // src/front/primitives/message.tsx
6771
- import { cjk } from "@streamdown/cjk";
6772
- import { code } from "@streamdown/code";
6773
- import { math } from "@streamdown/math";
6774
- import { mermaid } from "@streamdown/mermaid";
6775
7147
  import { ChevronLeftIcon as ChevronLeftIcon2, ChevronRightIcon } from "lucide-react";
6776
7148
  import {
6777
7149
  createContext as createContext4,
6778
7150
  memo as memo3,
6779
- useCallback as useCallback10,
7151
+ useCallback as useCallback11,
6780
7152
  useContext as useContext4,
6781
- useEffect as useEffect10,
6782
- useMemo as useMemo7,
6783
- useState as useState11
7153
+ useEffect as useEffect12,
7154
+ useMemo as useMemo8,
7155
+ useState as useState13
6784
7156
  } from "react";
6785
- import { Streamdown } from "streamdown";
7157
+ import { Streamdown as Streamdown2 } from "streamdown";
7158
+
7159
+ // src/front/primitives/useStreamdownPlugins.ts
7160
+ import { useEffect as useEffect10, useMemo as useMemo7, useState as useState11 } from "react";
7161
+ var streamdownPluginPromises = {};
7162
+ var streamdownPluginLoaders = {
7163
+ cjk: () => import("@streamdown/cjk").then((module) => module.cjk),
7164
+ code: () => import("@streamdown/code").then((module) => module.code),
7165
+ math: () => import("@streamdown/math").then((module) => module.math),
7166
+ mermaid: () => import("@streamdown/mermaid").then((module) => module.mermaid)
7167
+ };
7168
+ function loadStreamdownPlugin(name) {
7169
+ const existing = streamdownPluginPromises[name];
7170
+ if (existing) return existing;
7171
+ const promise = streamdownPluginLoaders[name]().catch((error) => {
7172
+ delete streamdownPluginPromises[name];
7173
+ throw error;
7174
+ });
7175
+ streamdownPluginPromises[name] = promise;
7176
+ return promise;
7177
+ }
7178
+ var CJK_SCRIPT = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/u;
7179
+ var INLINE_CODE = /(^|[^`])`[^`\n]+`(?!`)/;
7180
+ var INLINE_MATH = /(^|[^\\$])\$(?!\s|\$)(?:\\.|[^\n$])+?(?<!\\)\$(?![\d$])/;
7181
+ var BLOCK_MATH = /(^|\n)\s*\$\$\s*\n?[\s\S]+?\n?\s*\$\$(?=\s*(?:\n|$))/;
7182
+ function hasClosedFence(source, language) {
7183
+ const lines = source.split("\n");
7184
+ for (let index = 0; index < lines.length; index += 1) {
7185
+ const opening = lines[index]?.match(/^ {0,3}(`{3,}|~{3,})[ \t]*([^ \t`~]+)?[^\n]*$/);
7186
+ if (!opening) continue;
7187
+ if (language && opening[2]?.toLowerCase() !== language) continue;
7188
+ const marker = opening[1][0];
7189
+ const minimumLength = opening[1].length;
7190
+ for (let closing = index + 1; closing < lines.length; closing += 1) {
7191
+ const candidate = lines[closing]?.match(/^ {0,3}(`+|~+)[ \t]*$/)?.[1];
7192
+ if (candidate?.[0] === marker && candidate.length >= minimumLength) return true;
7193
+ }
7194
+ }
7195
+ return false;
7196
+ }
7197
+ function streamdownPluginNamesForSource(source, options = {}) {
7198
+ if (typeof source !== "string" || source.length === 0) return [];
7199
+ const names = [];
7200
+ if (CJK_SCRIPT.test(source)) names.push("cjk");
7201
+ if (options.code !== false && (hasClosedFence(source) || INLINE_CODE.test(source))) names.push("code");
7202
+ if (INLINE_MATH.test(source) || BLOCK_MATH.test(source)) names.push("math");
7203
+ if (hasClosedFence(source, "mermaid")) names.push("mermaid");
7204
+ return names;
7205
+ }
7206
+ function useStreamdownPlugins(source, options = {}) {
7207
+ const includeCode = options.code !== false;
7208
+ const names = useMemo7(() => streamdownPluginNamesForSource(source, { code: includeCode }), [includeCode, source]);
7209
+ const [plugins, setPlugins] = useState11({});
7210
+ useEffect10(() => {
7211
+ let cancelled = false;
7212
+ for (const name of names) {
7213
+ void loadStreamdownPlugin(name).then((plugin) => {
7214
+ if (cancelled) return;
7215
+ setPlugins((current) => current[name] ? current : { ...current, [name]: plugin });
7216
+ }).catch((error) => {
7217
+ console.error(`Failed to load the ${name} markdown renderer`, error);
7218
+ });
7219
+ }
7220
+ return () => {
7221
+ cancelled = true;
7222
+ };
7223
+ }, [names]);
7224
+ const selectedPlugins = useMemo7(() => {
7225
+ const selected = {};
7226
+ for (const name of names) {
7227
+ if (plugins[name]) selected[name] = plugins[name];
7228
+ }
7229
+ return selected;
7230
+ }, [names, plugins]);
7231
+ return Object.keys(selectedPlugins).length > 0 ? selectedPlugins : void 0;
7232
+ }
7233
+
7234
+ // src/front/primitives/markdownLink.tsx
7235
+ import { CheckIcon as CheckIcon2, CopyIcon as CopyIcon3, ExternalLinkIcon as ExternalLinkIcon3 } from "lucide-react";
7236
+ import {
7237
+ useCallback as useCallback10,
7238
+ useEffect as useEffect11,
7239
+ useRef as useRef8,
7240
+ useState as useState12
7241
+ } from "react";
7242
+ import { defaultRehypePlugins } from "streamdown";
6786
7243
  import { jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
6787
- var Message = ({ className, from, ...props }) => /* @__PURE__ */ jsx17(
7244
+ var LINK_ACTIONS_TAG = "boring-link-actions";
7245
+ var INCOMPLETE_LINK_HREF = "streamdown:incomplete-link";
7246
+ function decorateLinks(node) {
7247
+ if (!node.children) return;
7248
+ if (node.properties?.["data-boring-agent-part"] === "chat-url-group") return;
7249
+ for (const child of node.children) decorateLinks(child);
7250
+ node.children = node.children.flatMap((child) => {
7251
+ if (child.type !== "element" || child.tagName !== "a") return [child];
7252
+ const href = child.properties?.href;
7253
+ if (typeof href !== "string" || href === INCOMPLETE_LINK_HREF) return [child];
7254
+ return [{
7255
+ type: "element",
7256
+ tagName: "span",
7257
+ properties: {
7258
+ className: ["group/markdown-link"],
7259
+ "data-boring-agent-part": "chat-url-group"
7260
+ },
7261
+ children: [
7262
+ child,
7263
+ {
7264
+ type: "element",
7265
+ tagName: LINK_ACTIONS_TAG,
7266
+ properties: { href },
7267
+ children: []
7268
+ }
7269
+ ]
7270
+ }];
7271
+ });
7272
+ }
7273
+ var rehypeMarkdownLinkActions = () => (tree) => {
7274
+ decorateLinks(tree);
7275
+ };
7276
+ function markdownLinkRehypePlugins(configured, allowedTags) {
7277
+ if (configured) return [...configured, rehypeMarkdownLinkActions];
7278
+ const sanitize = defaultRehypePlugins.sanitize;
7279
+ if (!allowedTags || !Array.isArray(sanitize) || typeof sanitize[1] !== "object" || sanitize[1] === null) {
7280
+ return [...Object.values(defaultRehypePlugins), rehypeMarkdownLinkActions];
7281
+ }
7282
+ const schema = sanitize[1];
7283
+ const augmentedSchema = {
7284
+ ...schema,
7285
+ tagNames: [...schema.tagNames ?? [], ...Object.keys(allowedTags)],
7286
+ attributes: { ...schema.attributes, ...allowedTags }
7287
+ };
7288
+ const augmentedSanitize = [sanitize[0], augmentedSchema];
7289
+ const augmentedDefaults = Object.entries(defaultRehypePlugins).map(
7290
+ ([name, plugin]) => name === "sanitize" ? augmentedSanitize : plugin
7291
+ );
7292
+ return [...augmentedDefaults, rehypeMarkdownLinkActions];
7293
+ }
7294
+ var MarkdownLinkActions = ({ href }) => {
7295
+ const [copied, setCopied] = useState12(false);
7296
+ const copiedTimerRef = useRef8(void 0);
7297
+ useEffect11(() => () => clearTimeout(copiedTimerRef.current), []);
7298
+ const markCopied = useCallback10(() => {
7299
+ setCopied(true);
7300
+ clearTimeout(copiedTimerRef.current);
7301
+ copiedTimerRef.current = setTimeout(() => setCopied(false), 1500);
7302
+ }, []);
7303
+ const handleCopy = useCallback10(
7304
+ async (event) => {
7305
+ event.preventDefault();
7306
+ event.stopPropagation();
7307
+ if (href && await copyTextToClipboard(href)) markCopied();
7308
+ },
7309
+ [href, markCopied]
7310
+ );
7311
+ const handleOpen = useCallback10((event) => {
7312
+ event.preventDefault();
7313
+ event.stopPropagation();
7314
+ event.currentTarget.closest('[data-boring-agent-part="chat-url-group"]')?.querySelector('[data-streamdown="link"]')?.click();
7315
+ }, []);
7316
+ return /* @__PURE__ */ jsxs16(
7317
+ "span",
7318
+ {
7319
+ className: cn(
7320
+ "pointer-events-none inline-flex shrink-0 items-center gap-px opacity-0 transition-opacity duration-150",
7321
+ "group-hover/markdown-link:pointer-events-auto group-hover/markdown-link:opacity-100",
7322
+ "group-focus-within/markdown-link:pointer-events-auto group-focus-within/markdown-link:opacity-100"
7323
+ ),
7324
+ "data-boring-agent-part": "chat-url-actions",
7325
+ children: [
7326
+ /* @__PURE__ */ jsx17(
7327
+ "button",
7328
+ {
7329
+ type: "button",
7330
+ onClick: handleCopy,
7331
+ "aria-label": "Copy URL",
7332
+ title: copied ? "Copied" : "Copy URL",
7333
+ className: cn(
7334
+ "inline-flex size-4 items-center justify-center rounded-[2px]",
7335
+ "text-muted-foreground/70 transition-colors duration-150",
7336
+ "hover:bg-foreground/[0.06] hover:text-muted-foreground",
7337
+ "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[color:var(--accent)]/40"
7338
+ ),
7339
+ children: copied ? /* @__PURE__ */ jsx17(CheckIcon2, { className: "size-3", "aria-hidden": "true" }) : /* @__PURE__ */ jsx17(CopyIcon3, { className: "size-3", "aria-hidden": "true" })
7340
+ }
7341
+ ),
7342
+ /* @__PURE__ */ jsx17(
7343
+ "button",
7344
+ {
7345
+ type: "button",
7346
+ onClick: handleOpen,
7347
+ "aria-label": "Open URL in new tab",
7348
+ title: "Open URL in new tab",
7349
+ className: cn(
7350
+ "inline-flex size-4 items-center justify-center rounded-[2px]",
7351
+ "text-muted-foreground/70 transition-colors duration-150",
7352
+ "hover:bg-foreground/[0.06] hover:text-muted-foreground",
7353
+ "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[color:var(--accent)]/40"
7354
+ ),
7355
+ children: /* @__PURE__ */ jsx17(ExternalLinkIcon3, { className: "size-3", "aria-hidden": "true" })
7356
+ }
7357
+ )
7358
+ ]
7359
+ }
7360
+ );
7361
+ };
7362
+ var markdownLinkComponents = {
7363
+ [LINK_ACTIONS_TAG]: MarkdownLinkActions
7364
+ };
7365
+
7366
+ // src/front/primitives/message.tsx
7367
+ import { jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
7368
+ var Message = ({ className, from, ...props }) => /* @__PURE__ */ jsx18(
6788
7369
  "div",
6789
7370
  {
6790
7371
  "data-boring-agent-message-role": from,
@@ -6800,7 +7381,7 @@ var MessageContent = ({
6800
7381
  children,
6801
7382
  className,
6802
7383
  ...props
6803
- }) => /* @__PURE__ */ jsx17(
7384
+ }) => /* @__PURE__ */ jsx18(
6804
7385
  "div",
6805
7386
  {
6806
7387
  className: cn(
@@ -6817,7 +7398,7 @@ var MessageActions = ({
6817
7398
  className,
6818
7399
  children,
6819
7400
  ...props
6820
- }) => /* @__PURE__ */ jsx17("div", { className: cn("flex items-center gap-1", className), ...props, children });
7401
+ }) => /* @__PURE__ */ jsx18("div", { className: cn("flex items-center gap-1", className), ...props, children });
6821
7402
  var MessageAction = ({
6822
7403
  tooltip,
6823
7404
  children,
@@ -6826,14 +7407,14 @@ var MessageAction = ({
6826
7407
  size = "icon-sm",
6827
7408
  ...props
6828
7409
  }) => {
6829
- const button = /* @__PURE__ */ jsxs16(Button9, { size, type: "button", variant, ...props, children: [
7410
+ const button = /* @__PURE__ */ jsxs17(Button10, { size, type: "button", variant, ...props, children: [
6830
7411
  children,
6831
- /* @__PURE__ */ jsx17("span", { className: "sr-only", children: label || tooltip })
7412
+ /* @__PURE__ */ jsx18("span", { className: "sr-only", children: label || tooltip })
6832
7413
  ] });
6833
7414
  if (tooltip) {
6834
- return /* @__PURE__ */ jsx17(TooltipProvider, { children: /* @__PURE__ */ jsxs16(Tooltip, { children: [
6835
- /* @__PURE__ */ jsx17(TooltipTrigger, { asChild: true, children: button }),
6836
- /* @__PURE__ */ jsx17(TooltipContent, { children: /* @__PURE__ */ jsx17("p", { children: tooltip }) })
7415
+ return /* @__PURE__ */ jsx18(TooltipProvider, { children: /* @__PURE__ */ jsxs17(Tooltip, { children: [
7416
+ /* @__PURE__ */ jsx18(TooltipTrigger, { asChild: true, children: button }),
7417
+ /* @__PURE__ */ jsx18(TooltipContent, { children: /* @__PURE__ */ jsx18("p", { children: tooltip }) })
6837
7418
  ] }) });
6838
7419
  }
6839
7420
  return button;
@@ -6841,7 +7422,6 @@ var MessageAction = ({
6841
7422
  var MessageBranchContext = createContext4(
6842
7423
  null
6843
7424
  );
6844
- var streamdownPlugins = { cjk, code, math, mermaid };
6845
7425
  var DEFAULT_SHIKI_THEME = ["github-light", "github-dark-dimmed"];
6846
7426
  function extractCodeBlockLanguage(children) {
6847
7427
  if (!children) return void 0;
@@ -6872,11 +7452,11 @@ function extractCodeBlockText(children) {
6872
7452
  return walk(children);
6873
7453
  }
6874
7454
  var MarkdownPre = ({ children, filename }) => {
6875
- const code3 = extractCodeBlockText(children).replace(/\n$/, "");
7455
+ const code = extractCodeBlockText(children).replace(/\n$/, "");
6876
7456
  const language = extractCodeBlockLanguage(children);
6877
- return /* @__PURE__ */ jsx17(CodeBlock2, { code: code3, language: language ?? "text", className: "my-3", children: /* @__PURE__ */ jsxs16(CodeBlockHeader, { children: [
6878
- /* @__PURE__ */ jsx17(CodeBlockTitle, { children: /* @__PURE__ */ jsx17(CodeBlockFilename, { children: filename ?? language ?? "text" }) }),
6879
- /* @__PURE__ */ jsx17(CodeBlockCopyButton, {})
7457
+ return /* @__PURE__ */ jsx18(CodeBlock2, { code, language: language ?? "text", className: "my-3", children: /* @__PURE__ */ jsxs17(CodeBlockHeader, { children: [
7458
+ /* @__PURE__ */ jsx18(CodeBlockTitle, { children: /* @__PURE__ */ jsx18(CodeBlockFilename, { children: filename ?? language ?? "text" }) }),
7459
+ /* @__PURE__ */ jsx18(CodeBlockCopyButton, {})
6880
7460
  ] }) });
6881
7461
  };
6882
7462
  var MarkdownCode = ({
@@ -6886,9 +7466,9 @@ var MarkdownCode = ({
6886
7466
  ...props
6887
7467
  }) => {
6888
7468
  if (inline === false) {
6889
- return /* @__PURE__ */ jsx17("code", { className, ...props, children });
7469
+ return /* @__PURE__ */ jsx18("code", { className, ...props, children });
6890
7470
  }
6891
- return /* @__PURE__ */ jsx17(
7471
+ return /* @__PURE__ */ jsx18(
6892
7472
  "code",
6893
7473
  {
6894
7474
  className: cn(
@@ -6905,23 +7485,33 @@ var markdownComponents = {
6905
7485
  code: MarkdownCode
6906
7486
  };
6907
7487
  var MessageResponse = memo3(
6908
- ({ className, shikiTheme, components, codeFilename, ...props }) => /* @__PURE__ */ jsx17(
6909
- Streamdown,
6910
- {
6911
- className: cn(
6912
- "size-full [&>*:first-child]:mt-0 [&>*:last-child]:mb-0",
6913
- className
6914
- ),
6915
- plugins: streamdownPlugins,
6916
- shikiTheme: shikiTheme ?? DEFAULT_SHIKI_THEME,
6917
- components: {
6918
- ...markdownComponents,
6919
- ...components ?? {},
6920
- ...codeFilename ? { pre: (props2) => /* @__PURE__ */ jsx17(MarkdownPre, { ...props2, filename: codeFilename }) } : {}
6921
- },
6922
- ...props
6923
- }
6924
- ),
7488
+ ({ className, shikiTheme, components, codeFilename, rehypePlugins, allowedTags, ...props }) => {
7489
+ const streamdownPlugins = useStreamdownPlugins(props.children, { code: false });
7490
+ const linkRehypePlugins = useMemo8(
7491
+ () => markdownLinkRehypePlugins(rehypePlugins, allowedTags),
7492
+ [allowedTags, rehypePlugins]
7493
+ );
7494
+ return /* @__PURE__ */ jsx18(
7495
+ Streamdown2,
7496
+ {
7497
+ className: cn(
7498
+ "size-full [&>*:first-child]:mt-0 [&>*:last-child]:mb-0",
7499
+ className
7500
+ ),
7501
+ plugins: streamdownPlugins,
7502
+ rehypePlugins: linkRehypePlugins,
7503
+ allowedTags,
7504
+ shikiTheme: shikiTheme ?? DEFAULT_SHIKI_THEME,
7505
+ components: {
7506
+ ...markdownComponents,
7507
+ ...components ?? {},
7508
+ ...markdownLinkComponents,
7509
+ ...codeFilename ? { pre: (props2) => /* @__PURE__ */ jsx18(MarkdownPre, { ...props2, filename: codeFilename }) } : {}
7510
+ },
7511
+ ...props
7512
+ }
7513
+ );
7514
+ },
6925
7515
  (prevProps, nextProps) => prevProps.children === nextProps.children && nextProps.isAnimating === prevProps.isAnimating && nextProps.components === prevProps.components && prevProps.codeFilename === nextProps.codeFilename
6926
7516
  );
6927
7517
  MessageResponse.displayName = "MessageResponse";
@@ -6933,27 +7523,23 @@ import {
6933
7523
  CollapsibleContent as CollapsibleContent2,
6934
7524
  CollapsibleTrigger as CollapsibleTrigger3
6935
7525
  } from "@hachej/boring-ui-kit";
6936
- import { cjk as cjk2 } from "@streamdown/cjk";
6937
- import { code as code2 } from "@streamdown/code";
6938
- import { math as math2 } from "@streamdown/math";
6939
- import { mermaid as mermaid2 } from "@streamdown/mermaid";
6940
7526
  import { BrainIcon, ChevronDownIcon as ChevronDownIcon3 } from "lucide-react";
6941
7527
  import {
6942
7528
  createContext as createContext5,
6943
7529
  memo as memo5,
6944
- useCallback as useCallback11,
7530
+ useCallback as useCallback12,
6945
7531
  useContext as useContext5,
6946
- useEffect as useEffect11,
6947
- useMemo as useMemo9,
6948
- useRef as useRef8,
6949
- useState as useState12
7532
+ useEffect as useEffect13,
7533
+ useMemo as useMemo10,
7534
+ useRef as useRef9,
7535
+ useState as useState14
6950
7536
  } from "react";
6951
- import { Streamdown as Streamdown2 } from "streamdown";
7537
+ import { Streamdown as Streamdown3 } from "streamdown";
6952
7538
 
6953
7539
  // src/front/primitives/shimmer.tsx
6954
7540
  import { motion } from "motion/react";
6955
- import { memo as memo4, useMemo as useMemo8 } from "react";
6956
- import { jsx as jsx18 } from "react/jsx-runtime";
7541
+ import { memo as memo4, useMemo as useMemo9 } from "react";
7542
+ import { jsx as jsx19 } from "react/jsx-runtime";
6957
7543
  var motionComponentCache = /* @__PURE__ */ new Map();
6958
7544
  var getMotionComponent = (element) => {
6959
7545
  let component = motionComponentCache.get(element);
@@ -6973,11 +7559,11 @@ var ShimmerComponent = ({
6973
7559
  const MotionComponent = getMotionComponent(
6974
7560
  Component
6975
7561
  );
6976
- const dynamicSpread = useMemo8(
7562
+ const dynamicSpread = useMemo9(
6977
7563
  () => (children?.length ?? 0) * spread,
6978
7564
  [children, spread]
6979
7565
  );
6980
- return /* @__PURE__ */ jsx18(
7566
+ return /* @__PURE__ */ jsx19(
6981
7567
  MotionComponent,
6982
7568
  {
6983
7569
  animate: { backgroundPosition: "0% center" },
@@ -7003,7 +7589,7 @@ var ShimmerComponent = ({
7003
7589
  var Shimmer = memo4(ShimmerComponent);
7004
7590
 
7005
7591
  // src/front/primitives/reasoning.tsx
7006
- import { Fragment as Fragment8, jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
7592
+ import { Fragment as Fragment8, jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
7007
7593
  var ReasoningContext = createContext5(null);
7008
7594
  var useReasoning = () => {
7009
7595
  const context = useContext5(ReasoningContext);
@@ -7037,10 +7623,10 @@ var Reasoning = memo5(
7037
7623
  defaultProp: void 0,
7038
7624
  prop: durationProp
7039
7625
  });
7040
- const hasEverStreamedRef = useRef8(isStreaming);
7041
- const [hasAutoClosed, setHasAutoClosed] = useState12(false);
7042
- const startTimeRef = useRef8(null);
7043
- useEffect11(() => {
7626
+ const hasEverStreamedRef = useRef9(isStreaming);
7627
+ const [hasAutoClosed, setHasAutoClosed] = useState14(false);
7628
+ const startTimeRef = useRef9(null);
7629
+ useEffect13(() => {
7044
7630
  if (isStreaming) {
7045
7631
  hasEverStreamedRef.current = true;
7046
7632
  if (startTimeRef.current === null) {
@@ -7051,12 +7637,12 @@ var Reasoning = memo5(
7051
7637
  startTimeRef.current = null;
7052
7638
  }
7053
7639
  }, [isStreaming, setDuration]);
7054
- useEffect11(() => {
7640
+ useEffect13(() => {
7055
7641
  if (isStreaming && !isOpen && !isExplicitlyClosed) {
7056
7642
  setIsOpen(true);
7057
7643
  }
7058
7644
  }, [isStreaming, isOpen, setIsOpen, isExplicitlyClosed]);
7059
- useEffect11(() => {
7645
+ useEffect13(() => {
7060
7646
  if (hasEverStreamedRef.current && autoClose && !isStreaming && isOpen && !hasAutoClosed) {
7061
7647
  const timer = setTimeout(() => {
7062
7648
  setIsOpen(false);
@@ -7065,17 +7651,17 @@ var Reasoning = memo5(
7065
7651
  return () => clearTimeout(timer);
7066
7652
  }
7067
7653
  }, [isStreaming, isOpen, setIsOpen, hasAutoClosed, autoClose]);
7068
- const handleOpenChange = useCallback11(
7654
+ const handleOpenChange = useCallback12(
7069
7655
  (newOpen) => {
7070
7656
  setIsOpen(newOpen);
7071
7657
  },
7072
7658
  [setIsOpen]
7073
7659
  );
7074
- const contextValue = useMemo9(
7660
+ const contextValue = useMemo10(
7075
7661
  () => ({ duration, isOpen, isStreaming, setIsOpen }),
7076
7662
  [duration, isOpen, isStreaming, setIsOpen]
7077
7663
  );
7078
- return /* @__PURE__ */ jsx19(ReasoningContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx19(
7664
+ return /* @__PURE__ */ jsx20(ReasoningContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx20(
7079
7665
  Collapsible2,
7080
7666
  {
7081
7667
  className: cn("not-prose mb-2", className),
@@ -7089,12 +7675,12 @@ var Reasoning = memo5(
7089
7675
  );
7090
7676
  var defaultGetThinkingMessage = (isStreaming, duration) => {
7091
7677
  if (isStreaming || duration === 0) {
7092
- return /* @__PURE__ */ jsx19(Shimmer, { duration: 1, children: "thinking" });
7678
+ return /* @__PURE__ */ jsx20(Shimmer, { duration: 1, children: "thinking" });
7093
7679
  }
7094
7680
  if (duration === void 0) {
7095
- return /* @__PURE__ */ jsx19("span", { children: "thoughts" });
7681
+ return /* @__PURE__ */ jsx20("span", { children: "thoughts" });
7096
7682
  }
7097
- return /* @__PURE__ */ jsxs17("span", { children: [
7683
+ return /* @__PURE__ */ jsxs18("span", { children: [
7098
7684
  "thoughts \xB7 ",
7099
7685
  duration,
7100
7686
  "s"
@@ -7108,7 +7694,7 @@ var ReasoningTrigger = memo5(
7108
7694
  ...props
7109
7695
  }) => {
7110
7696
  const { isStreaming, isOpen, duration } = useReasoning();
7111
- return /* @__PURE__ */ jsx19(
7697
+ return /* @__PURE__ */ jsx20(
7112
7698
  CollapsibleTrigger3,
7113
7699
  {
7114
7700
  className: cn(
@@ -7116,10 +7702,10 @@ var ReasoningTrigger = memo5(
7116
7702
  className
7117
7703
  ),
7118
7704
  ...props,
7119
- children: children ?? /* @__PURE__ */ jsxs17(Fragment8, { children: [
7120
- /* @__PURE__ */ jsx19(BrainIcon, { className: "size-3" }),
7705
+ children: children ?? /* @__PURE__ */ jsxs18(Fragment8, { children: [
7706
+ /* @__PURE__ */ jsx20(BrainIcon, { className: "size-3" }),
7121
7707
  getThinkingMessage(isStreaming, duration),
7122
- /* @__PURE__ */ jsx19(
7708
+ /* @__PURE__ */ jsx20(
7123
7709
  ChevronDownIcon3,
7124
7710
  {
7125
7711
  className: cn(
@@ -7133,20 +7719,22 @@ var ReasoningTrigger = memo5(
7133
7719
  );
7134
7720
  }
7135
7721
  );
7136
- var streamdownPlugins2 = { cjk: cjk2, code: code2, math: math2, mermaid: mermaid2 };
7137
7722
  var ReasoningContent = memo5(
7138
- ({ className, children, ...props }) => /* @__PURE__ */ jsx19(
7139
- CollapsibleContent2,
7140
- {
7141
- className: cn(
7142
- "mt-2 border-l border-border/40 pl-3 text-xs",
7143
- "data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 text-muted-foreground/75 outline-none data-[state=closed]:animate-out data-[state=open]:animate-in",
7144
- className
7145
- ),
7146
- ...props,
7147
- children: /* @__PURE__ */ jsx19(Streamdown2, { plugins: streamdownPlugins2, children })
7148
- }
7149
- )
7723
+ ({ className, children, ...props }) => {
7724
+ const streamdownPlugins = useStreamdownPlugins(children);
7725
+ return /* @__PURE__ */ jsx20(
7726
+ CollapsibleContent2,
7727
+ {
7728
+ className: cn(
7729
+ "mt-2 border-l border-border/40 pl-3 text-xs",
7730
+ "data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 text-muted-foreground/75 outline-none data-[state=closed]:animate-out data-[state=open]:animate-in",
7731
+ className
7732
+ ),
7733
+ ...props,
7734
+ children: /* @__PURE__ */ jsx20(Streamdown3, { plugins: streamdownPlugins, children })
7735
+ }
7736
+ );
7737
+ }
7150
7738
  );
7151
7739
  Reasoning.displayName = "Reasoning";
7152
7740
  ReasoningTrigger.displayName = "ReasoningTrigger";
@@ -7154,7 +7742,7 @@ ReasoningContent.displayName = "ReasoningContent";
7154
7742
 
7155
7743
  // src/front/primitives/tool-call-group.tsx
7156
7744
  import { ChevronDownIcon as ChevronDownIcon4 } from "lucide-react";
7157
- import { memo as memo6, useCallback as useCallback12, useEffect as useEffect12, useMemo as useMemo10, useState as useState13 } from "react";
7745
+ import { memo as memo6, useCallback as useCallback13, useEffect as useEffect14, useMemo as useMemo11, useState as useState15 } from "react";
7158
7746
  import { Collapsible as Collapsible3, CollapsibleContent as CollapsibleContent3, CollapsibleTrigger as CollapsibleTrigger4 } from "@hachej/boring-ui-kit";
7159
7747
 
7160
7748
  // src/front/primitives/tool-call-group-state.ts
@@ -7193,7 +7781,7 @@ var TOOL_GROUP_VISUAL_PRESENTATION = {
7193
7781
  };
7194
7782
 
7195
7783
  // src/front/primitives/tool-call-group.tsx
7196
- import { jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
7784
+ import { jsx as jsx21, jsxs as jsxs19 } from "react/jsx-runtime";
7197
7785
  function isInputRunningState(state) {
7198
7786
  return state === "input-streaming" || state === "input-available";
7199
7787
  }
@@ -7243,7 +7831,7 @@ function resolveVisualState({
7243
7831
  return "settled";
7244
7832
  }
7245
7833
  var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
7246
- const toolParts = useMemo10(() => tools.map(({ part, key }) => {
7834
+ const toolParts = useMemo11(() => tools.map(({ part, key }) => {
7247
7835
  const toolPart = toToolPart(part);
7248
7836
  return toolPart ? { part: toolPart, key } : null;
7249
7837
  }).filter((entry) => Boolean(entry)), [tools]);
@@ -7252,8 +7840,8 @@ var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
7252
7840
  const hasAborted = toolParts.some(({ part }) => part.state === "aborted") && !workspaceNotReady;
7253
7841
  const hasRunningTool = toolParts.some(({ part }) => isInputRunningState(part.state) && !getWorkspaceNotReadyStatus(part.output));
7254
7842
  const approvalNeeded = toolParts.some(({ part }) => isApprovalRequestedState(part.state));
7255
- const [isOpen, setIsOpen] = useState13(false);
7256
- const handleOpenChange = useCallback12((open) => setIsOpen(open), []);
7843
+ const [isOpen, setIsOpen] = useState15(false);
7844
+ const handleOpenChange = useCallback13((open) => setIsOpen(open), []);
7257
7845
  const visualState = resolveVisualState({
7258
7846
  hasRunningTool,
7259
7847
  approvalNeeded,
@@ -7262,10 +7850,10 @@ var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
7262
7850
  hasAborted
7263
7851
  });
7264
7852
  const isRunning = visualState === RUNNING_TOOL_GROUP_VISUAL_STATE;
7265
- const [elapsedSeconds, setElapsedSeconds] = useState13(0);
7266
- const title = useMemo10(() => buildTitle(toolParts.map(({ part }) => part), visualState), [toolParts, visualState]);
7853
+ const [elapsedSeconds, setElapsedSeconds] = useState15(0);
7854
+ const title = useMemo11(() => buildTitle(toolParts.map(({ part }) => part), visualState), [toolParts, visualState]);
7267
7855
  const elapsedLabel = isRunning ? `Running ${formatElapsed(elapsedSeconds)}` : null;
7268
- useEffect12(() => {
7856
+ useEffect14(() => {
7269
7857
  if (!isRunning) {
7270
7858
  setElapsedSeconds(0);
7271
7859
  return;
@@ -7282,7 +7870,7 @@ var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
7282
7870
  const { renderer } = resolveToolRendererForPart(part, mergedToolRenderers);
7283
7871
  return renderer.presentation === "inline" ? [key] : [];
7284
7872
  }));
7285
- return /* @__PURE__ */ jsxs18(
7873
+ return /* @__PURE__ */ jsxs19(
7286
7874
  Collapsible3,
7287
7875
  {
7288
7876
  open: isOpen,
@@ -7293,9 +7881,9 @@ var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
7293
7881
  toolParts.map(({ part, key }) => {
7294
7882
  if (!inlineToolKeys.has(key)) return null;
7295
7883
  const { renderer, part: resolvedPart, resolution } = resolveToolRendererForPart(part, mergedToolRenderers);
7296
- return /* @__PURE__ */ jsx20("div", { "data-boring-agent-part": "inline-tool-renderer", "data-tool-call-id": resolvedPart.toolCallId, "data-tool-renderer-key": resolution.key, children: renderer(resolvedPart) }, key);
7884
+ return /* @__PURE__ */ jsx21("div", { "data-boring-agent-part": "inline-tool-renderer", "data-tool-call-id": resolvedPart.toolCallId, "data-tool-renderer-key": resolution.key, children: renderer(resolvedPart) }, key);
7297
7885
  }),
7298
- /* @__PURE__ */ jsxs18(
7886
+ /* @__PURE__ */ jsxs19(
7299
7887
  CollapsibleTrigger4,
7300
7888
  {
7301
7889
  "aria-label": `Tool calls: ${title}`,
@@ -7307,7 +7895,7 @@ var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
7307
7895
  TOOL_GROUP_VISUAL_PRESENTATION[visualState].triggerClassName
7308
7896
  ),
7309
7897
  children: [
7310
- /* @__PURE__ */ jsx20("span", { className: "flex size-4 shrink-0 items-center justify-center", "aria-hidden": "true", children: /* @__PURE__ */ jsx20(
7898
+ /* @__PURE__ */ jsx21("span", { className: "flex size-4 shrink-0 items-center justify-center", "aria-hidden": "true", children: /* @__PURE__ */ jsx21(
7311
7899
  "span",
7312
7900
  {
7313
7901
  "data-boring-agent-part": "tool-group-state-dot",
@@ -7317,10 +7905,10 @@ var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
7317
7905
  )
7318
7906
  }
7319
7907
  ) }),
7320
- /* @__PURE__ */ jsx20("span", { "data-boring-agent-part": "tool-group-title", children: workspaceNotReady ? workspaceNotReady.message : title }),
7321
- elapsedLabel ? /* @__PURE__ */ jsx20("span", { className: "ml-1 shrink-0 tabular-nums text-[12px] text-muted-foreground/70", children: elapsedLabel }) : null,
7322
- /* @__PURE__ */ jsx20("span", { className: "ml-1 shrink-0 tabular-nums text-[12px] text-muted-foreground/70", children: toolParts.length }),
7323
- /* @__PURE__ */ jsx20(
7908
+ /* @__PURE__ */ jsx21("span", { "data-boring-agent-part": "tool-group-title", children: workspaceNotReady ? workspaceNotReady.message : title }),
7909
+ elapsedLabel ? /* @__PURE__ */ jsx21("span", { className: "ml-1 shrink-0 tabular-nums text-[12px] text-muted-foreground/70", children: elapsedLabel }) : null,
7910
+ /* @__PURE__ */ jsx21("span", { className: "ml-1 shrink-0 tabular-nums text-[12px] text-muted-foreground/70", children: toolParts.length }),
7911
+ /* @__PURE__ */ jsx21(
7324
7912
  ChevronDownIcon4,
7325
7913
  {
7326
7914
  className: cn(
@@ -7332,19 +7920,19 @@ var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
7332
7920
  ]
7333
7921
  }
7334
7922
  ),
7335
- /* @__PURE__ */ jsx20(CollapsibleContent3, { className: "overflow-hidden data-[state=closed]:animate-[boring-collapse-close_200ms_ease] data-[state=open]:animate-[boring-collapse-open_200ms_ease]", children: /* @__PURE__ */ jsxs18(
7923
+ /* @__PURE__ */ jsx21(CollapsibleContent3, { className: "overflow-hidden data-[state=closed]:animate-[boring-collapse-close_200ms_ease] data-[state=open]:animate-[boring-collapse-open_200ms_ease]", children: /* @__PURE__ */ jsxs19(
7336
7924
  "div",
7337
7925
  {
7338
7926
  "data-boring-agent-part": "tool-group-details",
7339
7927
  className: "relative mt-1.5 w-full max-w-2xl pl-[18px]",
7340
7928
  children: [
7341
- /* @__PURE__ */ jsx20("div", { className: "absolute left-[5px] top-0 bottom-2 w-px bg-border/35" }),
7342
- /* @__PURE__ */ jsx20("div", { className: "flex min-w-0 flex-col gap-1.5 [&_[data-boring-agent-part=tool-card]]:!my-0", children: toolParts.map(({ part, key }) => {
7929
+ /* @__PURE__ */ jsx21("div", { className: "absolute left-[5px] top-0 bottom-2 w-px bg-border/35" }),
7930
+ /* @__PURE__ */ jsx21("div", { className: "flex min-w-0 flex-col gap-1.5 [&_[data-boring-agent-part=tool-card]]:!my-0", children: toolParts.map(({ part, key }) => {
7343
7931
  const { renderer, part: resolvedPart, resolution } = resolveToolRendererForPart(
7344
7932
  part,
7345
7933
  inlineToolKeys.has(key) ? {} : mergedToolRenderers
7346
7934
  );
7347
- return /* @__PURE__ */ jsx20(
7935
+ return /* @__PURE__ */ jsx21(
7348
7936
  "div",
7349
7937
  {
7350
7938
  className: "min-w-0",
@@ -7366,19 +7954,19 @@ var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
7366
7954
  ToolCallGroup.displayName = "ToolCallGroup";
7367
7955
 
7368
7956
  // src/front/chat/messageContributions.tsx
7369
- import { createContext as createContext6, createElement, useContext as useContext6, useMemo as useMemo11 } from "react";
7370
- import { jsx as jsx21 } from "react/jsx-runtime";
7957
+ import { createContext as createContext6, createElement, useContext as useContext6, useMemo as useMemo12 } from "react";
7958
+ import { jsx as jsx22 } from "react/jsx-runtime";
7371
7959
  var ChatMessageContributionContext = createContext6([]);
7372
7960
  function ChatMessageContributionProvider({
7373
7961
  contribution,
7374
7962
  children
7375
7963
  }) {
7376
7964
  const parent = useContext6(ChatMessageContributionContext);
7377
- const contributions = useMemo11(
7965
+ const contributions = useMemo12(
7378
7966
  () => [contribution, ...parent.filter((entry) => entry.id !== contribution.id)],
7379
7967
  [contribution, parent]
7380
7968
  );
7381
- return /* @__PURE__ */ jsx21(ChatMessageContributionContext.Provider, { value: contributions, children });
7969
+ return /* @__PURE__ */ jsx22(ChatMessageContributionContext.Provider, { value: contributions, children });
7382
7970
  }
7383
7971
  function useCustomChatMessage(message) {
7384
7972
  const contributions = useContext6(ChatMessageContributionContext);
@@ -7395,7 +7983,7 @@ function ChatMessageContributionBoundary({
7395
7983
 
7396
7984
  // src/front/chat/components/MessageMentions.tsx
7397
7985
  import { Children, cloneElement, isValidElement as isValidElement2 } from "react";
7398
- import { Fragment as Fragment9, jsx as jsx22 } from "react/jsx-runtime";
7986
+ import { Fragment as Fragment9, jsx as jsx23 } from "react/jsx-runtime";
7399
7987
  var TERMINAL_BOUNDARY = String.raw`(?=$|\s|[.,;!?)}\]"'”’]+(?=$|\s))`;
7400
7988
  var PREFIX_BOUNDARY = String.raw`(^|[\s(\[{"'“‘])`;
7401
7989
  function escapeRegExp2(value) {
@@ -7504,7 +8092,7 @@ function MessageMentions({
7504
8092
  }).map((segment, segmentIndex) => {
7505
8093
  if (!segment.mention) return segment.text;
7506
8094
  const actionLabel = mentionActionLabel(segment.mention);
7507
- return /* @__PURE__ */ jsx22(
8095
+ return /* @__PURE__ */ jsx23(
7508
8096
  "button",
7509
8097
  {
7510
8098
  type: "button",
@@ -7530,7 +8118,7 @@ function MessageMentions({
7530
8118
  return cloneElement(
7531
8119
  element,
7532
8120
  void 0,
7533
- /* @__PURE__ */ jsx22(
8121
+ /* @__PURE__ */ jsx23(
7534
8122
  MessageMentions,
7535
8123
  {
7536
8124
  catalog,
@@ -7548,19 +8136,19 @@ function MessageMentions({
7548
8136
  return nodes.map(decorate);
7549
8137
  }
7550
8138
  function createMessageMentionMarkdownComponents(catalog, onActivate) {
7551
- const decorate = (children) => /* @__PURE__ */ jsx22(MessageMentions, { catalog, onActivate, children });
8139
+ const decorate = (children) => /* @__PURE__ */ jsx23(MessageMentions, { catalog, onActivate, children });
7552
8140
  const paragraph = ({ node: _node, children, ...props }) => {
7553
8141
  const content = Children.toArray(children).filter((child) => child !== null && child !== "");
7554
8142
  if (content.length === 1 && isValidElement2(content[0])) {
7555
8143
  const child = content[0];
7556
- if (child.props.node?.tagName === "img") return /* @__PURE__ */ jsx22(Fragment9, { children });
7557
- if (child.props.node?.tagName === "code" && "data-block" in child.props) return /* @__PURE__ */ jsx22(Fragment9, { children });
8144
+ if (child.props.node?.tagName === "img") return /* @__PURE__ */ jsx23(Fragment9, { children });
8145
+ if (child.props.node?.tagName === "code" && "data-block" in child.props) return /* @__PURE__ */ jsx23(Fragment9, { children });
7558
8146
  }
7559
- return /* @__PURE__ */ jsx22("p", { ...props, children: decorate(children) });
8147
+ return /* @__PURE__ */ jsx23("p", { ...props, children: decorate(children) });
7560
8148
  };
7561
- const tableHeader = ({ node: _node, children, className, ...props }) => /* @__PURE__ */ jsx22("th", { ...props, "data-streamdown": "table-header-cell", className: cn("whitespace-nowrap px-4 py-2 text-left font-semibold text-sm", className), children: decorate(children) });
7562
- const tableCell = ({ node: _node, children, className, ...props }) => /* @__PURE__ */ jsx22("td", { ...props, "data-streamdown": "table-cell", className: cn("px-4 py-2 text-sm", className), children: decorate(children) });
7563
- const listItem = ({ node: _node, children, className, ...props }) => /* @__PURE__ */ jsx22("li", { ...props, "data-streamdown": "list-item", className: cn("py-1 [&>p]:inline", className), children: decorate(children) });
8149
+ const tableHeader = ({ node: _node, children, className, ...props }) => /* @__PURE__ */ jsx23("th", { ...props, "data-streamdown": "table-header-cell", className: cn("whitespace-nowrap px-4 py-2 text-left font-semibold text-sm", className), children: decorate(children) });
8150
+ const tableCell = ({ node: _node, children, className, ...props }) => /* @__PURE__ */ jsx23("td", { ...props, "data-streamdown": "table-cell", className: cn("px-4 py-2 text-sm", className), children: decorate(children) });
8151
+ const listItem = ({ node: _node, children, className, ...props }) => /* @__PURE__ */ jsx23("li", { ...props, "data-streamdown": "list-item", className: cn("py-1 [&>p]:inline", className), children: decorate(children) });
7564
8152
  const headingClasses = {
7565
8153
  h1: "text-3xl",
7566
8154
  h2: "text-2xl",
@@ -7569,8 +8157,8 @@ function createMessageMentionMarkdownComponents(catalog, onActivate) {
7569
8157
  h5: "text-base",
7570
8158
  h6: "text-sm"
7571
8159
  };
7572
- const heading = (Tag) => ({ node: _node, children, className, ...props }) => /* @__PURE__ */ jsx22(Tag, { ...props, "data-streamdown": `heading-${Tag.slice(1)}`, className: cn("mt-6 mb-2 font-semibold", headingClasses[Tag], className), children: decorate(children) });
7573
- const inlineCode = ({ node: _node, children, className, ...props }) => /* @__PURE__ */ jsx22(
8160
+ const heading = (Tag) => ({ node: _node, children, className, ...props }) => /* @__PURE__ */ jsx23(Tag, { ...props, "data-streamdown": `heading-${Tag.slice(1)}`, className: cn("mt-6 mb-2 font-semibold", headingClasses[Tag], className), children: decorate(children) });
8161
+ const inlineCode = ({ node: _node, children, className, ...props }) => /* @__PURE__ */ jsx23(
7574
8162
  "code",
7575
8163
  {
7576
8164
  ...props,
@@ -7594,7 +8182,7 @@ function createMessageMentionMarkdownComponents(catalog, onActivate) {
7594
8182
  }
7595
8183
 
7596
8184
  // src/front/chat/components/PiTimelineMessage.tsx
7597
- import { jsx as jsx23, jsxs as jsxs19 } from "react/jsx-runtime";
8185
+ import { jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
7598
8186
  var EMPTY_MENTION_CATALOG = { commands: [], skills: [], files: false };
7599
8187
  var COLLAPSIBLE_TOOL_NAMES = /* @__PURE__ */ new Set([
7600
8188
  "read",
@@ -7610,7 +8198,7 @@ function isCollapsibleTool(part) {
7610
8198
  return part.type === "tool-call" && COLLAPSIBLE_TOOL_NAMES.has(part.toolName);
7611
8199
  }
7612
8200
  function PiTimelineMessage(props) {
7613
- return /* @__PURE__ */ jsx23(ChatMessageContributionBoundary, { message: props.message, children: /* @__PURE__ */ jsx23(DefaultPiTimelineMessage, { ...props }) });
8201
+ return /* @__PURE__ */ jsx24(ChatMessageContributionBoundary, { message: props.message, children: /* @__PURE__ */ jsx24(DefaultPiTimelineMessage, { ...props }) });
7614
8202
  }
7615
8203
  function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts, toolRenderers, mentionCatalog = EMPTY_MENTION_CATALOG, onMentionActivate }) {
7616
8204
  const role = message.role;
@@ -7620,7 +8208,7 @@ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts,
7620
8208
  const finalParts = groupRenderableParts(message);
7621
8209
  const attachmentSummaryPaths = role === "user" ? attachmentPathsFromTextParts(textParts) : [];
7622
8210
  const openArtifact = useOpenArtifact();
7623
- const effectiveMentionCatalog = useMemo12(() => ({
8211
+ const effectiveMentionCatalog = useMemo13(() => ({
7624
8212
  commands: mentionCatalog.commands,
7625
8213
  skills: mentionCatalog.skills,
7626
8214
  files: mentionCatalog.files && Boolean(openArtifact)
@@ -7630,9 +8218,9 @@ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts,
7630
8218
  ...effectiveMentionCatalog.skills.map((skill) => `skill:${skill.name}:${skill.commandName}`),
7631
8219
  effectiveMentionCatalog.files ? "files:enabled" : "files:disabled"
7632
8220
  ].join("|");
7633
- const mentionActivateRef = useRef9(onMentionActivate);
8221
+ const mentionActivateRef = useRef10(onMentionActivate);
7634
8222
  mentionActivateRef.current = onMentionActivate;
7635
- const activateCurrentMention = useCallback13((mention) => {
8223
+ const activateCurrentMention = useCallback14((mention) => {
7636
8224
  if (mention.kind === "file") {
7637
8225
  openArtifact?.(mention.path);
7638
8226
  return;
@@ -7642,12 +8230,12 @@ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts,
7642
8230
  const hasMentions = effectiveMentionCatalog.files || effectiveMentionCatalog.commands.length > 0 || effectiveMentionCatalog.skills.length > 0;
7643
8231
  const messageIsStreaming = isAssistant && isLast && (isStreaming || message.status === "streaming");
7644
8232
  const mentionsEnabled = Boolean(isAssistant && !messageIsStreaming && hasMentions);
7645
- const mentionMarkdownComponents = useMemo12(
8233
+ const mentionMarkdownComponents = useMemo13(
7646
8234
  () => mentionsEnabled ? createMessageMentionMarkdownComponents(effectiveMentionCatalog, activateCurrentMention) : void 0,
7647
8235
  [activateCurrentMention, effectiveMentionCatalog, mentionSignature, mentionsEnabled]
7648
8236
  );
7649
8237
  const shouldReserveStreamingActions = messageIsStreaming;
7650
- return /* @__PURE__ */ jsxs19(
8238
+ return /* @__PURE__ */ jsxs20(
7651
8239
  Message,
7652
8240
  {
7653
8241
  from: role,
@@ -7656,8 +8244,8 @@ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts,
7656
8244
  "data-boring-agent-message-role": role,
7657
8245
  "data-boring-agent-message-status": message.status,
7658
8246
  children: [
7659
- /* @__PURE__ */ jsxs19(MessageContent, { children: [
7660
- fileParts.length > 0 ? /* @__PURE__ */ jsx23(
8247
+ /* @__PURE__ */ jsxs20(MessageContent, { children: [
8248
+ fileParts.length > 0 ? /* @__PURE__ */ jsx24(
7661
8249
  Attachments,
7662
8250
  {
7663
8251
  variant: role === "user" ? "inline" : "list",
@@ -7682,7 +8270,7 @@ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts,
7682
8270
  event.preventDefault();
7683
8271
  openAttachment();
7684
8272
  };
7685
- return /* @__PURE__ */ jsxs19(
8273
+ return /* @__PURE__ */ jsxs20(
7686
8274
  Attachment,
7687
8275
  {
7688
8276
  data: toAttachmentData(file, `file-${message.id}-${index}`),
@@ -7696,8 +8284,8 @@ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts,
7696
8284
  onKeyDown: openAttachmentFromKeyboard
7697
8285
  } : void 0,
7698
8286
  children: [
7699
- /* @__PURE__ */ jsx23(AttachmentPreview, { className: role === "user" ? "!size-5 shrink-0 rounded-[var(--radius-sm)]" : "size-10 shrink-0 rounded-[var(--radius-md)]" }),
7700
- /* @__PURE__ */ jsx23(AttachmentInfo, { className: role === "user" ? "min-w-0 max-w-[220px] flex-1 text-[12px]" : "min-w-0 flex-1" })
8287
+ /* @__PURE__ */ jsx24(AttachmentPreview, { className: role === "user" ? "!size-5 shrink-0 rounded-[var(--radius-sm)]" : "size-10 shrink-0 rounded-[var(--radius-md)]" }),
8288
+ /* @__PURE__ */ jsx24(AttachmentInfo, { className: role === "user" ? "min-w-0 max-w-[220px] flex-1 text-[12px]" : "min-w-0 flex-1" })
7701
8289
  ]
7702
8290
  },
7703
8291
  `file-${message.id}-${index}`
@@ -7707,7 +8295,7 @@ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts,
7707
8295
  ) : null,
7708
8296
  finalParts.map((item, index) => {
7709
8297
  if (item.kind === "reasoning") {
7710
- return /* @__PURE__ */ jsx23(
8298
+ return /* @__PURE__ */ jsx24(
7711
8299
  TimelineReasoningPart,
7712
8300
  {
7713
8301
  item,
@@ -7717,15 +8305,15 @@ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts,
7717
8305
  );
7718
8306
  }
7719
8307
  if (item.kind === "tool-group") {
7720
- return /* @__PURE__ */ jsx23("div", { "data-boring-agent-part": "message-tools", children: /* @__PURE__ */ jsx23(ToolCallGroup, { tools: item.tools, mergedToolRenderers: toolRenderers }) }, item.key);
8308
+ return /* @__PURE__ */ jsx24("div", { "data-boring-agent-part": "message-tools", children: /* @__PURE__ */ jsx24(ToolCallGroup, { tools: item.tools, mergedToolRenderers: toolRenderers }) }, item.key);
7721
8309
  }
7722
8310
  if (item.kind === "tool-plain") {
7723
- return /* @__PURE__ */ jsx23("div", { "data-boring-agent-part": "message-tools", children: /* @__PURE__ */ jsx23(PlainToolCard, { part: item.part, renderers: toolRenderers }) }, item.key);
8311
+ return /* @__PURE__ */ jsx24("div", { "data-boring-agent-part": "message-tools", children: /* @__PURE__ */ jsx24(PlainToolCard, { part: item.part, renderers: toolRenderers }) }, item.key);
7724
8312
  }
7725
8313
  if (item.part.type === "text") {
7726
8314
  const text = textForMessageDisplay(item.part.text, role);
7727
8315
  if (!text) return null;
7728
- return /* @__PURE__ */ jsx23("div", { "data-boring-agent-part": "message-text", children: /* @__PURE__ */ jsx23(
8316
+ return /* @__PURE__ */ jsx24("div", { "data-boring-agent-part": "message-text", children: /* @__PURE__ */ jsx24(
7729
8317
  MessageResponse,
7730
8318
  {
7731
8319
  components: mentionMarkdownComponents,
@@ -7737,12 +8325,12 @@ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts,
7737
8325
  ) }, item.key);
7738
8326
  }
7739
8327
  if (item.part.type === "notice") {
7740
- return /* @__PURE__ */ jsx23(NoticeBubble, { level: item.part.level, text: item.part.text }, item.key);
8328
+ return /* @__PURE__ */ jsx24(NoticeBubble, { level: item.part.level, text: item.part.text }, item.key);
7741
8329
  }
7742
8330
  return null;
7743
8331
  })
7744
8332
  ] }),
7745
- isAssistant && (textParts.length > 0 || shouldReserveStreamingActions) ? /* @__PURE__ */ jsx23(
8333
+ isAssistant && (textParts.length > 0 || shouldReserveStreamingActions) ? /* @__PURE__ */ jsx24(
7746
8334
  MessageActionsBar,
7747
8335
  {
7748
8336
  text: textParts.map((part) => part.text).join("\n\n"),
@@ -7754,20 +8342,20 @@ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts,
7754
8342
  );
7755
8343
  }
7756
8344
  function TimelineReasoningPart({ item, showThoughts }) {
7757
- const [manualOpen, setManualOpen] = useState14(null);
8345
+ const [manualOpen, setManualOpen] = useState16(null);
7758
8346
  const isStreaming = item.state === "streaming";
7759
8347
  const isOpen = manualOpen ?? showThoughts;
7760
- useEffect13(() => {
8348
+ useEffect15(() => {
7761
8349
  setManualOpen(null);
7762
8350
  }, [showThoughts]);
7763
- const handleOpenChange = useCallback13((open) => {
8351
+ const handleOpenChange = useCallback14((open) => {
7764
8352
  setManualOpen(open);
7765
8353
  }, []);
7766
- const toggleManualOpen = useCallback13((event) => {
8354
+ const toggleManualOpen = useCallback14((event) => {
7767
8355
  event.preventDefault();
7768
8356
  setManualOpen((open) => !(open ?? showThoughts));
7769
8357
  }, [showThoughts]);
7770
- return /* @__PURE__ */ jsxs19(
8358
+ return /* @__PURE__ */ jsxs20(
7771
8359
  Reasoning,
7772
8360
  {
7773
8361
  "data-boring-agent-part": "message-reasoning",
@@ -7777,15 +8365,15 @@ function TimelineReasoningPart({ item, showThoughts }) {
7777
8365
  onOpenChange: handleOpenChange,
7778
8366
  autoClose: false,
7779
8367
  children: [
7780
- /* @__PURE__ */ jsx23(
8368
+ /* @__PURE__ */ jsx24(
7781
8369
  ReasoningTrigger,
7782
8370
  {
7783
8371
  className: "mb-1 w-fit rounded-[var(--radius-sm)] px-0 py-0 !text-xs !font-normal !text-muted-foreground/75 hover:bg-transparent hover:!text-muted-foreground/75 [&_svg]:!text-muted-foreground/75",
7784
- getThinkingMessage: () => /* @__PURE__ */ jsx23("span", { children: "Reasoning" }),
8372
+ getThinkingMessage: () => /* @__PURE__ */ jsx24("span", { children: "Reasoning" }),
7785
8373
  onClick: toggleManualOpen
7786
8374
  }
7787
8375
  ),
7788
- /* @__PURE__ */ jsx23(ReasoningContent, { children: item.text })
8376
+ /* @__PURE__ */ jsx24(ReasoningContent, { children: item.text })
7789
8377
  ]
7790
8378
  }
7791
8379
  );
@@ -7794,7 +8382,7 @@ function PlainToolCard({ part, renderers }) {
7794
8382
  const toolPart = toToolPart(part);
7795
8383
  if (!toolPart) return null;
7796
8384
  const { renderer, part: resolved, resolution } = resolveToolRendererForPart(toolPart, renderers);
7797
- return /* @__PURE__ */ jsx23(
8385
+ return /* @__PURE__ */ jsx24(
7798
8386
  "div",
7799
8387
  {
7800
8388
  "data-tool-call-id": resolved.toolCallId,
@@ -7852,13 +8440,13 @@ function codeFilenameForPart(parts, index) {
7852
8440
  return typeof path === "string" && path.trim() ? path : void 0;
7853
8441
  }
7854
8442
  function NoticeBubble({ level, text }) {
7855
- return /* @__PURE__ */ jsx23(
8443
+ return /* @__PURE__ */ jsx24(
7856
8444
  "div",
7857
8445
  {
7858
8446
  "data-boring-agent-part": "message-notice",
7859
8447
  "data-notice-level": level,
7860
8448
  className: noticeSurfaceClass(level, "text-xs"),
7861
- children: /* @__PURE__ */ jsx23("div", { className: noticeTextClass(), children: text })
8449
+ children: /* @__PURE__ */ jsx24("div", { className: noticeTextClass(), children: text })
7862
8450
  }
7863
8451
  );
7864
8452
  }
@@ -7899,7 +8487,7 @@ function MessageActionsBar({
7899
8487
  text,
7900
8488
  visible = true
7901
8489
  }) {
7902
- const [copied, setCopied] = useState14(false);
8490
+ const [copied, setCopied] = useState16(false);
7903
8491
  const markCopied = () => {
7904
8492
  setCopied(true);
7905
8493
  setTimeout(() => setCopied(false), 1500);
@@ -7937,7 +8525,7 @@ function MessageActionsBar({
7937
8525
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[color:var(--accent)]/40"
7938
8526
  );
7939
8527
  const hiddenActionProps = visible ? {} : { tabIndex: -1 };
7940
- return /* @__PURE__ */ jsx23(
8528
+ return /* @__PURE__ */ jsx24(
7941
8529
  "div",
7942
8530
  {
7943
8531
  "aria-hidden": !visible,
@@ -7945,8 +8533,8 @@ function MessageActionsBar({
7945
8533
  "flex min-h-8 items-center gap-1 -mt-1 text-xs text-muted-foreground transition-opacity duration-200",
7946
8534
  visible ? "opacity-100" : "pointer-events-none opacity-0"
7947
8535
  ),
7948
- children: /* @__PURE__ */ jsx23(
7949
- Button10,
8536
+ children: /* @__PURE__ */ jsx24(
8537
+ Button11,
7950
8538
  {
7951
8539
  type: "button",
7952
8540
  variant: "ghost",
@@ -7956,7 +8544,7 @@ function MessageActionsBar({
7956
8544
  "aria-label": copied ? "Copied" : "Copy message",
7957
8545
  title: copied ? "Copied" : "Copy",
7958
8546
  ...hiddenActionProps,
7959
- children: copied ? /* @__PURE__ */ jsx23(CheckIcon2, { className: "h-3.5 w-3.5 text-[color:var(--accent)]" }) : /* @__PURE__ */ jsx23(CopyIcon3, { className: "h-3.5 w-3.5" })
8547
+ children: copied ? /* @__PURE__ */ jsx24(CheckIcon3, { className: "h-3.5 w-3.5 text-[color:var(--accent)]" }) : /* @__PURE__ */ jsx24(CopyIcon4, { className: "h-3.5 w-3.5" })
7960
8548
  }
7961
8549
  )
7962
8550
  }
@@ -7977,7 +8565,7 @@ function partKey(messageId, part, index) {
7977
8565
  }
7978
8566
 
7979
8567
  // src/front/chat/components/PiConversationSurface.tsx
7980
- import { jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
8568
+ import { jsx as jsx25, jsxs as jsxs21 } from "react/jsx-runtime";
7981
8569
  var TRANSCRIPT_WINDOW = 60;
7982
8570
  var TRANSCRIPT_WINDOW_STEP = 40;
7983
8571
  var LOAD_OLDER_THRESHOLD_PX = 320;
@@ -8005,17 +8593,17 @@ function PiConversationSurface({
8005
8593
  const total = messageItems.length;
8006
8594
  const historyEmpty = messages.length === 0;
8007
8595
  const terminalError = hasTerminalChatError(runtimeNotices, historyEmpty);
8008
- const [visibleCount, setVisibleCount] = useState15(TRANSCRIPT_WINDOW);
8009
- useEffect14(() => {
8596
+ const [visibleCount, setVisibleCount] = useState17(TRANSCRIPT_WINDOW);
8597
+ useEffect16(() => {
8010
8598
  setVisibleCount(TRANSCRIPT_WINDOW);
8011
8599
  }, [windowResetKey]);
8012
8600
  const hasOlder = total > visibleCount;
8013
8601
  const visibleItems = hasOlder ? messageItems.slice(total - visibleCount) : messageItems;
8014
8602
  const olderCount = hasOlder ? total - visibleCount : 0;
8015
- const loadOlder = useCallback14(() => {
8603
+ const loadOlder = useCallback15(() => {
8016
8604
  setVisibleCount((count) => count + TRANSCRIPT_WINDOW_STEP);
8017
8605
  }, []);
8018
- return /* @__PURE__ */ jsxs20(
8606
+ return /* @__PURE__ */ jsxs21(
8019
8607
  Conversation,
8020
8608
  {
8021
8609
  className: emptyHero ? cn("flex-none", runtimeNotices.length > 0 ? "max-h-[45vh]" : "max-h-[20vh]") : "flex-1",
@@ -8023,13 +8611,13 @@ function PiConversationSurface({
8023
8611
  "aria-live": "polite",
8024
8612
  onScrollToBottomReady,
8025
8613
  children: [
8026
- /* @__PURE__ */ jsxs20(ConversationContent, { className: cn(
8614
+ /* @__PURE__ */ jsxs21(ConversationContent, { className: cn(
8027
8615
  "mx-auto flex w-full flex-col gap-6",
8028
8616
  chrome ? "max-w-3xl px-6 py-8" : "max-w-[680px] px-4 py-4",
8029
8617
  emptyHero && "py-4 text-center"
8030
8618
  ), children: [
8031
- messages.length === 0 && emptyStateHydrating && !terminalError ? /* @__PURE__ */ jsx24(ConversationHistoryLoadingState, {}) : null,
8032
- messages.length === 0 && !emptyStateHydrating && !terminalError ? /* @__PURE__ */ jsx24(
8619
+ messages.length === 0 && emptyStateHydrating && !terminalError ? /* @__PURE__ */ jsx25(ConversationHistoryLoadingState, {}) : null,
8620
+ messages.length === 0 && !emptyStateHydrating && !terminalError ? /* @__PURE__ */ jsx25(
8033
8621
  ChatEmptyState,
8034
8622
  {
8035
8623
  eyebrow: emptyState?.eyebrow,
@@ -8047,8 +8635,8 @@ function PiConversationSurface({
8047
8635
  }
8048
8636
  }
8049
8637
  ) : null,
8050
- hasOlder ? /* @__PURE__ */ jsx24(TranscriptHistoryLoader, { olderCount, onLoadOlder: loadOlder }) : null,
8051
- visibleItems.map(({ message, key }, index) => /* @__PURE__ */ jsx24(
8638
+ hasOlder ? /* @__PURE__ */ jsx25(TranscriptHistoryLoader, { olderCount, onLoadOlder: loadOlder }) : null,
8639
+ visibleItems.map(({ message, key }, index) => /* @__PURE__ */ jsx25(
8052
8640
  PiTimelineMessage,
8053
8641
  {
8054
8642
  message,
@@ -8061,16 +8649,16 @@ function PiConversationSurface({
8061
8649
  },
8062
8650
  key
8063
8651
  )),
8064
- /* @__PURE__ */ jsx24(RuntimeNoticeMessages, { notices: runtimeNotices, onDismiss: onDismissNotice, renderAction: renderNoticeAction, historyEmpty })
8652
+ /* @__PURE__ */ jsx25(RuntimeNoticeMessages, { notices: runtimeNotices, onDismiss: onDismissNotice, renderAction: renderNoticeAction, historyEmpty })
8065
8653
  ] }),
8066
- /* @__PURE__ */ jsx24(ConversationScrollButton, {})
8654
+ /* @__PURE__ */ jsx25(ConversationScrollButton, {})
8067
8655
  ]
8068
8656
  }
8069
8657
  );
8070
8658
  }
8071
8659
  function ConversationHistoryLoadingState() {
8072
8660
  const skeletonClassName = "motion-reduce:animate-none";
8073
- return /* @__PURE__ */ jsxs20(
8661
+ return /* @__PURE__ */ jsxs21(
8074
8662
  "div",
8075
8663
  {
8076
8664
  role: "status",
@@ -8079,21 +8667,21 @@ function ConversationHistoryLoadingState() {
8079
8667
  "data-boring-agent-part": "chat-history-loading",
8080
8668
  className: "flex flex-col gap-7 py-4",
8081
8669
  children: [
8082
- /* @__PURE__ */ jsxs20("div", { "aria-hidden": "true", className: "flex items-start gap-3", children: [
8083
- /* @__PURE__ */ jsx24(Skeleton, { className: cn("mt-0.5 size-7 shrink-0 rounded-lg", skeletonClassName) }),
8084
- /* @__PURE__ */ jsxs20("div", { className: "flex min-w-0 flex-1 flex-col gap-2.5", children: [
8085
- /* @__PURE__ */ jsx24(Skeleton, { className: cn("h-3 w-24 rounded-sm", skeletonClassName) }),
8086
- /* @__PURE__ */ jsx24(Skeleton, { className: cn("h-3 w-full rounded-sm", skeletonClassName) }),
8087
- /* @__PURE__ */ jsx24(Skeleton, { className: cn("h-3 w-4/5 rounded-sm", skeletonClassName) })
8670
+ /* @__PURE__ */ jsxs21("div", { "aria-hidden": "true", className: "flex items-start gap-3", children: [
8671
+ /* @__PURE__ */ jsx25(Skeleton, { className: cn("mt-0.5 size-7 shrink-0 rounded-lg", skeletonClassName) }),
8672
+ /* @__PURE__ */ jsxs21("div", { className: "flex min-w-0 flex-1 flex-col gap-2.5", children: [
8673
+ /* @__PURE__ */ jsx25(Skeleton, { className: cn("h-3 w-24 rounded-sm", skeletonClassName) }),
8674
+ /* @__PURE__ */ jsx25(Skeleton, { className: cn("h-3 w-full rounded-sm", skeletonClassName) }),
8675
+ /* @__PURE__ */ jsx25(Skeleton, { className: cn("h-3 w-4/5 rounded-sm", skeletonClassName) })
8088
8676
  ] })
8089
8677
  ] }),
8090
- /* @__PURE__ */ jsx24("div", { "aria-hidden": "true", className: "flex justify-end", children: /* @__PURE__ */ jsx24(Skeleton, { className: cn("h-11 w-[min(72%,30rem)] rounded-xl", skeletonClassName) }) }),
8091
- /* @__PURE__ */ jsxs20("div", { "aria-hidden": "true", className: "flex items-start gap-3", children: [
8092
- /* @__PURE__ */ jsx24(Skeleton, { className: cn("mt-0.5 size-7 shrink-0 rounded-lg", skeletonClassName) }),
8093
- /* @__PURE__ */ jsxs20("div", { className: "flex min-w-0 flex-1 flex-col gap-3", children: [
8094
- /* @__PURE__ */ jsx24(Skeleton, { className: cn("h-3 w-20 rounded-sm", skeletonClassName) }),
8095
- /* @__PURE__ */ jsx24(Skeleton, { className: cn("h-20 w-full rounded-lg", skeletonClassName) }),
8096
- /* @__PURE__ */ jsx24(Skeleton, { className: cn("h-3 w-3/5 rounded-sm", skeletonClassName) })
8678
+ /* @__PURE__ */ jsx25("div", { "aria-hidden": "true", className: "flex justify-end", children: /* @__PURE__ */ jsx25(Skeleton, { className: cn("h-11 w-[min(72%,30rem)] rounded-xl", skeletonClassName) }) }),
8679
+ /* @__PURE__ */ jsxs21("div", { "aria-hidden": "true", className: "flex items-start gap-3", children: [
8680
+ /* @__PURE__ */ jsx25(Skeleton, { className: cn("mt-0.5 size-7 shrink-0 rounded-lg", skeletonClassName) }),
8681
+ /* @__PURE__ */ jsxs21("div", { className: "flex min-w-0 flex-1 flex-col gap-3", children: [
8682
+ /* @__PURE__ */ jsx25(Skeleton, { className: cn("h-3 w-20 rounded-sm", skeletonClassName) }),
8683
+ /* @__PURE__ */ jsx25(Skeleton, { className: cn("h-20 w-full rounded-lg", skeletonClassName) }),
8684
+ /* @__PURE__ */ jsx25(Skeleton, { className: cn("h-3 w-3/5 rounded-sm", skeletonClassName) })
8097
8685
  ] })
8098
8686
  ] })
8099
8687
  ]
@@ -8102,9 +8690,9 @@ function ConversationHistoryLoadingState() {
8102
8690
  }
8103
8691
  function TranscriptHistoryLoader({ olderCount, onLoadOlder }) {
8104
8692
  const { scrollRef } = useStickToBottomContext2();
8105
- const pendingAnchor = useRef10(null);
8106
- const armed = useRef10(true);
8107
- useEffect14(() => {
8693
+ const pendingAnchor = useRef11(null);
8694
+ const armed = useRef11(true);
8695
+ useEffect16(() => {
8108
8696
  const el = scrollRef.current;
8109
8697
  if (!el) return;
8110
8698
  const onScroll = () => {
@@ -8127,7 +8715,7 @@ function TranscriptHistoryLoader({ olderCount, onLoadOlder }) {
8127
8715
  }
8128
8716
  if (el.scrollTop > LOAD_OLDER_THRESHOLD_PX) armed.current = true;
8129
8717
  });
8130
- return /* @__PURE__ */ jsx24("div", { className: "flex justify-center", "data-boring-agent-part": "transcript-load-older", children: /* @__PURE__ */ jsxs20(
8718
+ return /* @__PURE__ */ jsx25("div", { className: "flex justify-center", "data-boring-agent-part": "transcript-load-older", children: /* @__PURE__ */ jsxs21(
8131
8719
  "button",
8132
8720
  {
8133
8721
  type: "button",
@@ -8164,14 +8752,14 @@ function buildMessageRenderItems(messages) {
8164
8752
  }
8165
8753
 
8166
8754
  // src/front/chat/components/PiChatComposerSurface.tsx
8167
- import { useCallback as useCallback16, useLayoutEffect as useLayoutEffect3 } from "react";
8755
+ import { useCallback as useCallback17, useLayoutEffect as useLayoutEffect3 } from "react";
8168
8756
  import { motion as motion2, useReducedMotion } from "motion/react";
8169
8757
 
8170
8758
  // src/front/composer/PluginUpdateStatus.tsx
8171
- import { useEffect as useEffect15, useRef as useRef11 } from "react";
8759
+ import { useEffect as useEffect17, useRef as useRef12 } from "react";
8172
8760
 
8173
8761
  // src/front/composer/ComposerStatusBanner.tsx
8174
- import { jsx as jsx25, jsxs as jsxs21 } from "react/jsx-runtime";
8762
+ import { jsx as jsx26, jsxs as jsxs22 } from "react/jsx-runtime";
8175
8763
  function ComposerStatusBanner({
8176
8764
  tone,
8177
8765
  dataAttribute,
@@ -8188,7 +8776,7 @@ function ComposerStatusBanner({
8188
8776
  }) {
8189
8777
  const toneAttr = { [dataAttribute]: tone };
8190
8778
  if (tone === "running") {
8191
- return /* @__PURE__ */ jsxs21(
8779
+ return /* @__PURE__ */ jsxs22(
8192
8780
  "div",
8193
8781
  {
8194
8782
  ...toneAttr,
@@ -8200,14 +8788,14 @@ function ComposerStatusBanner({
8200
8788
  maxWidthClassName
8201
8789
  ),
8202
8790
  children: [
8203
- /* @__PURE__ */ jsx25("span", { className: "inline-block h-2 w-2 animate-pulse rounded-full bg-accent", "aria-hidden": "true" }),
8204
- /* @__PURE__ */ jsx25("span", { children: runningContent })
8791
+ /* @__PURE__ */ jsx26("span", { className: "inline-block h-2 w-2 animate-pulse rounded-full bg-accent", "aria-hidden": "true" }),
8792
+ /* @__PURE__ */ jsx26("span", { children: runningContent })
8205
8793
  ]
8206
8794
  }
8207
8795
  );
8208
8796
  }
8209
8797
  if (tone === "success") {
8210
- return /* @__PURE__ */ jsxs21(
8798
+ return /* @__PURE__ */ jsxs22(
8211
8799
  "div",
8212
8800
  {
8213
8801
  ...toneAttr,
@@ -8219,13 +8807,13 @@ function ComposerStatusBanner({
8219
8807
  maxWidthClassName
8220
8808
  ),
8221
8809
  children: [
8222
- /* @__PURE__ */ jsxs21("div", { className: "flex items-start gap-2", children: [
8223
- /* @__PURE__ */ jsx25("span", { className: "mt-0.5 text-[oklch(0.45_0.13_148)]", "aria-hidden": "true", children: "\u2713" }),
8224
- /* @__PURE__ */ jsxs21("span", { className: "min-w-0 flex-1", children: [
8225
- /* @__PURE__ */ jsx25("span", { className: "block font-medium leading-5", children: title }),
8226
- detail ? /* @__PURE__ */ jsx25("span", { className: "block leading-4 text-muted-foreground", children: detail }) : null
8810
+ /* @__PURE__ */ jsxs22("div", { className: "flex items-start gap-2", children: [
8811
+ /* @__PURE__ */ jsx26("span", { className: "mt-0.5 text-[oklch(0.45_0.13_148)]", "aria-hidden": "true", children: "\u2713" }),
8812
+ /* @__PURE__ */ jsxs22("span", { className: "min-w-0 flex-1", children: [
8813
+ /* @__PURE__ */ jsx26("span", { className: "block font-medium leading-5", children: title }),
8814
+ detail ? /* @__PURE__ */ jsx26("span", { className: "block leading-4 text-muted-foreground", children: detail }) : null
8227
8815
  ] }),
8228
- onDismiss ? /* @__PURE__ */ jsx25(
8816
+ onDismiss ? /* @__PURE__ */ jsx26(
8229
8817
  "button",
8230
8818
  {
8231
8819
  type: "button",
@@ -8241,7 +8829,7 @@ function ComposerStatusBanner({
8241
8829
  }
8242
8830
  );
8243
8831
  }
8244
- return /* @__PURE__ */ jsxs21(
8832
+ return /* @__PURE__ */ jsxs22(
8245
8833
  "div",
8246
8834
  {
8247
8835
  ...toneAttr,
@@ -8253,10 +8841,10 @@ function ComposerStatusBanner({
8253
8841
  maxWidthClassName
8254
8842
  ),
8255
8843
  children: [
8256
- /* @__PURE__ */ jsxs21("div", { className: "flex items-center gap-2", children: [
8257
- /* @__PURE__ */ jsx25("span", { className: "text-destructive", "aria-hidden": "true", children: "\u26A0" }),
8258
- /* @__PURE__ */ jsx25("span", { className: "flex-1 font-medium", children: title }),
8259
- onRetry ? /* @__PURE__ */ jsx25(
8844
+ /* @__PURE__ */ jsxs22("div", { className: "flex items-center gap-2", children: [
8845
+ /* @__PURE__ */ jsx26("span", { className: "text-destructive", "aria-hidden": "true", children: "\u26A0" }),
8846
+ /* @__PURE__ */ jsx26("span", { className: "flex-1 font-medium", children: title }),
8847
+ onRetry ? /* @__PURE__ */ jsx26(
8260
8848
  "button",
8261
8849
  {
8262
8850
  type: "button",
@@ -8265,7 +8853,7 @@ function ComposerStatusBanner({
8265
8853
  children: retryLabel
8266
8854
  }
8267
8855
  ) : null,
8268
- onDismiss ? /* @__PURE__ */ jsx25(
8856
+ onDismiss ? /* @__PURE__ */ jsx26(
8269
8857
  "button",
8270
8858
  {
8271
8859
  type: "button",
@@ -8276,14 +8864,14 @@ function ComposerStatusBanner({
8276
8864
  }
8277
8865
  ) : null
8278
8866
  ] }),
8279
- message ? /* @__PURE__ */ jsx25("pre", { className: "mt-2 whitespace-pre-wrap break-words text-[11px] text-destructive/90", children: message }) : null
8867
+ message ? /* @__PURE__ */ jsx26("pre", { className: "mt-2 whitespace-pre-wrap break-words text-[11px] text-destructive/90", children: message }) : null
8280
8868
  ]
8281
8869
  }
8282
8870
  );
8283
8871
  }
8284
8872
 
8285
8873
  // src/front/composer/PluginUpdateStatus.tsx
8286
- import { jsx as jsx26, jsxs as jsxs22 } from "react/jsx-runtime";
8874
+ import { jsx as jsx27, jsxs as jsxs23 } from "react/jsx-runtime";
8287
8875
  function PluginUpdateStatus({
8288
8876
  state,
8289
8877
  onDismiss,
@@ -8291,12 +8879,12 @@ function PluginUpdateStatus({
8291
8879
  successAutoDismissMs = 1400,
8292
8880
  maxWidthClassName = "max-w-3xl"
8293
8881
  }) {
8294
- const onDismissRef = useRef11(onDismiss);
8295
- useEffect15(() => {
8882
+ const onDismissRef = useRef12(onDismiss);
8883
+ useEffect17(() => {
8296
8884
  onDismissRef.current = onDismiss;
8297
8885
  }, [onDismiss]);
8298
8886
  const successDismissKey = state?.kind === "success" ? `${state.reloaded}:${(state.restartWarnings?.length ?? 0) > 0 || (state.diagnostics?.length ?? 0) > 0}` : null;
8299
- useEffect15(() => {
8887
+ useEffect17(() => {
8300
8888
  if (!state || state.kind !== "success" || successAutoDismissMs <= 0) return;
8301
8889
  const hasWarningsOrDiagnostics = (state.restartWarnings?.length ?? 0) > 0 || (state.diagnostics?.length ?? 0) > 0;
8302
8890
  if (hasWarningsOrDiagnostics) return;
@@ -8305,7 +8893,7 @@ function PluginUpdateStatus({
8305
8893
  }, [state?.kind, successDismissKey, successAutoDismissMs]);
8306
8894
  if (!state) return null;
8307
8895
  if (state.kind === "running") {
8308
- return /* @__PURE__ */ jsx26(
8896
+ return /* @__PURE__ */ jsx27(
8309
8897
  ComposerStatusBanner,
8310
8898
  {
8311
8899
  tone: "running",
@@ -8322,7 +8910,7 @@ function PluginUpdateStatus({
8322
8910
  const hasWarningsOrDiagnostics = warnings.length > 0 || diagnostics.length > 0;
8323
8911
  const title = state.reloaded ? hasWarningsOrDiagnostics ? "Reload finished with warnings" : "Reload complete" : "Reload queued";
8324
8912
  const detail = state.reloaded ? hasWarningsOrDiagnostics ? "Some extension changes need attention." : frontEvents.length > 0 ? `${frontEvents.length} extension module${frontEvents.length === 1 ? "" : "s"} refreshed. Changes are live.` : "Changes are live." : void 0;
8325
- return /* @__PURE__ */ jsxs22(
8913
+ return /* @__PURE__ */ jsxs23(
8326
8914
  ComposerStatusBanner,
8327
8915
  {
8328
8916
  tone: "success",
@@ -8333,7 +8921,7 @@ function PluginUpdateStatus({
8333
8921
  onDismiss,
8334
8922
  dismissAriaLabel: "Dismiss plugin update status",
8335
8923
  children: [
8336
- diagnostics.length > 0 ? /* @__PURE__ */ jsxs22(
8924
+ diagnostics.length > 0 ? /* @__PURE__ */ jsxs23(
8337
8925
  "div",
8338
8926
  {
8339
8927
  "data-boring-plugin-update-diagnostics": "",
@@ -8342,24 +8930,24 @@ function PluginUpdateStatus({
8342
8930
  "px-2 py-1.5 text-[11px] text-foreground"
8343
8931
  ),
8344
8932
  children: [
8345
- /* @__PURE__ */ jsxs22("div", { className: "flex items-center gap-1.5 font-medium text-[oklch(0.48_0.15_60)]", children: [
8346
- /* @__PURE__ */ jsx26("span", { "aria-hidden": "true", children: "\u26A0" }),
8347
- /* @__PURE__ */ jsxs22("span", { children: [
8933
+ /* @__PURE__ */ jsxs23("div", { className: "flex items-center gap-1.5 font-medium text-[oklch(0.48_0.15_60)]", children: [
8934
+ /* @__PURE__ */ jsx27("span", { "aria-hidden": "true", children: "\u26A0" }),
8935
+ /* @__PURE__ */ jsxs23("span", { children: [
8348
8936
  "Reload diagnostics for ",
8349
8937
  diagnostics.length,
8350
8938
  " extension",
8351
8939
  diagnostics.length === 1 ? "" : "s"
8352
8940
  ] })
8353
8941
  ] }),
8354
- /* @__PURE__ */ jsx26("ul", { className: "mt-1 ml-4 list-disc text-foreground/85", children: diagnostics.map((diagnostic, index) => /* @__PURE__ */ jsxs22("li", { children: [
8355
- /* @__PURE__ */ jsx26("code", { className: "font-mono text-[10.5px]", children: diagnostic.pluginId ?? diagnostic.source ?? "plugin" }),
8942
+ /* @__PURE__ */ jsx27("ul", { className: "mt-1 ml-4 list-disc text-foreground/85", children: diagnostics.map((diagnostic, index) => /* @__PURE__ */ jsxs23("li", { children: [
8943
+ /* @__PURE__ */ jsx27("code", { className: "font-mono text-[10.5px]", children: diagnostic.pluginId ?? diagnostic.source ?? "plugin" }),
8356
8944
  " \u2014 ",
8357
8945
  diagnostic.message ?? "reload diagnostic"
8358
8946
  ] }, `${diagnostic.pluginId ?? diagnostic.source ?? "plugin"}-${index}`)) })
8359
8947
  ]
8360
8948
  }
8361
8949
  ) : null,
8362
- warnings.length > 0 ? /* @__PURE__ */ jsxs22(
8950
+ warnings.length > 0 ? /* @__PURE__ */ jsxs23(
8363
8951
  "div",
8364
8952
  {
8365
8953
  "data-boring-plugin-update-restart-warning": "",
@@ -8368,21 +8956,21 @@ function PluginUpdateStatus({
8368
8956
  "px-2 py-1.5 text-[11px] text-foreground"
8369
8957
  ),
8370
8958
  children: [
8371
- /* @__PURE__ */ jsxs22("div", { className: "flex items-center gap-1.5 font-medium text-[oklch(0.48_0.15_60)]", children: [
8372
- /* @__PURE__ */ jsx26("span", { "aria-hidden": "true", children: "\u26A0" }),
8373
- /* @__PURE__ */ jsxs22("span", { children: [
8959
+ /* @__PURE__ */ jsxs23("div", { className: "flex items-center gap-1.5 font-medium text-[oklch(0.48_0.15_60)]", children: [
8960
+ /* @__PURE__ */ jsx27("span", { "aria-hidden": "true", children: "\u26A0" }),
8961
+ /* @__PURE__ */ jsxs23("span", { children: [
8374
8962
  "Restart needed for ",
8375
8963
  warnings.length,
8376
8964
  " extension",
8377
8965
  warnings.length === 1 ? "" : "s"
8378
8966
  ] })
8379
8967
  ] }),
8380
- /* @__PURE__ */ jsx26("ul", { className: "mt-1 ml-4 list-disc text-foreground/85", children: warnings.map((w) => /* @__PURE__ */ jsxs22("li", { children: [
8381
- /* @__PURE__ */ jsx26("code", { className: "font-mono text-[10.5px]", children: w.id }),
8968
+ /* @__PURE__ */ jsx27("ul", { className: "mt-1 ml-4 list-disc text-foreground/85", children: warnings.map((w) => /* @__PURE__ */ jsxs23("li", { children: [
8969
+ /* @__PURE__ */ jsx27("code", { className: "font-mono text-[10.5px]", children: w.id }),
8382
8970
  " \u2014 ",
8383
8971
  w.surfaces.join(" + ")
8384
8972
  ] }, w.id)) }),
8385
- /* @__PURE__ */ jsx26("p", { className: "mt-1 text-foreground/70", children: "The interface reloaded successfully, but routes and tools were wired at boot. Restart the workspace process to pick up the new code." })
8973
+ /* @__PURE__ */ jsx27("p", { className: "mt-1 text-foreground/70", children: "The interface reloaded successfully, but routes and tools were wired at boot. Restart the workspace process to pick up the new code." })
8386
8974
  ]
8387
8975
  }
8388
8976
  ) : null
@@ -8390,7 +8978,7 @@ function PluginUpdateStatus({
8390
8978
  }
8391
8979
  );
8392
8980
  }
8393
- return /* @__PURE__ */ jsx26(
8981
+ return /* @__PURE__ */ jsx27(
8394
8982
  ComposerStatusBanner,
8395
8983
  {
8396
8984
  tone: "error",
@@ -8407,35 +8995,35 @@ function PluginUpdateStatus({
8407
8995
  }
8408
8996
 
8409
8997
  // src/front/composer/CommandRunStatus.tsx
8410
- import { useEffect as useEffect16, useRef as useRef12 } from "react";
8411
- import { Fragment as Fragment10, jsx as jsx27, jsxs as jsxs23 } from "react/jsx-runtime";
8998
+ import { useEffect as useEffect18, useRef as useRef13 } from "react";
8999
+ import { Fragment as Fragment10, jsx as jsx28, jsxs as jsxs24 } from "react/jsx-runtime";
8412
9000
  function CommandRunStatus({
8413
9001
  state,
8414
9002
  onDismiss,
8415
9003
  successAutoDismissMs = 1400,
8416
9004
  maxWidthClassName = "max-w-3xl"
8417
9005
  }) {
8418
- const onDismissRef = useRef12(onDismiss);
8419
- useEffect16(() => {
9006
+ const onDismissRef = useRef13(onDismiss);
9007
+ useEffect18(() => {
8420
9008
  onDismissRef.current = onDismiss;
8421
9009
  }, [onDismiss]);
8422
9010
  const successKey = state?.kind === "success" ? `${state.runId}:${state.command}:${state.detail ?? ""}` : null;
8423
- useEffect16(() => {
9011
+ useEffect18(() => {
8424
9012
  if (!state || state.kind !== "success" || successAutoDismissMs <= 0) return;
8425
9013
  const timeout = window.setTimeout(() => onDismissRef.current(), successAutoDismissMs);
8426
9014
  return () => window.clearTimeout(timeout);
8427
9015
  }, [state?.kind, successKey, successAutoDismissMs]);
8428
9016
  if (!state) return null;
8429
9017
  if (state.kind === "running") {
8430
- return /* @__PURE__ */ jsx27(
9018
+ return /* @__PURE__ */ jsx28(
8431
9019
  ComposerStatusBanner,
8432
9020
  {
8433
9021
  tone: "running",
8434
9022
  dataAttribute: "data-boring-command-run",
8435
9023
  maxWidthClassName,
8436
- runningContent: state.command ? /* @__PURE__ */ jsxs23(Fragment10, { children: [
9024
+ runningContent: state.command ? /* @__PURE__ */ jsxs24(Fragment10, { children: [
8437
9025
  " Running ",
8438
- /* @__PURE__ */ jsxs23("code", { className: "font-mono", children: [
9026
+ /* @__PURE__ */ jsxs24("code", { className: "font-mono", children: [
8439
9027
  "/",
8440
9028
  state.command
8441
9029
  ] }),
@@ -8445,15 +9033,15 @@ function CommandRunStatus({
8445
9033
  );
8446
9034
  }
8447
9035
  if (state.kind === "success") {
8448
- return /* @__PURE__ */ jsx27(
9036
+ return /* @__PURE__ */ jsx28(
8449
9037
  ComposerStatusBanner,
8450
9038
  {
8451
9039
  tone: "success",
8452
9040
  dataAttribute: "data-boring-command-run",
8453
9041
  maxWidthClassName,
8454
- title: state.command ? /* @__PURE__ */ jsxs23(Fragment10, { children: [
9042
+ title: state.command ? /* @__PURE__ */ jsxs24(Fragment10, { children: [
8455
9043
  " Ran ",
8456
- /* @__PURE__ */ jsxs23("code", { className: "font-mono", children: [
9044
+ /* @__PURE__ */ jsxs24("code", { className: "font-mono", children: [
8457
9045
  "/",
8458
9046
  state.command
8459
9047
  ] }),
@@ -8465,15 +9053,15 @@ function CommandRunStatus({
8465
9053
  }
8466
9054
  );
8467
9055
  }
8468
- return /* @__PURE__ */ jsx27(
9056
+ return /* @__PURE__ */ jsx28(
8469
9057
  ComposerStatusBanner,
8470
9058
  {
8471
9059
  tone: "error",
8472
9060
  dataAttribute: "data-boring-command-run",
8473
9061
  maxWidthClassName,
8474
- title: state.command ? /* @__PURE__ */ jsxs23(Fragment10, { children: [
9062
+ title: state.command ? /* @__PURE__ */ jsxs24(Fragment10, { children: [
8475
9063
  " ",
8476
- /* @__PURE__ */ jsxs23("code", { className: "font-mono", children: [
9064
+ /* @__PURE__ */ jsxs24("code", { className: "font-mono", children: [
8477
9065
  "/",
8478
9066
  state.command
8479
9067
  ] }),
@@ -8487,8 +9075,8 @@ function CommandRunStatus({
8487
9075
  }
8488
9076
 
8489
9077
  // src/front/chatPanelComposerControls.tsx
8490
- import { forwardRef, useEffect as useEffect17, useRef as useRef13, useState as useState16 } from "react";
8491
- import { CheckIcon as CheckIcon3, ChevronDownIcon as ChevronDownIcon5 } from "lucide-react";
9078
+ import { forwardRef, useEffect as useEffect19, useRef as useRef14, useState as useState18 } from "react";
9079
+ import { CheckIcon as CheckIcon4, ChevronDownIcon as ChevronDownIcon5 } from "lucide-react";
8492
9080
  import {
8493
9081
  Command,
8494
9082
  CommandEmpty,
@@ -8521,7 +9109,7 @@ function displayProviderLabel(provider) {
8521
9109
  }
8522
9110
 
8523
9111
  // src/front/chatPanelComposerControls.tsx
8524
- import { Fragment as Fragment11, jsx as jsx28, jsxs as jsxs24 } from "react/jsx-runtime";
9112
+ import { Fragment as Fragment11, jsx as jsx29, jsxs as jsxs25 } from "react/jsx-runtime";
8525
9113
  var composerActionClass = cn(
8526
9114
  "inline-flex h-8 items-center justify-center gap-1.5 rounded-[var(--radius-md)] border-0 bg-transparent",
8527
9115
  "text-muted-foreground shadow-none transition-colors motion-reduce:transition-none",
@@ -8571,7 +9159,7 @@ function isTextInputTarget(target) {
8571
9159
  return target instanceof HTMLInputElement;
8572
9160
  }
8573
9161
  function useDismissOnOutsidePointer(ref, onClose) {
8574
- useEffect17(() => {
9162
+ useEffect19(() => {
8575
9163
  if (!onClose) return;
8576
9164
  const handler = (event) => {
8577
9165
  const target = event.target;
@@ -8589,7 +9177,7 @@ function useDismissOnOutsidePointer(ref, onClose) {
8589
9177
  }
8590
9178
  function ThinkingLevelGlyph({ level }) {
8591
9179
  const lit = level === "off" ? 0 : level === "low" ? 1 : level === "medium" ? 2 : 3;
8592
- return /* @__PURE__ */ jsx28(
9180
+ return /* @__PURE__ */ jsx29(
8593
9181
  "svg",
8594
9182
  {
8595
9183
  "aria-hidden": "true",
@@ -8598,7 +9186,7 @@ function ThinkingLevelGlyph({ level }) {
8598
9186
  viewBox: "0 0 14 14",
8599
9187
  fill: "none",
8600
9188
  className: "shrink-0",
8601
- children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx28(
9189
+ children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx29(
8602
9190
  "rect",
8603
9191
  {
8604
9192
  x: 2 + i * 4,
@@ -8650,7 +9238,7 @@ var ModelSelectTrigger = forwardRef(function ModelSelectTrigger2({
8650
9238
  const triggerDisplay = value ? `${triggerLabel} (${displayProviderLabel(value.provider)})` : triggerLabel;
8651
9239
  const sessionDisplay = sessionModel && sessionTriggerLabel ? `${sessionTriggerLabel} (${displayProviderLabel(sessionModel.provider)})` : triggerDisplay;
8652
9240
  const accessibleDisplay = isOverride && value ? `Current model: ${sessionDisplay}. Next-message override: ${triggerDisplay}` : `Current model: ${sessionDisplay}`;
8653
- return /* @__PURE__ */ jsx28(
9241
+ return /* @__PURE__ */ jsx29(
8654
9242
  "button",
8655
9243
  {
8656
9244
  ref,
@@ -8669,25 +9257,25 @@ var ModelSelectTrigger = forwardRef(function ModelSelectTrigger2({
8669
9257
  className
8670
9258
  ),
8671
9259
  ...props,
8672
- children: trigger === "slash" ? /* @__PURE__ */ jsxs24(Fragment11, { children: [
8673
- /* @__PURE__ */ jsx28("span", { className: "text-muted-foreground", children: "Model \xB7 " }),
8674
- /* @__PURE__ */ jsx28("span", { className: "min-w-0 truncate text-foreground", children: sessionTriggerLabel ?? triggerLabel }),
8675
- sessionModel ? /* @__PURE__ */ jsxs24("span", { className: "shrink-0 text-muted-foreground", children: [
9260
+ children: trigger === "slash" ? /* @__PURE__ */ jsxs25(Fragment11, { children: [
9261
+ /* @__PURE__ */ jsx29("span", { className: "text-muted-foreground", children: "Model \xB7 " }),
9262
+ /* @__PURE__ */ jsx29("span", { className: "min-w-0 truncate text-foreground", children: sessionTriggerLabel ?? triggerLabel }),
9263
+ sessionModel ? /* @__PURE__ */ jsxs25("span", { className: "shrink-0 text-muted-foreground", children: [
8676
9264
  " \xB7 ",
8677
9265
  displayProviderLabel(sessionModel.provider)
8678
- ] }) : value ? /* @__PURE__ */ jsxs24("span", { className: "shrink-0 text-muted-foreground", children: [
9266
+ ] }) : value ? /* @__PURE__ */ jsxs25("span", { className: "shrink-0 text-muted-foreground", children: [
8679
9267
  " \xB7 ",
8680
9268
  displayProviderLabel(value.provider)
8681
9269
  ] }) : null,
8682
- isOverride && value ? /* @__PURE__ */ jsxs24("span", { className: "shrink-0 text-amber-600 dark:text-amber-400", children: [
9270
+ isOverride && value ? /* @__PURE__ */ jsxs25("span", { className: "shrink-0 text-amber-600 dark:text-amber-400", children: [
8683
9271
  " \xB7 Next override: ",
8684
9272
  triggerLabel,
8685
9273
  " \xB7 ",
8686
9274
  displayProviderLabel(value.provider)
8687
9275
  ] }) : null
8688
- ] }) : /* @__PURE__ */ jsxs24(Fragment11, { children: [
8689
- /* @__PURE__ */ jsx28("span", { className: "min-w-0 truncate", children: triggerDisplay }),
8690
- /* @__PURE__ */ jsx28(ChevronDownIcon5, { className: "h-3 w-3 shrink-0 text-muted-foreground/50", "aria-hidden": "true" })
9276
+ ] }) : /* @__PURE__ */ jsxs25(Fragment11, { children: [
9277
+ /* @__PURE__ */ jsx29("span", { className: "min-w-0 truncate", children: triggerDisplay }),
9278
+ /* @__PURE__ */ jsx29(ChevronDownIcon5, { className: "h-3 w-3 shrink-0 text-muted-foreground/50", "aria-hidden": "true" })
8691
9279
  ] })
8692
9280
  }
8693
9281
  );
@@ -8707,19 +9295,19 @@ function ModelPickerMenu({
8707
9295
  const groupedOptions = [...groups.values()].flat();
8708
9296
  const keyboardOptions = hideDefaultOption ? groupedOptions : [null, ...groupedOptions];
8709
9297
  const selectedIndex = currentKey ? Math.max(0, keyboardOptions.findIndex((option) => option && encodeModelKey(option) === currentKey)) : 0;
8710
- const [activeIndex, setActiveIndex] = useState16(selectedIndex);
8711
- const activeIndexRef = useRef13(selectedIndex);
8712
- const menuRef = useRef13(null);
9298
+ const [activeIndex, setActiveIndex] = useState18(selectedIndex);
9299
+ const activeIndexRef = useRef14(selectedIndex);
9300
+ const menuRef = useRef14(null);
8713
9301
  useDismissOnOutsidePointer(menuRef, onClose);
8714
9302
  const setKeyboardActiveIndex = (next) => {
8715
9303
  const resolved = typeof next === "function" ? next(activeIndexRef.current) : next;
8716
9304
  activeIndexRef.current = resolved;
8717
9305
  setActiveIndex(resolved);
8718
9306
  };
8719
- useEffect17(() => {
9307
+ useEffect19(() => {
8720
9308
  setKeyboardActiveIndex(selectedIndex);
8721
9309
  }, [selectedIndex]);
8722
- useEffect17(() => {
9310
+ useEffect19(() => {
8723
9311
  const handler = (event) => {
8724
9312
  if (event.key === "Escape") {
8725
9313
  event.preventDefault();
@@ -8744,8 +9332,8 @@ function ModelPickerMenu({
8744
9332
  }, [activeIndex, disabled, keyboardOptions, onChange, onClose]);
8745
9333
  const optionIndexOffset = hideDefaultOption ? 0 : 1;
8746
9334
  const optionIndexes = new Map(groupedOptions.map((option, index) => [encodeModelKey(option), index + optionIndexOffset]));
8747
- return /* @__PURE__ */ jsx28("div", { ref: menuRef, "data-boring-agent": "", "data-boring-agent-part": "model-picker-menu", className: cn(composerPickerMenuClass, className), children: /* @__PURE__ */ jsxs24(Command, { className: "bg-transparent text-[color:var(--popover-foreground)]", children: [
8748
- menuOptions.length > 8 && /* @__PURE__ */ jsx28("div", { className: "border-b border-border/60 px-2", children: /* @__PURE__ */ jsx28(
9335
+ return /* @__PURE__ */ jsx29("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: [
9336
+ menuOptions.length > 8 && /* @__PURE__ */ jsx29("div", { className: "border-b border-border/60 px-2", children: /* @__PURE__ */ jsx29(
8749
9337
  CommandInput,
8750
9338
  {
8751
9339
  placeholder: "Search models\u2026",
@@ -8753,9 +9341,9 @@ function ModelPickerMenu({
8753
9341
  className: "h-8 w-full border-0 bg-transparent text-sm outline-none focus:ring-0"
8754
9342
  }
8755
9343
  ) }),
8756
- /* @__PURE__ */ jsxs24(CommandList, { className: "max-h-[300px] p-0.5", children: [
8757
- /* @__PURE__ */ jsx28(CommandEmpty, { className: "py-4 text-center text-sm text-muted-foreground", children: "No models found" }),
8758
- !hideDefaultOption ? /* @__PURE__ */ jsx28(CommandGroup, { children: /* @__PURE__ */ jsxs24(
9344
+ /* @__PURE__ */ jsxs25(CommandList, { className: "max-h-[300px] p-0.5", children: [
9345
+ /* @__PURE__ */ jsx29(CommandEmpty, { className: "py-4 text-center text-sm text-muted-foreground", children: "No models found" }),
9346
+ !hideDefaultOption ? /* @__PURE__ */ jsx29(CommandGroup, { children: /* @__PURE__ */ jsxs25(
8759
9347
  CommandItem,
8760
9348
  {
8761
9349
  value: "Default model automatic auto",
@@ -8766,8 +9354,8 @@ function ModelPickerMenu({
8766
9354
  },
8767
9355
  className: selectorItemClass(!value || activeIndex === 0),
8768
9356
  children: [
8769
- /* @__PURE__ */ jsx28(
8770
- CheckIcon3,
9357
+ /* @__PURE__ */ jsx29(
9358
+ CheckIcon4,
8771
9359
  {
8772
9360
  className: cn(
8773
9361
  "h-3.5 w-3.5 shrink-0",
@@ -8775,12 +9363,12 @@ function ModelPickerMenu({
8775
9363
  )
8776
9364
  }
8777
9365
  ),
8778
- /* @__PURE__ */ jsx28("span", { className: "truncate", children: "Default model" }),
8779
- /* @__PURE__ */ jsx28("span", { className: "ml-auto shrink-0 text-xs text-muted-foreground", children: "Auto" })
9366
+ /* @__PURE__ */ jsx29("span", { className: "truncate", children: "Default model" }),
9367
+ /* @__PURE__ */ jsx29("span", { className: "ml-auto shrink-0 text-xs text-muted-foreground", children: "Auto" })
8780
9368
  ]
8781
9369
  }
8782
9370
  ) }) : null,
8783
- [...groups.entries()].map(([provider, list]) => /* @__PURE__ */ jsx28(
9371
+ [...groups.entries()].map(([provider, list]) => /* @__PURE__ */ jsx29(
8784
9372
  CommandGroup,
8785
9373
  {
8786
9374
  heading: displayProviderLabel(provider),
@@ -8789,7 +9377,7 @@ function ModelPickerMenu({
8789
9377
  const key = encodeModelKey(m);
8790
9378
  const label = m.label || displayModelLabel(m.id);
8791
9379
  const itemIndex = optionIndexes.get(key) ?? 0;
8792
- return /* @__PURE__ */ jsxs24(
9380
+ return /* @__PURE__ */ jsxs25(
8793
9381
  CommandItem,
8794
9382
  {
8795
9383
  value: `${label} ${m.id} ${displayProviderLabel(m.provider)}`,
@@ -8800,8 +9388,8 @@ function ModelPickerMenu({
8800
9388
  },
8801
9389
  className: cn(selectorItemClass(key === currentKey || activeIndex === itemIndex)),
8802
9390
  children: [
8803
- /* @__PURE__ */ jsx28(
8804
- CheckIcon3,
9391
+ /* @__PURE__ */ jsx29(
9392
+ CheckIcon4,
8805
9393
  {
8806
9394
  className: cn(
8807
9395
  "h-3.5 w-3.5 shrink-0",
@@ -8809,8 +9397,8 @@ function ModelPickerMenu({
8809
9397
  )
8810
9398
  }
8811
9399
  ),
8812
- /* @__PURE__ */ jsx28("span", { className: "min-w-0 flex-1 truncate whitespace-nowrap", children: label }),
8813
- /* @__PURE__ */ jsx28("span", { className: "ml-auto max-w-[45%] shrink-0 truncate whitespace-nowrap text-right text-xs text-muted-foreground", children: m.id })
9400
+ /* @__PURE__ */ jsx29("span", { className: "min-w-0 flex-1 truncate whitespace-nowrap", children: label }),
9401
+ /* @__PURE__ */ jsx29("span", { className: "ml-auto max-w-[45%] shrink-0 truncate whitespace-nowrap text-right text-xs text-muted-foreground", children: m.id })
8814
9402
  ]
8815
9403
  },
8816
9404
  key
@@ -8835,12 +9423,12 @@ function ModelSelect({
8835
9423
  ariaDescribedBy,
8836
9424
  hideDefaultOption = false
8837
9425
  }) {
8838
- const [open, setOpen] = useState16(false);
8839
- const previousOpenSignalRef = useRef13(openSignal);
8840
- useEffect17(() => {
9426
+ const [open, setOpen] = useState18(false);
9427
+ const previousOpenSignalRef = useRef14(openSignal);
9428
+ useEffect19(() => {
8841
9429
  if (disabled) setOpen(false);
8842
9430
  }, [disabled]);
8843
- useEffect17(() => {
9431
+ useEffect19(() => {
8844
9432
  if (disabled || openSignal === void 0) {
8845
9433
  previousOpenSignalRef.current = openSignal;
8846
9434
  return;
@@ -8848,8 +9436,8 @@ function ModelSelect({
8848
9436
  if (previousOpenSignalRef.current !== openSignal) setOpen(true);
8849
9437
  previousOpenSignalRef.current = openSignal;
8850
9438
  }, [disabled, openSignal]);
8851
- return /* @__PURE__ */ jsxs24(Popover, { open, onOpenChange: disabled ? void 0 : setOpen, children: [
8852
- /* @__PURE__ */ jsx28(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx28(
9439
+ return /* @__PURE__ */ jsxs25(Popover, { open, onOpenChange: disabled ? void 0 : setOpen, children: [
9440
+ /* @__PURE__ */ jsx29(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx29(
8853
9441
  ModelSelectTrigger,
8854
9442
  {
8855
9443
  value,
@@ -8863,7 +9451,7 @@ function ModelSelect({
8863
9451
  "aria-describedby": ariaDescribedBy
8864
9452
  }
8865
9453
  ) }),
8866
- /* @__PURE__ */ jsx28(
9454
+ /* @__PURE__ */ jsx29(
8867
9455
  PopoverContent,
8868
9456
  {
8869
9457
  align: trigger === "slash" ? "center" : "start",
@@ -8872,7 +9460,7 @@ function ModelSelect({
8872
9460
  collisionPadding: 12,
8873
9461
  "data-boring-agent": "",
8874
9462
  className: cn("w-[min(92vw,340px)]", selectorContentClass),
8875
- children: /* @__PURE__ */ jsx28(
9463
+ children: /* @__PURE__ */ jsx29(
8876
9464
  ModelPickerMenu,
8877
9465
  {
8878
9466
  value,
@@ -8896,12 +9484,12 @@ function ThinkingSelect({
8896
9484
  openSignal,
8897
9485
  className
8898
9486
  }) {
8899
- const [open, setOpen] = useState16(false);
8900
- const previousOpenSignalRef = useRef13(openSignal);
8901
- useEffect17(() => {
9487
+ const [open, setOpen] = useState18(false);
9488
+ const previousOpenSignalRef = useRef14(openSignal);
9489
+ useEffect19(() => {
8902
9490
  if (disabled) setOpen(false);
8903
9491
  }, [disabled]);
8904
- useEffect17(() => {
9492
+ useEffect19(() => {
8905
9493
  if (disabled || openSignal === void 0) {
8906
9494
  previousOpenSignalRef.current = openSignal;
8907
9495
  return;
@@ -8909,8 +9497,8 @@ function ThinkingSelect({
8909
9497
  if (previousOpenSignalRef.current !== openSignal) setOpen(true);
8910
9498
  previousOpenSignalRef.current = openSignal;
8911
9499
  }, [disabled, openSignal]);
8912
- return /* @__PURE__ */ jsxs24(Popover, { open, onOpenChange: disabled ? void 0 : setOpen, children: [
8913
- /* @__PURE__ */ jsx28(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx28(
9500
+ return /* @__PURE__ */ jsxs25(Popover, { open, onOpenChange: disabled ? void 0 : setOpen, children: [
9501
+ /* @__PURE__ */ jsx29(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx29(
8914
9502
  ThinkingSelectTrigger,
8915
9503
  {
8916
9504
  value,
@@ -8920,7 +9508,7 @@ function ThinkingSelect({
8920
9508
  className
8921
9509
  }
8922
9510
  ) }),
8923
- /* @__PURE__ */ jsx28(
9511
+ /* @__PURE__ */ jsx29(
8924
9512
  PopoverContent,
8925
9513
  {
8926
9514
  align: trigger === "slash" ? "center" : "end",
@@ -8929,7 +9517,7 @@ function ThinkingSelect({
8929
9517
  collisionPadding: 12,
8930
9518
  "data-boring-agent": "",
8931
9519
  className: cn("w-[min(92vw,240px)]", selectorContentClass),
8932
- children: /* @__PURE__ */ jsx28(
9520
+ children: /* @__PURE__ */ jsx29(
8933
9521
  ThinkingPickerMenu,
8934
9522
  {
8935
9523
  value,
@@ -8952,7 +9540,7 @@ var ThinkingSelectTrigger = forwardRef(function ThinkingSelectTrigger2({
8952
9540
  className,
8953
9541
  ...props
8954
9542
  }, ref) {
8955
- return /* @__PURE__ */ jsxs24(
9543
+ return /* @__PURE__ */ jsxs25(
8956
9544
  "button",
8957
9545
  {
8958
9546
  ref,
@@ -8973,13 +9561,13 @@ var ThinkingSelectTrigger = forwardRef(function ThinkingSelectTrigger2({
8973
9561
  onClick,
8974
9562
  ...props,
8975
9563
  children: [
8976
- trigger === "button" ? /* @__PURE__ */ jsx28(ThinkingLevelGlyph, { level: value }) : null,
8977
- trigger === "slash" ? /* @__PURE__ */ jsxs24(Fragment11, { children: [
8978
- /* @__PURE__ */ jsx28("span", { className: "text-muted-foreground", children: "Thinking \xB7 " }),
8979
- /* @__PURE__ */ jsx28("span", { className: "text-foreground", children: THINKING_LEVEL_STATUS_LABELS[value] })
8980
- ] }) : /* @__PURE__ */ jsxs24(Fragment11, { children: [
8981
- /* @__PURE__ */ jsx28("span", { children: THINKING_LEVEL_LABELS[value] }),
8982
- /* @__PURE__ */ jsx28(ChevronDownIcon5, { className: "h-3 w-3 shrink-0 text-muted-foreground/50", "aria-hidden": "true" })
9564
+ trigger === "button" ? /* @__PURE__ */ jsx29(ThinkingLevelGlyph, { level: value }) : null,
9565
+ trigger === "slash" ? /* @__PURE__ */ jsxs25(Fragment11, { children: [
9566
+ /* @__PURE__ */ jsx29("span", { className: "text-muted-foreground", children: "Thinking \xB7 " }),
9567
+ /* @__PURE__ */ jsx29("span", { className: "text-foreground", children: THINKING_LEVEL_STATUS_LABELS[value] })
9568
+ ] }) : /* @__PURE__ */ jsxs25(Fragment11, { children: [
9569
+ /* @__PURE__ */ jsx29("span", { children: THINKING_LEVEL_LABELS[value] }),
9570
+ /* @__PURE__ */ jsx29(ChevronDownIcon5, { className: "h-3 w-3 shrink-0 text-muted-foreground/50", "aria-hidden": "true" })
8983
9571
  ] })
8984
9572
  ]
8985
9573
  }
@@ -8993,19 +9581,19 @@ function ThinkingPickerMenu({
8993
9581
  className
8994
9582
  }) {
8995
9583
  const selectedIndex = Math.max(0, THINKING_LEVELS.indexOf(value));
8996
- const [activeIndex, setActiveIndex] = useState16(selectedIndex);
8997
- const activeIndexRef = useRef13(selectedIndex);
8998
- const menuRef = useRef13(null);
9584
+ const [activeIndex, setActiveIndex] = useState18(selectedIndex);
9585
+ const activeIndexRef = useRef14(selectedIndex);
9586
+ const menuRef = useRef14(null);
8999
9587
  useDismissOnOutsidePointer(menuRef, onClose);
9000
9588
  const setKeyboardActiveIndex = (next) => {
9001
9589
  const resolved = typeof next === "function" ? next(activeIndexRef.current) : next;
9002
9590
  activeIndexRef.current = resolved;
9003
9591
  setActiveIndex(resolved);
9004
9592
  };
9005
- useEffect17(() => {
9593
+ useEffect19(() => {
9006
9594
  setKeyboardActiveIndex(selectedIndex);
9007
9595
  }, [selectedIndex]);
9008
- useEffect17(() => {
9596
+ useEffect19(() => {
9009
9597
  const handler = (event) => {
9010
9598
  if (event.key === "Escape") {
9011
9599
  event.preventDefault();
@@ -9028,7 +9616,7 @@ function ThinkingPickerMenu({
9028
9616
  window.addEventListener("keydown", handler, { capture: true });
9029
9617
  return () => window.removeEventListener("keydown", handler, { capture: true });
9030
9618
  }, [activeIndex, disabled, onChange, onClose]);
9031
- return /* @__PURE__ */ jsx28("div", { ref: menuRef, "data-boring-agent": "", "data-boring-agent-part": "thinking-picker-menu", className: cn(composerPickerMenuClass, className), children: /* @__PURE__ */ jsx28(Command, { className: "bg-transparent text-[color:var(--popover-foreground)]", children: /* @__PURE__ */ jsx28(CommandList, { className: "max-h-[300px] p-0.5", children: THINKING_LEVELS.map((level, index) => /* @__PURE__ */ jsxs24(
9619
+ return /* @__PURE__ */ jsx29("div", { ref: menuRef, "data-boring-agent": "", "data-boring-agent-part": "thinking-picker-menu", className: cn(composerPickerMenuClass, className), children: /* @__PURE__ */ jsx29(Command, { className: "bg-transparent text-[color:var(--popover-foreground)]", children: /* @__PURE__ */ jsx29(CommandList, { className: "max-h-[300px] p-0.5", children: THINKING_LEVELS.map((level, index) => /* @__PURE__ */ jsxs25(
9032
9620
  CommandItem,
9033
9621
  {
9034
9622
  value: `Thinking ${THINKING_LEVEL_LABELS[level]} ${THINKING_LEVEL_DETAILS[level]}`,
@@ -9039,8 +9627,8 @@ function ThinkingPickerMenu({
9039
9627
  },
9040
9628
  className: selectorItemClass(level === value || index === activeIndex),
9041
9629
  children: [
9042
- /* @__PURE__ */ jsx28(
9043
- CheckIcon3,
9630
+ /* @__PURE__ */ jsx29(
9631
+ CheckIcon4,
9044
9632
  {
9045
9633
  className: cn(
9046
9634
  "h-3.5 w-3.5 shrink-0",
@@ -9048,9 +9636,9 @@ function ThinkingPickerMenu({
9048
9636
  )
9049
9637
  }
9050
9638
  ),
9051
- /* @__PURE__ */ jsx28(ThinkingLevelGlyph, { level }),
9052
- /* @__PURE__ */ jsx28("span", { className: "font-medium", children: THINKING_LEVEL_LABELS[level] }),
9053
- /* @__PURE__ */ jsx28("span", { className: "ml-auto text-xs text-muted-foreground", children: THINKING_LEVEL_DETAILS[level] })
9639
+ /* @__PURE__ */ jsx29(ThinkingLevelGlyph, { level }),
9640
+ /* @__PURE__ */ jsx29("span", { className: "font-medium", children: THINKING_LEVEL_LABELS[level] }),
9641
+ /* @__PURE__ */ jsx29("span", { className: "ml-auto text-xs text-muted-foreground", children: THINKING_LEVEL_DETAILS[level] })
9054
9642
  ]
9055
9643
  },
9056
9644
  level
@@ -9091,7 +9679,7 @@ import {
9091
9679
  XIcon as XIcon4
9092
9680
  } from "lucide-react";
9093
9681
  import { nanoid } from "nanoid";
9094
- import { useCallback as useCallback15, useEffect as useEffect18, useMemo as useMemo13, useRef as useRef14, useState as useState17 } from "react";
9682
+ import { useCallback as useCallback16, useEffect as useEffect20, useMemo as useMemo14, useRef as useRef15, useState as useState19 } from "react";
9095
9683
 
9096
9684
  // src/front/primitives/prompt-input-context.ts
9097
9685
  import { createContext as createContext7, useContext as useContext7 } from "react";
@@ -9126,12 +9714,12 @@ import {
9126
9714
  TooltipTrigger as TooltipTrigger2
9127
9715
  } from "@hachej/boring-ui-kit";
9128
9716
  import { Children as Children2 } from "react";
9129
- import { jsx as jsx29, jsxs as jsxs25 } from "react/jsx-runtime";
9717
+ import { jsx as jsx30, jsxs as jsxs26 } from "react/jsx-runtime";
9130
9718
  var PromptInputFooter = ({
9131
9719
  align = "block-end",
9132
9720
  className,
9133
9721
  ...props
9134
- }) => /* @__PURE__ */ jsx29(
9722
+ }) => /* @__PURE__ */ jsx30(
9135
9723
  InputGroupAddon,
9136
9724
  {
9137
9725
  align,
@@ -9141,7 +9729,7 @@ var PromptInputFooter = ({
9141
9729
  );
9142
9730
 
9143
9731
  // src/front/primitives/prompt-input.tsx
9144
- import { Fragment as Fragment12, jsx as jsx30, jsxs as jsxs26 } from "react/jsx-runtime";
9732
+ import { Fragment as Fragment12, jsx as jsx31, jsxs as jsxs27 } from "react/jsx-runtime";
9145
9733
  var PromptInput = ({
9146
9734
  className,
9147
9735
  accept,
@@ -9158,14 +9746,14 @@ var PromptInput = ({
9158
9746
  }) => {
9159
9747
  const controller = useOptionalPromptInputController();
9160
9748
  const usingProvider = !!controller;
9161
- const providerTextValueRef = useRef14("");
9749
+ const providerTextValueRef = useRef15("");
9162
9750
  if (usingProvider) {
9163
9751
  providerTextValueRef.current = controller.textInput.value;
9164
9752
  }
9165
- const inputRef = useRef14(null);
9166
- const formRef = useRef14(null);
9167
- const [items, setItems] = useState17([]);
9168
- const setFileUrlLocal = useCallback15((id, url, status, path) => {
9753
+ const inputRef = useRef15(null);
9754
+ const formRef = useRef15(null);
9755
+ const [items, setItems] = useState19([]);
9756
+ const setFileUrlLocal = useCallback16((id, url, status, path) => {
9169
9757
  setItems((prev) => prev.map((f) => {
9170
9758
  if (f.id !== id) return f;
9171
9759
  if (f.url !== url && f.url.startsWith("blob:")) URL.revokeObjectURL(f.url);
@@ -9173,15 +9761,15 @@ var PromptInput = ({
9173
9761
  }));
9174
9762
  }, []);
9175
9763
  const files = usingProvider ? controller.attachments.files : items;
9176
- const [referencedSources, setReferencedSources] = useState17([]);
9177
- const filesRef = useRef14(files);
9178
- useEffect18(() => {
9764
+ const [referencedSources, setReferencedSources] = useState19([]);
9765
+ const filesRef = useRef15(files);
9766
+ useEffect20(() => {
9179
9767
  filesRef.current = files;
9180
9768
  }, [files]);
9181
- const openFileDialogLocal = useCallback15(() => {
9769
+ const openFileDialogLocal = useCallback16(() => {
9182
9770
  inputRef.current?.click();
9183
9771
  }, []);
9184
- const matchesAccept = useCallback15(
9772
+ const matchesAccept = useCallback16(
9185
9773
  (f) => {
9186
9774
  if (!accept || accept.trim() === "") {
9187
9775
  return true;
@@ -9197,7 +9785,7 @@ var PromptInput = ({
9197
9785
  },
9198
9786
  [accept]
9199
9787
  );
9200
- const addLocal = useCallback15(
9788
+ const addLocal = useCallback16(
9201
9789
  (fileList) => {
9202
9790
  const incoming = [...fileList];
9203
9791
  const accepted = incoming.filter((f) => matchesAccept(f));
@@ -9244,7 +9832,7 @@ var PromptInput = ({
9244
9832
  },
9245
9833
  [matchesAccept, maxFiles, maxFileSize, onError, items.length, onUploadFile, setFileUrlLocal]
9246
9834
  );
9247
- const removeLocal = useCallback15(
9835
+ const removeLocal = useCallback16(
9248
9836
  (id) => setItems((prev) => {
9249
9837
  const found = prev.find((file) => file.id === id);
9250
9838
  if (found?.url.startsWith("blob:")) {
@@ -9254,7 +9842,7 @@ var PromptInput = ({
9254
9842
  }),
9255
9843
  []
9256
9844
  );
9257
- const addWithProviderValidation = useCallback15(
9845
+ const addWithProviderValidation = useCallback16(
9258
9846
  (fileList) => {
9259
9847
  const incoming = [...fileList];
9260
9848
  const accepted = incoming.filter((f) => matchesAccept(f));
@@ -9289,7 +9877,7 @@ var PromptInput = ({
9289
9877
  },
9290
9878
  [matchesAccept, maxFileSize, maxFiles, onError, files.length, controller]
9291
9879
  );
9292
- const clearAttachments = useCallback15(
9880
+ const clearAttachments = useCallback16(
9293
9881
  () => usingProvider ? controller?.attachments.clear() : setItems((prev) => {
9294
9882
  for (const file of prev) {
9295
9883
  if (file.url.startsWith("blob:")) {
@@ -9300,29 +9888,29 @@ var PromptInput = ({
9300
9888
  }),
9301
9889
  [usingProvider, controller]
9302
9890
  );
9303
- const clearReferencedSources = useCallback15(
9891
+ const clearReferencedSources = useCallback16(
9304
9892
  () => setReferencedSources([]),
9305
9893
  []
9306
9894
  );
9307
9895
  const add = usingProvider ? addWithProviderValidation : addLocal;
9308
9896
  const remove = usingProvider ? controller.attachments.remove : removeLocal;
9309
9897
  const openFileDialog = usingProvider ? controller.attachments.openFileDialog : openFileDialogLocal;
9310
- const clear = useCallback15(() => {
9898
+ const clear = useCallback16(() => {
9311
9899
  clearAttachments();
9312
9900
  clearReferencedSources();
9313
9901
  }, [clearAttachments, clearReferencedSources]);
9314
- useEffect18(() => {
9902
+ useEffect20(() => {
9315
9903
  if (!usingProvider) {
9316
9904
  return;
9317
9905
  }
9318
9906
  controller.__registerFileInput(inputRef, () => inputRef.current?.click());
9319
9907
  }, [usingProvider, controller]);
9320
- useEffect18(() => {
9908
+ useEffect20(() => {
9321
9909
  if (syncHiddenInput && inputRef.current && files.length === 0) {
9322
9910
  inputRef.current.value = "";
9323
9911
  }
9324
9912
  }, [files, syncHiddenInput]);
9325
- useEffect18(() => {
9913
+ useEffect20(() => {
9326
9914
  const form = formRef.current;
9327
9915
  if (!form) {
9328
9916
  return;
@@ -9350,7 +9938,7 @@ var PromptInput = ({
9350
9938
  form.removeEventListener("drop", onDrop);
9351
9939
  };
9352
9940
  }, [add, globalDrop]);
9353
- useEffect18(() => {
9941
+ useEffect20(() => {
9354
9942
  if (!globalDrop) {
9355
9943
  return;
9356
9944
  }
@@ -9374,7 +9962,7 @@ var PromptInput = ({
9374
9962
  document.removeEventListener("drop", onDrop);
9375
9963
  };
9376
9964
  }, [add, globalDrop]);
9377
- useEffect18(
9965
+ useEffect20(
9378
9966
  () => () => {
9379
9967
  if (!usingProvider) {
9380
9968
  for (const f of filesRef.current) {
@@ -9386,7 +9974,7 @@ var PromptInput = ({
9386
9974
  },
9387
9975
  [usingProvider]
9388
9976
  );
9389
- const handleChange = useCallback15(
9977
+ const handleChange = useCallback16(
9390
9978
  (event) => {
9391
9979
  if (event.currentTarget.files) {
9392
9980
  add(event.currentTarget.files);
@@ -9396,7 +9984,7 @@ var PromptInput = ({
9396
9984
  [add]
9397
9985
  );
9398
9986
  const setFileUrl = usingProvider ? controller.attachments.setFileUrl : setFileUrlLocal;
9399
- const attachmentsCtx = useMemo13(
9987
+ const attachmentsCtx = useMemo14(
9400
9988
  () => ({
9401
9989
  add,
9402
9990
  clear: clearAttachments,
@@ -9408,7 +9996,7 @@ var PromptInput = ({
9408
9996
  }),
9409
9997
  [files, add, remove, clearAttachments, openFileDialog, setFileUrl]
9410
9998
  );
9411
- const refsCtx = useMemo13(
9999
+ const refsCtx = useMemo14(
9412
10000
  () => ({
9413
10001
  add: (incoming) => {
9414
10002
  const array = Array.isArray(incoming) ? incoming : [incoming];
@@ -9425,7 +10013,7 @@ var PromptInput = ({
9425
10013
  }),
9426
10014
  [referencedSources, clearReferencedSources]
9427
10015
  );
9428
- const handleSubmit = useCallback15(
10016
+ const handleSubmit = useCallback16(
9429
10017
  async (event) => {
9430
10018
  event.preventDefault();
9431
10019
  const form = event.currentTarget;
@@ -9484,8 +10072,8 @@ var PromptInput = ({
9484
10072
  },
9485
10073
  [usingProvider, controller, files, onSubmit, clear]
9486
10074
  );
9487
- const inner = /* @__PURE__ */ jsxs26(Fragment12, { children: [
9488
- /* @__PURE__ */ jsx30(
10075
+ const inner = /* @__PURE__ */ jsxs27(Fragment12, { children: [
10076
+ /* @__PURE__ */ jsx31(
9489
10077
  Input,
9490
10078
  {
9491
10079
  accept,
@@ -9498,7 +10086,7 @@ var PromptInput = ({
9498
10086
  type: "file"
9499
10087
  }
9500
10088
  ),
9501
- /* @__PURE__ */ jsx30(
10089
+ /* @__PURE__ */ jsx31(
9502
10090
  "form",
9503
10091
  {
9504
10092
  "data-boring-agent-part": "composer",
@@ -9506,12 +10094,12 @@ var PromptInput = ({
9506
10094
  onSubmit: handleSubmit,
9507
10095
  ref: formRef,
9508
10096
  ...props,
9509
- children: /* @__PURE__ */ jsx30(InputGroup, { className: "overflow-hidden", children })
10097
+ children: /* @__PURE__ */ jsx31(InputGroup, { className: "overflow-hidden", children })
9510
10098
  }
9511
10099
  )
9512
10100
  ] });
9513
- const withReferencedSources = /* @__PURE__ */ jsx30(LocalReferencedSourcesContext.Provider, { value: refsCtx, children: inner });
9514
- return /* @__PURE__ */ jsx30(LocalAttachmentsContext.Provider, { value: attachmentsCtx, children: withReferencedSources });
10101
+ const withReferencedSources = /* @__PURE__ */ jsx31(LocalReferencedSourcesContext.Provider, { value: refsCtx, children: inner });
10102
+ return /* @__PURE__ */ jsx31(LocalAttachmentsContext.Provider, { value: attachmentsCtx, children: withReferencedSources });
9515
10103
  };
9516
10104
  var PromptInputTextarea = ({
9517
10105
  onChange,
@@ -9522,8 +10110,8 @@ var PromptInputTextarea = ({
9522
10110
  }) => {
9523
10111
  const controller = useOptionalPromptInputController();
9524
10112
  const attachments = usePromptInputAttachments();
9525
- const [isComposing, setIsComposing] = useState17(false);
9526
- const handleKeyDown = useCallback15(
10113
+ const [isComposing, setIsComposing] = useState19(false);
10114
+ const handleKeyDown = useCallback16(
9527
10115
  (e) => {
9528
10116
  onKeyDown?.(e);
9529
10117
  if (e.defaultPrevented) {
@@ -9556,7 +10144,7 @@ var PromptInputTextarea = ({
9556
10144
  },
9557
10145
  [onKeyDown, isComposing, attachments]
9558
10146
  );
9559
- const handlePaste = useCallback15(
10147
+ const handlePaste = useCallback16(
9560
10148
  (event) => {
9561
10149
  const items = event.clipboardData?.items;
9562
10150
  if (!items) {
@@ -9578,8 +10166,8 @@ var PromptInputTextarea = ({
9578
10166
  },
9579
10167
  [attachments]
9580
10168
  );
9581
- const handleCompositionEnd = useCallback15(() => setIsComposing(false), []);
9582
- const handleCompositionStart = useCallback15(() => setIsComposing(true), []);
10169
+ const handleCompositionEnd = useCallback16(() => setIsComposing(false), []);
10170
+ const handleCompositionStart = useCallback16(() => setIsComposing(true), []);
9583
10171
  const controlledProps = controller ? {
9584
10172
  onChange: (e) => {
9585
10173
  controller.textInput.setInput(e.currentTarget.value);
@@ -9589,7 +10177,7 @@ var PromptInputTextarea = ({
9589
10177
  } : {
9590
10178
  onChange
9591
10179
  };
9592
- return /* @__PURE__ */ jsx30(
10180
+ return /* @__PURE__ */ jsx31(
9593
10181
  InputGroupTextarea,
9594
10182
  {
9595
10183
  "data-boring-agent-part": "composer-input",
@@ -9616,15 +10204,15 @@ var PromptInputSubmit = ({
9616
10204
  ...props
9617
10205
  }) => {
9618
10206
  const isGenerating = status === "submitted" || status === "streaming";
9619
- let Icon = /* @__PURE__ */ jsx30(CornerDownLeftIcon, { className: "size-4" });
10207
+ let Icon = /* @__PURE__ */ jsx31(CornerDownLeftIcon, { className: "size-4" });
9620
10208
  if (status === "submitted") {
9621
- Icon = /* @__PURE__ */ jsx30(Spinner, {});
10209
+ Icon = /* @__PURE__ */ jsx31(Spinner, {});
9622
10210
  } else if (status === "streaming") {
9623
- Icon = /* @__PURE__ */ jsx30(SquareIcon, { className: "size-4" });
10211
+ Icon = /* @__PURE__ */ jsx31(SquareIcon, { className: "size-4" });
9624
10212
  } else if (status === "error") {
9625
- Icon = /* @__PURE__ */ jsx30(XIcon4, { className: "size-4" });
10213
+ Icon = /* @__PURE__ */ jsx31(XIcon4, { className: "size-4" });
9626
10214
  }
9627
- const handleClick = useCallback15(
10215
+ const handleClick = useCallback16(
9628
10216
  (e) => {
9629
10217
  if (isGenerating && onStop) {
9630
10218
  e.preventDefault();
@@ -9635,7 +10223,7 @@ var PromptInputSubmit = ({
9635
10223
  },
9636
10224
  [isGenerating, onStop, onClick]
9637
10225
  );
9638
- return /* @__PURE__ */ jsx30(
10226
+ return /* @__PURE__ */ jsx31(
9639
10227
  InputGroupButton2,
9640
10228
  {
9641
10229
  "aria-label": isGenerating ? "Stop" : "Submit",
@@ -9652,8 +10240,8 @@ var PromptInputSubmit = ({
9652
10240
  };
9653
10241
 
9654
10242
  // src/front/primitives/slash-command-picker.tsx
9655
- import { useEffect as useEffect19, useMemo as useMemo14, useRef as useRef15, useState as useState18 } from "react";
9656
- import { Fragment as Fragment13, jsx as jsx31, jsxs as jsxs27 } from "react/jsx-runtime";
10243
+ import { useEffect as useEffect21, useMemo as useMemo15, useRef as useRef16, useState as useState20 } from "react";
10244
+ import { Fragment as Fragment13, jsx as jsx32, jsxs as jsxs28 } from "react/jsx-runtime";
9657
10245
  var ALL_PLUGINS = "__all__";
9658
10246
  function commandGroup(cmd) {
9659
10247
  if (cmd.source === "skill") return "skills";
@@ -9661,20 +10249,20 @@ function commandGroup(cmd) {
9661
10249
  return "built-in";
9662
10250
  }
9663
10251
  function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9664
- const [plugin, setPlugin] = useState18(ALL_PLUGINS);
9665
- const [searchQuery, setSearchQuery] = useState18(query);
9666
- const [activeIdx, setActiveIdx] = useState18(0);
9667
- const containerRef = useRef15(null);
9668
- const listRef = useRef15(null);
9669
- const inputRef = useRef15(null);
9670
- useEffect19(() => {
10252
+ const [plugin, setPlugin] = useState20(ALL_PLUGINS);
10253
+ const [searchQuery, setSearchQuery] = useState20(query);
10254
+ const [activeIdx, setActiveIdx] = useState20(0);
10255
+ const containerRef = useRef16(null);
10256
+ const listRef = useRef16(null);
10257
+ const inputRef = useRef16(null);
10258
+ useEffect21(() => {
9671
10259
  setSearchQuery(query);
9672
10260
  }, [query]);
9673
- const groups = useMemo14(() => {
10261
+ const groups = useMemo15(() => {
9674
10262
  const set = new Set(commands.map(commandGroup));
9675
10263
  return Array.from(set).sort((a, b) => a.localeCompare(b));
9676
10264
  }, [commands]);
9677
- const filtered = useMemo14(() => {
10265
+ const filtered = useMemo15(() => {
9678
10266
  const q = searchQuery.trim().toLowerCase();
9679
10267
  return commands.filter((c) => {
9680
10268
  if (plugin !== ALL_PLUGINS && commandGroup(c) !== plugin) return false;
@@ -9682,10 +10270,10 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9682
10270
  return c.name.toLowerCase().includes(q) || c.description.toLowerCase().includes(q);
9683
10271
  });
9684
10272
  }, [commands, searchQuery, plugin]);
9685
- useEffect19(() => {
10273
+ useEffect21(() => {
9686
10274
  setActiveIdx((i) => filtered.length === 0 ? 0 : Math.min(i, filtered.length - 1));
9687
10275
  }, [filtered.length]);
9688
- useEffect19(() => {
10276
+ useEffect21(() => {
9689
10277
  const handler = (e) => {
9690
10278
  if (containerRef.current && !containerRef.current.contains(e.target)) {
9691
10279
  onDismiss();
@@ -9704,8 +10292,8 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9704
10292
  },
9705
10293
  onDismiss
9706
10294
  });
9707
- return /* @__PURE__ */ jsxs27("div", { ref: containerRef, className: "mb-1 w-full overflow-hidden rounded-lg border border-border/60 bg-popover shadow-lg", children: [
9708
- /* @__PURE__ */ jsx31(
10295
+ return /* @__PURE__ */ jsxs28("div", { ref: containerRef, className: "mb-1 w-full overflow-hidden rounded-lg border border-border/60 bg-popover shadow-lg", children: [
10296
+ /* @__PURE__ */ jsx32(
9709
10297
  "input",
9710
10298
  {
9711
10299
  ref: inputRef,
@@ -9720,9 +10308,9 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9720
10308
  autoComplete: "off"
9721
10309
  }
9722
10310
  ),
9723
- /* @__PURE__ */ jsx31("div", { className: "flex flex-wrap gap-1 border-b border-border/50 px-2 py-1.5", role: "tablist", "aria-label": "Filter by plugin", children: [ALL_PLUGINS, ...groups].map((g) => {
10311
+ /* @__PURE__ */ jsx32("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) => {
9724
10312
  const selected = plugin === g;
9725
- return /* @__PURE__ */ jsx31(
10313
+ return /* @__PURE__ */ jsx32(
9726
10314
  "button",
9727
10315
  {
9728
10316
  type: "button",
@@ -9742,7 +10330,7 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9742
10330
  g
9743
10331
  );
9744
10332
  }) }),
9745
- filtered.length === 0 ? /* @__PURE__ */ jsx31("div", { className: "px-3 py-2 text-[11px] text-muted-foreground", children: "No matching commands." }) : /* @__PURE__ */ jsx31("ul", { ref: listRef, className: "max-h-80 overflow-y-auto py-1", role: "listbox", "aria-label": "Commands", children: filtered.map((cmd, i) => /* @__PURE__ */ jsxs27(
10333
+ filtered.length === 0 ? /* @__PURE__ */ jsx32("div", { className: "px-3 py-2 text-[11px] text-muted-foreground", children: "No matching commands." }) : /* @__PURE__ */ jsx32("ul", { ref: listRef, className: "max-h-80 overflow-y-auto py-1", role: "listbox", "aria-label": "Commands", children: filtered.map((cmd, i) => /* @__PURE__ */ jsxs28(
9746
10334
  "li",
9747
10335
  {
9748
10336
  role: "option",
@@ -9760,20 +10348,20 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9760
10348
  onSelect(cmd.name);
9761
10349
  },
9762
10350
  children: [
9763
- /* @__PURE__ */ jsxs27("span", { className: "flex items-center gap-1.5", children: [
9764
- /* @__PURE__ */ jsxs27("span", { className: "font-medium text-foreground/80", children: [
10351
+ /* @__PURE__ */ jsxs28("span", { className: "flex items-center gap-1.5", children: [
10352
+ /* @__PURE__ */ jsxs28("span", { className: "font-medium text-foreground/80", children: [
9765
10353
  "/",
9766
10354
  cmd.name
9767
10355
  ] }),
9768
- cmd.source === "skill" ? /* @__PURE__ */ jsxs27(Fragment13, { children: [
9769
- /* @__PURE__ */ jsx31("span", { className: "rounded-sm bg-accent/15 px-1 py-px text-[9px] font-medium uppercase tracking-wide text-accent-foreground", children: "skill" }),
9770
- cmd.sourcePlugin ? /* @__PURE__ */ jsx31("span", { className: "rounded-sm bg-muted px-1 py-px text-[9px] font-medium text-muted-foreground", children: cmd.sourcePlugin }) : null
10356
+ cmd.source === "skill" ? /* @__PURE__ */ jsxs28(Fragment13, { children: [
10357
+ /* @__PURE__ */ jsx32("span", { className: "rounded-sm bg-accent/15 px-1 py-px text-[9px] font-medium uppercase tracking-wide text-accent-foreground", children: "skill" }),
10358
+ cmd.sourcePlugin ? /* @__PURE__ */ jsx32("span", { className: "rounded-sm bg-muted px-1 py-px text-[9px] font-medium text-muted-foreground", children: cmd.sourcePlugin }) : null
9771
10359
  ] }) : cmd.sourcePlugin ? (
9772
10360
  // Originating plugin/package for non-skill commands.
9773
- /* @__PURE__ */ jsx31("span", { className: "rounded-sm bg-muted px-1 py-px text-[9px] font-medium text-muted-foreground", children: cmd.sourcePlugin })
10361
+ /* @__PURE__ */ jsx32("span", { className: "rounded-sm bg-muted px-1 py-px text-[9px] font-medium text-muted-foreground", children: cmd.sourcePlugin })
9774
10362
  ) : null
9775
10363
  ] }),
9776
- /* @__PURE__ */ jsx31("span", { className: "truncate text-[11px] opacity-50", children: cmd.description })
10364
+ /* @__PURE__ */ jsx32("span", { className: "truncate text-[11px] opacity-50", children: cmd.description })
9777
10365
  ]
9778
10366
  },
9779
10367
  cmd.name
@@ -9784,10 +10372,10 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9784
10372
  // src/front/chat/components/ComposerAttachments.tsx
9785
10373
  import { AlertCircleIcon as AlertCircleIcon2, Loader2 as Loader22, PaperclipIcon as PaperclipIcon2 } from "lucide-react";
9786
10374
  import { IconButton as IconButton3 } from "@hachej/boring-ui-kit";
9787
- import { jsx as jsx32, jsxs as jsxs28 } from "react/jsx-runtime";
10375
+ import { jsx as jsx33, jsxs as jsxs29 } from "react/jsx-runtime";
9788
10376
  function AttachmentButton({ disabled, className }) {
9789
10377
  const attachments = usePromptInputAttachments();
9790
- return /* @__PURE__ */ jsx32(
10378
+ return /* @__PURE__ */ jsx33(
9791
10379
  IconButton3,
9792
10380
  {
9793
10381
  type: "button",
@@ -9800,21 +10388,21 @@ function AttachmentButton({ disabled, className }) {
9800
10388
  className: cn(composerActionClass, "composer-attachment-button !h-10 !w-10", className),
9801
10389
  "aria-label": "Attach files",
9802
10390
  title: disabled ? "Attachments are available when the composer is ready." : "Attach files",
9803
- children: /* @__PURE__ */ jsx32(PaperclipIcon2, { className: "h-4 w-4", strokeWidth: 1.75 })
10391
+ children: /* @__PURE__ */ jsx33(PaperclipIcon2, { className: "h-4 w-4", strokeWidth: 1.75 })
9804
10392
  }
9805
10393
  );
9806
10394
  }
9807
10395
  function AttachmentsList() {
9808
10396
  const attachments = usePromptInputAttachments();
9809
10397
  if (attachments.files.length === 0) return null;
9810
- return /* @__PURE__ */ jsx32(
10398
+ return /* @__PURE__ */ jsx33(
9811
10399
  Attachments,
9812
10400
  {
9813
10401
  "data-boring-agent-part": "composer-attachment-row",
9814
10402
  "data-align": "block-start",
9815
10403
  variant: "inline",
9816
10404
  className: "w-full flex-wrap items-center justify-start gap-2 px-5 pb-0 pt-3",
9817
- children: attachments.files.map((file) => /* @__PURE__ */ jsxs28(
10405
+ children: attachments.files.map((file) => /* @__PURE__ */ jsxs29(
9818
10406
  Attachment,
9819
10407
  {
9820
10408
  data: file,
@@ -9825,13 +10413,13 @@ function AttachmentsList() {
9825
10413
  file.status === "error" && "!border-destructive/50 !bg-destructive/10"
9826
10414
  ),
9827
10415
  children: [
9828
- /* @__PURE__ */ jsxs28("div", { className: "relative shrink-0", children: [
9829
- /* @__PURE__ */ jsx32(AttachmentPreview, { className: "!size-7 overflow-hidden !rounded-full bg-background/60" }),
9830
- file.status === "uploading" ? /* @__PURE__ */ jsx32("div", { className: "absolute inset-0 flex items-center justify-center rounded-full bg-background/70", children: /* @__PURE__ */ jsx32(Loader22, { className: "size-3.5 animate-spin text-muted-foreground" }) }) : null,
9831
- file.status === "error" ? /* @__PURE__ */ jsx32("div", { className: "absolute inset-0 flex items-center justify-center rounded-full bg-destructive/20", children: /* @__PURE__ */ jsx32(AlertCircleIcon2, { className: "size-3.5 text-destructive" }) }) : null
10416
+ /* @__PURE__ */ jsxs29("div", { className: "relative shrink-0", children: [
10417
+ /* @__PURE__ */ jsx33(AttachmentPreview, { className: "!size-7 overflow-hidden !rounded-full bg-background/60" }),
10418
+ file.status === "uploading" ? /* @__PURE__ */ jsx33("div", { className: "absolute inset-0 flex items-center justify-center rounded-full bg-background/70", children: /* @__PURE__ */ jsx33(Loader22, { className: "size-3.5 animate-spin text-muted-foreground" }) }) : null,
10419
+ file.status === "error" ? /* @__PURE__ */ jsx33("div", { className: "absolute inset-0 flex items-center justify-center rounded-full bg-destructive/20", children: /* @__PURE__ */ jsx33(AlertCircleIcon2, { className: "size-3.5 text-destructive" }) }) : null
9832
10420
  ] }),
9833
- /* @__PURE__ */ jsx32(AttachmentInfo, { className: "min-w-0 !max-w-[180px] truncate text-[13px] font-medium" }),
9834
- /* @__PURE__ */ jsx32(AttachmentRemove, { className: "composer-attachment-remove !size-5 !rounded-full !opacity-100 text-muted-foreground/80 hover:text-foreground" })
10421
+ /* @__PURE__ */ jsx33(AttachmentInfo, { className: "min-w-0 !max-w-[180px] truncate text-[13px] font-medium" }),
10422
+ /* @__PURE__ */ jsx33(AttachmentRemove, { className: "composer-attachment-remove !size-5 !rounded-full !opacity-100 text-muted-foreground/80 hover:text-foreground" })
9835
10423
  ]
9836
10424
  },
9837
10425
  file.id
@@ -9841,7 +10429,7 @@ function AttachmentsList() {
9841
10429
  }
9842
10430
 
9843
10431
  // src/front/chat/components/PiChatComposerSurface.tsx
9844
- import { jsx as jsx33, jsxs as jsxs29 } from "react/jsx-runtime";
10432
+ import { jsx as jsx34, jsxs as jsxs30 } from "react/jsx-runtime";
9845
10433
  var MAX_PROMPT_ATTACHMENTS = 2;
9846
10434
  var MAX_PROMPT_ATTACHMENT_BYTES = 10 * 1024 * 1024;
9847
10435
  var COMPOSER_INPUT_GROUP_MIN_HEIGHT = 56;
@@ -9880,6 +10468,10 @@ function PiChatComposerSurface({
9880
10468
  onComposerBlockerAction,
9881
10469
  queuePreview,
9882
10470
  onEditQueued,
10471
+ onResumeQueued,
10472
+ resumeQueuedPending,
10473
+ onRemoveQueued,
10474
+ queueMutationPending,
9883
10475
  hotReloadEnabled,
9884
10476
  pluginUpdateState,
9885
10477
  onDismissPluginUpdate,
@@ -9928,13 +10520,14 @@ function PiChatComposerSurface({
9928
10520
  const prefersReducedMotion = useReducedMotion();
9929
10521
  const workspaceRequestId = getHeaderValue(requestHeaders, "x-boring-workspace-id");
9930
10522
  const composerContributions = useComposerContributions();
9931
- const uploadAttachment = useCallback16((file) => uploadFile(file, {
10523
+ const uploadAttachment = useCallback17((file) => uploadFile(file, {
9932
10524
  apiBaseUrl,
9933
10525
  workspaceRequestId,
10526
+ requestHeaders,
9934
10527
  responseUrl: "raw",
9935
10528
  fetch: fetch2
9936
- }), [apiBaseUrl, fetch2, workspaceRequestId]);
9937
- const resizeTextarea = useCallback16((node) => {
10529
+ }), [apiBaseUrl, fetch2, requestHeaders, workspaceRequestId]);
10530
+ const resizeTextarea = useCallback17((node) => {
9938
10531
  if (!node) return;
9939
10532
  node.style.height = "auto";
9940
10533
  const style = window.getComputedStyle(node);
@@ -9958,11 +10551,11 @@ function PiChatComposerSurface({
9958
10551
  useLayoutEffect3(() => {
9959
10552
  resizeTextarea(textareaRef.current);
9960
10553
  }, [draft, resizeTextarea, textareaRef]);
9961
- return /* @__PURE__ */ jsxs29("div", { className: cn(
10554
+ return /* @__PURE__ */ jsxs30("div", { className: cn(
9962
10555
  "relative z-20",
9963
10556
  chrome ? "pb-[calc(1rem+env(safe-area-inset-bottom))] pl-[calc(1rem+env(safe-area-inset-left))] pr-[calc(1rem+env(safe-area-inset-right))] pt-2 sm:pb-[calc(1.25rem+env(safe-area-inset-bottom))] sm:pl-[calc(1.5rem+env(safe-area-inset-left))] sm:pr-[calc(1.5rem+env(safe-area-inset-right))]" : "pb-[calc(0.5rem+env(safe-area-inset-bottom))] pl-[calc(0.75rem+env(safe-area-inset-left))] pr-[calc(0.75rem+env(safe-area-inset-right))] pt-1"
9964
10557
  ), children: [
9965
- /* @__PURE__ */ jsx33(
10558
+ /* @__PURE__ */ jsx34(
9966
10559
  "div",
9967
10560
  {
9968
10561
  "data-boring-agent-part": "chat-working-slot",
@@ -9972,7 +10565,7 @@ function PiChatComposerSurface({
9972
10565
  isStreaming ? "mb-2 max-h-8 opacity-100" : "mb-0 max-h-0 opacity-0"
9973
10566
  ),
9974
10567
  "aria-hidden": !isStreaming,
9975
- children: /* @__PURE__ */ jsxs29(
10568
+ children: /* @__PURE__ */ jsxs30(
9976
10569
  "div",
9977
10570
  {
9978
10571
  "data-testid": isStreaming ? "chat-working" : void 0,
@@ -9980,7 +10573,7 @@ function PiChatComposerSurface({
9980
10573
  "aria-live": isStreaming ? "polite" : void 0,
9981
10574
  className: "inline-flex items-center gap-2 rounded-full border border-border/50 bg-background/85 px-2.5 py-1 text-[12px] text-foreground shadow-sm backdrop-blur",
9982
10575
  children: [
9983
- /* @__PURE__ */ jsx33(
10576
+ /* @__PURE__ */ jsx34(
9984
10577
  motion2.span,
9985
10578
  {
9986
10579
  "aria-hidden": "true",
@@ -9989,14 +10582,14 @@ function PiChatComposerSurface({
9989
10582
  transition: prefersReducedMotion ? { duration: 0 } : { duration: 1.2, repeat: Infinity, ease: "easeInOut" }
9990
10583
  }
9991
10584
  ),
9992
- /* @__PURE__ */ jsx33("span", { children: "Working\u2026" })
10585
+ /* @__PURE__ */ jsx34("span", { children: "Working\u2026" })
9993
10586
  ]
9994
10587
  }
9995
10588
  )
9996
10589
  }
9997
10590
  ),
9998
- composerStatusNotice ? /* @__PURE__ */ jsx33(ComposerRuntimeNotice, { notice: composerStatusNotice }) : null,
9999
- composerBlocked && !workspaceWarmupBlocked ? /* @__PURE__ */ jsx33(
10591
+ composerStatusNotice ? /* @__PURE__ */ jsx34(ComposerRuntimeNotice, { notice: composerStatusNotice }) : null,
10592
+ composerBlocked && !workspaceWarmupBlocked ? /* @__PURE__ */ jsx34(
10000
10593
  ComposerBlockerNotice,
10001
10594
  {
10002
10595
  blocker: primaryComposerBlocker,
@@ -10004,8 +10597,18 @@ function PiChatComposerSurface({
10004
10597
  onAction: onComposerBlockerAction
10005
10598
  }
10006
10599
  ) : null,
10007
- queuePreview.length > 0 ? /* @__PURE__ */ jsx33(QueuedComposerNotice, { followUps: queuePreview, onEdit: onEditQueued }) : null,
10008
- hotReloadEnabled ? /* @__PURE__ */ jsx33(
10600
+ queuePreview.length > 0 ? /* @__PURE__ */ jsx34(
10601
+ QueuedComposerNotice,
10602
+ {
10603
+ followUps: queuePreview,
10604
+ onEdit: onEditQueued,
10605
+ onResume: onResumeQueued,
10606
+ resumePending: resumeQueuedPending,
10607
+ onRemove: onRemoveQueued,
10608
+ actionPending: queueMutationPending
10609
+ }
10610
+ ) : null,
10611
+ hotReloadEnabled ? /* @__PURE__ */ jsx34(
10009
10612
  PluginUpdateStatus,
10010
10613
  {
10011
10614
  state: pluginUpdateState,
@@ -10013,14 +10616,14 @@ function PiChatComposerSurface({
10013
10616
  onRetry: onRunPluginUpdate
10014
10617
  }
10015
10618
  ) : null,
10016
- /* @__PURE__ */ jsx33(
10619
+ /* @__PURE__ */ jsx34(
10017
10620
  CommandRunStatus,
10018
10621
  {
10019
10622
  state: commandNotifyState,
10020
10623
  onDismiss: onDismissCommandNotify
10021
10624
  }
10022
10625
  ),
10023
- attachmentNotice ? /* @__PURE__ */ jsx33(
10626
+ attachmentNotice ? /* @__PURE__ */ jsx34(
10024
10627
  "div",
10025
10628
  {
10026
10629
  role: "status",
@@ -10029,12 +10632,12 @@ function PiChatComposerSurface({
10029
10632
  children: attachmentNotice
10030
10633
  }
10031
10634
  ) : null,
10032
- /* @__PURE__ */ jsxs29("div", { className: cn("@container group/composer relative mx-auto w-full", chrome ? "max-w-3xl" : "max-w-[680px]"), children: [
10033
- /* @__PURE__ */ jsxs29("div", { className: cn(
10635
+ /* @__PURE__ */ jsxs30("div", { className: cn("@container group/composer relative mx-auto w-full", chrome ? "max-w-3xl" : "max-w-[680px]"), children: [
10636
+ /* @__PURE__ */ jsxs30("div", { className: cn(
10034
10637
  "absolute inset-x-0 z-30",
10035
10638
  pickerPlacement === "above-compact" ? "bottom-[calc(100%+4px)] [&_[cmdk-list]]:!max-h-[200px]" : "bottom-[calc(100%+4px)]"
10036
10639
  ), children: [
10037
- mentionState ? /* @__PURE__ */ jsx33("div", { className: "mr-px ml-[43px]", children: /* @__PURE__ */ jsx33(
10640
+ mentionState ? /* @__PURE__ */ jsx34("div", { className: "mr-px ml-[43px]", children: /* @__PURE__ */ jsx34(
10038
10641
  MentionPicker,
10039
10642
  {
10040
10643
  mention: mentionState,
@@ -10046,7 +10649,7 @@ function PiChatComposerSurface({
10046
10649
  onDismiss: onDismissMention
10047
10650
  }
10048
10651
  ) }) : null,
10049
- slashQuery !== null ? /* @__PURE__ */ jsx33("div", { className: "mr-px ml-[43px]", children: /* @__PURE__ */ jsx33(
10652
+ slashQuery !== null ? /* @__PURE__ */ jsx34("div", { className: "mr-px ml-[43px]", children: /* @__PURE__ */ jsx34(
10050
10653
  SlashCommandPicker,
10051
10654
  {
10052
10655
  query: slashQuery,
@@ -10055,7 +10658,7 @@ function PiChatComposerSurface({
10055
10658
  onDismiss: onDismissSlash
10056
10659
  }
10057
10660
  ) }) : null,
10058
- mentionState === null && slashQuery === null && modelPickerOpen ? /* @__PURE__ */ jsx33("div", { className: "mr-px mb-1 ml-[43px]", children: /* @__PURE__ */ jsx33(
10661
+ mentionState === null && slashQuery === null && modelPickerOpen ? /* @__PURE__ */ jsx34("div", { className: "mr-px mb-1 ml-[43px]", children: /* @__PURE__ */ jsx34(
10059
10662
  ModelPickerMenu,
10060
10663
  {
10061
10664
  value: selectedModel,
@@ -10066,7 +10669,7 @@ function PiChatComposerSurface({
10066
10669
  onClose: () => onSetModelPickerOpen(false)
10067
10670
  }
10068
10671
  ) }) : null,
10069
- mentionState === null && slashQuery === null && thinkingPickerOpen ? /* @__PURE__ */ jsx33("div", { className: "mr-px mb-1 ml-[43px]", children: /* @__PURE__ */ jsx33(
10672
+ mentionState === null && slashQuery === null && thinkingPickerOpen ? /* @__PURE__ */ jsx34("div", { className: "mr-px mb-1 ml-[43px]", children: /* @__PURE__ */ jsx34(
10070
10673
  ThinkingPickerMenu,
10071
10674
  {
10072
10675
  value: selectedThinking,
@@ -10076,17 +10679,17 @@ function PiChatComposerSurface({
10076
10679
  }
10077
10680
  ) }) : null
10078
10681
  ] }),
10079
- composerContributions.map(({ id, Top }) => Top ? /* @__PURE__ */ jsx33(
10682
+ composerContributions.map(({ id, Top }) => Top ? /* @__PURE__ */ jsx34(
10080
10683
  "div",
10081
10684
  {
10082
10685
  "data-boring-agent-part": "composer-contribution-top",
10083
10686
  "data-boring-agent-contribution-id": id,
10084
10687
  className: cn("mx-auto mb-2 w-full empty:hidden", chrome ? "max-w-3xl" : "max-w-[680px]"),
10085
- children: /* @__PURE__ */ jsx33(Top, {})
10688
+ children: /* @__PURE__ */ jsx34(Top, {})
10086
10689
  },
10087
10690
  id
10088
10691
  ) : null),
10089
- /* @__PURE__ */ jsx33(
10692
+ /* @__PURE__ */ jsx34(
10090
10693
  "div",
10091
10694
  {
10092
10695
  "data-boring-agent-part": "composer-rail",
@@ -10105,7 +10708,7 @@ function PiChatComposerSurface({
10105
10708
  "[&[data-composer-multiline=true]_[data-boring-agent-part=composer-submit-addon]]:self-end",
10106
10709
  "[&[data-composer-multiline=true]_[data-boring-agent-part=composer-submit-addon]]:mb-2"
10107
10710
  ),
10108
- children: /* @__PURE__ */ jsxs29(
10711
+ children: /* @__PURE__ */ jsxs30(
10109
10712
  PromptInput,
10110
10713
  {
10111
10714
  "data-boring-state": status,
@@ -10121,15 +10724,15 @@ function PiChatComposerSurface({
10121
10724
  else onAttachmentNotice(err.message || "Attachment rejected.");
10122
10725
  },
10123
10726
  children: [
10124
- /* @__PURE__ */ jsx33(AttachmentsList, {}),
10125
- /* @__PURE__ */ jsxs29(
10727
+ /* @__PURE__ */ jsx34(AttachmentsList, {}),
10728
+ /* @__PURE__ */ jsxs30(
10126
10729
  "div",
10127
10730
  {
10128
10731
  "data-boring-agent-part": "composer-input-row",
10129
10732
  className: "flex min-h-[var(--composer-input-group-height)] w-full items-center",
10130
10733
  children: [
10131
- /* @__PURE__ */ jsx33("div", { className: "flex h-14 shrink-0 items-center pl-2", children: /* @__PURE__ */ jsx33(AttachmentButton, { disabled: disabled || isStreaming }) }),
10132
- /* @__PURE__ */ jsx33(
10734
+ /* @__PURE__ */ jsx34("div", { className: "flex h-14 shrink-0 items-center pl-2", children: /* @__PURE__ */ jsx34(AttachmentButton, { disabled: disabled || isStreaming }) }),
10735
+ /* @__PURE__ */ jsx34(
10133
10736
  PromptInputTextarea,
10134
10737
  {
10135
10738
  value: draft,
@@ -10153,24 +10756,24 @@ function PiChatComposerSurface({
10153
10756
  )
10154
10757
  }
10155
10758
  ),
10156
- /* @__PURE__ */ jsx33(
10759
+ /* @__PURE__ */ jsx34(
10157
10760
  PromptInputFooter,
10158
10761
  {
10159
10762
  align: "inline-end",
10160
10763
  "data-boring-agent-part": "composer-submit-addon",
10161
10764
  className: "!order-none !w-auto shrink-0 self-center justify-between border-0 bg-transparent !px-2 !py-0",
10162
- children: /* @__PURE__ */ jsxs29("div", { className: "ml-auto flex items-center gap-1.5", children: [
10163
- composerContributions.map(({ id, Action }) => Action ? /* @__PURE__ */ jsx33(
10765
+ children: /* @__PURE__ */ jsxs30("div", { className: "ml-auto flex items-center gap-1.5", children: [
10766
+ composerContributions.map(({ id, Action }) => Action ? /* @__PURE__ */ jsx34(
10164
10767
  "span",
10165
10768
  {
10166
10769
  "data-boring-agent-part": "composer-contribution-action",
10167
10770
  "data-boring-agent-contribution-id": id,
10168
10771
  className: "contents",
10169
- children: /* @__PURE__ */ jsx33(Action, { updateDraft })
10772
+ children: /* @__PURE__ */ jsx34(Action, { updateDraft })
10170
10773
  },
10171
10774
  id
10172
10775
  ) : null),
10173
- /* @__PURE__ */ jsx33(
10776
+ /* @__PURE__ */ jsx34(
10174
10777
  PromptInputSubmit,
10175
10778
  {
10176
10779
  "data-boring-agent-part": "composer-submit",
@@ -10200,7 +10803,7 @@ function PiChatComposerSurface({
10200
10803
  )
10201
10804
  }
10202
10805
  ),
10203
- hideComposerSettings ? null : /* @__PURE__ */ jsx33(
10806
+ hideComposerSettings ? null : /* @__PURE__ */ jsx34(
10204
10807
  "div",
10205
10808
  {
10206
10809
  "data-boring-agent-part": "composer-settings-row",
@@ -10208,8 +10811,8 @@ function PiChatComposerSurface({
10208
10811
  "mx-auto mt-2 grid min-h-8 w-full grid-cols-[minmax(0,1fr)_auto] items-center text-xs text-muted-foreground",
10209
10812
  chrome ? "max-w-3xl" : "max-w-[680px]"
10210
10813
  ),
10211
- children: /* @__PURE__ */ jsxs29("div", { className: "flex min-w-0 items-center justify-center gap-1", children: [
10212
- /* @__PURE__ */ jsx33(
10814
+ children: /* @__PURE__ */ jsxs30("div", { className: "flex min-w-0 items-center justify-center gap-1", children: [
10815
+ /* @__PURE__ */ jsx34(
10213
10816
  ModelSelectTrigger,
10214
10817
  {
10215
10818
  value: selectedModel,
@@ -10229,7 +10832,7 @@ function PiChatComposerSurface({
10229
10832
  }
10230
10833
  }
10231
10834
  ),
10232
- thinkingControl ? /* @__PURE__ */ jsx33(
10835
+ thinkingControl ? /* @__PURE__ */ jsx34(
10233
10836
  ThinkingSelectTrigger,
10234
10837
  {
10235
10838
  value: selectedThinking,
@@ -10258,7 +10861,7 @@ function getHeaderValue(headers, name) {
10258
10861
  }
10259
10862
 
10260
10863
  // src/front/chat/piChatPanelHooks.ts
10261
- import { useCallback as useCallback17, useEffect as useEffect20, useMemo as useMemo15, useRef as useRef16, useState as useState19, useSyncExternalStore } from "react";
10864
+ import { useCallback as useCallback18, useEffect as useEffect22, useMemo as useMemo16, useRef as useRef17, useState as useState21, useSyncExternalStore } from "react";
10262
10865
 
10263
10866
  // src/front/chat/piChatPanelUtils.ts
10264
10867
  function headersContentKey(headers) {
@@ -10378,19 +10981,19 @@ function useExternalRemotePiSession({
10378
10981
  remoteSessionOptions,
10379
10982
  enabled = true
10380
10983
  }) {
10381
- const [session, setSession] = useState19();
10382
- const remoteSessionOptionsRef = useRef16(remoteSessionOptions);
10984
+ const [session, setSession] = useState21();
10985
+ const remoteSessionOptionsRef = useRef17(remoteSessionOptions);
10383
10986
  remoteSessionOptionsRef.current = remoteSessionOptions;
10384
10987
  const requestHeadersKey = headersContentKey(requestHeaders);
10385
- const stableRequestHeaders = useMemo15(
10988
+ const stableRequestHeaders = useMemo16(
10386
10989
  () => normalizedHeadersFromContentKey(requestHeadersKey),
10387
10990
  [requestHeadersKey]
10388
10991
  );
10389
- const remoteSessionOptionsKey = useMemo15(
10992
+ const remoteSessionOptionsKey = useMemo16(
10390
10993
  () => remoteSessionOptionsIdentity2(remoteSessionOptions),
10391
10994
  [remoteSessionOptions]
10392
10995
  );
10393
- useEffect20(() => {
10996
+ useEffect22(() => {
10394
10997
  if (!sessionId) {
10395
10998
  setSession(void 0);
10396
10999
  return;
@@ -10409,7 +11012,7 @@ function useExternalRemotePiSession({
10409
11012
  setSession(next);
10410
11013
  return () => next.dispose();
10411
11014
  }, [agentTypeId, apiBaseUrl, createRemoteSession, fetch2, remoteSessionOptionsKey, sessionId, stableRequestHeaders, storageScope, workspaceId]);
10412
- useEffect20(() => {
11015
+ useEffect22(() => {
10413
11016
  if (!session) return;
10414
11017
  if (enabled) session.resumeStream?.();
10415
11018
  else session.suspendStream?.();
@@ -10452,20 +11055,83 @@ function remoteSessionOptionsIdentity2(options) {
10452
11055
  }
10453
11056
  function useRemotePiSessionState(session) {
10454
11057
  return useSyncExternalStore(
10455
- useCallback17((listener) => session?.subscribe(listener) ?? (() => {
11058
+ useCallback18((listener) => session?.subscribe(listener) ?? (() => {
10456
11059
  }), [session]),
10457
- useCallback17(() => session?.getState(), [session]),
10458
- useCallback17(() => session?.getState(), [session])
11060
+ useCallback18(() => session?.getState(), [session]),
11061
+ useCallback18(() => session?.getState(), [session])
10459
11062
  );
10460
11063
  }
10461
11064
 
11065
+ // src/front/chat/largePromptSpill.ts
11066
+ var DEFAULT_LARGE_PROMPT_THRESHOLD_CHARS = 12e3;
11067
+ var LargePromptSpillCache = class {
11068
+ constructor(maxEntries = 4) {
11069
+ this.maxEntries = maxEntries;
11070
+ }
11071
+ maxEntries;
11072
+ receipts = /* @__PURE__ */ new Map();
11073
+ getOrUpload(destinationIdentity, sessionId, text, upload) {
11074
+ const key = `${destinationIdentity}\0${sessionId}\0${text}`;
11075
+ const existing = this.receipts.get(key);
11076
+ if (existing) return existing;
11077
+ const pending = upload().catch((error) => {
11078
+ this.receipts.delete(key);
11079
+ throw error;
11080
+ });
11081
+ this.receipts.set(key, pending);
11082
+ while (this.receipts.size > this.maxEntries) {
11083
+ const oldest = this.receipts.keys().next().value;
11084
+ if (oldest === void 0) break;
11085
+ this.receipts.delete(oldest);
11086
+ }
11087
+ return pending;
11088
+ }
11089
+ };
11090
+ async function spillLargePrompt(text, options) {
11091
+ const thresholdChars = options.thresholdChars ?? DEFAULT_LARGE_PROMPT_THRESHOLD_CHARS;
11092
+ if (options.enabled === false || thresholdChars < 1 || text.length <= thresholdChars) return void 0;
11093
+ const upload = options.upload ?? ((file) => uploadFile(file, {
11094
+ apiBaseUrl: options.apiBaseUrl,
11095
+ workspaceRequestId: options.workspaceRequestId,
11096
+ requestHeaders: options.requestHeaders,
11097
+ responseUrl: "raw",
11098
+ fetch: options.fetch
11099
+ }));
11100
+ const uploadPrompt = () => upload(new File(
11101
+ [text],
11102
+ `composer-input-${options.sessionId}.md`,
11103
+ { type: "text/markdown" }
11104
+ ));
11105
+ const receipt = await (options.cache ? options.cache.getOrUpload(options.destinationIdentity ?? "", options.sessionId, text, uploadPrompt) : uploadPrompt());
11106
+ return largePromptReference(receipt.path, text.length);
11107
+ }
11108
+ function largePromptReference(path, characters) {
11109
+ return {
11110
+ replacement: {
11111
+ displayText: `Large input saved \xB7 ${characters.toLocaleString()} characters \xB7 ${path}`,
11112
+ text: [
11113
+ "[Large composer input stored in workspace]",
11114
+ `path: ${path}`,
11115
+ `characters: ${characters}`,
11116
+ "The full user input is stored locally at the workspace path above and was intentionally omitted from this chat message.",
11117
+ "Treat the file contents as the user input. Read the file before answering when its contents are needed."
11118
+ ].join("\n")
11119
+ }
11120
+ };
11121
+ }
11122
+
10462
11123
  // src/front/chat/PiChatPanel.tsx
10463
- import { jsx as jsx34, jsxs as jsxs30 } from "react/jsx-runtime";
11124
+ import { jsx as jsx35, jsxs as jsxs31 } from "react/jsx-runtime";
10464
11125
  var DebugDrawer2 = lazy(() => import("../DebugDrawer-JNA43SVR.js").then((m) => ({ default: m.DebugDrawer })));
10465
11126
  var EMPTY_COMMANDS = [];
10466
11127
  var EMPTY_COMMAND_NAMES = [];
10467
11128
  var EMPTY_BLOCKERS = [];
10468
11129
  var RUN_REJECTED_NOTICE_ID = "run-rejected";
11130
+ var RESUME_QUEUED_ERROR_PREFIX = "resume-queued-error:";
11131
+ function requestHeaderValue(headers, name) {
11132
+ const target = name.toLowerCase();
11133
+ return Object.entries(headers ?? {}).find(([key]) => key.toLowerCase() === target)?.[1];
11134
+ }
10469
11135
  function normalizeAgentPluginReloadResult(result) {
10470
11136
  if (typeof result !== "string") return result;
10471
11137
  const firstLine = result.trim().split(/\r?\n/).find((line) => line.trim().length > 0)?.trim() ?? "";
@@ -10516,6 +11182,8 @@ function PiChatPanel({
10516
11182
  allowPromptDuringInitialHydration = false,
10517
11183
  workspaceWarmupStatus,
10518
11184
  onSessionReset,
11185
+ spillLargePrompts = true,
11186
+ largePromptThresholdChars,
10519
11187
  onBeforeSubmit,
10520
11188
  onReloadAgentPlugins,
10521
11189
  onCommandResult,
@@ -10532,14 +11200,14 @@ function PiChatPanel({
10532
11200
  }) {
10533
11201
  const externalSessionId = sessionId?.trim() || void 0;
10534
11202
  const showSessionSidebar = showSessions ?? externalSessionId === void 0;
10535
- const onDataRef = useRef17(onData);
11203
+ const onDataRef = useRef18(onData);
10536
11204
  onDataRef.current = onData;
10537
- const onTurnCompleteRef = useRef17(onTurnComplete);
11205
+ const onTurnCompleteRef = useRef18(onTurnComplete);
10538
11206
  onTurnCompleteRef.current = onTurnComplete;
10539
- const sessionListRefreshRef = useRef17(void 0);
10540
- const requestHeadersKey = useMemo16(() => headersContentKey(requestHeaders), [requestHeaders]);
10541
- const normalizedRequestHeaders = useMemo16(() => normalizedHeadersFromContentKey(requestHeadersKey), [requestHeadersKey]);
10542
- const remoteSessionOptionsWithEvents = useMemo16(() => ({
11207
+ const sessionListRefreshRef = useRef18(void 0);
11208
+ const requestHeadersKey = useMemo17(() => headersContentKey(requestHeaders), [requestHeaders]);
11209
+ const normalizedRequestHeaders = useMemo17(() => normalizedHeadersFromContentKey(requestHeadersKey), [requestHeadersKey]);
11210
+ const remoteSessionOptionsWithEvents = useMemo17(() => ({
10543
11211
  ...remoteSessionOptions,
10544
11212
  ...hydrateMessages ? {} : { autoStart: false },
10545
11213
  onEvent: (event) => {
@@ -10561,7 +11229,7 @@ function PiChatPanel({
10561
11229
  remoteSessionOptions: remoteSessionOptionsWithEvents,
10562
11230
  enabled: externalSessionId === void 0
10563
11231
  });
10564
- useEffect21(() => {
11232
+ useEffect23(() => {
10565
11233
  if (externalSessionId) {
10566
11234
  sessionListRefreshRef.current = void 0;
10567
11235
  return;
@@ -10615,7 +11283,7 @@ function PiChatPanel({
10615
11283
  const sessionModel = selectedChatState?.currentModel;
10616
11284
  const selectedModel = model === void 0 ? modelDiscovery.model : model;
10617
11285
  const modelOverride = model === void 0 ? modelDiscovery.isOverride : Boolean(model && sessionModel && (model.provider !== sessionModel.provider || model.id !== sessionModel.id));
10618
- const modelOptions = useMemo16(
11286
+ const modelOptions = useMemo17(
10619
11287
  () => modelOptionsForSelection(availableModels ?? modelDiscovery.availableModels, selectedModel),
10620
11288
  [availableModels, modelDiscovery.availableModels, selectedModel]
10621
11289
  );
@@ -10625,42 +11293,46 @@ function PiChatPanel({
10625
11293
  const serverModelSelectionPending = modelDiscoveryEnabled && !modelDiscovery.loaded;
10626
11294
  const serverModelSelectionUnavailable = modelDiscoveryEnabled && modelDiscovery.loaded && !selectedModelAuthorizedByDiscovery;
10627
11295
  const serverModelSelectionReady = !serverModelSelectionPending && !serverModelSelectionUnavailable;
10628
- const [storedThinkingLevel, setStoredThinkingLevel] = useState20(
11296
+ const [storedThinkingLevel, setStoredThinkingLevel] = useState22(
10629
11297
  () => thinkingControl ? readPiComposerSettings({ storageScope, storage }).thinkingLevel : DEFAULT_THINKING
10630
11298
  );
10631
- const [showThoughts, setShowThoughts] = useState20(
11299
+ const [showThoughts, setShowThoughts] = useState22(
10632
11300
  () => readPiComposerSettings({ storageScope, storage }).showThoughts
10633
11301
  );
10634
- const [composerSettingsOwner, setComposerSettingsOwner] = useState20(() => ({ storageScope, storage }));
10635
- useEffect21(() => {
11302
+ const [composerSettingsOwner, setComposerSettingsOwner] = useState22(() => ({ storageScope, storage }));
11303
+ useEffect23(() => {
10636
11304
  const settings = readPiComposerSettings({ storageScope, storage });
10637
11305
  setStoredThinkingLevel(thinkingControl ? settings.thinkingLevel : DEFAULT_THINKING);
10638
11306
  setShowThoughts(settings.showThoughts);
10639
11307
  setComposerSettingsOwner({ storageScope, storage });
10640
11308
  }, [storage, storageScope, thinkingControl]);
10641
11309
  const composerSettingsLoaded = composerSettingsOwner.storageScope === storageScope && composerSettingsOwner.storage === storage;
10642
- useEffect21(() => {
11310
+ useEffect23(() => {
10643
11311
  if (!thinkingControl) return;
10644
11312
  if (!composerSettingsLoaded) return;
10645
11313
  writePiComposerThinking(storedThinkingLevel, { storageScope, storage });
10646
11314
  }, [composerSettingsLoaded, storage, storageScope, storedThinkingLevel, thinkingControl]);
10647
- useEffect21(() => {
11315
+ useEffect23(() => {
10648
11316
  if (!composerSettingsLoaded) return;
10649
11317
  writePiComposerShowThoughts(showThoughts, { storageScope, storage });
10650
11318
  }, [composerSettingsLoaded, showThoughts, storage, storageScope]);
10651
11319
  const selectedThinking = thinkingLevel ?? storedThinkingLevel;
10652
11320
  const submitThinkingControl = thinkingControl || thinkingLevel !== void 0;
10653
- const [modelPickerOpen, setModelPickerOpen] = useState20(false);
10654
- const [thinkingPickerOpen, setThinkingPickerOpen] = useState20(false);
10655
- const [draft, setDraft] = useState20(() => initialDraft ?? "");
10656
- const draftRef = useRef17(draft);
11321
+ const [modelPickerOpen, setModelPickerOpen] = useState22(false);
11322
+ const [thinkingPickerOpen, setThinkingPickerOpen] = useState22(false);
11323
+ const [draft, setDraft] = useState22(() => initialDraft ?? "");
11324
+ const draftRef = useRef18(draft);
10657
11325
  draftRef.current = draft;
10658
- const initialDraftGuard = useRef17(new InitialDraftAutoSubmitGuard());
10659
- const pendingAutoSubmitSettleRef = useRef17(void 0);
10660
- const acceptedAutoSubmitSettleRef = useRef17(void 0);
10661
- const resetInProgressRef = useRef17(false);
10662
- const autoCreateInFlightRef = useRef17(false);
10663
- const settlePendingAutoSubmit = useCallback18((sessionId2) => {
11326
+ const [queueMutationPending, setQueueMutationPending] = useState22(false);
11327
+ const [resumeQueuedPendingSessionIds, setResumeQueuedPendingSessionIds] = useState22(() => /* @__PURE__ */ new Set());
11328
+ const [resumeQueuedErrorsBySessionId, setResumeQueuedErrorsBySessionId] = useState22(() => /* @__PURE__ */ new Map());
11329
+ const resumeQueuedInFlightRef = useRef18(/* @__PURE__ */ new Map());
11330
+ const initialDraftGuard = useRef18(new InitialDraftAutoSubmitGuard());
11331
+ const pendingAutoSubmitSettleRef = useRef18(void 0);
11332
+ const acceptedAutoSubmitSettleRef = useRef18(void 0);
11333
+ const resetInProgressRef = useRef18(false);
11334
+ const autoCreateInFlightRef = useRef18(false);
11335
+ const settlePendingAutoSubmit = useCallback19((sessionId2) => {
10664
11336
  const pendingSessionId = pendingAutoSubmitSettleRef.current;
10665
11337
  if (!pendingSessionId || sessionId2 && pendingSessionId !== sessionId2) return false;
10666
11338
  pendingAutoSubmitSettleRef.current = void 0;
@@ -10668,35 +11340,35 @@ function PiChatPanel({
10668
11340
  onAutoSubmitInitialDraftSettled?.();
10669
11341
  return true;
10670
11342
  }, [onAutoSubmitInitialDraftSettled]);
10671
- const prevStatusRef = useRef17("idle");
10672
- const statusRef = useRef17("idle");
10673
- const scrollToBottomRef = useRef17(() => {
11343
+ const prevStatusRef = useRef18("idle");
11344
+ const statusRef = useRef18("idle");
11345
+ const scrollToBottomRef = useRef18(() => {
10674
11346
  });
10675
- const textareaRef = useRef17(null);
10676
- const [localNotices, setLocalNotices] = useState20([]);
10677
- const [dismissedNoticeIds, setDismissedNoticeIds] = useState20(() => /* @__PURE__ */ new Set());
10678
- const [pluginUpdateState, setPluginUpdateState] = useState20(null);
10679
- const [commandNotifyState, setCommandNotifyState] = useState20(null);
10680
- const commandRunIdRef = useRef17(0);
10681
- const [serverSkillsRefreshKey, setServerSkillsRefreshKey] = useState20(0);
10682
- const [localSubmittedSessionId, setLocalSubmittedSessionId] = useState20();
10683
- const localSubmittedSessionRef = useRef17(void 0);
11347
+ const textareaRef = useRef18(null);
11348
+ const [localNotices, setLocalNotices] = useState22([]);
11349
+ const [dismissedNoticeIds, setDismissedNoticeIds] = useState22(() => /* @__PURE__ */ new Set());
11350
+ const [pluginUpdateState, setPluginUpdateState] = useState22(null);
11351
+ const [commandNotifyState, setCommandNotifyState] = useState22(null);
11352
+ const commandRunIdRef = useRef18(0);
11353
+ const [serverSkillsRefreshKey, setServerSkillsRefreshKey] = useState22(0);
11354
+ const [localSubmittedSessionId, setLocalSubmittedSessionId] = useState22();
11355
+ const localSubmittedSessionRef = useRef18(void 0);
10684
11356
  const { attachmentNotice, setAttachmentNotice } = useAttachmentNotice();
10685
11357
  const composerContributions = useComposerContributions();
10686
- const contributedCommands = useMemo16(
11358
+ const contributedCommands = useMemo17(
10687
11359
  () => composerContributions.flatMap((contribution) => contribution.commands ?? []),
10688
11360
  [composerContributions]
10689
11361
  );
10690
- const markLocalSubmitted = useCallback18((sessionId2) => {
11362
+ const markLocalSubmitted = useCallback19((sessionId2) => {
10691
11363
  localSubmittedSessionRef.current = sessionId2;
10692
11364
  setLocalSubmittedSessionId(sessionId2);
10693
11365
  }, []);
10694
- const clearLocalSubmitted = useCallback18((sessionId2) => {
11366
+ const clearLocalSubmitted = useCallback19((sessionId2) => {
10695
11367
  if (sessionId2 && localSubmittedSessionRef.current !== sessionId2) return;
10696
11368
  localSubmittedSessionRef.current = void 0;
10697
11369
  setLocalSubmittedSessionId(void 0);
10698
11370
  }, []);
10699
- const registry = useMemo16(() => {
11371
+ const registry = useMemo17(() => {
10700
11372
  const excludedBuiltins = new Set(excludeBuiltinCommands);
10701
11373
  const effectiveBuiltins = builtinCommands.filter((command) => {
10702
11374
  if (!hotReloadEnabled && command.name === "reload") return false;
@@ -10719,16 +11391,33 @@ function PiChatPanel({
10719
11391
  refreshKey: serverSkillsRefreshKey,
10720
11392
  enabled: serverResourcesEnabled
10721
11393
  });
10722
- const allCommands = useMemo16(() => registry.list(), [registry, commandsStamp]);
10723
- const actionableSlashCommands = useMemo16(
11394
+ const allCommands = useMemo17(() => registry.list(), [registry, commandsStamp]);
11395
+ const actionableSlashCommands = useMemo17(
10724
11396
  () => allCommands.flatMap((command) => command.clickBehavior === "execute" || command.clickBehavior === "insert" ? [{ name: command.name, clickBehavior: command.clickBehavior }] : []),
10725
11397
  [allCommands]
10726
11398
  );
10727
11399
  const activeChatSessionId = selectedChatState?.sessionId;
11400
+ const activeChatIdentity = `${agentTypeId}\0${workspaceId ?? ""}\0${storageScope ?? ""}\0${activeChatSessionId ?? ""}`;
11401
+ const selectedPiSessionRef = useRef18(selectedPiSession);
11402
+ const activeChatIdentityRef = useRef18(activeChatIdentity);
11403
+ const largePromptSpillCacheRef = useRef18(new LargePromptSpillCache());
11404
+ const submissionCoordinatorRef = useRef18(new PiComposerSubmissionCoordinator());
11405
+ selectedPiSessionRef.current = selectedPiSession;
11406
+ activeChatIdentityRef.current = activeChatIdentity;
11407
+ const isPolicySessionActive = useCallback19(() => selectedPiSessionRef.current === selectedPiSession && activeChatIdentityRef.current === activeChatIdentity, [activeChatIdentity, selectedPiSession]);
11408
+ const resumeQueuedPending = Boolean(activeChatSessionId && resumeQueuedPendingSessionIds.has(activeChatSessionId));
11409
+ const resumeQueuedError = activeChatSessionId ? resumeQueuedErrorsBySessionId.get(activeChatSessionId) : void 0;
11410
+ const chatScopeKey = `${externalSessionId ? "external" : "managed"}\0${agentTypeId}\0${storageScope ?? ""}`;
11411
+ useEffect23(() => {
11412
+ setQueueMutationPending(false);
11413
+ setResumeQueuedPendingSessionIds(/* @__PURE__ */ new Set());
11414
+ setResumeQueuedErrorsBySessionId(/* @__PURE__ */ new Map());
11415
+ resumeQueuedInFlightRef.current.clear();
11416
+ }, [chatScopeKey]);
10728
11417
  const warmupNotice = composerNoticeForWarmup(workspaceWarmupStatus);
10729
11418
  const runtimeDependenciesNotice = composerNoticeForRuntimeDependencies(workspaceWarmupStatus);
10730
11419
  const workspaceWarmupBlocked = Boolean(warmupNotice);
10731
- const activeBlockers = useMemo16(
11420
+ const activeBlockers = useMemo17(
10732
11421
  // A missing active session id means a single/sessionless chat host. In that
10733
11422
  // mode, keep scoped blockers visible instead of hiding the only attention UI.
10734
11423
  // Multi-session hosts should pass a session id so unrelated blockers filter out.
@@ -10738,7 +11427,7 @@ function PiChatPanel({
10738
11427
  const canonicalMessages = selectedChatState ? selectMessagesForRender(selectedChatState) : [];
10739
11428
  const queuePreview = selectedChatState ? selectQueuePreview(selectedChatState) : [];
10740
11429
  const messages = canonicalMessages;
10741
- const userHistory = useMemo16(() => selectComposerHistoryFromCanonicalUsers(canonicalMessages), [canonicalMessages]);
11430
+ const userHistory = useMemo17(() => selectComposerHistoryFromCanonicalUsers(canonicalMessages), [canonicalMessages]);
10742
11431
  const awaitingHydration = Boolean(hydrateMessages && selectedChatState && !selectedChatState.hydrated);
10743
11432
  const emptyStateHydrating = statusForState(selectedChatState, sessionsLoading || chatStatePending || selectedSessionPending) === "hydrating" || awaitingHydration;
10744
11433
  const emptyHero = emptyPlacement === "hero" && messages.length === 0 && queuePreview.length === 0 && !emptyStateHydrating;
@@ -10747,7 +11436,7 @@ function PiChatPanel({
10747
11436
  const primaryComposerBlocker = activeBlockers[0];
10748
11437
  const composerBlockerLabel = workspaceWarmupBlocked ? warmupNotice?.title ?? "Preparing workspace..." : primaryComposerBlocker?.label ?? primaryComposerBlocker?.reason ?? "Complete the pending workspace action to continue.";
10749
11438
  const composerStatusNotice = warmupNotice ?? runtimeDependenciesNotice;
10750
- const runtimeNotices = useMemo16(() => {
11439
+ const runtimeNotices = useMemo17(() => {
10751
11440
  const fromState = selectedChatState ? selectRuntimeNotices(selectedChatState) : [];
10752
11441
  const sessionNotice = sessionsError ? [{ id: "session-navigation-error", level: "error", text: sessionsError.message, dismissible: true }] : [];
10753
11442
  const largeStateNotice = debug && debugState?.largeStateWarning ? [{
@@ -10756,20 +11445,29 @@ function PiChatPanel({
10756
11445
  text: `Large chat state: ${debugState.largeStateWarning.messageCount} messages, approximately ${debugState.largeStateWarning.approxBytes} bytes.`,
10757
11446
  dismissible: true
10758
11447
  }] : [];
10759
- const combined = [...fromState, ...sessionNotice, ...largeStateNotice, ...localNotices].filter((notice) => !dismissedNoticeIds.has(notice.id));
11448
+ const combined = [...fromState, ...sessionNotice, ...largeStateNotice, ...resumeQueuedError ? [resumeQueuedError] : [], ...localNotices].filter((notice) => !dismissedNoticeIds.has(notice.id));
10760
11449
  return filterCompetingNoiseNotices(combined, messages.length === 0);
10761
- }, [debug, debugState?.largeStateWarning, dismissedNoticeIds, localNotices, messages.length, selectedChatState, sessionsError]);
10762
- const addLocalNotice = useCallback18((notice) => {
11450
+ }, [debug, debugState?.largeStateWarning, dismissedNoticeIds, localNotices, messages.length, resumeQueuedError, selectedChatState, sessionsError]);
11451
+ const addLocalNotice = useCallback19((notice) => {
10763
11452
  setLocalNotices((previous) => {
10764
11453
  const next = previous.filter((candidate) => candidate.id !== notice.id);
10765
11454
  return [...next, notice];
10766
11455
  });
10767
11456
  }, []);
10768
- const clearLocalNotice = useCallback18((id) => {
11457
+ const clearLocalNotice = useCallback19((id) => {
10769
11458
  setDismissedNoticeIds((previous) => new Set(previous).add(id));
10770
11459
  setLocalNotices((previous) => previous.filter((notice) => notice.id !== id));
11460
+ if (id.startsWith(RESUME_QUEUED_ERROR_PREFIX)) {
11461
+ const sessionId2 = id.slice(RESUME_QUEUED_ERROR_PREFIX.length);
11462
+ setResumeQueuedErrorsBySessionId((previous) => {
11463
+ if (!previous.has(sessionId2)) return previous;
11464
+ const next = new Map(previous);
11465
+ next.delete(sessionId2);
11466
+ return next;
11467
+ });
11468
+ }
10771
11469
  }, []);
10772
- const dropLocalNotice = useCallback18((id) => {
11470
+ const dropLocalNotice = useCallback19((id) => {
10773
11471
  setDismissedNoticeIds((previous) => {
10774
11472
  if (!previous.has(id)) return previous;
10775
11473
  const next = new Set(previous);
@@ -10778,13 +11476,13 @@ function PiChatPanel({
10778
11476
  });
10779
11477
  setLocalNotices((previous) => previous.filter((notice) => notice.id !== id));
10780
11478
  }, []);
10781
- const createSession = useCallback18(() => {
11479
+ const createSession = useCallback19(() => {
10782
11480
  if (externalSessionId) return;
10783
11481
  void sessions.create().catch((error) => {
10784
11482
  addLocalNotice({ id: "session-create-error", level: "error", text: errorMessage2(error, "Could not create a chat session."), dismissible: true });
10785
11483
  });
10786
11484
  }, [addLocalNotice, externalSessionId, sessions.create]);
10787
- useEffect21(() => {
11485
+ useEffect23(() => {
10788
11486
  if (externalSessionId || sessionsLoading || sessionsError || activeSessionId || sessionList.length > 0) return;
10789
11487
  if (resetInProgressRef.current) return;
10790
11488
  if (autoCreateInFlightRef.current) return;
@@ -10794,18 +11492,18 @@ function PiChatPanel({
10794
11492
  addLocalNotice({ id: "session-auto-create-error", level: "error", text: errorMessage2(error, "Could not create a chat session."), dismissible: true });
10795
11493
  });
10796
11494
  }, [activeSessionId, addLocalNotice, externalSessionId, sessionList.length, sessions.create, sessions.resumeSessionId, sessionsError, sessionsLoading]);
10797
- useEffect21(() => {
11495
+ useEffect23(() => {
10798
11496
  if (externalSessionId || sessionsError || activeSessionId || sessionList.length > 0) {
10799
11497
  autoCreateInFlightRef.current = false;
10800
11498
  }
10801
11499
  }, [activeSessionId, externalSessionId, sessionList.length, sessionsError]);
10802
- const deleteSession = useCallback18((sessionId2) => {
11500
+ const deleteSession = useCallback19((sessionId2) => {
10803
11501
  if (externalSessionId) return;
10804
11502
  void sessions.delete(sessionId2).catch((error) => {
10805
11503
  addLocalNotice({ id: `session-delete-error:${sessionId2}`, level: "error", text: errorMessage2(error, "Could not delete the chat session."), dismissible: true });
10806
11504
  });
10807
11505
  }, [addLocalNotice, externalSessionId, sessions.delete]);
10808
- const resetSession = useCallback18(() => {
11506
+ const resetSession = useCallback19(() => {
10809
11507
  const currentSessionId = activeSessionId;
10810
11508
  if (externalSessionId) {
10811
11509
  void onSessionReset?.();
@@ -10823,11 +11521,11 @@ function PiChatPanel({
10823
11521
  resetInProgressRef.current = false;
10824
11522
  });
10825
11523
  }, [activeSessionId, addLocalNotice, externalSessionId, onSessionReset, sessions.create, sessions.delete]);
10826
- const reloadAgentPlugins = useCallback18(async () => {
11524
+ const reloadAgentPlugins = useCallback19(async () => {
10827
11525
  if (!onReloadAgentPlugins) throw new Error("Agent plugin reload is not configured.");
10828
11526
  return normalizeAgentPluginReloadResult(await onReloadAgentPlugins());
10829
11527
  }, [onReloadAgentPlugins]);
10830
- const runPluginUpdate = useCallback18(async () => {
11528
+ const runPluginUpdate = useCallback19(async () => {
10831
11529
  setPluginUpdateState({ kind: "running" });
10832
11530
  try {
10833
11531
  const result = await reloadAgentPlugins();
@@ -10840,9 +11538,9 @@ function PiChatPanel({
10840
11538
  return `Extension update failed: ${message}`;
10841
11539
  }
10842
11540
  }, [reloadAgentPlugins]);
10843
- const dismissPluginUpdate = useCallback18(() => setPluginUpdateState(null), []);
10844
- const dismissCommandNotify = useCallback18(() => setCommandNotifyState(null), []);
10845
- useEffect21(() => {
11541
+ const dismissPluginUpdate = useCallback19(() => setPluginUpdateState(null), []);
11542
+ const dismissCommandNotify = useCallback19(() => setCommandNotifyState(null), []);
11543
+ useEffect23(() => {
10846
11544
  if (typeof window === "undefined") return;
10847
11545
  const onCommandNotify = (event) => {
10848
11546
  const payload = event.detail;
@@ -10859,7 +11557,7 @@ function PiChatPanel({
10859
11557
  window.addEventListener(WORKSPACE_COMMAND_NOTIFY_EVENT, onCommandNotify);
10860
11558
  return () => window.removeEventListener(WORKSPACE_COMMAND_NOTIFY_EVENT, onCommandNotify);
10861
11559
  }, []);
10862
- useEffect21(() => {
11560
+ useEffect23(() => {
10863
11561
  if (!hotReloadEnabled || typeof window === "undefined") return;
10864
11562
  const onBrowserPluginReload = (event) => {
10865
11563
  const parsed = parseBrowserPluginReloadDetail(event.detail);
@@ -10897,7 +11595,7 @@ function PiChatPanel({
10897
11595
  textareaRef,
10898
11596
  disabled: mentionState !== null || slashQuery !== null || modelPickerOpen || thinkingPickerOpen
10899
11597
  });
10900
- const setComposerDraft = useCallback18((next, focus = true) => {
11598
+ const setComposerDraft = useCallback19((next, focus = true) => {
10901
11599
  draftRef.current = next;
10902
11600
  setDraft(next);
10903
11601
  if (textareaRef.current) {
@@ -10905,11 +11603,11 @@ function PiChatPanel({
10905
11603
  if (focus) textareaRef.current.focus();
10906
11604
  }
10907
11605
  }, []);
10908
- const warnComposer = useCallback18((message) => {
11606
+ const warnComposer = useCallback19((message) => {
10909
11607
  onComposerWarning?.(message);
10910
11608
  addLocalNotice({ id: `composer-warning:${Date.now()}`, level: "warning", text: message, dismissible: true });
10911
11609
  }, [addLocalNotice, onComposerWarning]);
10912
- const openModelPicker = useCallback18(() => {
11610
+ const openModelPicker = useCallback19(() => {
10913
11611
  if (isPiBusyStatus(statusRef.current)) {
10914
11612
  warnComposer("Model picker is unavailable while the agent is running.");
10915
11613
  return false;
@@ -10922,7 +11620,7 @@ function PiChatPanel({
10922
11620
  setModelPickerOpen(true);
10923
11621
  return true;
10924
11622
  }, [model, warnComposer]);
10925
- const openThinkingPicker = useCallback18(() => {
11623
+ const openThinkingPicker = useCallback19(() => {
10926
11624
  if (isPiBusyStatus(statusRef.current)) {
10927
11625
  warnComposer("Thinking picker is unavailable while the agent is running.");
10928
11626
  return false;
@@ -10935,21 +11633,21 @@ function PiChatPanel({
10935
11633
  setThinkingPickerOpen(true);
10936
11634
  return true;
10937
11635
  }, [thinkingControl, thinkingLevel, warnComposer]);
10938
- const selectComposerModel = useCallback18((query) => {
11636
+ const selectComposerModel = useCallback19((query) => {
10939
11637
  if (model !== void 0) return "Model selection is controlled by the host.";
10940
11638
  const match = resolveModelSlashSelection(query, modelOptions);
10941
11639
  if (!match) return `No model matched "${query}".`;
10942
11640
  modelDiscovery.setModel(match);
10943
11641
  return `Model set to ${match.label ?? match.id}.`;
10944
11642
  }, [model, modelDiscovery, modelOptions]);
10945
- const selectComposerThinking = useCallback18((query) => {
11643
+ const selectComposerThinking = useCallback19((query) => {
10946
11644
  if (!thinkingControl || thinkingLevel !== void 0) return "Thinking level is controlled by the host.";
10947
11645
  const match = resolveThinkingSlashSelection(query);
10948
11646
  if (!match) return `No thinking level matched "${query}".`;
10949
11647
  setStoredThinkingLevel(match);
10950
11648
  return `Thinking set to ${thinkingLabel(match)}.`;
10951
11649
  }, [thinkingControl, thinkingLevel]);
10952
- const selectSlashCommand = useCallback18((name) => {
11650
+ const selectSlashCommand = useCallback19((name) => {
10953
11651
  if (name === "model") {
10954
11652
  dismissSlash();
10955
11653
  if (openModelPicker()) setComposerDraft("");
@@ -10962,7 +11660,7 @@ function PiChatPanel({
10962
11660
  }
10963
11661
  insertSlashCommand(name);
10964
11662
  }, [dismissSlash, insertSlashCommand, openModelPicker, openThinkingPicker, setComposerDraft]);
10965
- const policy = useMemo16(() => {
11663
+ const policy = useMemo17(() => {
10966
11664
  if (!selectedPiSession || !activeChatSessionId || !serverModelSelectionReady) return void 0;
10967
11665
  const policySession = {
10968
11666
  getState: () => {
@@ -11007,13 +11705,29 @@ function PiChatPanel({
11007
11705
  getDraft: () => draftRef.current,
11008
11706
  onDraftChange: setComposerDraft,
11009
11707
  allowPromptDuringInitialHydration,
11708
+ isActiveSession: isPolicySessionActive,
11709
+ submissionCoordinator: submissionCoordinatorRef.current,
11710
+ submissionIdentity: activeChatIdentity,
11711
+ onQueueMutationPending: setQueueMutationPending,
11010
11712
  onPromptSubmitStarted: () => {
11011
11713
  markLocalSubmitted(activeChatSessionId);
11012
11714
  },
11013
- onBeforeSubmit: onBeforeSubmit ? async (draft2, context) => {
11014
- const result = await onBeforeSubmit(draft2, { ...context, sessionId: activeChatSessionId, source: context.source ?? "composer" });
11015
- return result !== false;
11016
- } : void 0,
11715
+ onBeforeSubmit: onBeforeSubmit ? async (draft2, context) => await onBeforeSubmit(draft2, {
11716
+ ...context,
11717
+ sessionId: activeChatSessionId,
11718
+ source: context.source ?? "composer"
11719
+ }) : void 0,
11720
+ onTransformPrompt: async (text) => await spillLargePrompt(text, {
11721
+ sessionId: activeChatSessionId,
11722
+ destinationIdentity: `${apiBaseUrl ?? ""}\0${workspaceId ?? ""}\0${headersContentKey(requestHeaders)}`,
11723
+ cache: largePromptSpillCacheRef.current,
11724
+ enabled: spillLargePrompts,
11725
+ thresholdChars: largePromptThresholdChars,
11726
+ apiBaseUrl,
11727
+ workspaceRequestId: requestHeaderValue(requestHeaders, "x-boring-workspace-id"),
11728
+ requestHeaders,
11729
+ fetch: fetch2
11730
+ }),
11017
11731
  onCommandResult: (message) => {
11018
11732
  onCommandResult?.(message);
11019
11733
  addLocalNotice({ id: `command:${Date.now()}`, level: "info", text: message, dismissible: true });
@@ -11028,8 +11742,17 @@ function PiChatPanel({
11028
11742
  onMentionedFilesConsumed?.();
11029
11743
  }
11030
11744
  });
11031
- }, [activeChatSessionId, addLocalNotice, allowPromptDuringInitialHydration, clearMentionedFiles, composerBlocked, composerBlockerLabel, effectiveMentionedFiles, markLocalSubmitted, onBeforeSubmit, onCommandResult, onComposerWarning, onMentionedFilesConsumed, onPromptSubmitStarted, openModelPicker, openThinkingPicker, registry, reloadAgentPlugins, resetSession, runPluginUpdate, selectComposerModel, selectComposerThinking, selectedModel, selectedPiSession, selectedThinking, serverModelSelectionReady, setComposerDraft, submitThinkingControl, suppressPreSubmitCancelledWarning]);
11032
- const surfaceRunRejected = useCallback18((error) => {
11745
+ }, [activeChatIdentity, activeChatSessionId, addLocalNotice, allowPromptDuringInitialHydration, apiBaseUrl, clearMentionedFiles, composerBlocked, composerBlockerLabel, effectiveMentionedFiles, fetch2, isPolicySessionActive, largePromptThresholdChars, markLocalSubmitted, onBeforeSubmit, onCommandResult, onComposerWarning, onMentionedFilesConsumed, onPromptSubmitStarted, openModelPicker, openThinkingPicker, registry, reloadAgentPlugins, requestHeaders, resetSession, runPluginUpdate, selectComposerModel, selectComposerThinking, selectedModel, selectedPiSession, selectedThinking, serverModelSelectionReady, setComposerDraft, spillLargePrompts, submitThinkingControl, suppressPreSubmitCancelledWarning, workspaceId]);
11746
+ const surfaceHandledSubmit = useCallback19((message) => {
11747
+ if (!message) return;
11748
+ addLocalNotice({
11749
+ id: `handled-submit:${Date.now()}`,
11750
+ level: "info",
11751
+ text: message,
11752
+ dismissible: true
11753
+ });
11754
+ }, [addLocalNotice]);
11755
+ const surfaceRunRejected = useCallback19((error) => {
11033
11756
  const errorCode = piChatErrorCode(error);
11034
11757
  dropLocalNotice(RUN_REJECTED_NOTICE_ID);
11035
11758
  addLocalNotice({
@@ -11040,7 +11763,7 @@ function PiChatPanel({
11040
11763
  ...errorCode ? { errorCode } : {}
11041
11764
  });
11042
11765
  }, [addLocalNotice, dropLocalNotice]);
11043
- const sendComposerMessage = useCallback18(async ({ text, files, source = "composer", preserveExistingDraft = false }) => {
11766
+ const sendComposerMessage = useCallback19(async ({ text, files, source = "composer", preserveExistingDraft = false }) => {
11044
11767
  if (!policy) {
11045
11768
  addLocalNotice({ id: "composer-no-session", level: "warning", text: "Create or select a chat session before sending.", dismissible: true });
11046
11769
  return false;
@@ -11060,6 +11783,7 @@ function PiChatPanel({
11060
11783
  if (result.type === "prompt" || result.type === "followup") {
11061
11784
  dropLocalNotice(RUN_REJECTED_NOTICE_ID);
11062
11785
  }
11786
+ if (result.type === "handled") surfaceHandledSubmit(result.message);
11063
11787
  if (result.type === "prompt" && activeChatSessionId) {
11064
11788
  onPromptSubmitStarted?.({ sessionId: activeChatSessionId, clientNonce: result.clientNonce });
11065
11789
  if (shouldHoldLocalSubmitted(selectedPiSession, result.cursor)) markLocalSubmitted(activeChatSessionId);
@@ -11067,17 +11791,18 @@ function PiChatPanel({
11067
11791
  }
11068
11792
  return void 0;
11069
11793
  } catch (error) {
11794
+ if (!isPolicySessionActive()) return void 0;
11070
11795
  clearLocalSubmitted(activeChatSessionId);
11071
11796
  restoreSubmittedDraft();
11072
11797
  surfaceRunRejected(error);
11073
11798
  return false;
11074
11799
  }
11075
- }, [activeChatSessionId, clearLocalSubmitted, dropLocalNotice, markLocalSubmitted, onPromptSubmitStarted, policy, selectedPiSession, setComposerDraft, surfaceRunRejected]);
11076
- const availableAssistantSlashCommands = useMemo16(
11800
+ }, [activeChatSessionId, clearLocalSubmitted, dropLocalNotice, isPolicySessionActive, markLocalSubmitted, onPromptSubmitStarted, policy, selectedPiSession, setComposerDraft, surfaceHandledSubmit, surfaceRunRejected]);
11801
+ const availableAssistantSlashCommands = useMemo17(
11077
11802
  () => policy ? actionableSlashCommands : actionableSlashCommands.filter((command) => command.clickBehavior === "insert"),
11078
11803
  [actionableSlashCommands, policy]
11079
11804
  );
11080
- const assistantMentionCatalog = useMemo16(() => {
11805
+ const assistantMentionCatalog = useMemo17(() => {
11081
11806
  const skills = /* @__PURE__ */ new Map();
11082
11807
  for (const command of allCommands) {
11083
11808
  if (command.source !== "skill" && command.kind !== "skill") continue;
@@ -11093,12 +11818,12 @@ function PiChatPanel({
11093
11818
  files: true
11094
11819
  };
11095
11820
  }, [allCommands, availableAssistantSlashCommands]);
11096
- const insertMentionDraft = useCallback18((invocation) => {
11821
+ const insertMentionDraft = useCallback19((invocation) => {
11097
11822
  const currentDraft = draftRef.current;
11098
11823
  const separator = currentDraft && !/\s$/.test(invocation) ? "\n" : "";
11099
11824
  setComposerDraft(`${invocation}${separator}${currentDraft}`, true);
11100
11825
  }, [setComposerDraft]);
11101
- const activateAssistantMention = useCallback18((mention) => {
11826
+ const activateAssistantMention = useCallback19((mention) => {
11102
11827
  const command = registry.get(mention.kind === "skill" ? mention.commandName : mention.name);
11103
11828
  if (mention.kind === "skill") {
11104
11829
  if (command?.source !== "skill" && command?.kind !== "skill") return;
@@ -11120,7 +11845,7 @@ function PiChatPanel({
11120
11845
  preserveExistingDraft: true
11121
11846
  });
11122
11847
  }, [insertMentionDraft, policy, registry, sendComposerMessage]);
11123
- const editQueued = useCallback18(() => {
11848
+ const editQueued = useCallback19(() => {
11124
11849
  if (!policy) return;
11125
11850
  void policy.editQueued().then((result) => {
11126
11851
  if (result.type === "clear-failed") {
@@ -11128,25 +11853,65 @@ function PiChatPanel({
11128
11853
  }
11129
11854
  });
11130
11855
  }, [addLocalNotice, policy]);
11131
- const stop = useCallback18(() => {
11856
+ const removeQueued = useCallback19(() => {
11857
+ if (!policy) return;
11858
+ void policy.removeAllQueued().then((result) => {
11859
+ if (!result.ok) {
11860
+ addLocalNotice({ id: "remove-queued", level: "warning", text: result.message, dismissible: true });
11861
+ }
11862
+ });
11863
+ }, [addLocalNotice, policy]);
11864
+ const stop = useCallback19(() => {
11132
11865
  onComposerStop?.();
11133
11866
  clearLocalSubmitted(activeChatSessionId);
11134
- void policy?.stop().catch((error) => {
11867
+ void policy?.interrupt({ queueAction: "hold" }).catch((error) => {
11135
11868
  addLocalNotice({ id: "stop-error", level: "error", text: errorMessage2(error, "Could not stop the chat session."), dismissible: true });
11136
11869
  });
11137
11870
  }, [activeChatSessionId, addLocalNotice, clearLocalSubmitted, onComposerStop, policy]);
11138
- const interrupt = useCallback18(() => {
11871
+ const interrupt = useCallback19(() => {
11139
11872
  void policy?.interrupt().catch((error) => {
11140
11873
  addLocalNotice({ id: "interrupt-error", level: "error", text: errorMessage2(error, "Could not interrupt the chat session."), dismissible: true });
11141
11874
  });
11142
11875
  }, [addLocalNotice, policy]);
11143
- useEffect21(() => {
11876
+ const resumeQueued = useCallback19(() => {
11877
+ const sessionId2 = activeChatSessionId;
11878
+ if (!policy || !sessionId2 || resumeQueuedInFlightRef.current.has(sessionId2)) return;
11879
+ const errorNoticeId = `${RESUME_QUEUED_ERROR_PREFIX}${sessionId2}`;
11880
+ dropLocalNotice(errorNoticeId);
11881
+ setResumeQueuedErrorsBySessionId((previous) => {
11882
+ if (!previous.has(sessionId2)) return previous;
11883
+ const next = new Map(previous);
11884
+ next.delete(sessionId2);
11885
+ return next;
11886
+ });
11887
+ setResumeQueuedPendingSessionIds((previous) => new Set(previous).add(sessionId2));
11888
+ const run = policy.resumeQueued();
11889
+ resumeQueuedInFlightRef.current.set(sessionId2, run);
11890
+ void run.catch((error) => {
11891
+ setResumeQueuedErrorsBySessionId((previous) => new Map(previous).set(sessionId2, {
11892
+ id: errorNoticeId,
11893
+ level: "error",
11894
+ text: errorMessage2(error, "Could not resume queued follow-ups."),
11895
+ dismissible: true
11896
+ }));
11897
+ }).finally(() => {
11898
+ if (resumeQueuedInFlightRef.current.get(sessionId2) !== run) return;
11899
+ resumeQueuedInFlightRef.current.delete(sessionId2);
11900
+ setResumeQueuedPendingSessionIds((previous) => {
11901
+ if (!previous.has(sessionId2)) return previous;
11902
+ const next = new Set(previous);
11903
+ next.delete(sessionId2);
11904
+ return next;
11905
+ });
11906
+ });
11907
+ }, [activeChatSessionId, dropLocalNotice, policy]);
11908
+ useEffect23(() => {
11144
11909
  setPluginUpdateState(null);
11145
11910
  setCommandNotifyState(null);
11146
11911
  setLocalNotices([]);
11147
11912
  setDismissedNoticeIds(/* @__PURE__ */ new Set());
11148
11913
  }, [activeSessionId]);
11149
- useEffect21(() => {
11914
+ useEffect23(() => {
11150
11915
  const currentSessionId = activeSessionId ?? "__none__";
11151
11916
  if (initialDraftGuard.current.shouldRestore(currentSessionId, initialDraft) && initialDraft !== void 0) {
11152
11917
  setComposerDraft(initialDraft, initialDraft.length > 0);
@@ -11155,7 +11920,7 @@ function PiChatPanel({
11155
11920
  }, [activeSessionId, initialDraft, onDraftRestored, setComposerDraft]);
11156
11921
  const remoteStatus = selectedChatState?.status ?? (sessionsLoading || chatStatePending || selectedSessionPending ? "hydrating" : "idle");
11157
11922
  const status = localSubmittedSessionId === activeSessionId && remoteStatus === "idle" ? "submitted" : remoteStatus;
11158
- useEffect21(() => {
11923
+ useEffect23(() => {
11159
11924
  if (!localSubmittedSessionId) return;
11160
11925
  if (localSubmittedSessionId !== activeSessionId) {
11161
11926
  clearLocalSubmitted();
@@ -11163,7 +11928,7 @@ function PiChatPanel({
11163
11928
  }
11164
11929
  if (remoteStatus !== "idle") clearLocalSubmitted(localSubmittedSessionId);
11165
11930
  }, [activeSessionId, clearLocalSubmitted, localSubmittedSessionId, remoteStatus]);
11166
- useEffect21(() => {
11931
+ useEffect23(() => {
11167
11932
  const previous = prevStatusRef.current;
11168
11933
  statusRef.current = status;
11169
11934
  prevStatusRef.current = status;
@@ -11173,7 +11938,7 @@ function PiChatPanel({
11173
11938
  if (!isPiBusyStatus(previous) && previous !== "submitted") return;
11174
11939
  settlePendingAutoSubmit();
11175
11940
  }, [settlePendingAutoSubmit, status]);
11176
- useEffect21(() => {
11941
+ useEffect23(() => {
11177
11942
  if (!autoSubmitInitialDraft || !policy || !activeSessionId || composerBlocked) return;
11178
11943
  if (!initialDraftGuard.current.claimAutoSubmit(activeSessionId, initialDraft)) return;
11179
11944
  pendingAutoSubmitSettleRef.current = activeSessionId;
@@ -11192,9 +11957,14 @@ function PiChatPanel({
11192
11957
  }
11193
11958
  return;
11194
11959
  }
11960
+ if (result.type === "stale") {
11961
+ settlePendingAutoSubmit(activeSessionId);
11962
+ return;
11963
+ }
11195
11964
  if (result.type === "prompt" || result.type === "followup") {
11196
11965
  dropLocalNotice(RUN_REJECTED_NOTICE_ID);
11197
11966
  }
11967
+ if (result.type === "handled") surfaceHandledSubmit(result.message);
11198
11968
  if (result.type === "prompt") {
11199
11969
  onPromptSubmitStarted?.({ sessionId: activeSessionId, clientNonce: result.clientNonce });
11200
11970
  if (shouldHoldLocalSubmitted(selectedPiSession, result.cursor)) markLocalSubmitted(activeSessionId);
@@ -11206,13 +11976,14 @@ function PiChatPanel({
11206
11976
  settlePendingAutoSubmit(activeSessionId);
11207
11977
  }
11208
11978
  }).catch((error) => {
11979
+ if (!isPolicySessionActive()) return;
11209
11980
  clearLocalSubmitted(activeSessionId);
11210
11981
  restoreSubmittedDraft();
11211
11982
  settlePendingAutoSubmit(activeSessionId);
11212
11983
  surfaceRunRejected(error);
11213
11984
  });
11214
- }, [activeSessionId, autoSubmitInitialDraft, clearLocalSubmitted, composerBlocked, dropLocalNotice, initialDraft, markLocalSubmitted, onAutoSubmitInitialDraftAccepted, onPromptSubmitStarted, policy, selectedPiSession, setComposerDraft, settlePendingAutoSubmit, surfaceRunRejected]);
11215
- useEffect21(() => {
11985
+ }, [activeSessionId, autoSubmitInitialDraft, clearLocalSubmitted, composerBlocked, dropLocalNotice, initialDraft, isPolicySessionActive, markLocalSubmitted, onAutoSubmitInitialDraftAccepted, onPromptSubmitStarted, policy, selectedPiSession, setComposerDraft, settlePendingAutoSubmit, surfaceHandledSubmit, surfaceRunRejected]);
11986
+ useEffect23(() => {
11216
11987
  if (workspaceWarmupStatus?.status === "ready") {
11217
11988
  clearLocalNotice("workspace-warmup");
11218
11989
  }
@@ -11226,20 +11997,20 @@ function PiChatPanel({
11226
11997
  const isStreaming = isPiBusyStatus(status);
11227
11998
  const submitStatus = initialHydrationPromptAllowed ? "ready" : toPromptSubmitStatus(status);
11228
11999
  const submitDisabled = !policy || sessionsLoading || modelSelectionBlocked || composerBlocked && !isStreaming;
11229
- const mergedToolRenderers = useMemo16(() => mergeShadcnToolRenderers(toolRenderers), [toolRenderers]);
11230
- const debugMessages = useMemo16(() => messages.map(toDebugUiMessage), [messages]);
11231
- const onTextareaChange = useCallback18((event) => {
12000
+ const mergedToolRenderers = useMemo17(() => mergeShadcnToolRenderers(toolRenderers), [toolRenderers]);
12001
+ const debugMessages = useMemo17(() => messages.map(toDebugUiMessage), [messages]);
12002
+ const onTextareaChange = useCallback19((event) => {
11232
12003
  setModelPickerOpen(false);
11233
12004
  setThinkingPickerOpen(false);
11234
12005
  setDraft(event.currentTarget.value);
11235
12006
  handleComposerChange(event);
11236
12007
  }, [handleComposerChange]);
11237
- useEffect21(() => {
12008
+ useEffect23(() => {
11238
12009
  if (!isStreaming) return;
11239
12010
  setModelPickerOpen(false);
11240
12011
  setThinkingPickerOpen(false);
11241
12012
  }, [isStreaming]);
11242
- useEffect21(() => {
12013
+ useEffect23(() => {
11243
12014
  if (typeof window === "undefined" || !activeChatSessionId) return;
11244
12015
  const emitStatus = () => window.dispatchEvent(new CustomEvent("boring:chat-session-status", {
11245
12016
  detail: {
@@ -11253,7 +12024,7 @@ function PiChatPanel({
11253
12024
  window.addEventListener("boring:chat-session-status-request", emitStatus);
11254
12025
  return () => window.removeEventListener("boring:chat-session-status-request", emitStatus);
11255
12026
  }, [activeChatSessionId, agentTypeId, isStreaming, workspaceId]);
11256
- const onTextareaKeyDown = useCallback18((event) => {
12027
+ const onTextareaKeyDown = useCallback19((event) => {
11257
12028
  if (event.key === "Escape" && isStreaming) {
11258
12029
  if (event.defaultPrevented || mentionState !== null || slashQuery !== null) {
11259
12030
  handleComposerKeyDown(event);
@@ -11265,14 +12036,14 @@ function PiChatPanel({
11265
12036
  }
11266
12037
  handleComposerKeyDown(event);
11267
12038
  }, [handleComposerKeyDown, interrupt, isStreaming, mentionState, slashQuery]);
11268
- const selectEmptySuggestion = useCallback18((suggestion) => {
12039
+ const selectEmptySuggestion = useCallback19((suggestion) => {
11269
12040
  const text = suggestion.prompt ?? suggestion.label;
11270
12041
  if (!text.trim()) return;
11271
12042
  void sendComposerMessage({ text, files: [], source: "suggestion" }).then((result) => {
11272
12043
  if (result === false) setComposerDraft(text);
11273
12044
  });
11274
12045
  }, [sendComposerMessage, setComposerDraft]);
11275
- const composerSurface = /* @__PURE__ */ jsx34(
12046
+ const composerSurface = /* @__PURE__ */ jsx35(
11276
12047
  PiChatComposerSurface,
11277
12048
  {
11278
12049
  chrome,
@@ -11291,6 +12062,10 @@ function PiChatPanel({
11291
12062
  onComposerBlockerAction,
11292
12063
  queuePreview,
11293
12064
  onEditQueued: editQueued,
12065
+ onRemoveQueued: removeQueued,
12066
+ queueMutationPending,
12067
+ onResumeQueued: resumeQueued,
12068
+ resumeQueuedPending,
11294
12069
  hotReloadEnabled,
11295
12070
  pluginUpdateState,
11296
12071
  onDismissPluginUpdate: dismissPluginUpdate,
@@ -11339,7 +12114,7 @@ function PiChatPanel({
11339
12114
  onStop: stop
11340
12115
  }
11341
12116
  );
11342
- return /* @__PURE__ */ jsx34(ArtifactOpenProvider, { onOpenArtifact, children: /* @__PURE__ */ jsxs30(
12117
+ return /* @__PURE__ */ jsx35(ArtifactOpenProvider, { onOpenArtifact, children: /* @__PURE__ */ jsxs31(
11343
12118
  "div",
11344
12119
  {
11345
12120
  "data-boring-agent": "",
@@ -11357,7 +12132,7 @@ function PiChatPanel({
11357
12132
  role: "region",
11358
12133
  "aria-label": "Agent assistant",
11359
12134
  children: [
11360
- showSessionSidebar ? /* @__PURE__ */ jsx34("aside", { "data-boring-agent-part": "pi-chat-session-sidebar", className: "min-h-0 w-64 shrink-0 border-r border-border/60", children: /* @__PURE__ */ jsx34(
12135
+ showSessionSidebar ? /* @__PURE__ */ jsx35("aside", { "data-boring-agent-part": "pi-chat-session-sidebar", className: "min-h-0 w-64 shrink-0 border-r border-border/60", children: /* @__PURE__ */ jsx35(
11361
12136
  SessionList,
11362
12137
  {
11363
12138
  sessions: sessionList,
@@ -11371,7 +12146,7 @@ function PiChatPanel({
11371
12146
  loadingMore: sessions.loadingMore
11372
12147
  }
11373
12148
  ) }) : null,
11374
- /* @__PURE__ */ jsx34("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col", children: /* @__PURE__ */ jsxs30(
12149
+ /* @__PURE__ */ jsx35("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col", children: /* @__PURE__ */ jsxs31(
11375
12150
  "div",
11376
12151
  {
11377
12152
  className: cn(
@@ -11380,7 +12155,7 @@ function PiChatPanel({
11380
12155
  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)]"
11381
12156
  ),
11382
12157
  children: [
11383
- /* @__PURE__ */ jsx34(
12158
+ /* @__PURE__ */ jsx35(
11384
12159
  PiConversationSurface,
11385
12160
  {
11386
12161
  chrome,
@@ -11406,13 +12181,13 @@ function PiChatPanel({
11406
12181
  }
11407
12182
  ),
11408
12183
  composerSurface,
11409
- emptyHero ? /* @__PURE__ */ jsxs30(
12184
+ emptyHero ? /* @__PURE__ */ jsxs31(
11410
12185
  "div",
11411
12186
  {
11412
12187
  "data-boring-agent-part": "empty-hero-suggestions",
11413
12188
  className: "@container mx-auto w-full max-w-[640px] px-2 pb-4",
11414
12189
  children: [
11415
- /* @__PURE__ */ jsx34(ChatSuggestionGrid, { suggestions, onSelect: selectEmptySuggestion, className: "mt-4" }),
12190
+ /* @__PURE__ */ jsx35(ChatSuggestionGrid, { suggestions, onSelect: selectEmptySuggestion, className: "mt-4" }),
11416
12191
  emptyState?.footer
11417
12192
  ]
11418
12193
  }
@@ -11420,7 +12195,7 @@ function PiChatPanel({
11420
12195
  ]
11421
12196
  }
11422
12197
  ) }),
11423
- debug ? /* @__PURE__ */ jsx34(Suspense, { fallback: null, children: /* @__PURE__ */ jsx34("div", { "aria-label": "Chat debug metadata", className: "contents", role: "region", children: /* @__PURE__ */ jsx34(
12198
+ debug ? /* @__PURE__ */ jsx35(Suspense, { fallback: null, children: /* @__PURE__ */ jsx35("div", { "aria-label": "Chat debug metadata", className: "contents", role: "region", children: /* @__PURE__ */ jsx35(
11424
12199
  DebugDrawer2,
11425
12200
  {
11426
12201
  agentTypeId,
@@ -11441,17 +12216,18 @@ function PiChatPanel({
11441
12216
  }
11442
12217
 
11443
12218
  // src/front/chat/useAddressedAgentSelection.ts
11444
- import { useCallback as useCallback19, useEffect as useEffect22, useMemo as useMemo17, useState as useState21 } from "react";
12219
+ import { useCallback as useCallback20, useEffect as useEffect24, useMemo as useMemo18, useState as useState23 } from "react";
11445
12220
  var inFlightDiscoveries = /* @__PURE__ */ new WeakMap();
11446
12221
  function useAddressedAgentSelection({
11447
12222
  apiBaseUrl,
11448
12223
  requestHeaders,
11449
12224
  storageScope,
12225
+ preferredAgentTypeId,
11450
12226
  fetch: fetch2,
11451
12227
  enabled = false
11452
12228
  } = {}) {
11453
12229
  const normalizedBaseUrl = apiBaseUrl?.replace(/\/$/, "") ?? "";
11454
- const headersKey = useMemo17(
12230
+ const headersKey = useMemo18(
11455
12231
  () => JSON.stringify({
11456
12232
  storageScope: storageScope ?? "",
11457
12233
  headers: Object.entries(requestHeaders ?? {}).sort(([a], [b]) => a.localeCompare(b))
@@ -11461,14 +12237,14 @@ function useAddressedAgentSelection({
11461
12237
  const discoveryKey = `${normalizedBaseUrl}
11462
12238
  ${headersKey}`;
11463
12239
  const fetchImpl = fetch2 ?? globalThis.fetch;
11464
- const [state, setState] = useState21(() => ({
12240
+ const [state, setState] = useState23(() => ({
11465
12241
  discoveryKey: enabled ? "" : discoveryKey,
11466
12242
  agents: [],
11467
12243
  selectedAgentTypeId: void 0,
11468
12244
  loading: enabled,
11469
12245
  error: void 0
11470
12246
  }));
11471
- useEffect22(() => {
12247
+ useEffect24(() => {
11472
12248
  if (!enabled) {
11473
12249
  setState({
11474
12250
  discoveryKey,
@@ -11494,7 +12270,7 @@ ${headersKey}`;
11494
12270
  setState((previous) => ({
11495
12271
  discoveryKey,
11496
12272
  agents: agents2,
11497
- selectedAgentTypeId: agents2.some((agent) => agent.agentTypeId === previous.selectedAgentTypeId) ? previous.selectedAgentTypeId : agents2[0]?.agentTypeId,
12273
+ selectedAgentTypeId: agents2.some((agent) => agent.agentTypeId === previous.selectedAgentTypeId) ? previous.selectedAgentTypeId : preferredAgentTypeId ?? agents2[0]?.agentTypeId,
11498
12274
  loading: false,
11499
12275
  error: void 0
11500
12276
  }));
@@ -11511,11 +12287,11 @@ ${headersKey}`;
11511
12287
  return () => {
11512
12288
  cancelled = true;
11513
12289
  };
11514
- }, [discoveryKey, enabled, fetchImpl, normalizedBaseUrl]);
12290
+ }, [discoveryKey, enabled, fetchImpl, normalizedBaseUrl, preferredAgentTypeId]);
11515
12291
  const current = state.discoveryKey === discoveryKey;
11516
12292
  const agents = enabled && current ? state.agents : [];
11517
12293
  const selectedAgentTypeId = enabled && current ? state.selectedAgentTypeId : void 0;
11518
- const selectAgentTypeId = useCallback19((agentTypeId) => {
12294
+ const selectAgentTypeId = useCallback20((agentTypeId) => {
11519
12295
  setState((previous) => {
11520
12296
  if (previous.discoveryKey !== discoveryKey) return previous;
11521
12297
  if (!previous.agents.some((agent) => agent.agentTypeId === agentTypeId)) return previous;