@hachej/boring-agent 0.1.105 → 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-F2OJI2LX.js → chunk-GS4ADE34.js} +863 -358
  5. package/dist/{chunk-LFN45QDZ.js → chunk-KDXIXVF6.js} +5076 -3640
  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-GMBZZPTN.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 +36 -7
  14. package/dist/front/index.js +1491 -727
  15. package/dist/front/styles.css +57 -0
  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 +167 -212
  23. package/dist/server/index.js +13 -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-BTsdaAQ2.d.ts → types-B7-PN5jV.d.ts} +47 -14
  28. package/docs/AGENT_GATEWAY_V0.md +40 -2
  29. package/docs/ERROR_CODES.md +1 -0
  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";
@@ -550,7 +557,7 @@ import { useCallback, useState as useState3 } from "react";
550
557
  import { Button as Button4 } from "@hachej/boring-ui-kit";
551
558
  import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
552
559
  function CodeBlock({
553
- code: code3,
560
+ code,
554
561
  language,
555
562
  filename,
556
563
  className,
@@ -559,13 +566,13 @@ function CodeBlock({
559
566
  }) {
560
567
  const [copied, setCopied] = useState3(false);
561
568
  const handleCopy = useCallback(() => {
562
- void copyTextToClipboard(code3).then((ok) => {
569
+ void copyTextToClipboard(code).then((ok) => {
563
570
  if (!ok) return;
564
571
  setCopied(true);
565
572
  setTimeout(() => setCopied(false), 2e3);
566
573
  });
567
- }, [code3]);
568
- const lines = code3.split("\n");
574
+ }, [code]);
575
+ const lines = code.split("\n");
569
576
  const lineNumberWidth = `${String(lines.length).length + 1}ch`;
570
577
  return /* @__PURE__ */ jsxs5(
571
578
  "div",
@@ -582,7 +589,7 @@ function CodeBlock({
582
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: [
583
590
  /* @__PURE__ */ jsx5("span", { className: "mr-4 inline-block select-none text-right opacity-40", style: { width: lineNumberWidth }, children: i + 1 }),
584
591
  line
585
- ] }, i)) : code3 }) }),
592
+ ] }, i)) : code }) }),
586
593
  copyable && !filename && !language && /* @__PURE__ */ jsx5(
587
594
  Button4,
588
595
  {
@@ -929,7 +936,6 @@ import {
929
936
  useRef as useRef2,
930
937
  useState as useState4
931
938
  } from "react";
932
- import { createHighlighter } from "shiki";
933
939
  import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
934
940
  var isItalic = (fontStyle) => fontStyle && fontStyle & 1;
935
941
  var isBold = (fontStyle) => fontStyle && fontStyle & 2;
@@ -981,27 +987,27 @@ var CodeBlockContext = createContext({
981
987
  var highlighterCache = /* @__PURE__ */ new Map();
982
988
  var tokensCache = /* @__PURE__ */ new Map();
983
989
  var subscribers = /* @__PURE__ */ new Map();
984
- var getTokensCacheKey = (code3, language) => {
985
- const start = code3.slice(0, 100);
986
- const end = code3.length > 100 ? code3.slice(-100) : "";
987
- 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}`;
988
994
  };
989
995
  var getHighlighter = (language) => {
990
996
  const cached = highlighterCache.get(language);
991
997
  if (cached) {
992
998
  return cached;
993
999
  }
994
- const highlighterPromise = createHighlighter({
1000
+ const highlighterPromise = import("shiki").then(({ createHighlighter }) => createHighlighter({
995
1001
  langs: [language],
996
1002
  themes: ["github-light", "github-dark"]
997
- });
1003
+ }));
998
1004
  highlighterCache.set(language, highlighterPromise);
999
1005
  return highlighterPromise;
1000
1006
  };
1001
- var createRawTokens = (code3) => ({
1007
+ var createRawTokens = (code) => ({
1002
1008
  bg: "transparent",
1003
1009
  fg: "inherit",
1004
- tokens: code3.split("\n").map(
1010
+ tokens: code.split("\n").map(
1005
1011
  (line) => line === "" ? [] : [
1006
1012
  {
1007
1013
  color: "inherit",
@@ -1010,8 +1016,8 @@ var createRawTokens = (code3) => ({
1010
1016
  ]
1011
1017
  )
1012
1018
  });
1013
- var highlightCode = (code3, language, callback) => {
1014
- const tokensCacheKey = getTokensCacheKey(code3, language);
1019
+ var highlightCode = (code, language, callback) => {
1020
+ const tokensCacheKey = getTokensCacheKey(code, language);
1015
1021
  const cached = tokensCache.get(tokensCacheKey);
1016
1022
  if (cached) {
1017
1023
  return cached;
@@ -1025,7 +1031,7 @@ var highlightCode = (code3, language, callback) => {
1025
1031
  getHighlighter(language).then((highlighter) => {
1026
1032
  const availableLangs = highlighter.getLoadedLanguages();
1027
1033
  const langToUse = availableLangs.includes(language) ? language : "text";
1028
- const result = highlighter.codeToTokens(code3, {
1034
+ const result = highlighter.codeToTokens(code, {
1029
1035
  lang: langToUse,
1030
1036
  themes: {
1031
1037
  dark: "github-dark",
@@ -1155,25 +1161,25 @@ var CodeBlockFilename = ({
1155
1161
  }
1156
1162
  );
1157
1163
  var CodeBlockContent = ({
1158
- code: code3,
1164
+ code,
1159
1165
  language,
1160
1166
  showLineNumbers = false
1161
1167
  }) => {
1162
- const rawTokens = useMemo(() => createRawTokens(code3), [code3]);
1168
+ const rawTokens = useMemo(() => createRawTokens(code), [code]);
1163
1169
  const lang = language;
1164
1170
  const syncTokens = useMemo(
1165
- () => highlightCode(code3, lang) ?? rawTokens,
1166
- [code3, lang, rawTokens]
1171
+ () => highlightCode(code, lang) ?? rawTokens,
1172
+ [code, lang, rawTokens]
1167
1173
  );
1168
1174
  const [asyncTokens, setAsyncTokens] = useState4(null);
1169
- const asyncKeyRef = useRef2({ code: code3, language });
1170
- if (asyncKeyRef.current.code !== code3 || asyncKeyRef.current.language !== language) {
1171
- 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 };
1172
1178
  setAsyncTokens(null);
1173
1179
  }
1174
1180
  useEffect2(() => {
1175
1181
  let cancelled = false;
1176
- highlightCode(code3, lang, (result) => {
1182
+ highlightCode(code, lang, (result) => {
1177
1183
  if (!cancelled) {
1178
1184
  setAsyncTokens(result);
1179
1185
  }
@@ -1181,25 +1187,25 @@ var CodeBlockContent = ({
1181
1187
  return () => {
1182
1188
  cancelled = true;
1183
1189
  };
1184
- }, [code3, language]);
1190
+ }, [code, language]);
1185
1191
  const tokenized = asyncTokens ?? syncTokens;
1186
1192
  return /* @__PURE__ */ jsx7("div", { className: "relative overflow-auto", children: /* @__PURE__ */ jsx7(CodeBlockBody, { showLineNumbers, tokenized }) });
1187
1193
  };
1188
1194
  var CodeBlock2 = ({
1189
- code: code3,
1195
+ code,
1190
1196
  language,
1191
1197
  showLineNumbers = false,
1192
1198
  className,
1193
1199
  children,
1194
1200
  ...props
1195
1201
  }) => {
1196
- const contextValue = useMemo(() => ({ code: code3 }), [code3]);
1202
+ const contextValue = useMemo(() => ({ code }), [code]);
1197
1203
  return /* @__PURE__ */ jsx7(CodeBlockContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs7(CodeBlockContainer, { className, language, ...props, children: [
1198
1204
  children,
1199
1205
  /* @__PURE__ */ jsx7(
1200
1206
  CodeBlockContent,
1201
1207
  {
1202
- code: code3,
1208
+ code,
1203
1209
  language,
1204
1210
  showLineNumbers
1205
1211
  }
@@ -1216,7 +1222,7 @@ var CodeBlockCopyButton = ({
1216
1222
  }) => {
1217
1223
  const [isCopied, setIsCopied] = useState4(false);
1218
1224
  const timeoutRef = useRef2(0);
1219
- const { code: code3 } = useContext(CodeBlockContext);
1225
+ const { code } = useContext(CodeBlockContext);
1220
1226
  const copyToClipboard = useCallback2(async () => {
1221
1227
  if (typeof window === "undefined") {
1222
1228
  onError?.(new Error("Clipboard API not available"));
@@ -1225,7 +1231,7 @@ var CodeBlockCopyButton = ({
1225
1231
  if (isCopied) {
1226
1232
  return;
1227
1233
  }
1228
- const copied = await copyTextToClipboard(code3);
1234
+ const copied = await copyTextToClipboard(code);
1229
1235
  if (!copied) {
1230
1236
  onError?.(new Error("Clipboard API not available"));
1231
1237
  return;
@@ -1236,7 +1242,7 @@ var CodeBlockCopyButton = ({
1236
1242
  () => setIsCopied(false),
1237
1243
  timeout
1238
1244
  );
1239
- }, [code3, onCopy, onError, timeout, isCopied]);
1245
+ }, [code, onCopy, onError, timeout, isCopied]);
1240
1246
  useEffect2(
1241
1247
  () => () => {
1242
1248
  window.clearTimeout(timeoutRef.current);
@@ -2228,6 +2234,7 @@ var PiFollowUpQueueController = class {
2228
2234
  session;
2229
2235
  options;
2230
2236
  nextClientSeqFloor;
2237
+ queueMutationPending = false;
2231
2238
  async submit(input) {
2232
2239
  const text = input.text.trim();
2233
2240
  const attachments = input.attachments ?? [];
@@ -2265,30 +2272,65 @@ var PiFollowUpQueueController = class {
2265
2272
  });
2266
2273
  return { type: "prompt", clientNonce, cursor: receipt.cursor };
2267
2274
  }
2268
- async editQueued() {
2269
- const followUps = this.session.getState().queue.followUps;
2270
- if (followUps.length === 0) {
2271
- const message = "No queued messages to edit.";
2272
- this.options.onWarning?.(message);
2273
- return { type: "empty", message };
2274
- }
2275
- const draft = buildEditedQueuedDraft(followUps, this.options.getDraft?.() ?? "");
2276
- this.options.onDraftChange?.(draft);
2277
- try {
2278
- await this.session.clearQueue();
2279
- return { type: "cleared", draft };
2280
- } catch (error) {
2281
- 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.";
2282
- this.options.onWarning?.(message);
2283
- return { type: "clear-failed", draft, error, message };
2284
- }
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
+ );
2285
2298
  }
2286
- interrupt() {
2287
- return this.session.interrupt();
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
+ );
2313
+ }
2314
+ interrupt(payload = {}) {
2315
+ return this.session.interrupt(payload);
2316
+ }
2317
+ resumeQueued() {
2318
+ return this.session.interrupt({ queueAction: "resume" });
2288
2319
  }
2289
2320
  stop() {
2290
2321
  return this.session.stop();
2291
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
+ }
2292
2334
  nextFollowUpClientSeq() {
2293
2335
  const next = nextFollowUpClientSeq(this.session.getState(), this.nextClientSeqFloor);
2294
2336
  this.nextClientSeqFloor = next + 1;
@@ -2329,6 +2371,9 @@ function buildEditedQueuedDraft(followUps, existingDraft = "") {
2329
2371
  const draft = existingDraft.trim();
2330
2372
  if (!queuedText) return draft;
2331
2373
  if (!draft) return queuedText;
2374
+ if (draft === queuedText || draft.startsWith(`${queuedText}
2375
+
2376
+ `)) return draft;
2332
2377
  return `${queuedText}
2333
2378
 
2334
2379
  ${draft}`;
@@ -2347,76 +2392,148 @@ function defaultClientNonce() {
2347
2392
  // src/front/chat/session/composerPolicy.ts
2348
2393
  var COMPOSER_SETTINGS_PREFIX = "boring-agent:v2";
2349
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
+ };
2350
2424
  var PiComposerPolicyController = class {
2351
2425
  constructor(options) {
2352
2426
  this.options = options;
2353
2427
  this.queueController = createPiFollowUpQueueController(options.session, options);
2428
+ this.submissionCoordinator = options.submissionCoordinator ?? new PiComposerSubmissionCoordinator();
2354
2429
  }
2355
2430
  options;
2356
2431
  queueController;
2357
- async submit(input) {
2358
- 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) {
2359
2440
  const files = input.files ?? [];
2360
2441
  const source = input.source ?? "composer";
2361
2442
  if (this.options.composerBlocked) {
2362
2443
  return this.block("composer-blocked", this.options.blockerMessage ?? "Composer is not ready yet.");
2363
2444
  }
2364
- if (!await this.runBeforeSubmit(input.text, files, source)) {
2365
- 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;
2366
2451
  }
2367
2452
  if (this.options.isActiveSession && !this.options.isActiveSession()) {
2368
- 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.");
2369
2457
  }
2370
- const parsed = parseSlashCommand(text);
2458
+ if (typeof beforeSubmit === "object" && "handled" in beforeSubmit && beforeSubmit.handled) {
2459
+ return { type: "handled", ...beforeSubmit.message ? { message: beforeSubmit.message } : {}, preserveDraft: false };
2460
+ }
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);
2371
2465
  if (parsed) {
2372
2466
  const command = this.options.registry.get(parsed.name);
2373
2467
  if (command?.kind === "skill") {
2374
- return this.submitExpandedText(skillCommandText(parsed.name, parsed.args), source, false);
2468
+ return this.submitExpandedText(skillCommandText(parsed.name, parsed.args), files, source, release);
2375
2469
  }
2376
- if (command) return this.runLocalCommand(parsed.name, parsed.args, source);
2470
+ if (command) return this.runLocalCommand(parsed.name, parsed.args, source, release);
2377
2471
  }
2378
2472
  if (isPiChatBusy(this.options.session.getState().status) && files.length > 0) {
2379
2473
  return this.fromQueueResult(await this.queueController.submit({ text, attachments: this.toAttachmentPayloads(files) }));
2380
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;
2381
2479
  const { serverMessage, attachments } = await createEnrichedSubmitPayload({
2382
- text,
2480
+ text: finalText,
2383
2481
  files,
2384
2482
  mentionedFiles: this.getMentionedFiles()
2385
2483
  });
2386
2484
  if (this.options.isActiveSession && !this.options.isActiveSession()) {
2387
- return this.block("inactive-session", "The active session changed before the message was sent.");
2485
+ return this.stale();
2388
2486
  }
2389
- const result = await this.queueController.submit({
2487
+ const admission = this.queueController.submit({
2390
2488
  text: serverMessage,
2391
- displayText: text,
2489
+ displayText: finalDisplayText,
2392
2490
  attachments,
2393
2491
  model: this.options.model ?? void 0,
2394
2492
  ...this.options.thinkingControl ? { thinkingLevel: this.options.thinkingLevel ?? DEFAULT_THINKING } : {}
2395
2493
  });
2494
+ release();
2495
+ const result = await admission;
2396
2496
  if (result.type !== "blocked") this.options.onMentionedFilesConsumed?.();
2397
2497
  return this.fromQueueResult(result);
2398
2498
  }
2399
2499
  async editQueued() {
2400
2500
  return this.queueController.editQueued();
2401
2501
  }
2402
- interrupt() {
2403
- 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();
2404
2510
  }
2405
2511
  stop() {
2406
2512
  return this.queueController.stop();
2407
2513
  }
2408
- async submitExpandedText(text, source, runBeforeSubmit = true) {
2409
- if (runBeforeSubmit && !await this.runBeforeSubmit(text, [], source)) {
2410
- return this.block("pre-submit-cancelled", "Submit was cancelled before sending.");
2411
- }
2412
- return this.fromQueueResult(await this.queueController.submit({
2413
- 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 } : {},
2414
2520
  kind: "expanded-text",
2415
2521
  model: this.options.model ?? void 0,
2416
2522
  ...this.options.thinkingControl ? { thinkingLevel: this.options.thinkingLevel ?? DEFAULT_THINKING } : {}
2417
- }));
2523
+ });
2524
+ release();
2525
+ return this.fromQueueResult(await admission);
2418
2526
  }
2419
- async runLocalCommand(commandName, args, source) {
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) {
2420
2537
  const command = this.options.registry.get(commandName);
2421
2538
  if (isPiChatBusy(this.options.session.getState().status) && command?.allowWhileBusy?.(args) !== true) {
2422
2539
  return this.block("busy-slash-command", "Slash commands are not queued while the agent is responding.");
@@ -2427,18 +2544,21 @@ var PiComposerPolicyController = class {
2427
2544
  if (message) this.options.onCommandResult?.(message);
2428
2545
  const modelMessage = result && typeof result === "object" ? result.modelMessage?.trim() : void 0;
2429
2546
  if (modelMessage) {
2430
- return await this.submitExpandedText(modelMessage, source, false);
2547
+ return await this.submitExpandedText(modelMessage, [], source, release);
2431
2548
  }
2549
+ release();
2432
2550
  return { type: "command", command: commandName, ...message ? { result: message } : {}, preserveDraft };
2433
2551
  }
2434
2552
  async runBeforeSubmit(draft, files, source) {
2435
- const result = await this.options.onBeforeSubmit?.(draft, { files, source });
2436
- return result !== false;
2553
+ return await this.options.onBeforeSubmit?.(draft, { files, source });
2437
2554
  }
2438
2555
  fromQueueResult(result) {
2439
2556
  if (result.type === "blocked") return { ...result, preserveDraft: true };
2440
2557
  return { ...result, preserveDraft: false };
2441
2558
  }
2559
+ stale() {
2560
+ return { type: "stale", reason: "inactive-session", preserveDraft: false };
2561
+ }
2442
2562
  block(reason, message) {
2443
2563
  this.options.onWarning?.(message);
2444
2564
  return { type: "blocked", reason, message, preserveDraft: true };
@@ -2652,15 +2772,17 @@ function useChatModelSelection({
2652
2772
  setAvailableModels(models);
2653
2773
  setSelection((current) => {
2654
2774
  const selected = current.pendingOverride ?? sessionModel ?? current.localDefault;
2655
- if (availableModel(models, selected)) return current;
2656
- if (sessionModel) return { ...current, pendingOverride: void 0 };
2657
- 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
+ }
2658
2779
  const firstAvailable = models.find((candidate) => candidate.available);
2659
- return {
2660
- ...current,
2661
- pendingOverride: void 0,
2662
- localDefault: payload.defaultModel ?? (firstAvailable ? { provider: firstAvailable.provider, id: firstAvailable.id } : null)
2663
- };
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 };
2664
2786
  });
2665
2787
  setLoadedDiscoveryKey(discoveryKey);
2666
2788
  setLoaded(true);
@@ -2688,7 +2810,8 @@ function useChatModelSelection({
2688
2810
  const selectionBelongsToSession = selection.sessionId === sessionId && selection.storage === storage && selection.storageScope === storageScope;
2689
2811
  const pendingOverride = selectionBelongsToSession ? selection.pendingOverride : void 0;
2690
2812
  const isOverride = Boolean(pendingOverride && sessionModel && !sameModel(pendingOverride, sessionModel));
2691
- const sessionAuthorityReady = !sessionId || sessionHydrated && (sessionModel !== void 0 || sessionIsNew);
2813
+ const discoveryVetted = enabled && currentDiscoveryLoaded;
2814
+ const sessionAuthorityReady = !sessionId || sessionHydrated && (sessionModel !== void 0 || sessionIsNew || discoveryVetted);
2692
2815
  const model = currentDiscoveryLoaded && sessionAuthorityReady ? pendingOverride ?? sessionModel ?? selection.localDefault : null;
2693
2816
  return {
2694
2817
  availableModels: currentDiscoveryLoaded ? availableModels : [],
@@ -3395,15 +3518,15 @@ function messageCreatedAtMs(message) {
3395
3518
 
3396
3519
  // src/front/chat/gatewayResponseError.ts
3397
3520
  var GatewayResponseError = class extends Error {
3398
- constructor(status, message, code3, details, body, operation) {
3521
+ constructor(status, message, code, details, body, operation) {
3399
3522
  super(message);
3400
3523
  this.status = status;
3401
- this.code = code3;
3524
+ this.code = code;
3402
3525
  this.details = details;
3403
3526
  this.body = body;
3404
3527
  this.operation = operation;
3405
3528
  this.name = "GatewayResponseError";
3406
- this.errorCode = code3;
3529
+ this.errorCode = code;
3407
3530
  }
3408
3531
  status;
3409
3532
  code;
@@ -3418,11 +3541,11 @@ async function gatewayResponseError(response, fallbackMessage, operation) {
3418
3541
  }
3419
3542
  function gatewayResponseErrorFromBody(status, body, fallbackMessage, operation) {
3420
3543
  const payload = errorPayload(body);
3421
- const code3 = gatewayResponseErrorCode(payload?.code);
3544
+ const code = gatewayResponseErrorCode(payload?.code);
3422
3545
  const serverMessage = typeof payload?.message === "string" && payload.message.trim().length > 0 ? payload.message : fallbackMessage;
3423
3546
  const message = serverMessage;
3424
3547
  const details = isJsonValue(payload?.details) ? payload.details : void 0;
3425
- return new GatewayResponseError(status, message, code3, details, body, operation);
3548
+ return new GatewayResponseError(status, message, code, details, body, operation);
3426
3549
  }
3427
3550
  function gatewayResponseErrorCode(value) {
3428
3551
  const sharedCode = ErrorCode.safeParse(value);
@@ -4695,12 +4818,30 @@ var RemotePiSession = class {
4695
4818
  }
4696
4819
  }
4697
4820
  async clearQueue(payload = {}) {
4698
- const receipt = await this.postCommand("/queue/clear", payload, QueueClearReceiptSchema);
4821
+ const receipt = await this.postQueueClear({ ...payload, requestId: `queue-clear:${safeRandomUUID()}` });
4699
4822
  if (!this.disposed && receipt.cleared > 0) {
4700
4823
  this.store.dispatch({ type: "clear-optimistic-followups", ...payload }, { flush: true });
4701
4824
  }
4702
4825
  return receipt;
4703
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
+ }
4704
4845
  async interrupt(payload = {}) {
4705
4846
  return this.postCommand("/interrupt", payload, CommandReceiptSchema);
4706
4847
  }
@@ -5310,17 +5451,27 @@ function usePiSessions(options) {
5310
5451
  const [loading, setLoading] = useState10(enabled);
5311
5452
  const [loadingMore, setLoadingMore] = useState10(false);
5312
5453
  const [hasMore, setHasMore] = useState10(false);
5454
+ const [archivedLoaded, setArchivedLoaded] = useState10(false);
5455
+ const [archivedLoading, setArchivedLoading] = useState10(false);
5456
+ const [hasMoreArchived, setHasMoreArchived] = useState10(false);
5313
5457
  const [error, setError] = useState10(void 0);
5314
5458
  const mountedRef = useRef7(false);
5315
5459
  const refreshVersionRef = useRef7(0);
5316
5460
  const retryTimerRef = useRef7(void 0);
5317
5461
  const sessionsRef = useRef7([]);
5462
+ const filterPagersRef = useRef7({
5463
+ active: { sessions: [], loaded: false },
5464
+ archived: { sessions: [], loaded: false }
5465
+ });
5466
+ const archiveMutationVersionRef = useRef7(0);
5318
5467
  const activeSessionIdRef = useRef7(activeSessionId);
5319
- const hasMoreRef = useRef7(hasMore);
5320
5468
  const canonicalLoadedCountRef = useRef7(0);
5321
5469
  const nextCursorRef = useRef7(void 0);
5322
- const loadMoreRequestSeqRef = useRef7(0);
5323
- 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);
5324
5475
  const pendingCreatedRef = useRef7(/* @__PURE__ */ new Map());
5325
5476
  const pendingDeletedRef = useRef7(/* @__PURE__ */ new Set());
5326
5477
  const pendingCreatedScopeRef = useRef7(requestScopeKey);
@@ -5352,14 +5503,11 @@ function usePiSessions(options) {
5352
5503
  useEffect8(() => {
5353
5504
  activeSessionIdRef.current = activeSessionId;
5354
5505
  }, [activeSessionId]);
5355
- useEffect8(() => {
5356
- hasMoreRef.current = hasMore;
5357
- }, [hasMore]);
5358
5506
  const activeSessionKnown = Boolean(activeSessionId && sessions.some((session) => session.id === activeSessionId));
5359
5507
  const requestHeaders = useCallback7(() => normalizedHeaders, [normalizedHeaders]);
5360
5508
  const sessionsUrl = useCallback7((suffix = "") => `${apiBaseUrl}${sessionsApiPath}${suffix}`, [apiBaseUrl, sessionsApiPath]);
5361
- const sessionsListUrl = useCallback7((cursor) => {
5362
- 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 });
5363
5511
  if (cursor) query.set("cursor", cursor);
5364
5512
  return sessionsUrl(`?${query.toString()}`);
5365
5513
  }, [sessionsUrl]);
@@ -5380,6 +5528,27 @@ function usePiSessions(options) {
5380
5528
  pendingCreatedRef.current.clear();
5381
5529
  pendingDeletedRef.current.clear();
5382
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
+ }, []);
5383
5552
  const preferredSessionId = useCallback7(() => {
5384
5553
  const persisted = options.initialActiveSessionId ?? readActiveSessionId({
5385
5554
  storageScope: activeSessionStorageScope,
@@ -5406,11 +5575,23 @@ function usePiSessions(options) {
5406
5575
  }
5407
5576
  }
5408
5577
  const canonicalCount = canonicalPageCount(filteredData);
5409
- const pageMayHaveMore = applyOptions.nextCursor !== void 0;
5410
- const wasExhaustedBeyondFirstPage = applyOptions.background && !hasMoreRef.current && canonicalLoadedCountRef.current >= canonicalCount;
5411
- const requestedActiveReturned = Boolean(requestedActiveId && filteredData.some((session) => session.id === requestedActiveId));
5412
- const current = applyOptions.background && pageMayHaveMore ? sessionsRef.current.filter((session) => !requestedActiveId || requestedActiveReturned || session.id !== requestedActiveId) : [];
5413
- 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
+ }
5414
5595
  const rememberedEmptyId = readBootResumeSessionId({
5415
5596
  bootResumeSource,
5416
5597
  storage: options.bootResumeStorage
@@ -5418,12 +5599,13 @@ function usePiSessions(options) {
5418
5599
  if (rememberedEmptyId && filteredData.some((session) => session.id === rememberedEmptyId)) {
5419
5600
  persistBootResume(void 0);
5420
5601
  }
5421
- const nextHasMore = pageMayHaveMore && !wasExhaustedBeyondFirstPage;
5602
+ const nextHasMore = applyOptions.nextCursor !== void 0;
5422
5603
  canonicalLoadedCountRef.current = applyOptions.background ? Math.max(canonicalLoadedCountRef.current, canonicalCount) : canonicalCount;
5604
+ if (replacingScope && !applyOptions.archivedPage) resetArchivePager();
5423
5605
  loadedDataSourceRef.current = dataSourceKey;
5424
5606
  dataStorageScopeRef.current = storageScope;
5425
5607
  setDataStorageScope(storageScope);
5426
- setSessions(merged);
5608
+ const merged = publishFilterPagers();
5427
5609
  nextCursorRef.current = applyOptions.nextCursor;
5428
5610
  setHasMore(nextHasMore);
5429
5611
  setActiveSessionId((previous) => {
@@ -5432,21 +5614,26 @@ function usePiSessions(options) {
5432
5614
  persistActive(next);
5433
5615
  return next;
5434
5616
  });
5435
- }, [bootResumeSource, dataSourceKey, ensurePendingScope, options.bootResumeStorage, persistActive, persistBootResume, preferredSessionId, storageScope]);
5617
+ }, [bootResumeSource, dataSourceKey, ensurePendingScope, options.bootResumeStorage, persistActive, persistBootResume, preferredSessionId, publishFilterPagers, resetArchivePager, storageScope]);
5436
5618
  const refresh = useCallback7(async (refreshOptions = {}) => {
5437
5619
  const scope = requestScopeKey;
5438
5620
  if (mountedRef.current && !sourceIsCurrent(scope)) throw new StaleSessionsSourceError();
5621
+ const requestOwner = {};
5622
+ activeRequestOwnerRef.current = requestOwner;
5439
5623
  const version = ++refreshVersionRef.current;
5440
- const isCurrent = () => sourceIsCurrent(scope) && version === refreshVersionRef.current;
5624
+ const mutationVersion = archiveMutationVersionRef.current;
5625
+ const isCurrent = () => sourceIsCurrent(scope) && version === refreshVersionRef.current && activeRequestOwnerRef.current === requestOwner;
5441
5626
  clearRetryTimer(retryTimerRef);
5442
5627
  const background = refreshOptions.background === true;
5628
+ let archivedRequestSeq;
5443
5629
  if (!enabled) {
5444
- loadMoreRequestSeqRef.current += 1;
5445
- loadMoreInFlightRef.current = false;
5446
5630
  canonicalLoadedCountRef.current = 0;
5447
5631
  loadedDataSourceRef.current = dataSourceKey;
5448
5632
  dataStorageScopeRef.current = storageScope;
5449
5633
  setDataStorageScope(storageScope);
5634
+ filterPagersRef.current.active = { sessions: [], loaded: true };
5635
+ resetArchivePager();
5636
+ sessionsRef.current = [];
5450
5637
  setSessions([]);
5451
5638
  setActiveSessionId(void 0);
5452
5639
  setError(void 0);
@@ -5454,17 +5641,22 @@ function usePiSessions(options) {
5454
5641
  setLoadingMore(false);
5455
5642
  setHasMore(false);
5456
5643
  persistActive(void 0);
5644
+ if (activeRequestOwnerRef.current === requestOwner) activeRequestOwnerRef.current = void 0;
5457
5645
  return;
5458
5646
  }
5459
- loadMoreRequestSeqRef.current += 1;
5460
- loadMoreInFlightRef.current = false;
5461
5647
  setLoadingMore(false);
5462
5648
  if (!background) setLoading(true);
5463
5649
  try {
5464
5650
  let page;
5465
5651
  for (let attempt = 0; ; attempt += 1) {
5466
5652
  try {
5467
- 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
+ });
5468
5660
  break;
5469
5661
  } catch (err) {
5470
5662
  const transient = err instanceof SessionsPreparingError || isNetworkFetchError(err);
@@ -5476,17 +5668,41 @@ function usePiSessions(options) {
5476
5668
  }
5477
5669
  }
5478
5670
  if (!isCurrent() || !page) return;
5479
- 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 });
5480
5690
  setError(void 0);
5481
5691
  setLoading(false);
5482
5692
  } catch (err) {
5483
- if (!isCurrent()) return;
5693
+ if (!isCurrent() || archivedRequestSeq !== void 0 && archivedRequestSeq !== archiveRequestSeqRef.current) return;
5484
5694
  const error2 = err instanceof Error ? err : new Error(String(err));
5485
5695
  if (!background) setError(error2);
5486
5696
  setLoading(false);
5487
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
+ }
5488
5704
  }
5489
- }, [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]);
5490
5706
  useEffect8(() => {
5491
5707
  mountedRef.current = true;
5492
5708
  void refresh();
@@ -5497,21 +5713,26 @@ function usePiSessions(options) {
5497
5713
  };
5498
5714
  }, [refresh, options.refreshKey]);
5499
5715
  const loadMore = useCallback7(async () => {
5500
- if (!enabled || loading || loadingMore || loadMoreInFlightRef.current || !hasMore) return;
5501
- const requestSeq = ++loadMoreRequestSeqRef.current;
5502
- loadMoreInFlightRef.current = true;
5716
+ if (!enabled || loading || loadingMore || activeRequestOwnerRef.current !== void 0 || !hasMore) return;
5717
+ const requestOwner = {};
5718
+ activeRequestOwnerRef.current = requestOwner;
5503
5719
  const version = refreshVersionRef.current;
5720
+ const mutationVersion = archiveMutationVersionRef.current;
5504
5721
  const scope = requestScopeKey;
5505
5722
  setLoadingMore(true);
5506
5723
  try {
5507
5724
  const page = await fetchSessionList(fetchImpl, sessionsListUrl(nextCursorRef.current), requestHeaders());
5508
5725
  const data = page.sessions.filter((session) => !pendingDeletedRef.current.has(session.id));
5509
- if (requestSeq !== loadMoreRequestSeqRef.current || version !== refreshVersionRef.current || !sourceIsCurrent(scope)) return;
5510
- 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();
5511
5733
  const nextHasMore = page.nextCursor !== void 0;
5512
5734
  canonicalLoadedCountRef.current += data.length;
5513
5735
  nextCursorRef.current = page.nextCursor;
5514
- setSessions(merged);
5515
5736
  setHasMore(nextHasMore);
5516
5737
  setError(void 0);
5517
5738
  setActiveSessionId((previous) => {
@@ -5521,16 +5742,52 @@ function usePiSessions(options) {
5521
5742
  return next;
5522
5743
  });
5523
5744
  } catch (err) {
5524
- if (requestSeq === loadMoreRequestSeqRef.current && version === refreshVersionRef.current && sourceIsCurrent(scope)) {
5745
+ if (activeRequestOwnerRef.current === requestOwner && version === refreshVersionRef.current && sourceIsCurrent(scope)) {
5525
5746
  setError(err instanceof Error ? err : new Error(String(err)));
5526
5747
  }
5527
5748
  } finally {
5528
- if (requestSeq === loadMoreRequestSeqRef.current) loadMoreInFlightRef.current = false;
5529
- if (requestSeq === loadMoreRequestSeqRef.current && version === refreshVersionRef.current && sourceIsCurrent(scope)) {
5530
- setLoadingMore(false);
5749
+ if (activeRequestOwnerRef.current === requestOwner) {
5750
+ activeRequestOwnerRef.current = void 0;
5751
+ if (version === refreshVersionRef.current && sourceIsCurrent(scope)) setLoadingMore(false);
5531
5752
  }
5532
5753
  }
5533
- }, [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]);
5534
5791
  useEffect8(() => {
5535
5792
  if (!enabled || !connectActiveSession || !activeSessionId || !activeSessionKnown) {
5536
5793
  setActivePiSession(void 0);
@@ -5572,12 +5829,12 @@ function usePiSessions(options) {
5572
5829
  rememberConfirmedEmptySession(session.id);
5573
5830
  pendingCreatedRef.current.set(session.id, session);
5574
5831
  setDataStorageScope(storageScope);
5575
- setSessions((previous) => mergeSessions([session], previous));
5832
+ publishFilterPagers();
5576
5833
  setActiveSessionId(session.id);
5577
5834
  persistActive(session.id);
5578
5835
  void refresh();
5579
5836
  return session;
5580
- }, [enabled, ensurePendingScope, fetchImpl, persistActive, refresh, rememberConfirmedEmptySession, requestHeaders, requestScopeKey, sessionsUrl, sourceIsCurrent, storageScope]);
5837
+ }, [enabled, ensurePendingScope, fetchImpl, persistActive, publishFilterPagers, refresh, rememberConfirmedEmptySession, requestHeaders, requestScopeKey, sessionsUrl, sourceIsCurrent, storageScope]);
5581
5838
  const switchSession = useCallback7((id) => {
5582
5839
  if (!sourceIsCurrent(requestScopeKey)) return;
5583
5840
  const known = sessionsRef.current.some((session) => session.id === id);
@@ -5599,9 +5856,34 @@ function usePiSessions(options) {
5599
5856
  if (!sourceIsCurrent(scope)) throw new StaleSessionsSourceError();
5600
5857
  ensurePendingScope();
5601
5858
  if (pendingCreatedRef.current.has(id)) pendingCreatedRef.current.set(id, renamed);
5602
- 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();
5603
5863
  return renamed;
5604
- }, [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]);
5605
5887
  const deleteSession = useCallback7(async (id) => {
5606
5888
  const scope = requestScopeKey;
5607
5889
  if (!sourceIsCurrent(scope)) throw new StaleSessionsSourceError();
@@ -5614,8 +5896,11 @@ function usePiSessions(options) {
5614
5896
  const deletedSessionWasActive = activeSessionIdRef.current === id;
5615
5897
  pendingCreatedRef.current.delete(id);
5616
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
+ }
5617
5902
  setDataStorageScope(storageScope);
5618
- setSessions((previous) => previous.filter((session) => session.id !== id));
5903
+ publishFilterPagers();
5619
5904
  setActiveSessionId((previous) => {
5620
5905
  if (previous !== id) return previous;
5621
5906
  const next = sessionsRef.current.find((session) => session.id !== id)?.id;
@@ -5633,7 +5918,9 @@ function usePiSessions(options) {
5633
5918
  if (sourceIsCurrent(scope)) {
5634
5919
  pendingDeletedRef.current.delete(id);
5635
5920
  if (deletedSession) {
5636
- 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();
5637
5924
  if (deletedSessionWasActive) {
5638
5925
  setActiveSessionId(id);
5639
5926
  persistActive(id);
@@ -5645,14 +5932,15 @@ function usePiSessions(options) {
5645
5932
  throw error2;
5646
5933
  }
5647
5934
  if (sourceIsCurrent(scope)) void refresh();
5648
- }, [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]);
5649
5936
  const reset = useCallback7(() => {
5650
5937
  if (!sourceIsCurrent(requestScopeKey)) return;
5651
5938
  pendingCreatedRef.current.clear();
5652
5939
  pendingDeletedRef.current.clear();
5653
- loadMoreRequestSeqRef.current += 1;
5654
- loadMoreInFlightRef.current = false;
5655
- canonicalLoadedCountRef.current = canonicalPageCount(sessionsRef.current);
5940
+ activeRequestOwnerRef.current = void 0;
5941
+ resetArchivePager();
5942
+ publishFilterPagers();
5943
+ canonicalLoadedCountRef.current = canonicalPageCount(filterPagersRef.current.active.sessions);
5656
5944
  loadedDataSourceRef.current = dataSourceKey;
5657
5945
  dataStorageScopeRef.current = storageScope;
5658
5946
  setDataStorageScope(storageScope);
@@ -5660,7 +5948,7 @@ function usePiSessions(options) {
5660
5948
  setActivePiSession(void 0);
5661
5949
  setLoadingMore(false);
5662
5950
  persistActive(void 0);
5663
- }, [dataSourceKey, persistActive, requestScopeKey, sourceIsCurrent, storageScope]);
5951
+ }, [dataSourceKey, persistActive, publishFilterPagers, requestScopeKey, resetArchivePager, sourceIsCurrent, storageScope]);
5664
5952
  const visibleActiveSessionId = enabled ? activeSessionId : void 0;
5665
5953
  const activeSession = enabled ? sessions.find((session) => session.id === visibleActiveSessionId) : void 0;
5666
5954
  return {
@@ -5674,10 +5962,15 @@ function usePiSessions(options) {
5674
5962
  loading: enabled ? loading : false,
5675
5963
  loadingMore,
5676
5964
  hasMore: enabled ? hasMore : false,
5965
+ archivedLoaded,
5966
+ archivedLoading,
5967
+ hasMoreArchived: enabled ? hasMoreArchived : false,
5677
5968
  error,
5678
5969
  refresh,
5679
5970
  create,
5680
5971
  rename,
5972
+ setArchived,
5973
+ loadArchived,
5681
5974
  switch: switchSession,
5682
5975
  delete: deleteSession,
5683
5976
  loadMore,
@@ -5698,6 +5991,19 @@ async function fetchSessionList(fetchImpl, url, headers) {
5698
5991
  ...typeof page.nextCursor === "string" ? { nextCursor: page.nextCursor } : {}
5699
5992
  };
5700
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
+ }
5701
6007
  function toAddressedSessionSummary(value) {
5702
6008
  if (typeof value !== "object" || value === null) throw new Error("invalid addressed session summary");
5703
6009
  const record = value;
@@ -5719,7 +6025,8 @@ function toAddressedSessionSummary(value) {
5719
6025
  ...typeof agentTypeId === "string" ? { agentTypeId } : {},
5720
6026
  ...typeof record.nativeSessionId === "string" ? { nativeSessionId: record.nativeSessionId } : {},
5721
6027
  ...typeof record.hasAssistantReply === "boolean" ? { hasAssistantReply: record.hasAssistantReply } : {},
5722
- ...addressedStatus ? { status: addressedStatus } : {}
6028
+ ...addressedStatus ? { status: addressedStatus } : {},
6029
+ ...record.archived === true ? { archived: true } : {}
5723
6030
  };
5724
6031
  }
5725
6032
  function addressedCreatedSession(value, expectedAgentTypeId, title) {
@@ -5773,6 +6080,13 @@ function remoteSessionOptionsIdentity(options) {
5773
6080
  } : void 0
5774
6081
  });
5775
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
+ }
5776
6090
  function mergeSessions(...lists) {
5777
6091
  const seen = /* @__PURE__ */ new Set();
5778
6092
  const merged = [];
@@ -5999,7 +6313,7 @@ function sortByUpdatedDesc(a, b) {
5999
6313
  return a.id.localeCompare(b.id);
6000
6314
  }
6001
6315
 
6002
- // ../../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
6003
6317
  function fuzzyMatch(query, text) {
6004
6318
  const queryLower = query.toLowerCase();
6005
6319
  const textLower = text.toLowerCase();
@@ -6185,7 +6499,7 @@ function searchPiSessions(sessions, query, options = {}) {
6185
6499
 
6186
6500
  // src/front/chat/components/PiConversationSurface.tsx
6187
6501
  import { Skeleton } from "@hachej/boring-ui-kit";
6188
- 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";
6189
6503
  import { useStickToBottomContext as useStickToBottomContext2 } from "use-stick-to-bottom";
6190
6504
 
6191
6505
  // src/front/primitives/conversation.tsx
@@ -6261,8 +6575,8 @@ var ConversationScrollButton = ({
6261
6575
  };
6262
6576
 
6263
6577
  // src/front/chat/components/ChatNotices.tsx
6264
- import { AlertCircleIcon, ExternalLinkIcon as ExternalLinkIcon2, ListRestartIcon, Loader2, XIcon as XIcon2 } from "lucide-react";
6265
- 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";
6266
6580
 
6267
6581
  // src/front/chat/components/noticeStyles.ts
6268
6582
  function noticeSurfaceClass(level, className) {
@@ -6496,7 +6810,15 @@ function composerBlockerActionIcon(actionId) {
6496
6810
  if (actionId === "cancel") return /* @__PURE__ */ jsx15(XIcon2, { className: "size-3.5", "aria-hidden": "true" });
6497
6811
  return null;
6498
6812
  }
6499
- 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;
6500
6822
  return /* @__PURE__ */ jsxs14(
6501
6823
  "div",
6502
6824
  {
@@ -6506,7 +6828,7 @@ function QueuedComposerNotice({ followUps, onEdit }) {
6506
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)]"
6507
6829
  ),
6508
6830
  children: [
6509
- /* @__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: [
6510
6832
  /* @__PURE__ */ jsxs14("div", { className: "font-medium text-[color:var(--foreground)]", children: [
6511
6833
  followUps.length,
6512
6834
  " queued follow-up",
@@ -6514,31 +6836,73 @@ function QueuedComposerNotice({ followUps, onEdit }) {
6514
6836
  ] }),
6515
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(" - ") })
6516
6838
  ] }),
6517
- /* @__PURE__ */ jsx15(
6518
- IconButton2,
6519
- {
6520
- type: "button",
6521
- variant: "ghost",
6522
- size: "icon-sm",
6523
- onClick: onEdit,
6524
- className: "shrink-0 text-[color:var(--muted-foreground)] hover:bg-[color:var(--muted)] hover:text-[color:var(--foreground)]",
6525
- "aria-label": "Edit queued follow-ups",
6526
- title: "Edit queued follow-ups",
6527
- children: /* @__PURE__ */ jsx15(ListRestartIcon, { className: "size-3.5", "aria-hidden": "true" })
6528
- }
6529
- )
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
+ ] })
6530
6894
  ]
6531
6895
  }
6532
6896
  );
6533
6897
  }
6534
6898
 
6535
6899
  // src/front/chat/components/PiTimelineMessage.tsx
6536
- import { useCallback as useCallback13, useEffect as useEffect13, useMemo as useMemo12, useRef as useRef9, useState as useState14 } from "react";
6537
- import { CheckIcon as CheckIcon2, CopyIcon as CopyIcon3 } from "lucide-react";
6538
- 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";
6539
6903
 
6540
6904
  // src/front/primitives/attachments.tsx
6541
- 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";
6542
6906
  import {
6543
6907
  FileTextIcon as FileTextIcon2,
6544
6908
  GlobeIcon,
@@ -6736,7 +7100,7 @@ var AttachmentRemove = ({
6736
7100
  return null;
6737
7101
  }
6738
7102
  return /* @__PURE__ */ jsxs15(
6739
- Button8,
7103
+ Button9,
6740
7104
  {
6741
7105
  "aria-label": label,
6742
7106
  className: cn(
@@ -6768,7 +7132,7 @@ var AttachmentRemove = ({
6768
7132
  };
6769
7133
 
6770
7134
  // src/front/primitives/message.tsx
6771
- import { Button as Button9, ButtonGroup, ButtonGroupText } from "@hachej/boring-ui-kit";
7135
+ import { Button as Button10, ButtonGroup, ButtonGroupText } from "@hachej/boring-ui-kit";
6772
7136
  import {
6773
7137
  Tooltip,
6774
7138
  TooltipContent,
@@ -6780,23 +7144,228 @@ import {
6780
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";
6781
7145
 
6782
7146
  // src/front/primitives/message.tsx
6783
- import { cjk } from "@streamdown/cjk";
6784
- import { code } from "@streamdown/code";
6785
- import { math } from "@streamdown/math";
6786
- import { mermaid } from "@streamdown/mermaid";
6787
7147
  import { ChevronLeftIcon as ChevronLeftIcon2, ChevronRightIcon } from "lucide-react";
6788
7148
  import {
6789
7149
  createContext as createContext4,
6790
7150
  memo as memo3,
6791
- useCallback as useCallback10,
7151
+ useCallback as useCallback11,
6792
7152
  useContext as useContext4,
6793
- useEffect as useEffect10,
6794
- useMemo as useMemo7,
6795
- useState as useState11
7153
+ useEffect as useEffect12,
7154
+ useMemo as useMemo8,
7155
+ useState as useState13
7156
+ } from "react";
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
6796
7241
  } from "react";
6797
- import { Streamdown } from "streamdown";
7242
+ import { defaultRehypePlugins } from "streamdown";
6798
7243
  import { jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
6799
- 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(
6800
7369
  "div",
6801
7370
  {
6802
7371
  "data-boring-agent-message-role": from,
@@ -6812,7 +7381,7 @@ var MessageContent = ({
6812
7381
  children,
6813
7382
  className,
6814
7383
  ...props
6815
- }) => /* @__PURE__ */ jsx17(
7384
+ }) => /* @__PURE__ */ jsx18(
6816
7385
  "div",
6817
7386
  {
6818
7387
  className: cn(
@@ -6829,7 +7398,7 @@ var MessageActions = ({
6829
7398
  className,
6830
7399
  children,
6831
7400
  ...props
6832
- }) => /* @__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 });
6833
7402
  var MessageAction = ({
6834
7403
  tooltip,
6835
7404
  children,
@@ -6838,14 +7407,14 @@ var MessageAction = ({
6838
7407
  size = "icon-sm",
6839
7408
  ...props
6840
7409
  }) => {
6841
- const button = /* @__PURE__ */ jsxs16(Button9, { size, type: "button", variant, ...props, children: [
7410
+ const button = /* @__PURE__ */ jsxs17(Button10, { size, type: "button", variant, ...props, children: [
6842
7411
  children,
6843
- /* @__PURE__ */ jsx17("span", { className: "sr-only", children: label || tooltip })
7412
+ /* @__PURE__ */ jsx18("span", { className: "sr-only", children: label || tooltip })
6844
7413
  ] });
6845
7414
  if (tooltip) {
6846
- return /* @__PURE__ */ jsx17(TooltipProvider, { children: /* @__PURE__ */ jsxs16(Tooltip, { children: [
6847
- /* @__PURE__ */ jsx17(TooltipTrigger, { asChild: true, children: button }),
6848
- /* @__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 }) })
6849
7418
  ] }) });
6850
7419
  }
6851
7420
  return button;
@@ -6853,7 +7422,6 @@ var MessageAction = ({
6853
7422
  var MessageBranchContext = createContext4(
6854
7423
  null
6855
7424
  );
6856
- var streamdownPlugins = { cjk, code, math, mermaid };
6857
7425
  var DEFAULT_SHIKI_THEME = ["github-light", "github-dark-dimmed"];
6858
7426
  function extractCodeBlockLanguage(children) {
6859
7427
  if (!children) return void 0;
@@ -6884,11 +7452,11 @@ function extractCodeBlockText(children) {
6884
7452
  return walk(children);
6885
7453
  }
6886
7454
  var MarkdownPre = ({ children, filename }) => {
6887
- const code3 = extractCodeBlockText(children).replace(/\n$/, "");
7455
+ const code = extractCodeBlockText(children).replace(/\n$/, "");
6888
7456
  const language = extractCodeBlockLanguage(children);
6889
- return /* @__PURE__ */ jsx17(CodeBlock2, { code: code3, language: language ?? "text", className: "my-3", children: /* @__PURE__ */ jsxs16(CodeBlockHeader, { children: [
6890
- /* @__PURE__ */ jsx17(CodeBlockTitle, { children: /* @__PURE__ */ jsx17(CodeBlockFilename, { children: filename ?? language ?? "text" }) }),
6891
- /* @__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, {})
6892
7460
  ] }) });
6893
7461
  };
6894
7462
  var MarkdownCode = ({
@@ -6898,9 +7466,9 @@ var MarkdownCode = ({
6898
7466
  ...props
6899
7467
  }) => {
6900
7468
  if (inline === false) {
6901
- return /* @__PURE__ */ jsx17("code", { className, ...props, children });
7469
+ return /* @__PURE__ */ jsx18("code", { className, ...props, children });
6902
7470
  }
6903
- return /* @__PURE__ */ jsx17(
7471
+ return /* @__PURE__ */ jsx18(
6904
7472
  "code",
6905
7473
  {
6906
7474
  className: cn(
@@ -6917,23 +7485,33 @@ var markdownComponents = {
6917
7485
  code: MarkdownCode
6918
7486
  };
6919
7487
  var MessageResponse = memo3(
6920
- ({ className, shikiTheme, components, codeFilename, ...props }) => /* @__PURE__ */ jsx17(
6921
- Streamdown,
6922
- {
6923
- className: cn(
6924
- "size-full [&>*:first-child]:mt-0 [&>*:last-child]:mb-0",
6925
- className
6926
- ),
6927
- plugins: streamdownPlugins,
6928
- shikiTheme: shikiTheme ?? DEFAULT_SHIKI_THEME,
6929
- components: {
6930
- ...markdownComponents,
6931
- ...components ?? {},
6932
- ...codeFilename ? { pre: (props2) => /* @__PURE__ */ jsx17(MarkdownPre, { ...props2, filename: codeFilename }) } : {}
6933
- },
6934
- ...props
6935
- }
6936
- ),
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
+ },
6937
7515
  (prevProps, nextProps) => prevProps.children === nextProps.children && nextProps.isAnimating === prevProps.isAnimating && nextProps.components === prevProps.components && prevProps.codeFilename === nextProps.codeFilename
6938
7516
  );
6939
7517
  MessageResponse.displayName = "MessageResponse";
@@ -6945,27 +7523,23 @@ import {
6945
7523
  CollapsibleContent as CollapsibleContent2,
6946
7524
  CollapsibleTrigger as CollapsibleTrigger3
6947
7525
  } from "@hachej/boring-ui-kit";
6948
- import { cjk as cjk2 } from "@streamdown/cjk";
6949
- import { code as code2 } from "@streamdown/code";
6950
- import { math as math2 } from "@streamdown/math";
6951
- import { mermaid as mermaid2 } from "@streamdown/mermaid";
6952
7526
  import { BrainIcon, ChevronDownIcon as ChevronDownIcon3 } from "lucide-react";
6953
7527
  import {
6954
7528
  createContext as createContext5,
6955
7529
  memo as memo5,
6956
- useCallback as useCallback11,
7530
+ useCallback as useCallback12,
6957
7531
  useContext as useContext5,
6958
- useEffect as useEffect11,
6959
- useMemo as useMemo9,
6960
- useRef as useRef8,
6961
- useState as useState12
7532
+ useEffect as useEffect13,
7533
+ useMemo as useMemo10,
7534
+ useRef as useRef9,
7535
+ useState as useState14
6962
7536
  } from "react";
6963
- import { Streamdown as Streamdown2 } from "streamdown";
7537
+ import { Streamdown as Streamdown3 } from "streamdown";
6964
7538
 
6965
7539
  // src/front/primitives/shimmer.tsx
6966
7540
  import { motion } from "motion/react";
6967
- import { memo as memo4, useMemo as useMemo8 } from "react";
6968
- 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";
6969
7543
  var motionComponentCache = /* @__PURE__ */ new Map();
6970
7544
  var getMotionComponent = (element) => {
6971
7545
  let component = motionComponentCache.get(element);
@@ -6985,11 +7559,11 @@ var ShimmerComponent = ({
6985
7559
  const MotionComponent = getMotionComponent(
6986
7560
  Component
6987
7561
  );
6988
- const dynamicSpread = useMemo8(
7562
+ const dynamicSpread = useMemo9(
6989
7563
  () => (children?.length ?? 0) * spread,
6990
7564
  [children, spread]
6991
7565
  );
6992
- return /* @__PURE__ */ jsx18(
7566
+ return /* @__PURE__ */ jsx19(
6993
7567
  MotionComponent,
6994
7568
  {
6995
7569
  animate: { backgroundPosition: "0% center" },
@@ -7015,7 +7589,7 @@ var ShimmerComponent = ({
7015
7589
  var Shimmer = memo4(ShimmerComponent);
7016
7590
 
7017
7591
  // src/front/primitives/reasoning.tsx
7018
- 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";
7019
7593
  var ReasoningContext = createContext5(null);
7020
7594
  var useReasoning = () => {
7021
7595
  const context = useContext5(ReasoningContext);
@@ -7049,10 +7623,10 @@ var Reasoning = memo5(
7049
7623
  defaultProp: void 0,
7050
7624
  prop: durationProp
7051
7625
  });
7052
- const hasEverStreamedRef = useRef8(isStreaming);
7053
- const [hasAutoClosed, setHasAutoClosed] = useState12(false);
7054
- const startTimeRef = useRef8(null);
7055
- useEffect11(() => {
7626
+ const hasEverStreamedRef = useRef9(isStreaming);
7627
+ const [hasAutoClosed, setHasAutoClosed] = useState14(false);
7628
+ const startTimeRef = useRef9(null);
7629
+ useEffect13(() => {
7056
7630
  if (isStreaming) {
7057
7631
  hasEverStreamedRef.current = true;
7058
7632
  if (startTimeRef.current === null) {
@@ -7063,12 +7637,12 @@ var Reasoning = memo5(
7063
7637
  startTimeRef.current = null;
7064
7638
  }
7065
7639
  }, [isStreaming, setDuration]);
7066
- useEffect11(() => {
7640
+ useEffect13(() => {
7067
7641
  if (isStreaming && !isOpen && !isExplicitlyClosed) {
7068
7642
  setIsOpen(true);
7069
7643
  }
7070
7644
  }, [isStreaming, isOpen, setIsOpen, isExplicitlyClosed]);
7071
- useEffect11(() => {
7645
+ useEffect13(() => {
7072
7646
  if (hasEverStreamedRef.current && autoClose && !isStreaming && isOpen && !hasAutoClosed) {
7073
7647
  const timer = setTimeout(() => {
7074
7648
  setIsOpen(false);
@@ -7077,17 +7651,17 @@ var Reasoning = memo5(
7077
7651
  return () => clearTimeout(timer);
7078
7652
  }
7079
7653
  }, [isStreaming, isOpen, setIsOpen, hasAutoClosed, autoClose]);
7080
- const handleOpenChange = useCallback11(
7654
+ const handleOpenChange = useCallback12(
7081
7655
  (newOpen) => {
7082
7656
  setIsOpen(newOpen);
7083
7657
  },
7084
7658
  [setIsOpen]
7085
7659
  );
7086
- const contextValue = useMemo9(
7660
+ const contextValue = useMemo10(
7087
7661
  () => ({ duration, isOpen, isStreaming, setIsOpen }),
7088
7662
  [duration, isOpen, isStreaming, setIsOpen]
7089
7663
  );
7090
- return /* @__PURE__ */ jsx19(ReasoningContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx19(
7664
+ return /* @__PURE__ */ jsx20(ReasoningContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx20(
7091
7665
  Collapsible2,
7092
7666
  {
7093
7667
  className: cn("not-prose mb-2", className),
@@ -7101,12 +7675,12 @@ var Reasoning = memo5(
7101
7675
  );
7102
7676
  var defaultGetThinkingMessage = (isStreaming, duration) => {
7103
7677
  if (isStreaming || duration === 0) {
7104
- return /* @__PURE__ */ jsx19(Shimmer, { duration: 1, children: "thinking" });
7678
+ return /* @__PURE__ */ jsx20(Shimmer, { duration: 1, children: "thinking" });
7105
7679
  }
7106
7680
  if (duration === void 0) {
7107
- return /* @__PURE__ */ jsx19("span", { children: "thoughts" });
7681
+ return /* @__PURE__ */ jsx20("span", { children: "thoughts" });
7108
7682
  }
7109
- return /* @__PURE__ */ jsxs17("span", { children: [
7683
+ return /* @__PURE__ */ jsxs18("span", { children: [
7110
7684
  "thoughts \xB7 ",
7111
7685
  duration,
7112
7686
  "s"
@@ -7120,7 +7694,7 @@ var ReasoningTrigger = memo5(
7120
7694
  ...props
7121
7695
  }) => {
7122
7696
  const { isStreaming, isOpen, duration } = useReasoning();
7123
- return /* @__PURE__ */ jsx19(
7697
+ return /* @__PURE__ */ jsx20(
7124
7698
  CollapsibleTrigger3,
7125
7699
  {
7126
7700
  className: cn(
@@ -7128,10 +7702,10 @@ var ReasoningTrigger = memo5(
7128
7702
  className
7129
7703
  ),
7130
7704
  ...props,
7131
- children: children ?? /* @__PURE__ */ jsxs17(Fragment8, { children: [
7132
- /* @__PURE__ */ jsx19(BrainIcon, { className: "size-3" }),
7705
+ children: children ?? /* @__PURE__ */ jsxs18(Fragment8, { children: [
7706
+ /* @__PURE__ */ jsx20(BrainIcon, { className: "size-3" }),
7133
7707
  getThinkingMessage(isStreaming, duration),
7134
- /* @__PURE__ */ jsx19(
7708
+ /* @__PURE__ */ jsx20(
7135
7709
  ChevronDownIcon3,
7136
7710
  {
7137
7711
  className: cn(
@@ -7145,20 +7719,22 @@ var ReasoningTrigger = memo5(
7145
7719
  );
7146
7720
  }
7147
7721
  );
7148
- var streamdownPlugins2 = { cjk: cjk2, code: code2, math: math2, mermaid: mermaid2 };
7149
7722
  var ReasoningContent = memo5(
7150
- ({ className, children, ...props }) => /* @__PURE__ */ jsx19(
7151
- CollapsibleContent2,
7152
- {
7153
- className: cn(
7154
- "mt-2 border-l border-border/40 pl-3 text-xs",
7155
- "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",
7156
- className
7157
- ),
7158
- ...props,
7159
- children: /* @__PURE__ */ jsx19(Streamdown2, { plugins: streamdownPlugins2, children })
7160
- }
7161
- )
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
+ }
7162
7738
  );
7163
7739
  Reasoning.displayName = "Reasoning";
7164
7740
  ReasoningTrigger.displayName = "ReasoningTrigger";
@@ -7166,7 +7742,7 @@ ReasoningContent.displayName = "ReasoningContent";
7166
7742
 
7167
7743
  // src/front/primitives/tool-call-group.tsx
7168
7744
  import { ChevronDownIcon as ChevronDownIcon4 } from "lucide-react";
7169
- 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";
7170
7746
  import { Collapsible as Collapsible3, CollapsibleContent as CollapsibleContent3, CollapsibleTrigger as CollapsibleTrigger4 } from "@hachej/boring-ui-kit";
7171
7747
 
7172
7748
  // src/front/primitives/tool-call-group-state.ts
@@ -7205,7 +7781,7 @@ var TOOL_GROUP_VISUAL_PRESENTATION = {
7205
7781
  };
7206
7782
 
7207
7783
  // src/front/primitives/tool-call-group.tsx
7208
- import { jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
7784
+ import { jsx as jsx21, jsxs as jsxs19 } from "react/jsx-runtime";
7209
7785
  function isInputRunningState(state) {
7210
7786
  return state === "input-streaming" || state === "input-available";
7211
7787
  }
@@ -7255,7 +7831,7 @@ function resolveVisualState({
7255
7831
  return "settled";
7256
7832
  }
7257
7833
  var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
7258
- const toolParts = useMemo10(() => tools.map(({ part, key }) => {
7834
+ const toolParts = useMemo11(() => tools.map(({ part, key }) => {
7259
7835
  const toolPart = toToolPart(part);
7260
7836
  return toolPart ? { part: toolPart, key } : null;
7261
7837
  }).filter((entry) => Boolean(entry)), [tools]);
@@ -7264,8 +7840,8 @@ var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
7264
7840
  const hasAborted = toolParts.some(({ part }) => part.state === "aborted") && !workspaceNotReady;
7265
7841
  const hasRunningTool = toolParts.some(({ part }) => isInputRunningState(part.state) && !getWorkspaceNotReadyStatus(part.output));
7266
7842
  const approvalNeeded = toolParts.some(({ part }) => isApprovalRequestedState(part.state));
7267
- const [isOpen, setIsOpen] = useState13(false);
7268
- const handleOpenChange = useCallback12((open) => setIsOpen(open), []);
7843
+ const [isOpen, setIsOpen] = useState15(false);
7844
+ const handleOpenChange = useCallback13((open) => setIsOpen(open), []);
7269
7845
  const visualState = resolveVisualState({
7270
7846
  hasRunningTool,
7271
7847
  approvalNeeded,
@@ -7274,10 +7850,10 @@ var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
7274
7850
  hasAborted
7275
7851
  });
7276
7852
  const isRunning = visualState === RUNNING_TOOL_GROUP_VISUAL_STATE;
7277
- const [elapsedSeconds, setElapsedSeconds] = useState13(0);
7278
- 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]);
7279
7855
  const elapsedLabel = isRunning ? `Running ${formatElapsed(elapsedSeconds)}` : null;
7280
- useEffect12(() => {
7856
+ useEffect14(() => {
7281
7857
  if (!isRunning) {
7282
7858
  setElapsedSeconds(0);
7283
7859
  return;
@@ -7294,7 +7870,7 @@ var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
7294
7870
  const { renderer } = resolveToolRendererForPart(part, mergedToolRenderers);
7295
7871
  return renderer.presentation === "inline" ? [key] : [];
7296
7872
  }));
7297
- return /* @__PURE__ */ jsxs18(
7873
+ return /* @__PURE__ */ jsxs19(
7298
7874
  Collapsible3,
7299
7875
  {
7300
7876
  open: isOpen,
@@ -7305,9 +7881,9 @@ var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
7305
7881
  toolParts.map(({ part, key }) => {
7306
7882
  if (!inlineToolKeys.has(key)) return null;
7307
7883
  const { renderer, part: resolvedPart, resolution } = resolveToolRendererForPart(part, mergedToolRenderers);
7308
- 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);
7309
7885
  }),
7310
- /* @__PURE__ */ jsxs18(
7886
+ /* @__PURE__ */ jsxs19(
7311
7887
  CollapsibleTrigger4,
7312
7888
  {
7313
7889
  "aria-label": `Tool calls: ${title}`,
@@ -7319,7 +7895,7 @@ var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
7319
7895
  TOOL_GROUP_VISUAL_PRESENTATION[visualState].triggerClassName
7320
7896
  ),
7321
7897
  children: [
7322
- /* @__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(
7323
7899
  "span",
7324
7900
  {
7325
7901
  "data-boring-agent-part": "tool-group-state-dot",
@@ -7329,10 +7905,10 @@ var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
7329
7905
  )
7330
7906
  }
7331
7907
  ) }),
7332
- /* @__PURE__ */ jsx20("span", { "data-boring-agent-part": "tool-group-title", children: workspaceNotReady ? workspaceNotReady.message : title }),
7333
- elapsedLabel ? /* @__PURE__ */ jsx20("span", { className: "ml-1 shrink-0 tabular-nums text-[12px] text-muted-foreground/70", children: elapsedLabel }) : null,
7334
- /* @__PURE__ */ jsx20("span", { className: "ml-1 shrink-0 tabular-nums text-[12px] text-muted-foreground/70", children: toolParts.length }),
7335
- /* @__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(
7336
7912
  ChevronDownIcon4,
7337
7913
  {
7338
7914
  className: cn(
@@ -7344,19 +7920,19 @@ var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
7344
7920
  ]
7345
7921
  }
7346
7922
  ),
7347
- /* @__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(
7348
7924
  "div",
7349
7925
  {
7350
7926
  "data-boring-agent-part": "tool-group-details",
7351
7927
  className: "relative mt-1.5 w-full max-w-2xl pl-[18px]",
7352
7928
  children: [
7353
- /* @__PURE__ */ jsx20("div", { className: "absolute left-[5px] top-0 bottom-2 w-px bg-border/35" }),
7354
- /* @__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 }) => {
7355
7931
  const { renderer, part: resolvedPart, resolution } = resolveToolRendererForPart(
7356
7932
  part,
7357
7933
  inlineToolKeys.has(key) ? {} : mergedToolRenderers
7358
7934
  );
7359
- return /* @__PURE__ */ jsx20(
7935
+ return /* @__PURE__ */ jsx21(
7360
7936
  "div",
7361
7937
  {
7362
7938
  className: "min-w-0",
@@ -7378,19 +7954,19 @@ var ToolCallGroup = memo6(({ tools, mergedToolRenderers }) => {
7378
7954
  ToolCallGroup.displayName = "ToolCallGroup";
7379
7955
 
7380
7956
  // src/front/chat/messageContributions.tsx
7381
- import { createContext as createContext6, createElement, useContext as useContext6, useMemo as useMemo11 } from "react";
7382
- 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";
7383
7959
  var ChatMessageContributionContext = createContext6([]);
7384
7960
  function ChatMessageContributionProvider({
7385
7961
  contribution,
7386
7962
  children
7387
7963
  }) {
7388
7964
  const parent = useContext6(ChatMessageContributionContext);
7389
- const contributions = useMemo11(
7965
+ const contributions = useMemo12(
7390
7966
  () => [contribution, ...parent.filter((entry) => entry.id !== contribution.id)],
7391
7967
  [contribution, parent]
7392
7968
  );
7393
- return /* @__PURE__ */ jsx21(ChatMessageContributionContext.Provider, { value: contributions, children });
7969
+ return /* @__PURE__ */ jsx22(ChatMessageContributionContext.Provider, { value: contributions, children });
7394
7970
  }
7395
7971
  function useCustomChatMessage(message) {
7396
7972
  const contributions = useContext6(ChatMessageContributionContext);
@@ -7407,7 +7983,7 @@ function ChatMessageContributionBoundary({
7407
7983
 
7408
7984
  // src/front/chat/components/MessageMentions.tsx
7409
7985
  import { Children, cloneElement, isValidElement as isValidElement2 } from "react";
7410
- import { Fragment as Fragment9, jsx as jsx22 } from "react/jsx-runtime";
7986
+ import { Fragment as Fragment9, jsx as jsx23 } from "react/jsx-runtime";
7411
7987
  var TERMINAL_BOUNDARY = String.raw`(?=$|\s|[.,;!?)}\]"'”’]+(?=$|\s))`;
7412
7988
  var PREFIX_BOUNDARY = String.raw`(^|[\s(\[{"'“‘])`;
7413
7989
  function escapeRegExp2(value) {
@@ -7516,7 +8092,7 @@ function MessageMentions({
7516
8092
  }).map((segment, segmentIndex) => {
7517
8093
  if (!segment.mention) return segment.text;
7518
8094
  const actionLabel = mentionActionLabel(segment.mention);
7519
- return /* @__PURE__ */ jsx22(
8095
+ return /* @__PURE__ */ jsx23(
7520
8096
  "button",
7521
8097
  {
7522
8098
  type: "button",
@@ -7542,7 +8118,7 @@ function MessageMentions({
7542
8118
  return cloneElement(
7543
8119
  element,
7544
8120
  void 0,
7545
- /* @__PURE__ */ jsx22(
8121
+ /* @__PURE__ */ jsx23(
7546
8122
  MessageMentions,
7547
8123
  {
7548
8124
  catalog,
@@ -7560,19 +8136,19 @@ function MessageMentions({
7560
8136
  return nodes.map(decorate);
7561
8137
  }
7562
8138
  function createMessageMentionMarkdownComponents(catalog, onActivate) {
7563
- const decorate = (children) => /* @__PURE__ */ jsx22(MessageMentions, { catalog, onActivate, children });
8139
+ const decorate = (children) => /* @__PURE__ */ jsx23(MessageMentions, { catalog, onActivate, children });
7564
8140
  const paragraph = ({ node: _node, children, ...props }) => {
7565
8141
  const content = Children.toArray(children).filter((child) => child !== null && child !== "");
7566
8142
  if (content.length === 1 && isValidElement2(content[0])) {
7567
8143
  const child = content[0];
7568
- if (child.props.node?.tagName === "img") return /* @__PURE__ */ jsx22(Fragment9, { children });
7569
- 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 });
7570
8146
  }
7571
- return /* @__PURE__ */ jsx22("p", { ...props, children: decorate(children) });
8147
+ return /* @__PURE__ */ jsx23("p", { ...props, children: decorate(children) });
7572
8148
  };
7573
- 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) });
7574
- 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) });
7575
- 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) });
7576
8152
  const headingClasses = {
7577
8153
  h1: "text-3xl",
7578
8154
  h2: "text-2xl",
@@ -7581,8 +8157,8 @@ function createMessageMentionMarkdownComponents(catalog, onActivate) {
7581
8157
  h5: "text-base",
7582
8158
  h6: "text-sm"
7583
8159
  };
7584
- 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) });
7585
- 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(
7586
8162
  "code",
7587
8163
  {
7588
8164
  ...props,
@@ -7606,7 +8182,7 @@ function createMessageMentionMarkdownComponents(catalog, onActivate) {
7606
8182
  }
7607
8183
 
7608
8184
  // src/front/chat/components/PiTimelineMessage.tsx
7609
- import { jsx as jsx23, jsxs as jsxs19 } from "react/jsx-runtime";
8185
+ import { jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
7610
8186
  var EMPTY_MENTION_CATALOG = { commands: [], skills: [], files: false };
7611
8187
  var COLLAPSIBLE_TOOL_NAMES = /* @__PURE__ */ new Set([
7612
8188
  "read",
@@ -7622,7 +8198,7 @@ function isCollapsibleTool(part) {
7622
8198
  return part.type === "tool-call" && COLLAPSIBLE_TOOL_NAMES.has(part.toolName);
7623
8199
  }
7624
8200
  function PiTimelineMessage(props) {
7625
- 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 }) });
7626
8202
  }
7627
8203
  function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts, toolRenderers, mentionCatalog = EMPTY_MENTION_CATALOG, onMentionActivate }) {
7628
8204
  const role = message.role;
@@ -7632,7 +8208,7 @@ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts,
7632
8208
  const finalParts = groupRenderableParts(message);
7633
8209
  const attachmentSummaryPaths = role === "user" ? attachmentPathsFromTextParts(textParts) : [];
7634
8210
  const openArtifact = useOpenArtifact();
7635
- const effectiveMentionCatalog = useMemo12(() => ({
8211
+ const effectiveMentionCatalog = useMemo13(() => ({
7636
8212
  commands: mentionCatalog.commands,
7637
8213
  skills: mentionCatalog.skills,
7638
8214
  files: mentionCatalog.files && Boolean(openArtifact)
@@ -7642,9 +8218,9 @@ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts,
7642
8218
  ...effectiveMentionCatalog.skills.map((skill) => `skill:${skill.name}:${skill.commandName}`),
7643
8219
  effectiveMentionCatalog.files ? "files:enabled" : "files:disabled"
7644
8220
  ].join("|");
7645
- const mentionActivateRef = useRef9(onMentionActivate);
8221
+ const mentionActivateRef = useRef10(onMentionActivate);
7646
8222
  mentionActivateRef.current = onMentionActivate;
7647
- const activateCurrentMention = useCallback13((mention) => {
8223
+ const activateCurrentMention = useCallback14((mention) => {
7648
8224
  if (mention.kind === "file") {
7649
8225
  openArtifact?.(mention.path);
7650
8226
  return;
@@ -7654,12 +8230,12 @@ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts,
7654
8230
  const hasMentions = effectiveMentionCatalog.files || effectiveMentionCatalog.commands.length > 0 || effectiveMentionCatalog.skills.length > 0;
7655
8231
  const messageIsStreaming = isAssistant && isLast && (isStreaming || message.status === "streaming");
7656
8232
  const mentionsEnabled = Boolean(isAssistant && !messageIsStreaming && hasMentions);
7657
- const mentionMarkdownComponents = useMemo12(
8233
+ const mentionMarkdownComponents = useMemo13(
7658
8234
  () => mentionsEnabled ? createMessageMentionMarkdownComponents(effectiveMentionCatalog, activateCurrentMention) : void 0,
7659
8235
  [activateCurrentMention, effectiveMentionCatalog, mentionSignature, mentionsEnabled]
7660
8236
  );
7661
8237
  const shouldReserveStreamingActions = messageIsStreaming;
7662
- return /* @__PURE__ */ jsxs19(
8238
+ return /* @__PURE__ */ jsxs20(
7663
8239
  Message,
7664
8240
  {
7665
8241
  from: role,
@@ -7668,8 +8244,8 @@ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts,
7668
8244
  "data-boring-agent-message-role": role,
7669
8245
  "data-boring-agent-message-status": message.status,
7670
8246
  children: [
7671
- /* @__PURE__ */ jsxs19(MessageContent, { children: [
7672
- fileParts.length > 0 ? /* @__PURE__ */ jsx23(
8247
+ /* @__PURE__ */ jsxs20(MessageContent, { children: [
8248
+ fileParts.length > 0 ? /* @__PURE__ */ jsx24(
7673
8249
  Attachments,
7674
8250
  {
7675
8251
  variant: role === "user" ? "inline" : "list",
@@ -7694,7 +8270,7 @@ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts,
7694
8270
  event.preventDefault();
7695
8271
  openAttachment();
7696
8272
  };
7697
- return /* @__PURE__ */ jsxs19(
8273
+ return /* @__PURE__ */ jsxs20(
7698
8274
  Attachment,
7699
8275
  {
7700
8276
  data: toAttachmentData(file, `file-${message.id}-${index}`),
@@ -7708,8 +8284,8 @@ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts,
7708
8284
  onKeyDown: openAttachmentFromKeyboard
7709
8285
  } : void 0,
7710
8286
  children: [
7711
- /* @__PURE__ */ jsx23(AttachmentPreview, { className: role === "user" ? "!size-5 shrink-0 rounded-[var(--radius-sm)]" : "size-10 shrink-0 rounded-[var(--radius-md)]" }),
7712
- /* @__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" })
7713
8289
  ]
7714
8290
  },
7715
8291
  `file-${message.id}-${index}`
@@ -7719,7 +8295,7 @@ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts,
7719
8295
  ) : null,
7720
8296
  finalParts.map((item, index) => {
7721
8297
  if (item.kind === "reasoning") {
7722
- return /* @__PURE__ */ jsx23(
8298
+ return /* @__PURE__ */ jsx24(
7723
8299
  TimelineReasoningPart,
7724
8300
  {
7725
8301
  item,
@@ -7729,15 +8305,15 @@ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts,
7729
8305
  );
7730
8306
  }
7731
8307
  if (item.kind === "tool-group") {
7732
- 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);
7733
8309
  }
7734
8310
  if (item.kind === "tool-plain") {
7735
- 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);
7736
8312
  }
7737
8313
  if (item.part.type === "text") {
7738
8314
  const text = textForMessageDisplay(item.part.text, role);
7739
8315
  if (!text) return null;
7740
- 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(
7741
8317
  MessageResponse,
7742
8318
  {
7743
8319
  components: mentionMarkdownComponents,
@@ -7749,12 +8325,12 @@ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts,
7749
8325
  ) }, item.key);
7750
8326
  }
7751
8327
  if (item.part.type === "notice") {
7752
- 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);
7753
8329
  }
7754
8330
  return null;
7755
8331
  })
7756
8332
  ] }),
7757
- isAssistant && (textParts.length > 0 || shouldReserveStreamingActions) ? /* @__PURE__ */ jsx23(
8333
+ isAssistant && (textParts.length > 0 || shouldReserveStreamingActions) ? /* @__PURE__ */ jsx24(
7758
8334
  MessageActionsBar,
7759
8335
  {
7760
8336
  text: textParts.map((part) => part.text).join("\n\n"),
@@ -7766,20 +8342,20 @@ function DefaultPiTimelineMessage({ message, isLast, isStreaming, showThoughts,
7766
8342
  );
7767
8343
  }
7768
8344
  function TimelineReasoningPart({ item, showThoughts }) {
7769
- const [manualOpen, setManualOpen] = useState14(null);
8345
+ const [manualOpen, setManualOpen] = useState16(null);
7770
8346
  const isStreaming = item.state === "streaming";
7771
8347
  const isOpen = manualOpen ?? showThoughts;
7772
- useEffect13(() => {
8348
+ useEffect15(() => {
7773
8349
  setManualOpen(null);
7774
8350
  }, [showThoughts]);
7775
- const handleOpenChange = useCallback13((open) => {
8351
+ const handleOpenChange = useCallback14((open) => {
7776
8352
  setManualOpen(open);
7777
8353
  }, []);
7778
- const toggleManualOpen = useCallback13((event) => {
8354
+ const toggleManualOpen = useCallback14((event) => {
7779
8355
  event.preventDefault();
7780
8356
  setManualOpen((open) => !(open ?? showThoughts));
7781
8357
  }, [showThoughts]);
7782
- return /* @__PURE__ */ jsxs19(
8358
+ return /* @__PURE__ */ jsxs20(
7783
8359
  Reasoning,
7784
8360
  {
7785
8361
  "data-boring-agent-part": "message-reasoning",
@@ -7789,15 +8365,15 @@ function TimelineReasoningPart({ item, showThoughts }) {
7789
8365
  onOpenChange: handleOpenChange,
7790
8366
  autoClose: false,
7791
8367
  children: [
7792
- /* @__PURE__ */ jsx23(
8368
+ /* @__PURE__ */ jsx24(
7793
8369
  ReasoningTrigger,
7794
8370
  {
7795
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",
7796
- getThinkingMessage: () => /* @__PURE__ */ jsx23("span", { children: "Reasoning" }),
8372
+ getThinkingMessage: () => /* @__PURE__ */ jsx24("span", { children: "Reasoning" }),
7797
8373
  onClick: toggleManualOpen
7798
8374
  }
7799
8375
  ),
7800
- /* @__PURE__ */ jsx23(ReasoningContent, { children: item.text })
8376
+ /* @__PURE__ */ jsx24(ReasoningContent, { children: item.text })
7801
8377
  ]
7802
8378
  }
7803
8379
  );
@@ -7806,7 +8382,7 @@ function PlainToolCard({ part, renderers }) {
7806
8382
  const toolPart = toToolPart(part);
7807
8383
  if (!toolPart) return null;
7808
8384
  const { renderer, part: resolved, resolution } = resolveToolRendererForPart(toolPart, renderers);
7809
- return /* @__PURE__ */ jsx23(
8385
+ return /* @__PURE__ */ jsx24(
7810
8386
  "div",
7811
8387
  {
7812
8388
  "data-tool-call-id": resolved.toolCallId,
@@ -7864,13 +8440,13 @@ function codeFilenameForPart(parts, index) {
7864
8440
  return typeof path === "string" && path.trim() ? path : void 0;
7865
8441
  }
7866
8442
  function NoticeBubble({ level, text }) {
7867
- return /* @__PURE__ */ jsx23(
8443
+ return /* @__PURE__ */ jsx24(
7868
8444
  "div",
7869
8445
  {
7870
8446
  "data-boring-agent-part": "message-notice",
7871
8447
  "data-notice-level": level,
7872
8448
  className: noticeSurfaceClass(level, "text-xs"),
7873
- children: /* @__PURE__ */ jsx23("div", { className: noticeTextClass(), children: text })
8449
+ children: /* @__PURE__ */ jsx24("div", { className: noticeTextClass(), children: text })
7874
8450
  }
7875
8451
  );
7876
8452
  }
@@ -7911,7 +8487,7 @@ function MessageActionsBar({
7911
8487
  text,
7912
8488
  visible = true
7913
8489
  }) {
7914
- const [copied, setCopied] = useState14(false);
8490
+ const [copied, setCopied] = useState16(false);
7915
8491
  const markCopied = () => {
7916
8492
  setCopied(true);
7917
8493
  setTimeout(() => setCopied(false), 1500);
@@ -7949,7 +8525,7 @@ function MessageActionsBar({
7949
8525
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[color:var(--accent)]/40"
7950
8526
  );
7951
8527
  const hiddenActionProps = visible ? {} : { tabIndex: -1 };
7952
- return /* @__PURE__ */ jsx23(
8528
+ return /* @__PURE__ */ jsx24(
7953
8529
  "div",
7954
8530
  {
7955
8531
  "aria-hidden": !visible,
@@ -7957,8 +8533,8 @@ function MessageActionsBar({
7957
8533
  "flex min-h-8 items-center gap-1 -mt-1 text-xs text-muted-foreground transition-opacity duration-200",
7958
8534
  visible ? "opacity-100" : "pointer-events-none opacity-0"
7959
8535
  ),
7960
- children: /* @__PURE__ */ jsx23(
7961
- Button10,
8536
+ children: /* @__PURE__ */ jsx24(
8537
+ Button11,
7962
8538
  {
7963
8539
  type: "button",
7964
8540
  variant: "ghost",
@@ -7968,7 +8544,7 @@ function MessageActionsBar({
7968
8544
  "aria-label": copied ? "Copied" : "Copy message",
7969
8545
  title: copied ? "Copied" : "Copy",
7970
8546
  ...hiddenActionProps,
7971
- 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" })
7972
8548
  }
7973
8549
  )
7974
8550
  }
@@ -7989,7 +8565,7 @@ function partKey(messageId, part, index) {
7989
8565
  }
7990
8566
 
7991
8567
  // src/front/chat/components/PiConversationSurface.tsx
7992
- import { jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
8568
+ import { jsx as jsx25, jsxs as jsxs21 } from "react/jsx-runtime";
7993
8569
  var TRANSCRIPT_WINDOW = 60;
7994
8570
  var TRANSCRIPT_WINDOW_STEP = 40;
7995
8571
  var LOAD_OLDER_THRESHOLD_PX = 320;
@@ -8017,17 +8593,17 @@ function PiConversationSurface({
8017
8593
  const total = messageItems.length;
8018
8594
  const historyEmpty = messages.length === 0;
8019
8595
  const terminalError = hasTerminalChatError(runtimeNotices, historyEmpty);
8020
- const [visibleCount, setVisibleCount] = useState15(TRANSCRIPT_WINDOW);
8021
- useEffect14(() => {
8596
+ const [visibleCount, setVisibleCount] = useState17(TRANSCRIPT_WINDOW);
8597
+ useEffect16(() => {
8022
8598
  setVisibleCount(TRANSCRIPT_WINDOW);
8023
8599
  }, [windowResetKey]);
8024
8600
  const hasOlder = total > visibleCount;
8025
8601
  const visibleItems = hasOlder ? messageItems.slice(total - visibleCount) : messageItems;
8026
8602
  const olderCount = hasOlder ? total - visibleCount : 0;
8027
- const loadOlder = useCallback14(() => {
8603
+ const loadOlder = useCallback15(() => {
8028
8604
  setVisibleCount((count) => count + TRANSCRIPT_WINDOW_STEP);
8029
8605
  }, []);
8030
- return /* @__PURE__ */ jsxs20(
8606
+ return /* @__PURE__ */ jsxs21(
8031
8607
  Conversation,
8032
8608
  {
8033
8609
  className: emptyHero ? cn("flex-none", runtimeNotices.length > 0 ? "max-h-[45vh]" : "max-h-[20vh]") : "flex-1",
@@ -8035,13 +8611,13 @@ function PiConversationSurface({
8035
8611
  "aria-live": "polite",
8036
8612
  onScrollToBottomReady,
8037
8613
  children: [
8038
- /* @__PURE__ */ jsxs20(ConversationContent, { className: cn(
8614
+ /* @__PURE__ */ jsxs21(ConversationContent, { className: cn(
8039
8615
  "mx-auto flex w-full flex-col gap-6",
8040
8616
  chrome ? "max-w-3xl px-6 py-8" : "max-w-[680px] px-4 py-4",
8041
8617
  emptyHero && "py-4 text-center"
8042
8618
  ), children: [
8043
- messages.length === 0 && emptyStateHydrating && !terminalError ? /* @__PURE__ */ jsx24(ConversationHistoryLoadingState, {}) : null,
8044
- 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(
8045
8621
  ChatEmptyState,
8046
8622
  {
8047
8623
  eyebrow: emptyState?.eyebrow,
@@ -8059,8 +8635,8 @@ function PiConversationSurface({
8059
8635
  }
8060
8636
  }
8061
8637
  ) : null,
8062
- hasOlder ? /* @__PURE__ */ jsx24(TranscriptHistoryLoader, { olderCount, onLoadOlder: loadOlder }) : null,
8063
- visibleItems.map(({ message, key }, index) => /* @__PURE__ */ jsx24(
8638
+ hasOlder ? /* @__PURE__ */ jsx25(TranscriptHistoryLoader, { olderCount, onLoadOlder: loadOlder }) : null,
8639
+ visibleItems.map(({ message, key }, index) => /* @__PURE__ */ jsx25(
8064
8640
  PiTimelineMessage,
8065
8641
  {
8066
8642
  message,
@@ -8073,16 +8649,16 @@ function PiConversationSurface({
8073
8649
  },
8074
8650
  key
8075
8651
  )),
8076
- /* @__PURE__ */ jsx24(RuntimeNoticeMessages, { notices: runtimeNotices, onDismiss: onDismissNotice, renderAction: renderNoticeAction, historyEmpty })
8652
+ /* @__PURE__ */ jsx25(RuntimeNoticeMessages, { notices: runtimeNotices, onDismiss: onDismissNotice, renderAction: renderNoticeAction, historyEmpty })
8077
8653
  ] }),
8078
- /* @__PURE__ */ jsx24(ConversationScrollButton, {})
8654
+ /* @__PURE__ */ jsx25(ConversationScrollButton, {})
8079
8655
  ]
8080
8656
  }
8081
8657
  );
8082
8658
  }
8083
8659
  function ConversationHistoryLoadingState() {
8084
8660
  const skeletonClassName = "motion-reduce:animate-none";
8085
- return /* @__PURE__ */ jsxs20(
8661
+ return /* @__PURE__ */ jsxs21(
8086
8662
  "div",
8087
8663
  {
8088
8664
  role: "status",
@@ -8091,21 +8667,21 @@ function ConversationHistoryLoadingState() {
8091
8667
  "data-boring-agent-part": "chat-history-loading",
8092
8668
  className: "flex flex-col gap-7 py-4",
8093
8669
  children: [
8094
- /* @__PURE__ */ jsxs20("div", { "aria-hidden": "true", className: "flex items-start gap-3", children: [
8095
- /* @__PURE__ */ jsx24(Skeleton, { className: cn("mt-0.5 size-7 shrink-0 rounded-lg", skeletonClassName) }),
8096
- /* @__PURE__ */ jsxs20("div", { className: "flex min-w-0 flex-1 flex-col gap-2.5", children: [
8097
- /* @__PURE__ */ jsx24(Skeleton, { className: cn("h-3 w-24 rounded-sm", skeletonClassName) }),
8098
- /* @__PURE__ */ jsx24(Skeleton, { className: cn("h-3 w-full rounded-sm", skeletonClassName) }),
8099
- /* @__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) })
8100
8676
  ] })
8101
8677
  ] }),
8102
- /* @__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) }) }),
8103
- /* @__PURE__ */ jsxs20("div", { "aria-hidden": "true", className: "flex items-start gap-3", children: [
8104
- /* @__PURE__ */ jsx24(Skeleton, { className: cn("mt-0.5 size-7 shrink-0 rounded-lg", skeletonClassName) }),
8105
- /* @__PURE__ */ jsxs20("div", { className: "flex min-w-0 flex-1 flex-col gap-3", children: [
8106
- /* @__PURE__ */ jsx24(Skeleton, { className: cn("h-3 w-20 rounded-sm", skeletonClassName) }),
8107
- /* @__PURE__ */ jsx24(Skeleton, { className: cn("h-20 w-full rounded-lg", skeletonClassName) }),
8108
- /* @__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) })
8109
8685
  ] })
8110
8686
  ] })
8111
8687
  ]
@@ -8114,9 +8690,9 @@ function ConversationHistoryLoadingState() {
8114
8690
  }
8115
8691
  function TranscriptHistoryLoader({ olderCount, onLoadOlder }) {
8116
8692
  const { scrollRef } = useStickToBottomContext2();
8117
- const pendingAnchor = useRef10(null);
8118
- const armed = useRef10(true);
8119
- useEffect14(() => {
8693
+ const pendingAnchor = useRef11(null);
8694
+ const armed = useRef11(true);
8695
+ useEffect16(() => {
8120
8696
  const el = scrollRef.current;
8121
8697
  if (!el) return;
8122
8698
  const onScroll = () => {
@@ -8139,7 +8715,7 @@ function TranscriptHistoryLoader({ olderCount, onLoadOlder }) {
8139
8715
  }
8140
8716
  if (el.scrollTop > LOAD_OLDER_THRESHOLD_PX) armed.current = true;
8141
8717
  });
8142
- 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(
8143
8719
  "button",
8144
8720
  {
8145
8721
  type: "button",
@@ -8176,14 +8752,14 @@ function buildMessageRenderItems(messages) {
8176
8752
  }
8177
8753
 
8178
8754
  // src/front/chat/components/PiChatComposerSurface.tsx
8179
- import { useCallback as useCallback16, useLayoutEffect as useLayoutEffect3 } from "react";
8755
+ import { useCallback as useCallback17, useLayoutEffect as useLayoutEffect3 } from "react";
8180
8756
  import { motion as motion2, useReducedMotion } from "motion/react";
8181
8757
 
8182
8758
  // src/front/composer/PluginUpdateStatus.tsx
8183
- import { useEffect as useEffect15, useRef as useRef11 } from "react";
8759
+ import { useEffect as useEffect17, useRef as useRef12 } from "react";
8184
8760
 
8185
8761
  // src/front/composer/ComposerStatusBanner.tsx
8186
- import { jsx as jsx25, jsxs as jsxs21 } from "react/jsx-runtime";
8762
+ import { jsx as jsx26, jsxs as jsxs22 } from "react/jsx-runtime";
8187
8763
  function ComposerStatusBanner({
8188
8764
  tone,
8189
8765
  dataAttribute,
@@ -8200,7 +8776,7 @@ function ComposerStatusBanner({
8200
8776
  }) {
8201
8777
  const toneAttr = { [dataAttribute]: tone };
8202
8778
  if (tone === "running") {
8203
- return /* @__PURE__ */ jsxs21(
8779
+ return /* @__PURE__ */ jsxs22(
8204
8780
  "div",
8205
8781
  {
8206
8782
  ...toneAttr,
@@ -8212,14 +8788,14 @@ function ComposerStatusBanner({
8212
8788
  maxWidthClassName
8213
8789
  ),
8214
8790
  children: [
8215
- /* @__PURE__ */ jsx25("span", { className: "inline-block h-2 w-2 animate-pulse rounded-full bg-accent", "aria-hidden": "true" }),
8216
- /* @__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 })
8217
8793
  ]
8218
8794
  }
8219
8795
  );
8220
8796
  }
8221
8797
  if (tone === "success") {
8222
- return /* @__PURE__ */ jsxs21(
8798
+ return /* @__PURE__ */ jsxs22(
8223
8799
  "div",
8224
8800
  {
8225
8801
  ...toneAttr,
@@ -8231,13 +8807,13 @@ function ComposerStatusBanner({
8231
8807
  maxWidthClassName
8232
8808
  ),
8233
8809
  children: [
8234
- /* @__PURE__ */ jsxs21("div", { className: "flex items-start gap-2", children: [
8235
- /* @__PURE__ */ jsx25("span", { className: "mt-0.5 text-[oklch(0.45_0.13_148)]", "aria-hidden": "true", children: "\u2713" }),
8236
- /* @__PURE__ */ jsxs21("span", { className: "min-w-0 flex-1", children: [
8237
- /* @__PURE__ */ jsx25("span", { className: "block font-medium leading-5", children: title }),
8238
- 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
8239
8815
  ] }),
8240
- onDismiss ? /* @__PURE__ */ jsx25(
8816
+ onDismiss ? /* @__PURE__ */ jsx26(
8241
8817
  "button",
8242
8818
  {
8243
8819
  type: "button",
@@ -8253,7 +8829,7 @@ function ComposerStatusBanner({
8253
8829
  }
8254
8830
  );
8255
8831
  }
8256
- return /* @__PURE__ */ jsxs21(
8832
+ return /* @__PURE__ */ jsxs22(
8257
8833
  "div",
8258
8834
  {
8259
8835
  ...toneAttr,
@@ -8265,10 +8841,10 @@ function ComposerStatusBanner({
8265
8841
  maxWidthClassName
8266
8842
  ),
8267
8843
  children: [
8268
- /* @__PURE__ */ jsxs21("div", { className: "flex items-center gap-2", children: [
8269
- /* @__PURE__ */ jsx25("span", { className: "text-destructive", "aria-hidden": "true", children: "\u26A0" }),
8270
- /* @__PURE__ */ jsx25("span", { className: "flex-1 font-medium", children: title }),
8271
- 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(
8272
8848
  "button",
8273
8849
  {
8274
8850
  type: "button",
@@ -8277,7 +8853,7 @@ function ComposerStatusBanner({
8277
8853
  children: retryLabel
8278
8854
  }
8279
8855
  ) : null,
8280
- onDismiss ? /* @__PURE__ */ jsx25(
8856
+ onDismiss ? /* @__PURE__ */ jsx26(
8281
8857
  "button",
8282
8858
  {
8283
8859
  type: "button",
@@ -8288,14 +8864,14 @@ function ComposerStatusBanner({
8288
8864
  }
8289
8865
  ) : null
8290
8866
  ] }),
8291
- 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
8292
8868
  ]
8293
8869
  }
8294
8870
  );
8295
8871
  }
8296
8872
 
8297
8873
  // src/front/composer/PluginUpdateStatus.tsx
8298
- import { jsx as jsx26, jsxs as jsxs22 } from "react/jsx-runtime";
8874
+ import { jsx as jsx27, jsxs as jsxs23 } from "react/jsx-runtime";
8299
8875
  function PluginUpdateStatus({
8300
8876
  state,
8301
8877
  onDismiss,
@@ -8303,12 +8879,12 @@ function PluginUpdateStatus({
8303
8879
  successAutoDismissMs = 1400,
8304
8880
  maxWidthClassName = "max-w-3xl"
8305
8881
  }) {
8306
- const onDismissRef = useRef11(onDismiss);
8307
- useEffect15(() => {
8882
+ const onDismissRef = useRef12(onDismiss);
8883
+ useEffect17(() => {
8308
8884
  onDismissRef.current = onDismiss;
8309
8885
  }, [onDismiss]);
8310
8886
  const successDismissKey = state?.kind === "success" ? `${state.reloaded}:${(state.restartWarnings?.length ?? 0) > 0 || (state.diagnostics?.length ?? 0) > 0}` : null;
8311
- useEffect15(() => {
8887
+ useEffect17(() => {
8312
8888
  if (!state || state.kind !== "success" || successAutoDismissMs <= 0) return;
8313
8889
  const hasWarningsOrDiagnostics = (state.restartWarnings?.length ?? 0) > 0 || (state.diagnostics?.length ?? 0) > 0;
8314
8890
  if (hasWarningsOrDiagnostics) return;
@@ -8317,7 +8893,7 @@ function PluginUpdateStatus({
8317
8893
  }, [state?.kind, successDismissKey, successAutoDismissMs]);
8318
8894
  if (!state) return null;
8319
8895
  if (state.kind === "running") {
8320
- return /* @__PURE__ */ jsx26(
8896
+ return /* @__PURE__ */ jsx27(
8321
8897
  ComposerStatusBanner,
8322
8898
  {
8323
8899
  tone: "running",
@@ -8334,7 +8910,7 @@ function PluginUpdateStatus({
8334
8910
  const hasWarningsOrDiagnostics = warnings.length > 0 || diagnostics.length > 0;
8335
8911
  const title = state.reloaded ? hasWarningsOrDiagnostics ? "Reload finished with warnings" : "Reload complete" : "Reload queued";
8336
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;
8337
- return /* @__PURE__ */ jsxs22(
8913
+ return /* @__PURE__ */ jsxs23(
8338
8914
  ComposerStatusBanner,
8339
8915
  {
8340
8916
  tone: "success",
@@ -8345,7 +8921,7 @@ function PluginUpdateStatus({
8345
8921
  onDismiss,
8346
8922
  dismissAriaLabel: "Dismiss plugin update status",
8347
8923
  children: [
8348
- diagnostics.length > 0 ? /* @__PURE__ */ jsxs22(
8924
+ diagnostics.length > 0 ? /* @__PURE__ */ jsxs23(
8349
8925
  "div",
8350
8926
  {
8351
8927
  "data-boring-plugin-update-diagnostics": "",
@@ -8354,24 +8930,24 @@ function PluginUpdateStatus({
8354
8930
  "px-2 py-1.5 text-[11px] text-foreground"
8355
8931
  ),
8356
8932
  children: [
8357
- /* @__PURE__ */ jsxs22("div", { className: "flex items-center gap-1.5 font-medium text-[oklch(0.48_0.15_60)]", children: [
8358
- /* @__PURE__ */ jsx26("span", { "aria-hidden": "true", children: "\u26A0" }),
8359
- /* @__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: [
8360
8936
  "Reload diagnostics for ",
8361
8937
  diagnostics.length,
8362
8938
  " extension",
8363
8939
  diagnostics.length === 1 ? "" : "s"
8364
8940
  ] })
8365
8941
  ] }),
8366
- /* @__PURE__ */ jsx26("ul", { className: "mt-1 ml-4 list-disc text-foreground/85", children: diagnostics.map((diagnostic, index) => /* @__PURE__ */ jsxs22("li", { children: [
8367
- /* @__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" }),
8368
8944
  " \u2014 ",
8369
8945
  diagnostic.message ?? "reload diagnostic"
8370
8946
  ] }, `${diagnostic.pluginId ?? diagnostic.source ?? "plugin"}-${index}`)) })
8371
8947
  ]
8372
8948
  }
8373
8949
  ) : null,
8374
- warnings.length > 0 ? /* @__PURE__ */ jsxs22(
8950
+ warnings.length > 0 ? /* @__PURE__ */ jsxs23(
8375
8951
  "div",
8376
8952
  {
8377
8953
  "data-boring-plugin-update-restart-warning": "",
@@ -8380,21 +8956,21 @@ function PluginUpdateStatus({
8380
8956
  "px-2 py-1.5 text-[11px] text-foreground"
8381
8957
  ),
8382
8958
  children: [
8383
- /* @__PURE__ */ jsxs22("div", { className: "flex items-center gap-1.5 font-medium text-[oklch(0.48_0.15_60)]", children: [
8384
- /* @__PURE__ */ jsx26("span", { "aria-hidden": "true", children: "\u26A0" }),
8385
- /* @__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: [
8386
8962
  "Restart needed for ",
8387
8963
  warnings.length,
8388
8964
  " extension",
8389
8965
  warnings.length === 1 ? "" : "s"
8390
8966
  ] })
8391
8967
  ] }),
8392
- /* @__PURE__ */ jsx26("ul", { className: "mt-1 ml-4 list-disc text-foreground/85", children: warnings.map((w) => /* @__PURE__ */ jsxs22("li", { children: [
8393
- /* @__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 }),
8394
8970
  " \u2014 ",
8395
8971
  w.surfaces.join(" + ")
8396
8972
  ] }, w.id)) }),
8397
- /* @__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." })
8398
8974
  ]
8399
8975
  }
8400
8976
  ) : null
@@ -8402,7 +8978,7 @@ function PluginUpdateStatus({
8402
8978
  }
8403
8979
  );
8404
8980
  }
8405
- return /* @__PURE__ */ jsx26(
8981
+ return /* @__PURE__ */ jsx27(
8406
8982
  ComposerStatusBanner,
8407
8983
  {
8408
8984
  tone: "error",
@@ -8419,35 +8995,35 @@ function PluginUpdateStatus({
8419
8995
  }
8420
8996
 
8421
8997
  // src/front/composer/CommandRunStatus.tsx
8422
- import { useEffect as useEffect16, useRef as useRef12 } from "react";
8423
- 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";
8424
9000
  function CommandRunStatus({
8425
9001
  state,
8426
9002
  onDismiss,
8427
9003
  successAutoDismissMs = 1400,
8428
9004
  maxWidthClassName = "max-w-3xl"
8429
9005
  }) {
8430
- const onDismissRef = useRef12(onDismiss);
8431
- useEffect16(() => {
9006
+ const onDismissRef = useRef13(onDismiss);
9007
+ useEffect18(() => {
8432
9008
  onDismissRef.current = onDismiss;
8433
9009
  }, [onDismiss]);
8434
9010
  const successKey = state?.kind === "success" ? `${state.runId}:${state.command}:${state.detail ?? ""}` : null;
8435
- useEffect16(() => {
9011
+ useEffect18(() => {
8436
9012
  if (!state || state.kind !== "success" || successAutoDismissMs <= 0) return;
8437
9013
  const timeout = window.setTimeout(() => onDismissRef.current(), successAutoDismissMs);
8438
9014
  return () => window.clearTimeout(timeout);
8439
9015
  }, [state?.kind, successKey, successAutoDismissMs]);
8440
9016
  if (!state) return null;
8441
9017
  if (state.kind === "running") {
8442
- return /* @__PURE__ */ jsx27(
9018
+ return /* @__PURE__ */ jsx28(
8443
9019
  ComposerStatusBanner,
8444
9020
  {
8445
9021
  tone: "running",
8446
9022
  dataAttribute: "data-boring-command-run",
8447
9023
  maxWidthClassName,
8448
- runningContent: state.command ? /* @__PURE__ */ jsxs23(Fragment10, { children: [
9024
+ runningContent: state.command ? /* @__PURE__ */ jsxs24(Fragment10, { children: [
8449
9025
  " Running ",
8450
- /* @__PURE__ */ jsxs23("code", { className: "font-mono", children: [
9026
+ /* @__PURE__ */ jsxs24("code", { className: "font-mono", children: [
8451
9027
  "/",
8452
9028
  state.command
8453
9029
  ] }),
@@ -8457,15 +9033,15 @@ function CommandRunStatus({
8457
9033
  );
8458
9034
  }
8459
9035
  if (state.kind === "success") {
8460
- return /* @__PURE__ */ jsx27(
9036
+ return /* @__PURE__ */ jsx28(
8461
9037
  ComposerStatusBanner,
8462
9038
  {
8463
9039
  tone: "success",
8464
9040
  dataAttribute: "data-boring-command-run",
8465
9041
  maxWidthClassName,
8466
- title: state.command ? /* @__PURE__ */ jsxs23(Fragment10, { children: [
9042
+ title: state.command ? /* @__PURE__ */ jsxs24(Fragment10, { children: [
8467
9043
  " Ran ",
8468
- /* @__PURE__ */ jsxs23("code", { className: "font-mono", children: [
9044
+ /* @__PURE__ */ jsxs24("code", { className: "font-mono", children: [
8469
9045
  "/",
8470
9046
  state.command
8471
9047
  ] }),
@@ -8477,15 +9053,15 @@ function CommandRunStatus({
8477
9053
  }
8478
9054
  );
8479
9055
  }
8480
- return /* @__PURE__ */ jsx27(
9056
+ return /* @__PURE__ */ jsx28(
8481
9057
  ComposerStatusBanner,
8482
9058
  {
8483
9059
  tone: "error",
8484
9060
  dataAttribute: "data-boring-command-run",
8485
9061
  maxWidthClassName,
8486
- title: state.command ? /* @__PURE__ */ jsxs23(Fragment10, { children: [
9062
+ title: state.command ? /* @__PURE__ */ jsxs24(Fragment10, { children: [
8487
9063
  " ",
8488
- /* @__PURE__ */ jsxs23("code", { className: "font-mono", children: [
9064
+ /* @__PURE__ */ jsxs24("code", { className: "font-mono", children: [
8489
9065
  "/",
8490
9066
  state.command
8491
9067
  ] }),
@@ -8499,8 +9075,8 @@ function CommandRunStatus({
8499
9075
  }
8500
9076
 
8501
9077
  // src/front/chatPanelComposerControls.tsx
8502
- import { forwardRef, useEffect as useEffect17, useRef as useRef13, useState as useState16 } from "react";
8503
- 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";
8504
9080
  import {
8505
9081
  Command,
8506
9082
  CommandEmpty,
@@ -8533,7 +9109,7 @@ function displayProviderLabel(provider) {
8533
9109
  }
8534
9110
 
8535
9111
  // src/front/chatPanelComposerControls.tsx
8536
- 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";
8537
9113
  var composerActionClass = cn(
8538
9114
  "inline-flex h-8 items-center justify-center gap-1.5 rounded-[var(--radius-md)] border-0 bg-transparent",
8539
9115
  "text-muted-foreground shadow-none transition-colors motion-reduce:transition-none",
@@ -8583,7 +9159,7 @@ function isTextInputTarget(target) {
8583
9159
  return target instanceof HTMLInputElement;
8584
9160
  }
8585
9161
  function useDismissOnOutsidePointer(ref, onClose) {
8586
- useEffect17(() => {
9162
+ useEffect19(() => {
8587
9163
  if (!onClose) return;
8588
9164
  const handler = (event) => {
8589
9165
  const target = event.target;
@@ -8601,7 +9177,7 @@ function useDismissOnOutsidePointer(ref, onClose) {
8601
9177
  }
8602
9178
  function ThinkingLevelGlyph({ level }) {
8603
9179
  const lit = level === "off" ? 0 : level === "low" ? 1 : level === "medium" ? 2 : 3;
8604
- return /* @__PURE__ */ jsx28(
9180
+ return /* @__PURE__ */ jsx29(
8605
9181
  "svg",
8606
9182
  {
8607
9183
  "aria-hidden": "true",
@@ -8610,7 +9186,7 @@ function ThinkingLevelGlyph({ level }) {
8610
9186
  viewBox: "0 0 14 14",
8611
9187
  fill: "none",
8612
9188
  className: "shrink-0",
8613
- children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx28(
9189
+ children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx29(
8614
9190
  "rect",
8615
9191
  {
8616
9192
  x: 2 + i * 4,
@@ -8662,7 +9238,7 @@ var ModelSelectTrigger = forwardRef(function ModelSelectTrigger2({
8662
9238
  const triggerDisplay = value ? `${triggerLabel} (${displayProviderLabel(value.provider)})` : triggerLabel;
8663
9239
  const sessionDisplay = sessionModel && sessionTriggerLabel ? `${sessionTriggerLabel} (${displayProviderLabel(sessionModel.provider)})` : triggerDisplay;
8664
9240
  const accessibleDisplay = isOverride && value ? `Current model: ${sessionDisplay}. Next-message override: ${triggerDisplay}` : `Current model: ${sessionDisplay}`;
8665
- return /* @__PURE__ */ jsx28(
9241
+ return /* @__PURE__ */ jsx29(
8666
9242
  "button",
8667
9243
  {
8668
9244
  ref,
@@ -8681,25 +9257,25 @@ var ModelSelectTrigger = forwardRef(function ModelSelectTrigger2({
8681
9257
  className
8682
9258
  ),
8683
9259
  ...props,
8684
- children: trigger === "slash" ? /* @__PURE__ */ jsxs24(Fragment11, { children: [
8685
- /* @__PURE__ */ jsx28("span", { className: "text-muted-foreground", children: "Model \xB7 " }),
8686
- /* @__PURE__ */ jsx28("span", { className: "min-w-0 truncate text-foreground", children: sessionTriggerLabel ?? triggerLabel }),
8687
- 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: [
8688
9264
  " \xB7 ",
8689
9265
  displayProviderLabel(sessionModel.provider)
8690
- ] }) : value ? /* @__PURE__ */ jsxs24("span", { className: "shrink-0 text-muted-foreground", children: [
9266
+ ] }) : value ? /* @__PURE__ */ jsxs25("span", { className: "shrink-0 text-muted-foreground", children: [
8691
9267
  " \xB7 ",
8692
9268
  displayProviderLabel(value.provider)
8693
9269
  ] }) : null,
8694
- 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: [
8695
9271
  " \xB7 Next override: ",
8696
9272
  triggerLabel,
8697
9273
  " \xB7 ",
8698
9274
  displayProviderLabel(value.provider)
8699
9275
  ] }) : null
8700
- ] }) : /* @__PURE__ */ jsxs24(Fragment11, { children: [
8701
- /* @__PURE__ */ jsx28("span", { className: "min-w-0 truncate", children: triggerDisplay }),
8702
- /* @__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" })
8703
9279
  ] })
8704
9280
  }
8705
9281
  );
@@ -8719,19 +9295,19 @@ function ModelPickerMenu({
8719
9295
  const groupedOptions = [...groups.values()].flat();
8720
9296
  const keyboardOptions = hideDefaultOption ? groupedOptions : [null, ...groupedOptions];
8721
9297
  const selectedIndex = currentKey ? Math.max(0, keyboardOptions.findIndex((option) => option && encodeModelKey(option) === currentKey)) : 0;
8722
- const [activeIndex, setActiveIndex] = useState16(selectedIndex);
8723
- const activeIndexRef = useRef13(selectedIndex);
8724
- const menuRef = useRef13(null);
9298
+ const [activeIndex, setActiveIndex] = useState18(selectedIndex);
9299
+ const activeIndexRef = useRef14(selectedIndex);
9300
+ const menuRef = useRef14(null);
8725
9301
  useDismissOnOutsidePointer(menuRef, onClose);
8726
9302
  const setKeyboardActiveIndex = (next) => {
8727
9303
  const resolved = typeof next === "function" ? next(activeIndexRef.current) : next;
8728
9304
  activeIndexRef.current = resolved;
8729
9305
  setActiveIndex(resolved);
8730
9306
  };
8731
- useEffect17(() => {
9307
+ useEffect19(() => {
8732
9308
  setKeyboardActiveIndex(selectedIndex);
8733
9309
  }, [selectedIndex]);
8734
- useEffect17(() => {
9310
+ useEffect19(() => {
8735
9311
  const handler = (event) => {
8736
9312
  if (event.key === "Escape") {
8737
9313
  event.preventDefault();
@@ -8756,8 +9332,8 @@ function ModelPickerMenu({
8756
9332
  }, [activeIndex, disabled, keyboardOptions, onChange, onClose]);
8757
9333
  const optionIndexOffset = hideDefaultOption ? 0 : 1;
8758
9334
  const optionIndexes = new Map(groupedOptions.map((option, index) => [encodeModelKey(option), index + optionIndexOffset]));
8759
- 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: [
8760
- 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(
8761
9337
  CommandInput,
8762
9338
  {
8763
9339
  placeholder: "Search models\u2026",
@@ -8765,9 +9341,9 @@ function ModelPickerMenu({
8765
9341
  className: "h-8 w-full border-0 bg-transparent text-sm outline-none focus:ring-0"
8766
9342
  }
8767
9343
  ) }),
8768
- /* @__PURE__ */ jsxs24(CommandList, { className: "max-h-[300px] p-0.5", children: [
8769
- /* @__PURE__ */ jsx28(CommandEmpty, { className: "py-4 text-center text-sm text-muted-foreground", children: "No models found" }),
8770
- !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(
8771
9347
  CommandItem,
8772
9348
  {
8773
9349
  value: "Default model automatic auto",
@@ -8778,8 +9354,8 @@ function ModelPickerMenu({
8778
9354
  },
8779
9355
  className: selectorItemClass(!value || activeIndex === 0),
8780
9356
  children: [
8781
- /* @__PURE__ */ jsx28(
8782
- CheckIcon3,
9357
+ /* @__PURE__ */ jsx29(
9358
+ CheckIcon4,
8783
9359
  {
8784
9360
  className: cn(
8785
9361
  "h-3.5 w-3.5 shrink-0",
@@ -8787,12 +9363,12 @@ function ModelPickerMenu({
8787
9363
  )
8788
9364
  }
8789
9365
  ),
8790
- /* @__PURE__ */ jsx28("span", { className: "truncate", children: "Default model" }),
8791
- /* @__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" })
8792
9368
  ]
8793
9369
  }
8794
9370
  ) }) : null,
8795
- [...groups.entries()].map(([provider, list]) => /* @__PURE__ */ jsx28(
9371
+ [...groups.entries()].map(([provider, list]) => /* @__PURE__ */ jsx29(
8796
9372
  CommandGroup,
8797
9373
  {
8798
9374
  heading: displayProviderLabel(provider),
@@ -8801,7 +9377,7 @@ function ModelPickerMenu({
8801
9377
  const key = encodeModelKey(m);
8802
9378
  const label = m.label || displayModelLabel(m.id);
8803
9379
  const itemIndex = optionIndexes.get(key) ?? 0;
8804
- return /* @__PURE__ */ jsxs24(
9380
+ return /* @__PURE__ */ jsxs25(
8805
9381
  CommandItem,
8806
9382
  {
8807
9383
  value: `${label} ${m.id} ${displayProviderLabel(m.provider)}`,
@@ -8812,8 +9388,8 @@ function ModelPickerMenu({
8812
9388
  },
8813
9389
  className: cn(selectorItemClass(key === currentKey || activeIndex === itemIndex)),
8814
9390
  children: [
8815
- /* @__PURE__ */ jsx28(
8816
- CheckIcon3,
9391
+ /* @__PURE__ */ jsx29(
9392
+ CheckIcon4,
8817
9393
  {
8818
9394
  className: cn(
8819
9395
  "h-3.5 w-3.5 shrink-0",
@@ -8821,8 +9397,8 @@ function ModelPickerMenu({
8821
9397
  )
8822
9398
  }
8823
9399
  ),
8824
- /* @__PURE__ */ jsx28("span", { className: "min-w-0 flex-1 truncate whitespace-nowrap", children: label }),
8825
- /* @__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 })
8826
9402
  ]
8827
9403
  },
8828
9404
  key
@@ -8847,12 +9423,12 @@ function ModelSelect({
8847
9423
  ariaDescribedBy,
8848
9424
  hideDefaultOption = false
8849
9425
  }) {
8850
- const [open, setOpen] = useState16(false);
8851
- const previousOpenSignalRef = useRef13(openSignal);
8852
- useEffect17(() => {
9426
+ const [open, setOpen] = useState18(false);
9427
+ const previousOpenSignalRef = useRef14(openSignal);
9428
+ useEffect19(() => {
8853
9429
  if (disabled) setOpen(false);
8854
9430
  }, [disabled]);
8855
- useEffect17(() => {
9431
+ useEffect19(() => {
8856
9432
  if (disabled || openSignal === void 0) {
8857
9433
  previousOpenSignalRef.current = openSignal;
8858
9434
  return;
@@ -8860,8 +9436,8 @@ function ModelSelect({
8860
9436
  if (previousOpenSignalRef.current !== openSignal) setOpen(true);
8861
9437
  previousOpenSignalRef.current = openSignal;
8862
9438
  }, [disabled, openSignal]);
8863
- return /* @__PURE__ */ jsxs24(Popover, { open, onOpenChange: disabled ? void 0 : setOpen, children: [
8864
- /* @__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(
8865
9441
  ModelSelectTrigger,
8866
9442
  {
8867
9443
  value,
@@ -8875,7 +9451,7 @@ function ModelSelect({
8875
9451
  "aria-describedby": ariaDescribedBy
8876
9452
  }
8877
9453
  ) }),
8878
- /* @__PURE__ */ jsx28(
9454
+ /* @__PURE__ */ jsx29(
8879
9455
  PopoverContent,
8880
9456
  {
8881
9457
  align: trigger === "slash" ? "center" : "start",
@@ -8884,7 +9460,7 @@ function ModelSelect({
8884
9460
  collisionPadding: 12,
8885
9461
  "data-boring-agent": "",
8886
9462
  className: cn("w-[min(92vw,340px)]", selectorContentClass),
8887
- children: /* @__PURE__ */ jsx28(
9463
+ children: /* @__PURE__ */ jsx29(
8888
9464
  ModelPickerMenu,
8889
9465
  {
8890
9466
  value,
@@ -8908,12 +9484,12 @@ function ThinkingSelect({
8908
9484
  openSignal,
8909
9485
  className
8910
9486
  }) {
8911
- const [open, setOpen] = useState16(false);
8912
- const previousOpenSignalRef = useRef13(openSignal);
8913
- useEffect17(() => {
9487
+ const [open, setOpen] = useState18(false);
9488
+ const previousOpenSignalRef = useRef14(openSignal);
9489
+ useEffect19(() => {
8914
9490
  if (disabled) setOpen(false);
8915
9491
  }, [disabled]);
8916
- useEffect17(() => {
9492
+ useEffect19(() => {
8917
9493
  if (disabled || openSignal === void 0) {
8918
9494
  previousOpenSignalRef.current = openSignal;
8919
9495
  return;
@@ -8921,8 +9497,8 @@ function ThinkingSelect({
8921
9497
  if (previousOpenSignalRef.current !== openSignal) setOpen(true);
8922
9498
  previousOpenSignalRef.current = openSignal;
8923
9499
  }, [disabled, openSignal]);
8924
- return /* @__PURE__ */ jsxs24(Popover, { open, onOpenChange: disabled ? void 0 : setOpen, children: [
8925
- /* @__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(
8926
9502
  ThinkingSelectTrigger,
8927
9503
  {
8928
9504
  value,
@@ -8932,7 +9508,7 @@ function ThinkingSelect({
8932
9508
  className
8933
9509
  }
8934
9510
  ) }),
8935
- /* @__PURE__ */ jsx28(
9511
+ /* @__PURE__ */ jsx29(
8936
9512
  PopoverContent,
8937
9513
  {
8938
9514
  align: trigger === "slash" ? "center" : "end",
@@ -8941,7 +9517,7 @@ function ThinkingSelect({
8941
9517
  collisionPadding: 12,
8942
9518
  "data-boring-agent": "",
8943
9519
  className: cn("w-[min(92vw,240px)]", selectorContentClass),
8944
- children: /* @__PURE__ */ jsx28(
9520
+ children: /* @__PURE__ */ jsx29(
8945
9521
  ThinkingPickerMenu,
8946
9522
  {
8947
9523
  value,
@@ -8964,7 +9540,7 @@ var ThinkingSelectTrigger = forwardRef(function ThinkingSelectTrigger2({
8964
9540
  className,
8965
9541
  ...props
8966
9542
  }, ref) {
8967
- return /* @__PURE__ */ jsxs24(
9543
+ return /* @__PURE__ */ jsxs25(
8968
9544
  "button",
8969
9545
  {
8970
9546
  ref,
@@ -8985,13 +9561,13 @@ var ThinkingSelectTrigger = forwardRef(function ThinkingSelectTrigger2({
8985
9561
  onClick,
8986
9562
  ...props,
8987
9563
  children: [
8988
- trigger === "button" ? /* @__PURE__ */ jsx28(ThinkingLevelGlyph, { level: value }) : null,
8989
- trigger === "slash" ? /* @__PURE__ */ jsxs24(Fragment11, { children: [
8990
- /* @__PURE__ */ jsx28("span", { className: "text-muted-foreground", children: "Thinking \xB7 " }),
8991
- /* @__PURE__ */ jsx28("span", { className: "text-foreground", children: THINKING_LEVEL_STATUS_LABELS[value] })
8992
- ] }) : /* @__PURE__ */ jsxs24(Fragment11, { children: [
8993
- /* @__PURE__ */ jsx28("span", { children: THINKING_LEVEL_LABELS[value] }),
8994
- /* @__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" })
8995
9571
  ] })
8996
9572
  ]
8997
9573
  }
@@ -9005,19 +9581,19 @@ function ThinkingPickerMenu({
9005
9581
  className
9006
9582
  }) {
9007
9583
  const selectedIndex = Math.max(0, THINKING_LEVELS.indexOf(value));
9008
- const [activeIndex, setActiveIndex] = useState16(selectedIndex);
9009
- const activeIndexRef = useRef13(selectedIndex);
9010
- const menuRef = useRef13(null);
9584
+ const [activeIndex, setActiveIndex] = useState18(selectedIndex);
9585
+ const activeIndexRef = useRef14(selectedIndex);
9586
+ const menuRef = useRef14(null);
9011
9587
  useDismissOnOutsidePointer(menuRef, onClose);
9012
9588
  const setKeyboardActiveIndex = (next) => {
9013
9589
  const resolved = typeof next === "function" ? next(activeIndexRef.current) : next;
9014
9590
  activeIndexRef.current = resolved;
9015
9591
  setActiveIndex(resolved);
9016
9592
  };
9017
- useEffect17(() => {
9593
+ useEffect19(() => {
9018
9594
  setKeyboardActiveIndex(selectedIndex);
9019
9595
  }, [selectedIndex]);
9020
- useEffect17(() => {
9596
+ useEffect19(() => {
9021
9597
  const handler = (event) => {
9022
9598
  if (event.key === "Escape") {
9023
9599
  event.preventDefault();
@@ -9040,7 +9616,7 @@ function ThinkingPickerMenu({
9040
9616
  window.addEventListener("keydown", handler, { capture: true });
9041
9617
  return () => window.removeEventListener("keydown", handler, { capture: true });
9042
9618
  }, [activeIndex, disabled, onChange, onClose]);
9043
- 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(
9044
9620
  CommandItem,
9045
9621
  {
9046
9622
  value: `Thinking ${THINKING_LEVEL_LABELS[level]} ${THINKING_LEVEL_DETAILS[level]}`,
@@ -9051,8 +9627,8 @@ function ThinkingPickerMenu({
9051
9627
  },
9052
9628
  className: selectorItemClass(level === value || index === activeIndex),
9053
9629
  children: [
9054
- /* @__PURE__ */ jsx28(
9055
- CheckIcon3,
9630
+ /* @__PURE__ */ jsx29(
9631
+ CheckIcon4,
9056
9632
  {
9057
9633
  className: cn(
9058
9634
  "h-3.5 w-3.5 shrink-0",
@@ -9060,9 +9636,9 @@ function ThinkingPickerMenu({
9060
9636
  )
9061
9637
  }
9062
9638
  ),
9063
- /* @__PURE__ */ jsx28(ThinkingLevelGlyph, { level }),
9064
- /* @__PURE__ */ jsx28("span", { className: "font-medium", children: THINKING_LEVEL_LABELS[level] }),
9065
- /* @__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] })
9066
9642
  ]
9067
9643
  },
9068
9644
  level
@@ -9103,7 +9679,7 @@ import {
9103
9679
  XIcon as XIcon4
9104
9680
  } from "lucide-react";
9105
9681
  import { nanoid } from "nanoid";
9106
- 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";
9107
9683
 
9108
9684
  // src/front/primitives/prompt-input-context.ts
9109
9685
  import { createContext as createContext7, useContext as useContext7 } from "react";
@@ -9138,12 +9714,12 @@ import {
9138
9714
  TooltipTrigger as TooltipTrigger2
9139
9715
  } from "@hachej/boring-ui-kit";
9140
9716
  import { Children as Children2 } from "react";
9141
- import { jsx as jsx29, jsxs as jsxs25 } from "react/jsx-runtime";
9717
+ import { jsx as jsx30, jsxs as jsxs26 } from "react/jsx-runtime";
9142
9718
  var PromptInputFooter = ({
9143
9719
  align = "block-end",
9144
9720
  className,
9145
9721
  ...props
9146
- }) => /* @__PURE__ */ jsx29(
9722
+ }) => /* @__PURE__ */ jsx30(
9147
9723
  InputGroupAddon,
9148
9724
  {
9149
9725
  align,
@@ -9153,7 +9729,7 @@ var PromptInputFooter = ({
9153
9729
  );
9154
9730
 
9155
9731
  // src/front/primitives/prompt-input.tsx
9156
- 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";
9157
9733
  var PromptInput = ({
9158
9734
  className,
9159
9735
  accept,
@@ -9170,14 +9746,14 @@ var PromptInput = ({
9170
9746
  }) => {
9171
9747
  const controller = useOptionalPromptInputController();
9172
9748
  const usingProvider = !!controller;
9173
- const providerTextValueRef = useRef14("");
9749
+ const providerTextValueRef = useRef15("");
9174
9750
  if (usingProvider) {
9175
9751
  providerTextValueRef.current = controller.textInput.value;
9176
9752
  }
9177
- const inputRef = useRef14(null);
9178
- const formRef = useRef14(null);
9179
- const [items, setItems] = useState17([]);
9180
- 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) => {
9181
9757
  setItems((prev) => prev.map((f) => {
9182
9758
  if (f.id !== id) return f;
9183
9759
  if (f.url !== url && f.url.startsWith("blob:")) URL.revokeObjectURL(f.url);
@@ -9185,15 +9761,15 @@ var PromptInput = ({
9185
9761
  }));
9186
9762
  }, []);
9187
9763
  const files = usingProvider ? controller.attachments.files : items;
9188
- const [referencedSources, setReferencedSources] = useState17([]);
9189
- const filesRef = useRef14(files);
9190
- useEffect18(() => {
9764
+ const [referencedSources, setReferencedSources] = useState19([]);
9765
+ const filesRef = useRef15(files);
9766
+ useEffect20(() => {
9191
9767
  filesRef.current = files;
9192
9768
  }, [files]);
9193
- const openFileDialogLocal = useCallback15(() => {
9769
+ const openFileDialogLocal = useCallback16(() => {
9194
9770
  inputRef.current?.click();
9195
9771
  }, []);
9196
- const matchesAccept = useCallback15(
9772
+ const matchesAccept = useCallback16(
9197
9773
  (f) => {
9198
9774
  if (!accept || accept.trim() === "") {
9199
9775
  return true;
@@ -9209,7 +9785,7 @@ var PromptInput = ({
9209
9785
  },
9210
9786
  [accept]
9211
9787
  );
9212
- const addLocal = useCallback15(
9788
+ const addLocal = useCallback16(
9213
9789
  (fileList) => {
9214
9790
  const incoming = [...fileList];
9215
9791
  const accepted = incoming.filter((f) => matchesAccept(f));
@@ -9256,7 +9832,7 @@ var PromptInput = ({
9256
9832
  },
9257
9833
  [matchesAccept, maxFiles, maxFileSize, onError, items.length, onUploadFile, setFileUrlLocal]
9258
9834
  );
9259
- const removeLocal = useCallback15(
9835
+ const removeLocal = useCallback16(
9260
9836
  (id) => setItems((prev) => {
9261
9837
  const found = prev.find((file) => file.id === id);
9262
9838
  if (found?.url.startsWith("blob:")) {
@@ -9266,7 +9842,7 @@ var PromptInput = ({
9266
9842
  }),
9267
9843
  []
9268
9844
  );
9269
- const addWithProviderValidation = useCallback15(
9845
+ const addWithProviderValidation = useCallback16(
9270
9846
  (fileList) => {
9271
9847
  const incoming = [...fileList];
9272
9848
  const accepted = incoming.filter((f) => matchesAccept(f));
@@ -9301,7 +9877,7 @@ var PromptInput = ({
9301
9877
  },
9302
9878
  [matchesAccept, maxFileSize, maxFiles, onError, files.length, controller]
9303
9879
  );
9304
- const clearAttachments = useCallback15(
9880
+ const clearAttachments = useCallback16(
9305
9881
  () => usingProvider ? controller?.attachments.clear() : setItems((prev) => {
9306
9882
  for (const file of prev) {
9307
9883
  if (file.url.startsWith("blob:")) {
@@ -9312,29 +9888,29 @@ var PromptInput = ({
9312
9888
  }),
9313
9889
  [usingProvider, controller]
9314
9890
  );
9315
- const clearReferencedSources = useCallback15(
9891
+ const clearReferencedSources = useCallback16(
9316
9892
  () => setReferencedSources([]),
9317
9893
  []
9318
9894
  );
9319
9895
  const add = usingProvider ? addWithProviderValidation : addLocal;
9320
9896
  const remove = usingProvider ? controller.attachments.remove : removeLocal;
9321
9897
  const openFileDialog = usingProvider ? controller.attachments.openFileDialog : openFileDialogLocal;
9322
- const clear = useCallback15(() => {
9898
+ const clear = useCallback16(() => {
9323
9899
  clearAttachments();
9324
9900
  clearReferencedSources();
9325
9901
  }, [clearAttachments, clearReferencedSources]);
9326
- useEffect18(() => {
9902
+ useEffect20(() => {
9327
9903
  if (!usingProvider) {
9328
9904
  return;
9329
9905
  }
9330
9906
  controller.__registerFileInput(inputRef, () => inputRef.current?.click());
9331
9907
  }, [usingProvider, controller]);
9332
- useEffect18(() => {
9908
+ useEffect20(() => {
9333
9909
  if (syncHiddenInput && inputRef.current && files.length === 0) {
9334
9910
  inputRef.current.value = "";
9335
9911
  }
9336
9912
  }, [files, syncHiddenInput]);
9337
- useEffect18(() => {
9913
+ useEffect20(() => {
9338
9914
  const form = formRef.current;
9339
9915
  if (!form) {
9340
9916
  return;
@@ -9362,7 +9938,7 @@ var PromptInput = ({
9362
9938
  form.removeEventListener("drop", onDrop);
9363
9939
  };
9364
9940
  }, [add, globalDrop]);
9365
- useEffect18(() => {
9941
+ useEffect20(() => {
9366
9942
  if (!globalDrop) {
9367
9943
  return;
9368
9944
  }
@@ -9386,7 +9962,7 @@ var PromptInput = ({
9386
9962
  document.removeEventListener("drop", onDrop);
9387
9963
  };
9388
9964
  }, [add, globalDrop]);
9389
- useEffect18(
9965
+ useEffect20(
9390
9966
  () => () => {
9391
9967
  if (!usingProvider) {
9392
9968
  for (const f of filesRef.current) {
@@ -9398,7 +9974,7 @@ var PromptInput = ({
9398
9974
  },
9399
9975
  [usingProvider]
9400
9976
  );
9401
- const handleChange = useCallback15(
9977
+ const handleChange = useCallback16(
9402
9978
  (event) => {
9403
9979
  if (event.currentTarget.files) {
9404
9980
  add(event.currentTarget.files);
@@ -9408,7 +9984,7 @@ var PromptInput = ({
9408
9984
  [add]
9409
9985
  );
9410
9986
  const setFileUrl = usingProvider ? controller.attachments.setFileUrl : setFileUrlLocal;
9411
- const attachmentsCtx = useMemo13(
9987
+ const attachmentsCtx = useMemo14(
9412
9988
  () => ({
9413
9989
  add,
9414
9990
  clear: clearAttachments,
@@ -9420,7 +9996,7 @@ var PromptInput = ({
9420
9996
  }),
9421
9997
  [files, add, remove, clearAttachments, openFileDialog, setFileUrl]
9422
9998
  );
9423
- const refsCtx = useMemo13(
9999
+ const refsCtx = useMemo14(
9424
10000
  () => ({
9425
10001
  add: (incoming) => {
9426
10002
  const array = Array.isArray(incoming) ? incoming : [incoming];
@@ -9437,7 +10013,7 @@ var PromptInput = ({
9437
10013
  }),
9438
10014
  [referencedSources, clearReferencedSources]
9439
10015
  );
9440
- const handleSubmit = useCallback15(
10016
+ const handleSubmit = useCallback16(
9441
10017
  async (event) => {
9442
10018
  event.preventDefault();
9443
10019
  const form = event.currentTarget;
@@ -9496,8 +10072,8 @@ var PromptInput = ({
9496
10072
  },
9497
10073
  [usingProvider, controller, files, onSubmit, clear]
9498
10074
  );
9499
- const inner = /* @__PURE__ */ jsxs26(Fragment12, { children: [
9500
- /* @__PURE__ */ jsx30(
10075
+ const inner = /* @__PURE__ */ jsxs27(Fragment12, { children: [
10076
+ /* @__PURE__ */ jsx31(
9501
10077
  Input,
9502
10078
  {
9503
10079
  accept,
@@ -9510,7 +10086,7 @@ var PromptInput = ({
9510
10086
  type: "file"
9511
10087
  }
9512
10088
  ),
9513
- /* @__PURE__ */ jsx30(
10089
+ /* @__PURE__ */ jsx31(
9514
10090
  "form",
9515
10091
  {
9516
10092
  "data-boring-agent-part": "composer",
@@ -9518,12 +10094,12 @@ var PromptInput = ({
9518
10094
  onSubmit: handleSubmit,
9519
10095
  ref: formRef,
9520
10096
  ...props,
9521
- children: /* @__PURE__ */ jsx30(InputGroup, { className: "overflow-hidden", children })
10097
+ children: /* @__PURE__ */ jsx31(InputGroup, { className: "overflow-hidden", children })
9522
10098
  }
9523
10099
  )
9524
10100
  ] });
9525
- const withReferencedSources = /* @__PURE__ */ jsx30(LocalReferencedSourcesContext.Provider, { value: refsCtx, children: inner });
9526
- 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 });
9527
10103
  };
9528
10104
  var PromptInputTextarea = ({
9529
10105
  onChange,
@@ -9534,8 +10110,8 @@ var PromptInputTextarea = ({
9534
10110
  }) => {
9535
10111
  const controller = useOptionalPromptInputController();
9536
10112
  const attachments = usePromptInputAttachments();
9537
- const [isComposing, setIsComposing] = useState17(false);
9538
- const handleKeyDown = useCallback15(
10113
+ const [isComposing, setIsComposing] = useState19(false);
10114
+ const handleKeyDown = useCallback16(
9539
10115
  (e) => {
9540
10116
  onKeyDown?.(e);
9541
10117
  if (e.defaultPrevented) {
@@ -9568,7 +10144,7 @@ var PromptInputTextarea = ({
9568
10144
  },
9569
10145
  [onKeyDown, isComposing, attachments]
9570
10146
  );
9571
- const handlePaste = useCallback15(
10147
+ const handlePaste = useCallback16(
9572
10148
  (event) => {
9573
10149
  const items = event.clipboardData?.items;
9574
10150
  if (!items) {
@@ -9590,8 +10166,8 @@ var PromptInputTextarea = ({
9590
10166
  },
9591
10167
  [attachments]
9592
10168
  );
9593
- const handleCompositionEnd = useCallback15(() => setIsComposing(false), []);
9594
- const handleCompositionStart = useCallback15(() => setIsComposing(true), []);
10169
+ const handleCompositionEnd = useCallback16(() => setIsComposing(false), []);
10170
+ const handleCompositionStart = useCallback16(() => setIsComposing(true), []);
9595
10171
  const controlledProps = controller ? {
9596
10172
  onChange: (e) => {
9597
10173
  controller.textInput.setInput(e.currentTarget.value);
@@ -9601,7 +10177,7 @@ var PromptInputTextarea = ({
9601
10177
  } : {
9602
10178
  onChange
9603
10179
  };
9604
- return /* @__PURE__ */ jsx30(
10180
+ return /* @__PURE__ */ jsx31(
9605
10181
  InputGroupTextarea,
9606
10182
  {
9607
10183
  "data-boring-agent-part": "composer-input",
@@ -9628,15 +10204,15 @@ var PromptInputSubmit = ({
9628
10204
  ...props
9629
10205
  }) => {
9630
10206
  const isGenerating = status === "submitted" || status === "streaming";
9631
- let Icon = /* @__PURE__ */ jsx30(CornerDownLeftIcon, { className: "size-4" });
10207
+ let Icon = /* @__PURE__ */ jsx31(CornerDownLeftIcon, { className: "size-4" });
9632
10208
  if (status === "submitted") {
9633
- Icon = /* @__PURE__ */ jsx30(Spinner, {});
10209
+ Icon = /* @__PURE__ */ jsx31(Spinner, {});
9634
10210
  } else if (status === "streaming") {
9635
- Icon = /* @__PURE__ */ jsx30(SquareIcon, { className: "size-4" });
10211
+ Icon = /* @__PURE__ */ jsx31(SquareIcon, { className: "size-4" });
9636
10212
  } else if (status === "error") {
9637
- Icon = /* @__PURE__ */ jsx30(XIcon4, { className: "size-4" });
10213
+ Icon = /* @__PURE__ */ jsx31(XIcon4, { className: "size-4" });
9638
10214
  }
9639
- const handleClick = useCallback15(
10215
+ const handleClick = useCallback16(
9640
10216
  (e) => {
9641
10217
  if (isGenerating && onStop) {
9642
10218
  e.preventDefault();
@@ -9647,7 +10223,7 @@ var PromptInputSubmit = ({
9647
10223
  },
9648
10224
  [isGenerating, onStop, onClick]
9649
10225
  );
9650
- return /* @__PURE__ */ jsx30(
10226
+ return /* @__PURE__ */ jsx31(
9651
10227
  InputGroupButton2,
9652
10228
  {
9653
10229
  "aria-label": isGenerating ? "Stop" : "Submit",
@@ -9664,8 +10240,8 @@ var PromptInputSubmit = ({
9664
10240
  };
9665
10241
 
9666
10242
  // src/front/primitives/slash-command-picker.tsx
9667
- import { useEffect as useEffect19, useMemo as useMemo14, useRef as useRef15, useState as useState18 } from "react";
9668
- 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";
9669
10245
  var ALL_PLUGINS = "__all__";
9670
10246
  function commandGroup(cmd) {
9671
10247
  if (cmd.source === "skill") return "skills";
@@ -9673,20 +10249,20 @@ function commandGroup(cmd) {
9673
10249
  return "built-in";
9674
10250
  }
9675
10251
  function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9676
- const [plugin, setPlugin] = useState18(ALL_PLUGINS);
9677
- const [searchQuery, setSearchQuery] = useState18(query);
9678
- const [activeIdx, setActiveIdx] = useState18(0);
9679
- const containerRef = useRef15(null);
9680
- const listRef = useRef15(null);
9681
- const inputRef = useRef15(null);
9682
- 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(() => {
9683
10259
  setSearchQuery(query);
9684
10260
  }, [query]);
9685
- const groups = useMemo14(() => {
10261
+ const groups = useMemo15(() => {
9686
10262
  const set = new Set(commands.map(commandGroup));
9687
10263
  return Array.from(set).sort((a, b) => a.localeCompare(b));
9688
10264
  }, [commands]);
9689
- const filtered = useMemo14(() => {
10265
+ const filtered = useMemo15(() => {
9690
10266
  const q = searchQuery.trim().toLowerCase();
9691
10267
  return commands.filter((c) => {
9692
10268
  if (plugin !== ALL_PLUGINS && commandGroup(c) !== plugin) return false;
@@ -9694,10 +10270,10 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9694
10270
  return c.name.toLowerCase().includes(q) || c.description.toLowerCase().includes(q);
9695
10271
  });
9696
10272
  }, [commands, searchQuery, plugin]);
9697
- useEffect19(() => {
10273
+ useEffect21(() => {
9698
10274
  setActiveIdx((i) => filtered.length === 0 ? 0 : Math.min(i, filtered.length - 1));
9699
10275
  }, [filtered.length]);
9700
- useEffect19(() => {
10276
+ useEffect21(() => {
9701
10277
  const handler = (e) => {
9702
10278
  if (containerRef.current && !containerRef.current.contains(e.target)) {
9703
10279
  onDismiss();
@@ -9716,8 +10292,8 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9716
10292
  },
9717
10293
  onDismiss
9718
10294
  });
9719
- return /* @__PURE__ */ jsxs27("div", { ref: containerRef, className: "mb-1 w-full overflow-hidden rounded-lg border border-border/60 bg-popover shadow-lg", children: [
9720
- /* @__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(
9721
10297
  "input",
9722
10298
  {
9723
10299
  ref: inputRef,
@@ -9732,9 +10308,9 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9732
10308
  autoComplete: "off"
9733
10309
  }
9734
10310
  ),
9735
- /* @__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) => {
9736
10312
  const selected = plugin === g;
9737
- return /* @__PURE__ */ jsx31(
10313
+ return /* @__PURE__ */ jsx32(
9738
10314
  "button",
9739
10315
  {
9740
10316
  type: "button",
@@ -9754,7 +10330,7 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9754
10330
  g
9755
10331
  );
9756
10332
  }) }),
9757
- 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(
9758
10334
  "li",
9759
10335
  {
9760
10336
  role: "option",
@@ -9772,20 +10348,20 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9772
10348
  onSelect(cmd.name);
9773
10349
  },
9774
10350
  children: [
9775
- /* @__PURE__ */ jsxs27("span", { className: "flex items-center gap-1.5", children: [
9776
- /* @__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: [
9777
10353
  "/",
9778
10354
  cmd.name
9779
10355
  ] }),
9780
- cmd.source === "skill" ? /* @__PURE__ */ jsxs27(Fragment13, { children: [
9781
- /* @__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" }),
9782
- 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
9783
10359
  ] }) : cmd.sourcePlugin ? (
9784
10360
  // Originating plugin/package for non-skill commands.
9785
- /* @__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 })
9786
10362
  ) : null
9787
10363
  ] }),
9788
- /* @__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 })
9789
10365
  ]
9790
10366
  },
9791
10367
  cmd.name
@@ -9796,10 +10372,10 @@ function SlashCommandPicker({ query, commands, onSelect, onDismiss }) {
9796
10372
  // src/front/chat/components/ComposerAttachments.tsx
9797
10373
  import { AlertCircleIcon as AlertCircleIcon2, Loader2 as Loader22, PaperclipIcon as PaperclipIcon2 } from "lucide-react";
9798
10374
  import { IconButton as IconButton3 } from "@hachej/boring-ui-kit";
9799
- import { jsx as jsx32, jsxs as jsxs28 } from "react/jsx-runtime";
10375
+ import { jsx as jsx33, jsxs as jsxs29 } from "react/jsx-runtime";
9800
10376
  function AttachmentButton({ disabled, className }) {
9801
10377
  const attachments = usePromptInputAttachments();
9802
- return /* @__PURE__ */ jsx32(
10378
+ return /* @__PURE__ */ jsx33(
9803
10379
  IconButton3,
9804
10380
  {
9805
10381
  type: "button",
@@ -9812,21 +10388,21 @@ function AttachmentButton({ disabled, className }) {
9812
10388
  className: cn(composerActionClass, "composer-attachment-button !h-10 !w-10", className),
9813
10389
  "aria-label": "Attach files",
9814
10390
  title: disabled ? "Attachments are available when the composer is ready." : "Attach files",
9815
- 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 })
9816
10392
  }
9817
10393
  );
9818
10394
  }
9819
10395
  function AttachmentsList() {
9820
10396
  const attachments = usePromptInputAttachments();
9821
10397
  if (attachments.files.length === 0) return null;
9822
- return /* @__PURE__ */ jsx32(
10398
+ return /* @__PURE__ */ jsx33(
9823
10399
  Attachments,
9824
10400
  {
9825
10401
  "data-boring-agent-part": "composer-attachment-row",
9826
10402
  "data-align": "block-start",
9827
10403
  variant: "inline",
9828
10404
  className: "w-full flex-wrap items-center justify-start gap-2 px-5 pb-0 pt-3",
9829
- children: attachments.files.map((file) => /* @__PURE__ */ jsxs28(
10405
+ children: attachments.files.map((file) => /* @__PURE__ */ jsxs29(
9830
10406
  Attachment,
9831
10407
  {
9832
10408
  data: file,
@@ -9837,13 +10413,13 @@ function AttachmentsList() {
9837
10413
  file.status === "error" && "!border-destructive/50 !bg-destructive/10"
9838
10414
  ),
9839
10415
  children: [
9840
- /* @__PURE__ */ jsxs28("div", { className: "relative shrink-0", children: [
9841
- /* @__PURE__ */ jsx32(AttachmentPreview, { className: "!size-7 overflow-hidden !rounded-full bg-background/60" }),
9842
- 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,
9843
- 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
9844
10420
  ] }),
9845
- /* @__PURE__ */ jsx32(AttachmentInfo, { className: "min-w-0 !max-w-[180px] truncate text-[13px] font-medium" }),
9846
- /* @__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" })
9847
10423
  ]
9848
10424
  },
9849
10425
  file.id
@@ -9853,7 +10429,7 @@ function AttachmentsList() {
9853
10429
  }
9854
10430
 
9855
10431
  // src/front/chat/components/PiChatComposerSurface.tsx
9856
- import { jsx as jsx33, jsxs as jsxs29 } from "react/jsx-runtime";
10432
+ import { jsx as jsx34, jsxs as jsxs30 } from "react/jsx-runtime";
9857
10433
  var MAX_PROMPT_ATTACHMENTS = 2;
9858
10434
  var MAX_PROMPT_ATTACHMENT_BYTES = 10 * 1024 * 1024;
9859
10435
  var COMPOSER_INPUT_GROUP_MIN_HEIGHT = 56;
@@ -9892,6 +10468,10 @@ function PiChatComposerSurface({
9892
10468
  onComposerBlockerAction,
9893
10469
  queuePreview,
9894
10470
  onEditQueued,
10471
+ onResumeQueued,
10472
+ resumeQueuedPending,
10473
+ onRemoveQueued,
10474
+ queueMutationPending,
9895
10475
  hotReloadEnabled,
9896
10476
  pluginUpdateState,
9897
10477
  onDismissPluginUpdate,
@@ -9940,13 +10520,14 @@ function PiChatComposerSurface({
9940
10520
  const prefersReducedMotion = useReducedMotion();
9941
10521
  const workspaceRequestId = getHeaderValue(requestHeaders, "x-boring-workspace-id");
9942
10522
  const composerContributions = useComposerContributions();
9943
- const uploadAttachment = useCallback16((file) => uploadFile(file, {
10523
+ const uploadAttachment = useCallback17((file) => uploadFile(file, {
9944
10524
  apiBaseUrl,
9945
10525
  workspaceRequestId,
10526
+ requestHeaders,
9946
10527
  responseUrl: "raw",
9947
10528
  fetch: fetch2
9948
- }), [apiBaseUrl, fetch2, workspaceRequestId]);
9949
- const resizeTextarea = useCallback16((node) => {
10529
+ }), [apiBaseUrl, fetch2, requestHeaders, workspaceRequestId]);
10530
+ const resizeTextarea = useCallback17((node) => {
9950
10531
  if (!node) return;
9951
10532
  node.style.height = "auto";
9952
10533
  const style = window.getComputedStyle(node);
@@ -9970,11 +10551,11 @@ function PiChatComposerSurface({
9970
10551
  useLayoutEffect3(() => {
9971
10552
  resizeTextarea(textareaRef.current);
9972
10553
  }, [draft, resizeTextarea, textareaRef]);
9973
- return /* @__PURE__ */ jsxs29("div", { className: cn(
10554
+ return /* @__PURE__ */ jsxs30("div", { className: cn(
9974
10555
  "relative z-20",
9975
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"
9976
10557
  ), children: [
9977
- /* @__PURE__ */ jsx33(
10558
+ /* @__PURE__ */ jsx34(
9978
10559
  "div",
9979
10560
  {
9980
10561
  "data-boring-agent-part": "chat-working-slot",
@@ -9984,7 +10565,7 @@ function PiChatComposerSurface({
9984
10565
  isStreaming ? "mb-2 max-h-8 opacity-100" : "mb-0 max-h-0 opacity-0"
9985
10566
  ),
9986
10567
  "aria-hidden": !isStreaming,
9987
- children: /* @__PURE__ */ jsxs29(
10568
+ children: /* @__PURE__ */ jsxs30(
9988
10569
  "div",
9989
10570
  {
9990
10571
  "data-testid": isStreaming ? "chat-working" : void 0,
@@ -9992,7 +10573,7 @@ function PiChatComposerSurface({
9992
10573
  "aria-live": isStreaming ? "polite" : void 0,
9993
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",
9994
10575
  children: [
9995
- /* @__PURE__ */ jsx33(
10576
+ /* @__PURE__ */ jsx34(
9996
10577
  motion2.span,
9997
10578
  {
9998
10579
  "aria-hidden": "true",
@@ -10001,14 +10582,14 @@ function PiChatComposerSurface({
10001
10582
  transition: prefersReducedMotion ? { duration: 0 } : { duration: 1.2, repeat: Infinity, ease: "easeInOut" }
10002
10583
  }
10003
10584
  ),
10004
- /* @__PURE__ */ jsx33("span", { children: "Working\u2026" })
10585
+ /* @__PURE__ */ jsx34("span", { children: "Working\u2026" })
10005
10586
  ]
10006
10587
  }
10007
10588
  )
10008
10589
  }
10009
10590
  ),
10010
- composerStatusNotice ? /* @__PURE__ */ jsx33(ComposerRuntimeNotice, { notice: composerStatusNotice }) : null,
10011
- composerBlocked && !workspaceWarmupBlocked ? /* @__PURE__ */ jsx33(
10591
+ composerStatusNotice ? /* @__PURE__ */ jsx34(ComposerRuntimeNotice, { notice: composerStatusNotice }) : null,
10592
+ composerBlocked && !workspaceWarmupBlocked ? /* @__PURE__ */ jsx34(
10012
10593
  ComposerBlockerNotice,
10013
10594
  {
10014
10595
  blocker: primaryComposerBlocker,
@@ -10016,8 +10597,18 @@ function PiChatComposerSurface({
10016
10597
  onAction: onComposerBlockerAction
10017
10598
  }
10018
10599
  ) : null,
10019
- queuePreview.length > 0 ? /* @__PURE__ */ jsx33(QueuedComposerNotice, { followUps: queuePreview, onEdit: onEditQueued }) : null,
10020
- 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(
10021
10612
  PluginUpdateStatus,
10022
10613
  {
10023
10614
  state: pluginUpdateState,
@@ -10025,14 +10616,14 @@ function PiChatComposerSurface({
10025
10616
  onRetry: onRunPluginUpdate
10026
10617
  }
10027
10618
  ) : null,
10028
- /* @__PURE__ */ jsx33(
10619
+ /* @__PURE__ */ jsx34(
10029
10620
  CommandRunStatus,
10030
10621
  {
10031
10622
  state: commandNotifyState,
10032
10623
  onDismiss: onDismissCommandNotify
10033
10624
  }
10034
10625
  ),
10035
- attachmentNotice ? /* @__PURE__ */ jsx33(
10626
+ attachmentNotice ? /* @__PURE__ */ jsx34(
10036
10627
  "div",
10037
10628
  {
10038
10629
  role: "status",
@@ -10041,12 +10632,12 @@ function PiChatComposerSurface({
10041
10632
  children: attachmentNotice
10042
10633
  }
10043
10634
  ) : null,
10044
- /* @__PURE__ */ jsxs29("div", { className: cn("@container group/composer relative mx-auto w-full", chrome ? "max-w-3xl" : "max-w-[680px]"), children: [
10045
- /* @__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(
10046
10637
  "absolute inset-x-0 z-30",
10047
10638
  pickerPlacement === "above-compact" ? "bottom-[calc(100%+4px)] [&_[cmdk-list]]:!max-h-[200px]" : "bottom-[calc(100%+4px)]"
10048
10639
  ), children: [
10049
- 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(
10050
10641
  MentionPicker,
10051
10642
  {
10052
10643
  mention: mentionState,
@@ -10058,7 +10649,7 @@ function PiChatComposerSurface({
10058
10649
  onDismiss: onDismissMention
10059
10650
  }
10060
10651
  ) }) : null,
10061
- 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(
10062
10653
  SlashCommandPicker,
10063
10654
  {
10064
10655
  query: slashQuery,
@@ -10067,7 +10658,7 @@ function PiChatComposerSurface({
10067
10658
  onDismiss: onDismissSlash
10068
10659
  }
10069
10660
  ) }) : null,
10070
- 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(
10071
10662
  ModelPickerMenu,
10072
10663
  {
10073
10664
  value: selectedModel,
@@ -10078,7 +10669,7 @@ function PiChatComposerSurface({
10078
10669
  onClose: () => onSetModelPickerOpen(false)
10079
10670
  }
10080
10671
  ) }) : null,
10081
- 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(
10082
10673
  ThinkingPickerMenu,
10083
10674
  {
10084
10675
  value: selectedThinking,
@@ -10088,17 +10679,17 @@ function PiChatComposerSurface({
10088
10679
  }
10089
10680
  ) }) : null
10090
10681
  ] }),
10091
- composerContributions.map(({ id, Top }) => Top ? /* @__PURE__ */ jsx33(
10682
+ composerContributions.map(({ id, Top }) => Top ? /* @__PURE__ */ jsx34(
10092
10683
  "div",
10093
10684
  {
10094
10685
  "data-boring-agent-part": "composer-contribution-top",
10095
10686
  "data-boring-agent-contribution-id": id,
10096
10687
  className: cn("mx-auto mb-2 w-full empty:hidden", chrome ? "max-w-3xl" : "max-w-[680px]"),
10097
- children: /* @__PURE__ */ jsx33(Top, {})
10688
+ children: /* @__PURE__ */ jsx34(Top, {})
10098
10689
  },
10099
10690
  id
10100
10691
  ) : null),
10101
- /* @__PURE__ */ jsx33(
10692
+ /* @__PURE__ */ jsx34(
10102
10693
  "div",
10103
10694
  {
10104
10695
  "data-boring-agent-part": "composer-rail",
@@ -10117,7 +10708,7 @@ function PiChatComposerSurface({
10117
10708
  "[&[data-composer-multiline=true]_[data-boring-agent-part=composer-submit-addon]]:self-end",
10118
10709
  "[&[data-composer-multiline=true]_[data-boring-agent-part=composer-submit-addon]]:mb-2"
10119
10710
  ),
10120
- children: /* @__PURE__ */ jsxs29(
10711
+ children: /* @__PURE__ */ jsxs30(
10121
10712
  PromptInput,
10122
10713
  {
10123
10714
  "data-boring-state": status,
@@ -10133,15 +10724,15 @@ function PiChatComposerSurface({
10133
10724
  else onAttachmentNotice(err.message || "Attachment rejected.");
10134
10725
  },
10135
10726
  children: [
10136
- /* @__PURE__ */ jsx33(AttachmentsList, {}),
10137
- /* @__PURE__ */ jsxs29(
10727
+ /* @__PURE__ */ jsx34(AttachmentsList, {}),
10728
+ /* @__PURE__ */ jsxs30(
10138
10729
  "div",
10139
10730
  {
10140
10731
  "data-boring-agent-part": "composer-input-row",
10141
10732
  className: "flex min-h-[var(--composer-input-group-height)] w-full items-center",
10142
10733
  children: [
10143
- /* @__PURE__ */ jsx33("div", { className: "flex h-14 shrink-0 items-center pl-2", children: /* @__PURE__ */ jsx33(AttachmentButton, { disabled: disabled || isStreaming }) }),
10144
- /* @__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(
10145
10736
  PromptInputTextarea,
10146
10737
  {
10147
10738
  value: draft,
@@ -10165,24 +10756,24 @@ function PiChatComposerSurface({
10165
10756
  )
10166
10757
  }
10167
10758
  ),
10168
- /* @__PURE__ */ jsx33(
10759
+ /* @__PURE__ */ jsx34(
10169
10760
  PromptInputFooter,
10170
10761
  {
10171
10762
  align: "inline-end",
10172
10763
  "data-boring-agent-part": "composer-submit-addon",
10173
10764
  className: "!order-none !w-auto shrink-0 self-center justify-between border-0 bg-transparent !px-2 !py-0",
10174
- children: /* @__PURE__ */ jsxs29("div", { className: "ml-auto flex items-center gap-1.5", children: [
10175
- 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(
10176
10767
  "span",
10177
10768
  {
10178
10769
  "data-boring-agent-part": "composer-contribution-action",
10179
10770
  "data-boring-agent-contribution-id": id,
10180
10771
  className: "contents",
10181
- children: /* @__PURE__ */ jsx33(Action, { updateDraft })
10772
+ children: /* @__PURE__ */ jsx34(Action, { updateDraft })
10182
10773
  },
10183
10774
  id
10184
10775
  ) : null),
10185
- /* @__PURE__ */ jsx33(
10776
+ /* @__PURE__ */ jsx34(
10186
10777
  PromptInputSubmit,
10187
10778
  {
10188
10779
  "data-boring-agent-part": "composer-submit",
@@ -10212,7 +10803,7 @@ function PiChatComposerSurface({
10212
10803
  )
10213
10804
  }
10214
10805
  ),
10215
- hideComposerSettings ? null : /* @__PURE__ */ jsx33(
10806
+ hideComposerSettings ? null : /* @__PURE__ */ jsx34(
10216
10807
  "div",
10217
10808
  {
10218
10809
  "data-boring-agent-part": "composer-settings-row",
@@ -10220,8 +10811,8 @@ function PiChatComposerSurface({
10220
10811
  "mx-auto mt-2 grid min-h-8 w-full grid-cols-[minmax(0,1fr)_auto] items-center text-xs text-muted-foreground",
10221
10812
  chrome ? "max-w-3xl" : "max-w-[680px]"
10222
10813
  ),
10223
- children: /* @__PURE__ */ jsxs29("div", { className: "flex min-w-0 items-center justify-center gap-1", children: [
10224
- /* @__PURE__ */ jsx33(
10814
+ children: /* @__PURE__ */ jsxs30("div", { className: "flex min-w-0 items-center justify-center gap-1", children: [
10815
+ /* @__PURE__ */ jsx34(
10225
10816
  ModelSelectTrigger,
10226
10817
  {
10227
10818
  value: selectedModel,
@@ -10241,7 +10832,7 @@ function PiChatComposerSurface({
10241
10832
  }
10242
10833
  }
10243
10834
  ),
10244
- thinkingControl ? /* @__PURE__ */ jsx33(
10835
+ thinkingControl ? /* @__PURE__ */ jsx34(
10245
10836
  ThinkingSelectTrigger,
10246
10837
  {
10247
10838
  value: selectedThinking,
@@ -10270,7 +10861,7 @@ function getHeaderValue(headers, name) {
10270
10861
  }
10271
10862
 
10272
10863
  // src/front/chat/piChatPanelHooks.ts
10273
- 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";
10274
10865
 
10275
10866
  // src/front/chat/piChatPanelUtils.ts
10276
10867
  function headersContentKey(headers) {
@@ -10390,19 +10981,19 @@ function useExternalRemotePiSession({
10390
10981
  remoteSessionOptions,
10391
10982
  enabled = true
10392
10983
  }) {
10393
- const [session, setSession] = useState19();
10394
- const remoteSessionOptionsRef = useRef16(remoteSessionOptions);
10984
+ const [session, setSession] = useState21();
10985
+ const remoteSessionOptionsRef = useRef17(remoteSessionOptions);
10395
10986
  remoteSessionOptionsRef.current = remoteSessionOptions;
10396
10987
  const requestHeadersKey = headersContentKey(requestHeaders);
10397
- const stableRequestHeaders = useMemo15(
10988
+ const stableRequestHeaders = useMemo16(
10398
10989
  () => normalizedHeadersFromContentKey(requestHeadersKey),
10399
10990
  [requestHeadersKey]
10400
10991
  );
10401
- const remoteSessionOptionsKey = useMemo15(
10992
+ const remoteSessionOptionsKey = useMemo16(
10402
10993
  () => remoteSessionOptionsIdentity2(remoteSessionOptions),
10403
10994
  [remoteSessionOptions]
10404
10995
  );
10405
- useEffect20(() => {
10996
+ useEffect22(() => {
10406
10997
  if (!sessionId) {
10407
10998
  setSession(void 0);
10408
10999
  return;
@@ -10421,7 +11012,7 @@ function useExternalRemotePiSession({
10421
11012
  setSession(next);
10422
11013
  return () => next.dispose();
10423
11014
  }, [agentTypeId, apiBaseUrl, createRemoteSession, fetch2, remoteSessionOptionsKey, sessionId, stableRequestHeaders, storageScope, workspaceId]);
10424
- useEffect20(() => {
11015
+ useEffect22(() => {
10425
11016
  if (!session) return;
10426
11017
  if (enabled) session.resumeStream?.();
10427
11018
  else session.suspendStream?.();
@@ -10464,20 +11055,83 @@ function remoteSessionOptionsIdentity2(options) {
10464
11055
  }
10465
11056
  function useRemotePiSessionState(session) {
10466
11057
  return useSyncExternalStore(
10467
- useCallback17((listener) => session?.subscribe(listener) ?? (() => {
11058
+ useCallback18((listener) => session?.subscribe(listener) ?? (() => {
10468
11059
  }), [session]),
10469
- useCallback17(() => session?.getState(), [session]),
10470
- useCallback17(() => session?.getState(), [session])
11060
+ useCallback18(() => session?.getState(), [session]),
11061
+ useCallback18(() => session?.getState(), [session])
10471
11062
  );
10472
11063
  }
10473
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
+
10474
11123
  // src/front/chat/PiChatPanel.tsx
10475
- import { jsx as jsx34, jsxs as jsxs30 } from "react/jsx-runtime";
11124
+ import { jsx as jsx35, jsxs as jsxs31 } from "react/jsx-runtime";
10476
11125
  var DebugDrawer2 = lazy(() => import("../DebugDrawer-JNA43SVR.js").then((m) => ({ default: m.DebugDrawer })));
10477
11126
  var EMPTY_COMMANDS = [];
10478
11127
  var EMPTY_COMMAND_NAMES = [];
10479
11128
  var EMPTY_BLOCKERS = [];
10480
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
+ }
10481
11135
  function normalizeAgentPluginReloadResult(result) {
10482
11136
  if (typeof result !== "string") return result;
10483
11137
  const firstLine = result.trim().split(/\r?\n/).find((line) => line.trim().length > 0)?.trim() ?? "";
@@ -10528,6 +11182,8 @@ function PiChatPanel({
10528
11182
  allowPromptDuringInitialHydration = false,
10529
11183
  workspaceWarmupStatus,
10530
11184
  onSessionReset,
11185
+ spillLargePrompts = true,
11186
+ largePromptThresholdChars,
10531
11187
  onBeforeSubmit,
10532
11188
  onReloadAgentPlugins,
10533
11189
  onCommandResult,
@@ -10544,14 +11200,14 @@ function PiChatPanel({
10544
11200
  }) {
10545
11201
  const externalSessionId = sessionId?.trim() || void 0;
10546
11202
  const showSessionSidebar = showSessions ?? externalSessionId === void 0;
10547
- const onDataRef = useRef17(onData);
11203
+ const onDataRef = useRef18(onData);
10548
11204
  onDataRef.current = onData;
10549
- const onTurnCompleteRef = useRef17(onTurnComplete);
11205
+ const onTurnCompleteRef = useRef18(onTurnComplete);
10550
11206
  onTurnCompleteRef.current = onTurnComplete;
10551
- const sessionListRefreshRef = useRef17(void 0);
10552
- const requestHeadersKey = useMemo16(() => headersContentKey(requestHeaders), [requestHeaders]);
10553
- const normalizedRequestHeaders = useMemo16(() => normalizedHeadersFromContentKey(requestHeadersKey), [requestHeadersKey]);
10554
- 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(() => ({
10555
11211
  ...remoteSessionOptions,
10556
11212
  ...hydrateMessages ? {} : { autoStart: false },
10557
11213
  onEvent: (event) => {
@@ -10573,7 +11229,7 @@ function PiChatPanel({
10573
11229
  remoteSessionOptions: remoteSessionOptionsWithEvents,
10574
11230
  enabled: externalSessionId === void 0
10575
11231
  });
10576
- useEffect21(() => {
11232
+ useEffect23(() => {
10577
11233
  if (externalSessionId) {
10578
11234
  sessionListRefreshRef.current = void 0;
10579
11235
  return;
@@ -10627,7 +11283,7 @@ function PiChatPanel({
10627
11283
  const sessionModel = selectedChatState?.currentModel;
10628
11284
  const selectedModel = model === void 0 ? modelDiscovery.model : model;
10629
11285
  const modelOverride = model === void 0 ? modelDiscovery.isOverride : Boolean(model && sessionModel && (model.provider !== sessionModel.provider || model.id !== sessionModel.id));
10630
- const modelOptions = useMemo16(
11286
+ const modelOptions = useMemo17(
10631
11287
  () => modelOptionsForSelection(availableModels ?? modelDiscovery.availableModels, selectedModel),
10632
11288
  [availableModels, modelDiscovery.availableModels, selectedModel]
10633
11289
  );
@@ -10637,42 +11293,46 @@ function PiChatPanel({
10637
11293
  const serverModelSelectionPending = modelDiscoveryEnabled && !modelDiscovery.loaded;
10638
11294
  const serverModelSelectionUnavailable = modelDiscoveryEnabled && modelDiscovery.loaded && !selectedModelAuthorizedByDiscovery;
10639
11295
  const serverModelSelectionReady = !serverModelSelectionPending && !serverModelSelectionUnavailable;
10640
- const [storedThinkingLevel, setStoredThinkingLevel] = useState20(
11296
+ const [storedThinkingLevel, setStoredThinkingLevel] = useState22(
10641
11297
  () => thinkingControl ? readPiComposerSettings({ storageScope, storage }).thinkingLevel : DEFAULT_THINKING
10642
11298
  );
10643
- const [showThoughts, setShowThoughts] = useState20(
11299
+ const [showThoughts, setShowThoughts] = useState22(
10644
11300
  () => readPiComposerSettings({ storageScope, storage }).showThoughts
10645
11301
  );
10646
- const [composerSettingsOwner, setComposerSettingsOwner] = useState20(() => ({ storageScope, storage }));
10647
- useEffect21(() => {
11302
+ const [composerSettingsOwner, setComposerSettingsOwner] = useState22(() => ({ storageScope, storage }));
11303
+ useEffect23(() => {
10648
11304
  const settings = readPiComposerSettings({ storageScope, storage });
10649
11305
  setStoredThinkingLevel(thinkingControl ? settings.thinkingLevel : DEFAULT_THINKING);
10650
11306
  setShowThoughts(settings.showThoughts);
10651
11307
  setComposerSettingsOwner({ storageScope, storage });
10652
11308
  }, [storage, storageScope, thinkingControl]);
10653
11309
  const composerSettingsLoaded = composerSettingsOwner.storageScope === storageScope && composerSettingsOwner.storage === storage;
10654
- useEffect21(() => {
11310
+ useEffect23(() => {
10655
11311
  if (!thinkingControl) return;
10656
11312
  if (!composerSettingsLoaded) return;
10657
11313
  writePiComposerThinking(storedThinkingLevel, { storageScope, storage });
10658
11314
  }, [composerSettingsLoaded, storage, storageScope, storedThinkingLevel, thinkingControl]);
10659
- useEffect21(() => {
11315
+ useEffect23(() => {
10660
11316
  if (!composerSettingsLoaded) return;
10661
11317
  writePiComposerShowThoughts(showThoughts, { storageScope, storage });
10662
11318
  }, [composerSettingsLoaded, showThoughts, storage, storageScope]);
10663
11319
  const selectedThinking = thinkingLevel ?? storedThinkingLevel;
10664
11320
  const submitThinkingControl = thinkingControl || thinkingLevel !== void 0;
10665
- const [modelPickerOpen, setModelPickerOpen] = useState20(false);
10666
- const [thinkingPickerOpen, setThinkingPickerOpen] = useState20(false);
10667
- const [draft, setDraft] = useState20(() => initialDraft ?? "");
10668
- 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);
10669
11325
  draftRef.current = draft;
10670
- const initialDraftGuard = useRef17(new InitialDraftAutoSubmitGuard());
10671
- const pendingAutoSubmitSettleRef = useRef17(void 0);
10672
- const acceptedAutoSubmitSettleRef = useRef17(void 0);
10673
- const resetInProgressRef = useRef17(false);
10674
- const autoCreateInFlightRef = useRef17(false);
10675
- 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) => {
10676
11336
  const pendingSessionId = pendingAutoSubmitSettleRef.current;
10677
11337
  if (!pendingSessionId || sessionId2 && pendingSessionId !== sessionId2) return false;
10678
11338
  pendingAutoSubmitSettleRef.current = void 0;
@@ -10680,35 +11340,35 @@ function PiChatPanel({
10680
11340
  onAutoSubmitInitialDraftSettled?.();
10681
11341
  return true;
10682
11342
  }, [onAutoSubmitInitialDraftSettled]);
10683
- const prevStatusRef = useRef17("idle");
10684
- const statusRef = useRef17("idle");
10685
- const scrollToBottomRef = useRef17(() => {
11343
+ const prevStatusRef = useRef18("idle");
11344
+ const statusRef = useRef18("idle");
11345
+ const scrollToBottomRef = useRef18(() => {
10686
11346
  });
10687
- const textareaRef = useRef17(null);
10688
- const [localNotices, setLocalNotices] = useState20([]);
10689
- const [dismissedNoticeIds, setDismissedNoticeIds] = useState20(() => /* @__PURE__ */ new Set());
10690
- const [pluginUpdateState, setPluginUpdateState] = useState20(null);
10691
- const [commandNotifyState, setCommandNotifyState] = useState20(null);
10692
- const commandRunIdRef = useRef17(0);
10693
- const [serverSkillsRefreshKey, setServerSkillsRefreshKey] = useState20(0);
10694
- const [localSubmittedSessionId, setLocalSubmittedSessionId] = useState20();
10695
- 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);
10696
11356
  const { attachmentNotice, setAttachmentNotice } = useAttachmentNotice();
10697
11357
  const composerContributions = useComposerContributions();
10698
- const contributedCommands = useMemo16(
11358
+ const contributedCommands = useMemo17(
10699
11359
  () => composerContributions.flatMap((contribution) => contribution.commands ?? []),
10700
11360
  [composerContributions]
10701
11361
  );
10702
- const markLocalSubmitted = useCallback18((sessionId2) => {
11362
+ const markLocalSubmitted = useCallback19((sessionId2) => {
10703
11363
  localSubmittedSessionRef.current = sessionId2;
10704
11364
  setLocalSubmittedSessionId(sessionId2);
10705
11365
  }, []);
10706
- const clearLocalSubmitted = useCallback18((sessionId2) => {
11366
+ const clearLocalSubmitted = useCallback19((sessionId2) => {
10707
11367
  if (sessionId2 && localSubmittedSessionRef.current !== sessionId2) return;
10708
11368
  localSubmittedSessionRef.current = void 0;
10709
11369
  setLocalSubmittedSessionId(void 0);
10710
11370
  }, []);
10711
- const registry = useMemo16(() => {
11371
+ const registry = useMemo17(() => {
10712
11372
  const excludedBuiltins = new Set(excludeBuiltinCommands);
10713
11373
  const effectiveBuiltins = builtinCommands.filter((command) => {
10714
11374
  if (!hotReloadEnabled && command.name === "reload") return false;
@@ -10731,16 +11391,33 @@ function PiChatPanel({
10731
11391
  refreshKey: serverSkillsRefreshKey,
10732
11392
  enabled: serverResourcesEnabled
10733
11393
  });
10734
- const allCommands = useMemo16(() => registry.list(), [registry, commandsStamp]);
10735
- const actionableSlashCommands = useMemo16(
11394
+ const allCommands = useMemo17(() => registry.list(), [registry, commandsStamp]);
11395
+ const actionableSlashCommands = useMemo17(
10736
11396
  () => allCommands.flatMap((command) => command.clickBehavior === "execute" || command.clickBehavior === "insert" ? [{ name: command.name, clickBehavior: command.clickBehavior }] : []),
10737
11397
  [allCommands]
10738
11398
  );
10739
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]);
10740
11417
  const warmupNotice = composerNoticeForWarmup(workspaceWarmupStatus);
10741
11418
  const runtimeDependenciesNotice = composerNoticeForRuntimeDependencies(workspaceWarmupStatus);
10742
11419
  const workspaceWarmupBlocked = Boolean(warmupNotice);
10743
- const activeBlockers = useMemo16(
11420
+ const activeBlockers = useMemo17(
10744
11421
  // A missing active session id means a single/sessionless chat host. In that
10745
11422
  // mode, keep scoped blockers visible instead of hiding the only attention UI.
10746
11423
  // Multi-session hosts should pass a session id so unrelated blockers filter out.
@@ -10750,7 +11427,7 @@ function PiChatPanel({
10750
11427
  const canonicalMessages = selectedChatState ? selectMessagesForRender(selectedChatState) : [];
10751
11428
  const queuePreview = selectedChatState ? selectQueuePreview(selectedChatState) : [];
10752
11429
  const messages = canonicalMessages;
10753
- const userHistory = useMemo16(() => selectComposerHistoryFromCanonicalUsers(canonicalMessages), [canonicalMessages]);
11430
+ const userHistory = useMemo17(() => selectComposerHistoryFromCanonicalUsers(canonicalMessages), [canonicalMessages]);
10754
11431
  const awaitingHydration = Boolean(hydrateMessages && selectedChatState && !selectedChatState.hydrated);
10755
11432
  const emptyStateHydrating = statusForState(selectedChatState, sessionsLoading || chatStatePending || selectedSessionPending) === "hydrating" || awaitingHydration;
10756
11433
  const emptyHero = emptyPlacement === "hero" && messages.length === 0 && queuePreview.length === 0 && !emptyStateHydrating;
@@ -10759,7 +11436,7 @@ function PiChatPanel({
10759
11436
  const primaryComposerBlocker = activeBlockers[0];
10760
11437
  const composerBlockerLabel = workspaceWarmupBlocked ? warmupNotice?.title ?? "Preparing workspace..." : primaryComposerBlocker?.label ?? primaryComposerBlocker?.reason ?? "Complete the pending workspace action to continue.";
10761
11438
  const composerStatusNotice = warmupNotice ?? runtimeDependenciesNotice;
10762
- const runtimeNotices = useMemo16(() => {
11439
+ const runtimeNotices = useMemo17(() => {
10763
11440
  const fromState = selectedChatState ? selectRuntimeNotices(selectedChatState) : [];
10764
11441
  const sessionNotice = sessionsError ? [{ id: "session-navigation-error", level: "error", text: sessionsError.message, dismissible: true }] : [];
10765
11442
  const largeStateNotice = debug && debugState?.largeStateWarning ? [{
@@ -10768,20 +11445,29 @@ function PiChatPanel({
10768
11445
  text: `Large chat state: ${debugState.largeStateWarning.messageCount} messages, approximately ${debugState.largeStateWarning.approxBytes} bytes.`,
10769
11446
  dismissible: true
10770
11447
  }] : [];
10771
- 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));
10772
11449
  return filterCompetingNoiseNotices(combined, messages.length === 0);
10773
- }, [debug, debugState?.largeStateWarning, dismissedNoticeIds, localNotices, messages.length, selectedChatState, sessionsError]);
10774
- const addLocalNotice = useCallback18((notice) => {
11450
+ }, [debug, debugState?.largeStateWarning, dismissedNoticeIds, localNotices, messages.length, resumeQueuedError, selectedChatState, sessionsError]);
11451
+ const addLocalNotice = useCallback19((notice) => {
10775
11452
  setLocalNotices((previous) => {
10776
11453
  const next = previous.filter((candidate) => candidate.id !== notice.id);
10777
11454
  return [...next, notice];
10778
11455
  });
10779
11456
  }, []);
10780
- const clearLocalNotice = useCallback18((id) => {
11457
+ const clearLocalNotice = useCallback19((id) => {
10781
11458
  setDismissedNoticeIds((previous) => new Set(previous).add(id));
10782
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
+ }
10783
11469
  }, []);
10784
- const dropLocalNotice = useCallback18((id) => {
11470
+ const dropLocalNotice = useCallback19((id) => {
10785
11471
  setDismissedNoticeIds((previous) => {
10786
11472
  if (!previous.has(id)) return previous;
10787
11473
  const next = new Set(previous);
@@ -10790,13 +11476,13 @@ function PiChatPanel({
10790
11476
  });
10791
11477
  setLocalNotices((previous) => previous.filter((notice) => notice.id !== id));
10792
11478
  }, []);
10793
- const createSession = useCallback18(() => {
11479
+ const createSession = useCallback19(() => {
10794
11480
  if (externalSessionId) return;
10795
11481
  void sessions.create().catch((error) => {
10796
11482
  addLocalNotice({ id: "session-create-error", level: "error", text: errorMessage2(error, "Could not create a chat session."), dismissible: true });
10797
11483
  });
10798
11484
  }, [addLocalNotice, externalSessionId, sessions.create]);
10799
- useEffect21(() => {
11485
+ useEffect23(() => {
10800
11486
  if (externalSessionId || sessionsLoading || sessionsError || activeSessionId || sessionList.length > 0) return;
10801
11487
  if (resetInProgressRef.current) return;
10802
11488
  if (autoCreateInFlightRef.current) return;
@@ -10806,18 +11492,18 @@ function PiChatPanel({
10806
11492
  addLocalNotice({ id: "session-auto-create-error", level: "error", text: errorMessage2(error, "Could not create a chat session."), dismissible: true });
10807
11493
  });
10808
11494
  }, [activeSessionId, addLocalNotice, externalSessionId, sessionList.length, sessions.create, sessions.resumeSessionId, sessionsError, sessionsLoading]);
10809
- useEffect21(() => {
11495
+ useEffect23(() => {
10810
11496
  if (externalSessionId || sessionsError || activeSessionId || sessionList.length > 0) {
10811
11497
  autoCreateInFlightRef.current = false;
10812
11498
  }
10813
11499
  }, [activeSessionId, externalSessionId, sessionList.length, sessionsError]);
10814
- const deleteSession = useCallback18((sessionId2) => {
11500
+ const deleteSession = useCallback19((sessionId2) => {
10815
11501
  if (externalSessionId) return;
10816
11502
  void sessions.delete(sessionId2).catch((error) => {
10817
11503
  addLocalNotice({ id: `session-delete-error:${sessionId2}`, level: "error", text: errorMessage2(error, "Could not delete the chat session."), dismissible: true });
10818
11504
  });
10819
11505
  }, [addLocalNotice, externalSessionId, sessions.delete]);
10820
- const resetSession = useCallback18(() => {
11506
+ const resetSession = useCallback19(() => {
10821
11507
  const currentSessionId = activeSessionId;
10822
11508
  if (externalSessionId) {
10823
11509
  void onSessionReset?.();
@@ -10835,11 +11521,11 @@ function PiChatPanel({
10835
11521
  resetInProgressRef.current = false;
10836
11522
  });
10837
11523
  }, [activeSessionId, addLocalNotice, externalSessionId, onSessionReset, sessions.create, sessions.delete]);
10838
- const reloadAgentPlugins = useCallback18(async () => {
11524
+ const reloadAgentPlugins = useCallback19(async () => {
10839
11525
  if (!onReloadAgentPlugins) throw new Error("Agent plugin reload is not configured.");
10840
11526
  return normalizeAgentPluginReloadResult(await onReloadAgentPlugins());
10841
11527
  }, [onReloadAgentPlugins]);
10842
- const runPluginUpdate = useCallback18(async () => {
11528
+ const runPluginUpdate = useCallback19(async () => {
10843
11529
  setPluginUpdateState({ kind: "running" });
10844
11530
  try {
10845
11531
  const result = await reloadAgentPlugins();
@@ -10852,9 +11538,9 @@ function PiChatPanel({
10852
11538
  return `Extension update failed: ${message}`;
10853
11539
  }
10854
11540
  }, [reloadAgentPlugins]);
10855
- const dismissPluginUpdate = useCallback18(() => setPluginUpdateState(null), []);
10856
- const dismissCommandNotify = useCallback18(() => setCommandNotifyState(null), []);
10857
- useEffect21(() => {
11541
+ const dismissPluginUpdate = useCallback19(() => setPluginUpdateState(null), []);
11542
+ const dismissCommandNotify = useCallback19(() => setCommandNotifyState(null), []);
11543
+ useEffect23(() => {
10858
11544
  if (typeof window === "undefined") return;
10859
11545
  const onCommandNotify = (event) => {
10860
11546
  const payload = event.detail;
@@ -10871,7 +11557,7 @@ function PiChatPanel({
10871
11557
  window.addEventListener(WORKSPACE_COMMAND_NOTIFY_EVENT, onCommandNotify);
10872
11558
  return () => window.removeEventListener(WORKSPACE_COMMAND_NOTIFY_EVENT, onCommandNotify);
10873
11559
  }, []);
10874
- useEffect21(() => {
11560
+ useEffect23(() => {
10875
11561
  if (!hotReloadEnabled || typeof window === "undefined") return;
10876
11562
  const onBrowserPluginReload = (event) => {
10877
11563
  const parsed = parseBrowserPluginReloadDetail(event.detail);
@@ -10909,7 +11595,7 @@ function PiChatPanel({
10909
11595
  textareaRef,
10910
11596
  disabled: mentionState !== null || slashQuery !== null || modelPickerOpen || thinkingPickerOpen
10911
11597
  });
10912
- const setComposerDraft = useCallback18((next, focus = true) => {
11598
+ const setComposerDraft = useCallback19((next, focus = true) => {
10913
11599
  draftRef.current = next;
10914
11600
  setDraft(next);
10915
11601
  if (textareaRef.current) {
@@ -10917,11 +11603,11 @@ function PiChatPanel({
10917
11603
  if (focus) textareaRef.current.focus();
10918
11604
  }
10919
11605
  }, []);
10920
- const warnComposer = useCallback18((message) => {
11606
+ const warnComposer = useCallback19((message) => {
10921
11607
  onComposerWarning?.(message);
10922
11608
  addLocalNotice({ id: `composer-warning:${Date.now()}`, level: "warning", text: message, dismissible: true });
10923
11609
  }, [addLocalNotice, onComposerWarning]);
10924
- const openModelPicker = useCallback18(() => {
11610
+ const openModelPicker = useCallback19(() => {
10925
11611
  if (isPiBusyStatus(statusRef.current)) {
10926
11612
  warnComposer("Model picker is unavailable while the agent is running.");
10927
11613
  return false;
@@ -10934,7 +11620,7 @@ function PiChatPanel({
10934
11620
  setModelPickerOpen(true);
10935
11621
  return true;
10936
11622
  }, [model, warnComposer]);
10937
- const openThinkingPicker = useCallback18(() => {
11623
+ const openThinkingPicker = useCallback19(() => {
10938
11624
  if (isPiBusyStatus(statusRef.current)) {
10939
11625
  warnComposer("Thinking picker is unavailable while the agent is running.");
10940
11626
  return false;
@@ -10947,21 +11633,21 @@ function PiChatPanel({
10947
11633
  setThinkingPickerOpen(true);
10948
11634
  return true;
10949
11635
  }, [thinkingControl, thinkingLevel, warnComposer]);
10950
- const selectComposerModel = useCallback18((query) => {
11636
+ const selectComposerModel = useCallback19((query) => {
10951
11637
  if (model !== void 0) return "Model selection is controlled by the host.";
10952
11638
  const match = resolveModelSlashSelection(query, modelOptions);
10953
11639
  if (!match) return `No model matched "${query}".`;
10954
11640
  modelDiscovery.setModel(match);
10955
11641
  return `Model set to ${match.label ?? match.id}.`;
10956
11642
  }, [model, modelDiscovery, modelOptions]);
10957
- const selectComposerThinking = useCallback18((query) => {
11643
+ const selectComposerThinking = useCallback19((query) => {
10958
11644
  if (!thinkingControl || thinkingLevel !== void 0) return "Thinking level is controlled by the host.";
10959
11645
  const match = resolveThinkingSlashSelection(query);
10960
11646
  if (!match) return `No thinking level matched "${query}".`;
10961
11647
  setStoredThinkingLevel(match);
10962
11648
  return `Thinking set to ${thinkingLabel(match)}.`;
10963
11649
  }, [thinkingControl, thinkingLevel]);
10964
- const selectSlashCommand = useCallback18((name) => {
11650
+ const selectSlashCommand = useCallback19((name) => {
10965
11651
  if (name === "model") {
10966
11652
  dismissSlash();
10967
11653
  if (openModelPicker()) setComposerDraft("");
@@ -10974,7 +11660,7 @@ function PiChatPanel({
10974
11660
  }
10975
11661
  insertSlashCommand(name);
10976
11662
  }, [dismissSlash, insertSlashCommand, openModelPicker, openThinkingPicker, setComposerDraft]);
10977
- const policy = useMemo16(() => {
11663
+ const policy = useMemo17(() => {
10978
11664
  if (!selectedPiSession || !activeChatSessionId || !serverModelSelectionReady) return void 0;
10979
11665
  const policySession = {
10980
11666
  getState: () => {
@@ -11019,13 +11705,29 @@ function PiChatPanel({
11019
11705
  getDraft: () => draftRef.current,
11020
11706
  onDraftChange: setComposerDraft,
11021
11707
  allowPromptDuringInitialHydration,
11708
+ isActiveSession: isPolicySessionActive,
11709
+ submissionCoordinator: submissionCoordinatorRef.current,
11710
+ submissionIdentity: activeChatIdentity,
11711
+ onQueueMutationPending: setQueueMutationPending,
11022
11712
  onPromptSubmitStarted: () => {
11023
11713
  markLocalSubmitted(activeChatSessionId);
11024
11714
  },
11025
- onBeforeSubmit: onBeforeSubmit ? async (draft2, context) => {
11026
- const result = await onBeforeSubmit(draft2, { ...context, sessionId: activeChatSessionId, source: context.source ?? "composer" });
11027
- return result !== false;
11028
- } : 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
+ }),
11029
11731
  onCommandResult: (message) => {
11030
11732
  onCommandResult?.(message);
11031
11733
  addLocalNotice({ id: `command:${Date.now()}`, level: "info", text: message, dismissible: true });
@@ -11040,8 +11742,17 @@ function PiChatPanel({
11040
11742
  onMentionedFilesConsumed?.();
11041
11743
  }
11042
11744
  });
11043
- }, [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]);
11044
- 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) => {
11045
11756
  const errorCode = piChatErrorCode(error);
11046
11757
  dropLocalNotice(RUN_REJECTED_NOTICE_ID);
11047
11758
  addLocalNotice({
@@ -11052,7 +11763,7 @@ function PiChatPanel({
11052
11763
  ...errorCode ? { errorCode } : {}
11053
11764
  });
11054
11765
  }, [addLocalNotice, dropLocalNotice]);
11055
- const sendComposerMessage = useCallback18(async ({ text, files, source = "composer", preserveExistingDraft = false }) => {
11766
+ const sendComposerMessage = useCallback19(async ({ text, files, source = "composer", preserveExistingDraft = false }) => {
11056
11767
  if (!policy) {
11057
11768
  addLocalNotice({ id: "composer-no-session", level: "warning", text: "Create or select a chat session before sending.", dismissible: true });
11058
11769
  return false;
@@ -11072,6 +11783,7 @@ function PiChatPanel({
11072
11783
  if (result.type === "prompt" || result.type === "followup") {
11073
11784
  dropLocalNotice(RUN_REJECTED_NOTICE_ID);
11074
11785
  }
11786
+ if (result.type === "handled") surfaceHandledSubmit(result.message);
11075
11787
  if (result.type === "prompt" && activeChatSessionId) {
11076
11788
  onPromptSubmitStarted?.({ sessionId: activeChatSessionId, clientNonce: result.clientNonce });
11077
11789
  if (shouldHoldLocalSubmitted(selectedPiSession, result.cursor)) markLocalSubmitted(activeChatSessionId);
@@ -11079,17 +11791,18 @@ function PiChatPanel({
11079
11791
  }
11080
11792
  return void 0;
11081
11793
  } catch (error) {
11794
+ if (!isPolicySessionActive()) return void 0;
11082
11795
  clearLocalSubmitted(activeChatSessionId);
11083
11796
  restoreSubmittedDraft();
11084
11797
  surfaceRunRejected(error);
11085
11798
  return false;
11086
11799
  }
11087
- }, [activeChatSessionId, clearLocalSubmitted, dropLocalNotice, markLocalSubmitted, onPromptSubmitStarted, policy, selectedPiSession, setComposerDraft, surfaceRunRejected]);
11088
- const availableAssistantSlashCommands = useMemo16(
11800
+ }, [activeChatSessionId, clearLocalSubmitted, dropLocalNotice, isPolicySessionActive, markLocalSubmitted, onPromptSubmitStarted, policy, selectedPiSession, setComposerDraft, surfaceHandledSubmit, surfaceRunRejected]);
11801
+ const availableAssistantSlashCommands = useMemo17(
11089
11802
  () => policy ? actionableSlashCommands : actionableSlashCommands.filter((command) => command.clickBehavior === "insert"),
11090
11803
  [actionableSlashCommands, policy]
11091
11804
  );
11092
- const assistantMentionCatalog = useMemo16(() => {
11805
+ const assistantMentionCatalog = useMemo17(() => {
11093
11806
  const skills = /* @__PURE__ */ new Map();
11094
11807
  for (const command of allCommands) {
11095
11808
  if (command.source !== "skill" && command.kind !== "skill") continue;
@@ -11105,12 +11818,12 @@ function PiChatPanel({
11105
11818
  files: true
11106
11819
  };
11107
11820
  }, [allCommands, availableAssistantSlashCommands]);
11108
- const insertMentionDraft = useCallback18((invocation) => {
11821
+ const insertMentionDraft = useCallback19((invocation) => {
11109
11822
  const currentDraft = draftRef.current;
11110
11823
  const separator = currentDraft && !/\s$/.test(invocation) ? "\n" : "";
11111
11824
  setComposerDraft(`${invocation}${separator}${currentDraft}`, true);
11112
11825
  }, [setComposerDraft]);
11113
- const activateAssistantMention = useCallback18((mention) => {
11826
+ const activateAssistantMention = useCallback19((mention) => {
11114
11827
  const command = registry.get(mention.kind === "skill" ? mention.commandName : mention.name);
11115
11828
  if (mention.kind === "skill") {
11116
11829
  if (command?.source !== "skill" && command?.kind !== "skill") return;
@@ -11132,7 +11845,7 @@ function PiChatPanel({
11132
11845
  preserveExistingDraft: true
11133
11846
  });
11134
11847
  }, [insertMentionDraft, policy, registry, sendComposerMessage]);
11135
- const editQueued = useCallback18(() => {
11848
+ const editQueued = useCallback19(() => {
11136
11849
  if (!policy) return;
11137
11850
  void policy.editQueued().then((result) => {
11138
11851
  if (result.type === "clear-failed") {
@@ -11140,25 +11853,65 @@ function PiChatPanel({
11140
11853
  }
11141
11854
  });
11142
11855
  }, [addLocalNotice, policy]);
11143
- 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(() => {
11144
11865
  onComposerStop?.();
11145
11866
  clearLocalSubmitted(activeChatSessionId);
11146
- void policy?.stop().catch((error) => {
11867
+ void policy?.interrupt({ queueAction: "hold" }).catch((error) => {
11147
11868
  addLocalNotice({ id: "stop-error", level: "error", text: errorMessage2(error, "Could not stop the chat session."), dismissible: true });
11148
11869
  });
11149
11870
  }, [activeChatSessionId, addLocalNotice, clearLocalSubmitted, onComposerStop, policy]);
11150
- const interrupt = useCallback18(() => {
11871
+ const interrupt = useCallback19(() => {
11151
11872
  void policy?.interrupt().catch((error) => {
11152
11873
  addLocalNotice({ id: "interrupt-error", level: "error", text: errorMessage2(error, "Could not interrupt the chat session."), dismissible: true });
11153
11874
  });
11154
11875
  }, [addLocalNotice, policy]);
11155
- 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(() => {
11156
11909
  setPluginUpdateState(null);
11157
11910
  setCommandNotifyState(null);
11158
11911
  setLocalNotices([]);
11159
11912
  setDismissedNoticeIds(/* @__PURE__ */ new Set());
11160
11913
  }, [activeSessionId]);
11161
- useEffect21(() => {
11914
+ useEffect23(() => {
11162
11915
  const currentSessionId = activeSessionId ?? "__none__";
11163
11916
  if (initialDraftGuard.current.shouldRestore(currentSessionId, initialDraft) && initialDraft !== void 0) {
11164
11917
  setComposerDraft(initialDraft, initialDraft.length > 0);
@@ -11167,7 +11920,7 @@ function PiChatPanel({
11167
11920
  }, [activeSessionId, initialDraft, onDraftRestored, setComposerDraft]);
11168
11921
  const remoteStatus = selectedChatState?.status ?? (sessionsLoading || chatStatePending || selectedSessionPending ? "hydrating" : "idle");
11169
11922
  const status = localSubmittedSessionId === activeSessionId && remoteStatus === "idle" ? "submitted" : remoteStatus;
11170
- useEffect21(() => {
11923
+ useEffect23(() => {
11171
11924
  if (!localSubmittedSessionId) return;
11172
11925
  if (localSubmittedSessionId !== activeSessionId) {
11173
11926
  clearLocalSubmitted();
@@ -11175,7 +11928,7 @@ function PiChatPanel({
11175
11928
  }
11176
11929
  if (remoteStatus !== "idle") clearLocalSubmitted(localSubmittedSessionId);
11177
11930
  }, [activeSessionId, clearLocalSubmitted, localSubmittedSessionId, remoteStatus]);
11178
- useEffect21(() => {
11931
+ useEffect23(() => {
11179
11932
  const previous = prevStatusRef.current;
11180
11933
  statusRef.current = status;
11181
11934
  prevStatusRef.current = status;
@@ -11185,7 +11938,7 @@ function PiChatPanel({
11185
11938
  if (!isPiBusyStatus(previous) && previous !== "submitted") return;
11186
11939
  settlePendingAutoSubmit();
11187
11940
  }, [settlePendingAutoSubmit, status]);
11188
- useEffect21(() => {
11941
+ useEffect23(() => {
11189
11942
  if (!autoSubmitInitialDraft || !policy || !activeSessionId || composerBlocked) return;
11190
11943
  if (!initialDraftGuard.current.claimAutoSubmit(activeSessionId, initialDraft)) return;
11191
11944
  pendingAutoSubmitSettleRef.current = activeSessionId;
@@ -11204,9 +11957,14 @@ function PiChatPanel({
11204
11957
  }
11205
11958
  return;
11206
11959
  }
11960
+ if (result.type === "stale") {
11961
+ settlePendingAutoSubmit(activeSessionId);
11962
+ return;
11963
+ }
11207
11964
  if (result.type === "prompt" || result.type === "followup") {
11208
11965
  dropLocalNotice(RUN_REJECTED_NOTICE_ID);
11209
11966
  }
11967
+ if (result.type === "handled") surfaceHandledSubmit(result.message);
11210
11968
  if (result.type === "prompt") {
11211
11969
  onPromptSubmitStarted?.({ sessionId: activeSessionId, clientNonce: result.clientNonce });
11212
11970
  if (shouldHoldLocalSubmitted(selectedPiSession, result.cursor)) markLocalSubmitted(activeSessionId);
@@ -11218,13 +11976,14 @@ function PiChatPanel({
11218
11976
  settlePendingAutoSubmit(activeSessionId);
11219
11977
  }
11220
11978
  }).catch((error) => {
11979
+ if (!isPolicySessionActive()) return;
11221
11980
  clearLocalSubmitted(activeSessionId);
11222
11981
  restoreSubmittedDraft();
11223
11982
  settlePendingAutoSubmit(activeSessionId);
11224
11983
  surfaceRunRejected(error);
11225
11984
  });
11226
- }, [activeSessionId, autoSubmitInitialDraft, clearLocalSubmitted, composerBlocked, dropLocalNotice, initialDraft, markLocalSubmitted, onAutoSubmitInitialDraftAccepted, onPromptSubmitStarted, policy, selectedPiSession, setComposerDraft, settlePendingAutoSubmit, surfaceRunRejected]);
11227
- useEffect21(() => {
11985
+ }, [activeSessionId, autoSubmitInitialDraft, clearLocalSubmitted, composerBlocked, dropLocalNotice, initialDraft, isPolicySessionActive, markLocalSubmitted, onAutoSubmitInitialDraftAccepted, onPromptSubmitStarted, policy, selectedPiSession, setComposerDraft, settlePendingAutoSubmit, surfaceHandledSubmit, surfaceRunRejected]);
11986
+ useEffect23(() => {
11228
11987
  if (workspaceWarmupStatus?.status === "ready") {
11229
11988
  clearLocalNotice("workspace-warmup");
11230
11989
  }
@@ -11238,20 +11997,20 @@ function PiChatPanel({
11238
11997
  const isStreaming = isPiBusyStatus(status);
11239
11998
  const submitStatus = initialHydrationPromptAllowed ? "ready" : toPromptSubmitStatus(status);
11240
11999
  const submitDisabled = !policy || sessionsLoading || modelSelectionBlocked || composerBlocked && !isStreaming;
11241
- const mergedToolRenderers = useMemo16(() => mergeShadcnToolRenderers(toolRenderers), [toolRenderers]);
11242
- const debugMessages = useMemo16(() => messages.map(toDebugUiMessage), [messages]);
11243
- const onTextareaChange = useCallback18((event) => {
12000
+ const mergedToolRenderers = useMemo17(() => mergeShadcnToolRenderers(toolRenderers), [toolRenderers]);
12001
+ const debugMessages = useMemo17(() => messages.map(toDebugUiMessage), [messages]);
12002
+ const onTextareaChange = useCallback19((event) => {
11244
12003
  setModelPickerOpen(false);
11245
12004
  setThinkingPickerOpen(false);
11246
12005
  setDraft(event.currentTarget.value);
11247
12006
  handleComposerChange(event);
11248
12007
  }, [handleComposerChange]);
11249
- useEffect21(() => {
12008
+ useEffect23(() => {
11250
12009
  if (!isStreaming) return;
11251
12010
  setModelPickerOpen(false);
11252
12011
  setThinkingPickerOpen(false);
11253
12012
  }, [isStreaming]);
11254
- useEffect21(() => {
12013
+ useEffect23(() => {
11255
12014
  if (typeof window === "undefined" || !activeChatSessionId) return;
11256
12015
  const emitStatus = () => window.dispatchEvent(new CustomEvent("boring:chat-session-status", {
11257
12016
  detail: {
@@ -11265,7 +12024,7 @@ function PiChatPanel({
11265
12024
  window.addEventListener("boring:chat-session-status-request", emitStatus);
11266
12025
  return () => window.removeEventListener("boring:chat-session-status-request", emitStatus);
11267
12026
  }, [activeChatSessionId, agentTypeId, isStreaming, workspaceId]);
11268
- const onTextareaKeyDown = useCallback18((event) => {
12027
+ const onTextareaKeyDown = useCallback19((event) => {
11269
12028
  if (event.key === "Escape" && isStreaming) {
11270
12029
  if (event.defaultPrevented || mentionState !== null || slashQuery !== null) {
11271
12030
  handleComposerKeyDown(event);
@@ -11277,14 +12036,14 @@ function PiChatPanel({
11277
12036
  }
11278
12037
  handleComposerKeyDown(event);
11279
12038
  }, [handleComposerKeyDown, interrupt, isStreaming, mentionState, slashQuery]);
11280
- const selectEmptySuggestion = useCallback18((suggestion) => {
12039
+ const selectEmptySuggestion = useCallback19((suggestion) => {
11281
12040
  const text = suggestion.prompt ?? suggestion.label;
11282
12041
  if (!text.trim()) return;
11283
12042
  void sendComposerMessage({ text, files: [], source: "suggestion" }).then((result) => {
11284
12043
  if (result === false) setComposerDraft(text);
11285
12044
  });
11286
12045
  }, [sendComposerMessage, setComposerDraft]);
11287
- const composerSurface = /* @__PURE__ */ jsx34(
12046
+ const composerSurface = /* @__PURE__ */ jsx35(
11288
12047
  PiChatComposerSurface,
11289
12048
  {
11290
12049
  chrome,
@@ -11303,6 +12062,10 @@ function PiChatPanel({
11303
12062
  onComposerBlockerAction,
11304
12063
  queuePreview,
11305
12064
  onEditQueued: editQueued,
12065
+ onRemoveQueued: removeQueued,
12066
+ queueMutationPending,
12067
+ onResumeQueued: resumeQueued,
12068
+ resumeQueuedPending,
11306
12069
  hotReloadEnabled,
11307
12070
  pluginUpdateState,
11308
12071
  onDismissPluginUpdate: dismissPluginUpdate,
@@ -11351,7 +12114,7 @@ function PiChatPanel({
11351
12114
  onStop: stop
11352
12115
  }
11353
12116
  );
11354
- return /* @__PURE__ */ jsx34(ArtifactOpenProvider, { onOpenArtifact, children: /* @__PURE__ */ jsxs30(
12117
+ return /* @__PURE__ */ jsx35(ArtifactOpenProvider, { onOpenArtifact, children: /* @__PURE__ */ jsxs31(
11355
12118
  "div",
11356
12119
  {
11357
12120
  "data-boring-agent": "",
@@ -11369,7 +12132,7 @@ function PiChatPanel({
11369
12132
  role: "region",
11370
12133
  "aria-label": "Agent assistant",
11371
12134
  children: [
11372
- 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(
11373
12136
  SessionList,
11374
12137
  {
11375
12138
  sessions: sessionList,
@@ -11383,7 +12146,7 @@ function PiChatPanel({
11383
12146
  loadingMore: sessions.loadingMore
11384
12147
  }
11385
12148
  ) }) : null,
11386
- /* @__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(
11387
12150
  "div",
11388
12151
  {
11389
12152
  className: cn(
@@ -11392,7 +12155,7 @@ function PiChatPanel({
11392
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)]"
11393
12156
  ),
11394
12157
  children: [
11395
- /* @__PURE__ */ jsx34(
12158
+ /* @__PURE__ */ jsx35(
11396
12159
  PiConversationSurface,
11397
12160
  {
11398
12161
  chrome,
@@ -11418,13 +12181,13 @@ function PiChatPanel({
11418
12181
  }
11419
12182
  ),
11420
12183
  composerSurface,
11421
- emptyHero ? /* @__PURE__ */ jsxs30(
12184
+ emptyHero ? /* @__PURE__ */ jsxs31(
11422
12185
  "div",
11423
12186
  {
11424
12187
  "data-boring-agent-part": "empty-hero-suggestions",
11425
12188
  className: "@container mx-auto w-full max-w-[640px] px-2 pb-4",
11426
12189
  children: [
11427
- /* @__PURE__ */ jsx34(ChatSuggestionGrid, { suggestions, onSelect: selectEmptySuggestion, className: "mt-4" }),
12190
+ /* @__PURE__ */ jsx35(ChatSuggestionGrid, { suggestions, onSelect: selectEmptySuggestion, className: "mt-4" }),
11428
12191
  emptyState?.footer
11429
12192
  ]
11430
12193
  }
@@ -11432,7 +12195,7 @@ function PiChatPanel({
11432
12195
  ]
11433
12196
  }
11434
12197
  ) }),
11435
- 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(
11436
12199
  DebugDrawer2,
11437
12200
  {
11438
12201
  agentTypeId,
@@ -11453,17 +12216,18 @@ function PiChatPanel({
11453
12216
  }
11454
12217
 
11455
12218
  // src/front/chat/useAddressedAgentSelection.ts
11456
- 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";
11457
12220
  var inFlightDiscoveries = /* @__PURE__ */ new WeakMap();
11458
12221
  function useAddressedAgentSelection({
11459
12222
  apiBaseUrl,
11460
12223
  requestHeaders,
11461
12224
  storageScope,
12225
+ preferredAgentTypeId,
11462
12226
  fetch: fetch2,
11463
12227
  enabled = false
11464
12228
  } = {}) {
11465
12229
  const normalizedBaseUrl = apiBaseUrl?.replace(/\/$/, "") ?? "";
11466
- const headersKey = useMemo17(
12230
+ const headersKey = useMemo18(
11467
12231
  () => JSON.stringify({
11468
12232
  storageScope: storageScope ?? "",
11469
12233
  headers: Object.entries(requestHeaders ?? {}).sort(([a], [b]) => a.localeCompare(b))
@@ -11473,14 +12237,14 @@ function useAddressedAgentSelection({
11473
12237
  const discoveryKey = `${normalizedBaseUrl}
11474
12238
  ${headersKey}`;
11475
12239
  const fetchImpl = fetch2 ?? globalThis.fetch;
11476
- const [state, setState] = useState21(() => ({
12240
+ const [state, setState] = useState23(() => ({
11477
12241
  discoveryKey: enabled ? "" : discoveryKey,
11478
12242
  agents: [],
11479
12243
  selectedAgentTypeId: void 0,
11480
12244
  loading: enabled,
11481
12245
  error: void 0
11482
12246
  }));
11483
- useEffect22(() => {
12247
+ useEffect24(() => {
11484
12248
  if (!enabled) {
11485
12249
  setState({
11486
12250
  discoveryKey,
@@ -11506,7 +12270,7 @@ ${headersKey}`;
11506
12270
  setState((previous) => ({
11507
12271
  discoveryKey,
11508
12272
  agents: agents2,
11509
- 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,
11510
12274
  loading: false,
11511
12275
  error: void 0
11512
12276
  }));
@@ -11523,11 +12287,11 @@ ${headersKey}`;
11523
12287
  return () => {
11524
12288
  cancelled = true;
11525
12289
  };
11526
- }, [discoveryKey, enabled, fetchImpl, normalizedBaseUrl]);
12290
+ }, [discoveryKey, enabled, fetchImpl, normalizedBaseUrl, preferredAgentTypeId]);
11527
12291
  const current = state.discoveryKey === discoveryKey;
11528
12292
  const agents = enabled && current ? state.agents : [];
11529
12293
  const selectedAgentTypeId = enabled && current ? state.selectedAgentTypeId : void 0;
11530
- const selectAgentTypeId = useCallback19((agentTypeId) => {
12294
+ const selectAgentTypeId = useCallback20((agentTypeId) => {
11531
12295
  setState((previous) => {
11532
12296
  if (previous.discoveryKey !== discoveryKey) return previous;
11533
12297
  if (!previous.agents.some((agent) => agent.agentTypeId === agentTypeId)) return previous;