@alivault/pico 0.13.0 → 0.14.0

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 (32) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/{index-CW1T9dk-.js → index-DBVxXDtw.js} +4 -4
  3. package/.output/public/assets/routes-yUxD2q-l.js +2194 -0
  4. package/.output/public/assets/styles-CUVz7jV9.css +2 -0
  5. package/.output/public/screenshots/pico-ios-dark.png +0 -0
  6. package/.output/public/screenshots/pico-ios-light.png +0 -0
  7. package/.output/public/screenshots/pico-macos-dark.jpg +0 -0
  8. package/.output/public/screenshots/pico-macos-light.jpg +0 -0
  9. package/.output/server/_chunks/ssr-renderer.mjs +3 -3
  10. package/.output/server/_libs/crossws.mjs +2929 -0
  11. package/.output/server/_libs/h3+rou3+srvx.mjs +7 -1
  12. package/.output/server/_libs/unctx.mjs +1 -1
  13. package/.output/server/_routes/api/terminal/[id]/ws.mjs +8874 -0
  14. package/.output/server/_ssr/{router-l__gf5Y7.mjs → router-CN4JMm2A.mjs} +1074 -316
  15. package/.output/server/_ssr/{routes-DySm_W8N.mjs → routes-BsQ26pbY.mjs} +738 -189
  16. package/.output/server/_ssr/{routes-CuWg5WTI.mjs → routes-DbjuMLFI.mjs} +19 -10
  17. package/.output/server/_ssr/ssr.mjs +3 -3
  18. package/.output/server/_ssr/start-jIczsXlR.mjs +4 -0
  19. package/.output/server/{_tanstack-start-manifest_v-CVoKKKc_.mjs → _tanstack-start-manifest_v-Bs4g_j1_.mjs} +7 -4
  20. package/.output/server/index.mjs +621 -542
  21. package/.output/server/node_modules/node-pty/build/Makefile +2 -2
  22. package/.output/server/node_modules/node-pty/build/pty.target.mk +14 -14
  23. package/README.md +68 -6
  24. package/bin/pico.mjs +103 -1
  25. package/package.json +4 -4
  26. package/public/screenshots/pico-ios-dark.png +0 -0
  27. package/public/screenshots/pico-ios-light.png +0 -0
  28. package/public/screenshots/pico-macos-dark.jpg +0 -0
  29. package/public/screenshots/pico-macos-light.jpg +0 -0
  30. package/.output/public/assets/routes-2LboeUx4.js +0 -2194
  31. package/.output/public/assets/styles-B1ITYh-r.css +0 -2
  32. package/.output/server/_ssr/start-Captdpmb.mjs +0 -4
@@ -2,7 +2,7 @@ import { i as __toESM } from "../_runtime.mjs";
2
2
  import { $ as require_react, Q as require_jsx_runtime } from "../_libs/@base-ui/react+[...].mjs";
3
3
  import { a as createRouter, c as createRootRoute, n as Scripts, r as HeadContent, s as createFileRoute } from "../_libs/@tanstack/react-router+[...].mjs";
4
4
  import { T as Info, a as TriangleAlert, y as OctagonX, z as CircleCheck } from "../_libs/lucide-react.mjs";
5
- import { B as buildItemsFromSync, H as createCompactionSummaryItem, J as extractToolText, K as extractMessageImages, Lt as toolCategoryFromTool, P as TooltipProvider, R as appliedThemeClassColorMode, T as Spinner, Z as mergeAssistantBlocksForStreaming, f as PICO_SHIKI_VARIABLE_DEFAULTS, n as APPLIED_THEME_STORAGE_KEY, q as extractMessageText$1, t as APPLIED_THEME_CLASSES, u as PICO_CODE_SHIKI_THEME, x as Route$59, z as assistantBlocksFromMessage } from "./routes-CuWg5WTI.mjs";
5
+ import { B as buildItemsFromSync, H as createCompactionSummaryItem, J as extractToolText, K as extractMessageImages, Lt as toolCategoryFromTool, P as TooltipProvider, R as appliedThemeClassColorMode, T as Spinner, Z as mergeAssistantBlocksForStreaming, f as PICO_SHIKI_VARIABLE_DEFAULTS, n as APPLIED_THEME_STORAGE_KEY, q as extractMessageText$1, t as APPLIED_THEME_CLASSES, u as PICO_CODE_SHIKI_THEME, x as Route$62, z as assistantBlocksFromMessage } from "./routes-DbjuMLFI.mjs";
6
6
  import { t as QueryClient } from "../_libs/tanstack__query-core.mjs";
7
7
  import { t as setupRouterSsrQueryIntegration } from "../_libs/@tanstack/react-router-ssr-query+[...].mjs";
8
8
  import { n as HotkeysProvider } from "../_libs/tanstack__react-hotkeys.mjs";
@@ -21,7 +21,7 @@ import os, { homedir } from "node:os";
21
21
  import { performance } from "node:perf_hooks";
22
22
  import { isMainThread } from "node:worker_threads";
23
23
  import { execSync, spawn } from "node:child_process";
24
- //#region node_modules/.nitro/vite/services/ssr/assets/router-l__gf5Y7.js
24
+ //#region node_modules/.nitro/vite/services/ssr/assets/router-CN4JMm2A.js
25
25
  var import_react = /* @__PURE__ */ __toESM(require_react());
26
26
  var import_jsx_runtime = require_jsx_runtime();
27
27
  function sonnerThemeFromAppliedTheme(theme) {
@@ -88,7 +88,7 @@ function AppProviders({ children }) {
88
88
  })
89
89
  });
90
90
  }
91
- var styles_default = "/assets/styles-B1ITYh-r.css";
91
+ var styles_default = "/assets/styles-CUVz7jV9.css";
92
92
  var TANSTACK_DEVTOOLS_SETTINGS_KEY = "tanstack_devtools_settings";
93
93
  var TANSTACK_DEVTOOLS_DEFAULTS_STORAGE_KEY = "pico-tanstack-devtools-defaults-v2";
94
94
  var TANSTACK_DEVTOOLS_CONFIG = {
@@ -100,7 +100,7 @@ var TANSTACK_DEVTOOLS_CONFIG = {
100
100
  ],
101
101
  triggerHidden: true
102
102
  };
103
- var Route$58 = createRootRoute({
103
+ var Route$61 = createRootRoute({
104
104
  head: () => ({
105
105
  meta: [
106
106
  { charSet: "utf-8" },
@@ -269,15 +269,18 @@ async function loadPiSdk() {
269
269
  const sdkDir = resolvePiSdkDir();
270
270
  return patchSdkForWorkerThreads(await importExternalModule(path.join(sdkDir, "dist", "index.js")));
271
271
  }
272
+ function addPiAiCandidateEntries(candidates, packageDir) {
273
+ candidates.push(path.join(packageDir, "dist", "compat.js"), path.join(packageDir, "dist", "index.js"));
274
+ }
272
275
  function getPiAiCandidates(sdkDir) {
273
276
  const candidates = [];
274
- for (const scope of ["@earendil-works", "@mariozechner"]) candidates.push(path.join(sdkDir, "node_modules", scope, "pi-ai", "dist", "index.js"));
277
+ for (const scope of ["@earendil-works", "@mariozechner"]) addPiAiCandidateEntries(candidates, path.join(sdkDir, "node_modules", scope, "pi-ai"));
275
278
  try {
276
279
  const realSdkDir = fs.realpathSync(sdkDir);
277
- for (const scope of ["@earendil-works", "@mariozechner"]) candidates.push(path.join(realSdkDir, "node_modules", scope, "pi-ai", "dist", "index.js"));
278
- candidates.push(path.join(path.dirname(realSdkDir), "pi-ai", "dist", "index.js"));
280
+ for (const scope of ["@earendil-works", "@mariozechner"]) addPiAiCandidateEntries(candidates, path.join(realSdkDir, "node_modules", scope, "pi-ai"));
281
+ addPiAiCandidateEntries(candidates, path.join(path.dirname(realSdkDir), "pi-ai"));
279
282
  } catch {}
280
- candidates.push(path.join(path.dirname(sdkDir), "pi-ai", "dist", "index.js"));
283
+ addPiAiCandidateEntries(candidates, path.join(path.dirname(sdkDir), "pi-ai"));
281
284
  return candidates;
282
285
  }
283
286
  function resolvePiAiEntry(sdkDir) {
@@ -461,11 +464,62 @@ function buildSessionNamePrompt(prompt, cwdBasename, imageCount) {
461
464
  normalizedPrompt
462
465
  ].filter(Boolean).join("\n");
463
466
  }
467
+ function modelLabel(model) {
468
+ return `${model.provider}/${model.id}`;
469
+ }
470
+ function formatSessionNamingError(error) {
471
+ if (error instanceof Error) return error.message;
472
+ if (typeof error === "string") return error;
473
+ return "Unknown error";
474
+ }
475
+ function modelSupportsTextInput(model) {
476
+ if (!Array.isArray(model.input)) return true;
477
+ return model.input.includes("text");
478
+ }
479
+ function collectSessionNamingModelCandidates(entry) {
480
+ const candidates = [];
481
+ const seen = /* @__PURE__ */ new Set();
482
+ const addCandidate = (model, source) => {
483
+ if (!model || !modelSupportsTextInput(model)) return;
484
+ const key = modelLabel(model);
485
+ if (seen.has(key)) return;
486
+ seen.add(key);
487
+ candidates.push({
488
+ model,
489
+ source
490
+ });
491
+ };
492
+ addCandidate(entry.model, "active");
493
+ for (const model of entry.services.modelRegistry.getAvailable()) addCandidate(model, "available");
494
+ return candidates;
495
+ }
496
+ async function selectSessionNamingModel(entry) {
497
+ const candidates = collectSessionNamingModelCandidates(entry);
498
+ if (candidates.length === 0) return {
499
+ ok: false,
500
+ reason: "no text-capable models are available for session naming"
501
+ };
502
+ const failures = [];
503
+ for (const candidate of candidates) try {
504
+ const auth = await entry.services.modelRegistry.getApiKeyAndHeaders(candidate.model);
505
+ if (auth?.ok) return {
506
+ ok: true,
507
+ ...candidate,
508
+ auth
509
+ };
510
+ failures.push(`${modelLabel(candidate.model)}: ${auth?.error || "not authenticated"}`);
511
+ } catch (error) {
512
+ failures.push(`${modelLabel(candidate.model)}: ${formatSessionNamingError(error)}`);
513
+ }
514
+ return {
515
+ ok: false,
516
+ reason: `no authenticated text model is available for session naming${failures.length > 0 ? ` (${failures.slice(0, 3).join("; ")})` : ""}`
517
+ };
518
+ }
464
519
  async function generateSessionNameWithLlm(entry, text, imageCount) {
465
- const model = entry.services.modelRegistry.find("openai-codex", "gpt-5.4-mini");
466
- if (!model) return { reason: "refinement model openai-codex/gpt-5.4-mini is unavailable" };
467
- const auth = await entry.services.modelRegistry.getApiKeyAndHeaders(model);
468
- if (!auth?.ok) return { reason: auth?.error || `failed to authenticate ${model.provider}/${model.id} for session naming` };
520
+ const selectedModel = await selectSessionNamingModel(entry);
521
+ if (!selectedModel.ok) return { reason: selectedModel.reason };
522
+ const { model, auth } = selectedModel;
469
523
  const response = await (await loadPiAi()).complete(model, {
470
524
  systemPrompt: SESSION_NAME_SYSTEM_PROMPT,
471
525
  messages: [{
@@ -493,8 +547,12 @@ function normalizeSessionScope(rawScope, defaultCwd) {
493
547
  function resolveScopeCwd(scope, defaultCwd) {
494
548
  return normalizeSessionScope(scope ?? null, defaultCwd);
495
549
  }
496
- function getSsePayloadText(payload) {
497
- return `${JSON.stringify(payload).split(/\r?\n/).map((line) => `data: ${line}`).join("\n")}\n\n`;
550
+ function formatSsePayloadText(payload, options) {
551
+ const prefixLines = [];
552
+ if (options?.id) prefixLines.push(`id: ${options.id}`);
553
+ if (typeof options?.retry === "number" && Number.isFinite(options.retry)) prefixLines.push(`retry: ${Math.max(0, Math.round(options.retry))}`);
554
+ const dataLines = JSON.stringify(payload).split(/\r?\n/).map((line) => `data: ${line}`);
555
+ return `${[...prefixLines, ...dataLines].join("\n")}\n\n`;
498
556
  }
499
557
  function writeRawToClient(options) {
500
558
  const { encoder, context, client, text, closeSseClient } = options;
@@ -508,12 +566,15 @@ function writeRawToClient(options) {
508
566
  }
509
567
  }
510
568
  function sendPayloadToClient(options) {
511
- const { context, client, payload, closeSseClient, encoder } = options;
569
+ const { context, client, payload, closeSseClient, encoder, eventId, retry } = options;
512
570
  return writeRawToClient({
513
571
  encoder,
514
572
  context,
515
573
  client,
516
- text: getSsePayloadText(payload),
574
+ text: formatSsePayloadText(payload, {
575
+ id: eventId,
576
+ retry
577
+ }),
517
578
  closeSseClient
518
579
  });
519
580
  }
@@ -694,7 +755,7 @@ function formatTerminalError(error) {
694
755
  return "Unknown terminal error";
695
756
  }
696
757
  function sseFrame(event) {
697
- return `data: ${JSON.stringify(event)}\n\n`;
758
+ return `${event.type === "output" ? `id: ${event.seq}\n` : ""}data: ${JSON.stringify(event)}\n\n`;
698
759
  }
699
760
  function exitPayload(exitCode, signal) {
700
761
  return typeof signal === "number" && signal > 0 ? {
@@ -708,6 +769,23 @@ function exitEvent(exitCode, signal) {
708
769
  ...exitPayload(exitCode, signal)
709
770
  };
710
771
  }
772
+ function readyEvent(record) {
773
+ return {
774
+ type: "ready",
775
+ backend: record.backend,
776
+ id: record.id,
777
+ cwd: record.cwd,
778
+ shell: terminalLabel(record.shell),
779
+ nextInputSeq: record.lastInputSeq + 1
780
+ };
781
+ }
782
+ function parseTerminalWebSocketMessage(text) {
783
+ const parsed = JSON.parse(text);
784
+ if (!parsed || typeof parsed !== "object") throw new Error("Terminal WebSocket message must be an object.");
785
+ const message = parsed;
786
+ if (message.type !== "input" && message.type !== "resize" && message.type !== "ping") throw new Error("Unknown terminal WebSocket message type.");
787
+ return message;
788
+ }
711
789
  function normalizeTerminalKey(value) {
712
790
  if (typeof value !== "string") return void 0;
713
791
  const normalized = value.trim();
@@ -763,6 +841,8 @@ var PicoTerminalManager = class {
763
841
  subscribers: /* @__PURE__ */ new Set(),
764
842
  backlog: [],
765
843
  backlogBytes: 0,
844
+ nextOutputSeq: 1,
845
+ lastInputSeq: 0,
766
846
  createdAt: now,
767
847
  lastUsedAt: now
768
848
  };
@@ -779,7 +859,7 @@ var PicoTerminalManager = class {
779
859
  reused: false
780
860
  };
781
861
  }
782
- createEventsResponse(id, scopeKey, signal) {
862
+ createEventsResponse(id, scopeKey, signal, lastSeq) {
783
863
  const record = this.assertTerminalScope(id, scopeKey);
784
864
  record.lastUsedAt = Date.now();
785
865
  let cleanupStream = () => {};
@@ -806,15 +886,7 @@ var PicoTerminalManager = class {
806
886
  cleanupStream = cleanup;
807
887
  record.subscribers.add(send);
808
888
  signal.addEventListener("abort", cleanup, { once: true });
809
- send({
810
- type: "ready",
811
- backend: record.backend,
812
- id: record.id,
813
- cwd: record.cwd,
814
- shell: terminalLabel(record.shell)
815
- });
816
- for (const event of record.backlog) send(event);
817
- if (record.exited) send(exitEvent(record.exited.exitCode, record.exited.signal));
889
+ this.sendInitialEvents(record, send, lastSeq);
818
890
  },
819
891
  cancel: () => {
820
892
  cleanupStream();
@@ -827,12 +899,69 @@ var PicoTerminalManager = class {
827
899
  "x-accel-buffering": "no"
828
900
  } });
829
901
  }
830
- writeTerminal(id, scopeKey, data) {
902
+ validateTerminal(id, scopeKey) {
903
+ this.assertTerminalScope(id, scopeKey);
904
+ }
905
+ connectWebSocket(options) {
906
+ const { id, lastSeq, peer, scopeKey } = options;
907
+ const record = this.assertTerminalScope(id, scopeKey);
908
+ record.lastUsedAt = Date.now();
909
+ let closed = false;
910
+ const send = (event) => {
911
+ if (closed) return;
912
+ try {
913
+ peer.send(JSON.stringify(event));
914
+ } catch {
915
+ cleanup();
916
+ }
917
+ };
918
+ const cleanup = () => {
919
+ if (closed) return;
920
+ closed = true;
921
+ record.subscribers.delete(send);
922
+ };
923
+ record.subscribers.add(send);
924
+ this.sendInitialEvents(record, send, lastSeq);
925
+ return cleanup;
926
+ }
927
+ handleWebSocketMessage(options) {
928
+ const { id, message, peer, scopeKey } = options;
929
+ try {
930
+ const payload = parseTerminalWebSocketMessage(message);
931
+ if (payload.type === "input") {
932
+ const inputSeq = this.writeTerminal(id, scopeKey, payload.data, payload.inputSeq);
933
+ if (typeof inputSeq === "number" && inputSeq > 0) peer.send(JSON.stringify({
934
+ type: "input_ack",
935
+ inputSeq
936
+ }));
937
+ return;
938
+ }
939
+ if (payload.type === "resize") {
940
+ this.resizeTerminal(id, scopeKey, payload);
941
+ return;
942
+ }
943
+ peer.send(JSON.stringify({ type: "pong" }));
944
+ } catch (error) {
945
+ try {
946
+ peer.send(JSON.stringify({
947
+ type: "error",
948
+ error: formatTerminalError(error)
949
+ }));
950
+ } catch {}
951
+ }
952
+ }
953
+ writeTerminal(id, scopeKey, data, inputSeq) {
831
954
  const record = this.assertTerminalScope(id, scopeKey);
832
955
  if (record.exited) throw new Error("Terminal has exited.");
833
956
  if (typeof data !== "string") throw new Error("Terminal input data is required.");
957
+ const normalizedInputSeq = normalizeDimension(inputSeq, 0, 0, Number.MAX_SAFE_INTEGER);
958
+ if (normalizedInputSeq > 0) {
959
+ if (normalizedInputSeq <= record.lastInputSeq) return normalizedInputSeq;
960
+ record.lastInputSeq = normalizedInputSeq;
961
+ }
834
962
  record.lastUsedAt = Date.now();
835
963
  record.pty.write(data);
964
+ return normalizedInputSeq > 0 ? normalizedInputSeq : void 0;
836
965
  }
837
966
  resizeTerminal(id, scopeKey, dimensions) {
838
967
  const record = this.assertTerminalScope(id, scopeKey);
@@ -861,9 +990,12 @@ var PicoTerminalManager = class {
861
990
  }
862
991
  attachPtyHandlers(record) {
863
992
  record.dataDisposable = record.pty.onData((data) => {
993
+ const seq = record.nextOutputSeq;
994
+ record.nextOutputSeq += 1;
864
995
  this.publish(record, {
865
996
  type: "output",
866
- data
997
+ data,
998
+ seq
867
999
  });
868
1000
  });
869
1001
  record.exitDisposable = record.pty.onExit((event) => {
@@ -890,6 +1022,21 @@ var PicoTerminalManager = class {
890
1022
  if (record.scopeKey !== scopeKey) throw new Error("Terminal not found for this session.");
891
1023
  return record;
892
1024
  }
1025
+ sendInitialEvents(record, send, lastSeq) {
1026
+ send(readyEvent(record));
1027
+ const firstSeq = record.backlog[0]?.seq;
1028
+ if (typeof lastSeq === "number" && firstSeq !== void 0 && lastSeq < firstSeq - 1) send({
1029
+ type: "reset",
1030
+ reason: "backlog_gap",
1031
+ firstSeq,
1032
+ nextSeq: record.nextOutputSeq
1033
+ });
1034
+ for (const event of record.backlog) {
1035
+ if (typeof lastSeq === "number" && event.seq <= lastSeq) continue;
1036
+ send(event);
1037
+ }
1038
+ if (record.exited) send(exitEvent(record.exited.exitCode, record.exited.signal));
1039
+ }
893
1040
  publish(record, event) {
894
1041
  record.lastUsedAt = Date.now();
895
1042
  this.rememberBacklog(record, event);
@@ -1710,6 +1857,22 @@ function serializeSessionTreeNode(node) {
1710
1857
  });
1711
1858
  return serialized;
1712
1859
  }
1860
+ function extractBranchableMessages(entry) {
1861
+ const manager = entry.session.sessionManager;
1862
+ const leafId = manager.getLeafId?.() ?? null;
1863
+ const branchEntries = manager.getBranch?.(leafId) ?? [];
1864
+ return (branchEntries.length > 0 ? branchEntries : []).flatMap((entry) => {
1865
+ const entryId = typeof entry.id === "string" ? entry.id.trim() : "";
1866
+ const message = entry.message;
1867
+ const role = typeof message?.role === "string" ? message.role : "";
1868
+ if (!entryId || entry.type !== "message" || role !== "user" && role !== "assistant") return [];
1869
+ return [{
1870
+ entryId,
1871
+ role,
1872
+ text: extractMessageText(message)
1873
+ }];
1874
+ });
1875
+ }
1713
1876
  function extractForkableUserMessages(entry) {
1714
1877
  const messages = entry.session.getUserMessagesForForking?.();
1715
1878
  if (Array.isArray(messages) && messages.length > 0) return messages.flatMap((message) => {
@@ -3883,12 +4046,20 @@ var VALID_THINKING_LEVELS = new Set([
3883
4046
  "high",
3884
4047
  "xhigh"
3885
4048
  ]);
4049
+ var PICO_DEFAULT_MODEL_PROVIDER = "openai-codex";
4050
+ var PICO_DEFAULT_MODEL_ID = "gpt-5.5";
4051
+ var PICO_DEFAULT_THINKING_LEVEL = "xhigh";
3886
4052
  var SESSION_LIST_LIMIT_DEFAULT = 5;
3887
4053
  var SESSION_LIST_LIMIT_MAX = 100;
3888
4054
  var SESSION_HISTORY_PAGE_LIMIT_DEFAULT = 50;
3889
4055
  var SESSION_HISTORY_PAGE_LIMIT_MAX = 200;
3890
4056
  var SESSION_NAME_MAX_LENGTH = 48;
3891
4057
  var HEARTBEAT_INTERVAL_MS = 2e4;
4058
+ var SSE_RETRY_MS = 1e3;
4059
+ var SSE_REPLAY_MAX_EVENTS = 200;
4060
+ var CONTEXT_INACTIVE_GRACE_MS = 3e4;
4061
+ var PROMPT_IDEMPOTENCY_TTL_MS = 1440 * 60 * 1e3;
4062
+ var PROMPT_IDEMPOTENCY_MAX_RECORDS = 200;
3892
4063
  var SESSION_INDEX_CACHE_TTL_MS = 5e3;
3893
4064
  var SESSION_LOAD_DEBUG_ENV_KEYS = [
3894
4065
  "PICO_DEBUG_SESSION_LOAD",
@@ -4046,17 +4217,40 @@ function normalizeRequestedDirectories(values) {
4046
4217
  }
4047
4218
  return nextDirectories;
4048
4219
  }
4220
+ function textFromThinkingSignature(value) {
4221
+ if (typeof value !== "string" || !value.trim()) return "";
4222
+ let parsed;
4223
+ try {
4224
+ parsed = JSON.parse(value);
4225
+ } catch {
4226
+ return "";
4227
+ }
4228
+ if (!parsed || typeof parsed !== "object") return "";
4229
+ const content = parsed.content;
4230
+ if (!Array.isArray(content)) return "";
4231
+ return content.flatMap((part) => {
4232
+ if (!part || typeof part !== "object") return [];
4233
+ const type = part.type;
4234
+ const text = part.text;
4235
+ return type === "reasoning_text" && typeof text === "string" && text.trim() ? [text] : [];
4236
+ }).join("\n\n").trim();
4237
+ }
4049
4238
  function sanitizeMessageContentPart(part) {
4050
4239
  const type = typeof part?.type === "string" ? part.type : "";
4051
4240
  if (type === "text") return {
4052
4241
  type: "text",
4053
4242
  text: typeof part.text === "string" ? part.text : ""
4054
4243
  };
4055
- if (type === "thinking") return {
4056
- type: "thinking",
4057
- thinking: typeof part.thinking === "string" ? part.thinking : "",
4058
- ...typeof part.summaryLabel === "string" ? { summaryLabel: part.summaryLabel } : {}
4059
- };
4244
+ if (type === "thinking") {
4245
+ const rawThinking = typeof part.thinking === "string" ? part.thinking : "";
4246
+ const signatureThinking = textFromThinkingSignature(part.thinkingSignature);
4247
+ const summaryLabel = typeof part.summaryLabel === "string" ? part.summaryLabel : signatureThinking && rawThinking.trim() ? rawThinking : void 0;
4248
+ return {
4249
+ type: "thinking",
4250
+ thinking: signatureThinking || rawThinking,
4251
+ ...summaryLabel ? { summaryLabel } : {}
4252
+ };
4253
+ }
4060
4254
  if (type === "toolCall") return {
4061
4255
  type: "toolCall",
4062
4256
  ...typeof part.id === "string" ? { id: part.id } : {},
@@ -4077,6 +4271,8 @@ function sanitizeSessionMessage(message) {
4077
4271
  const sanitizedPart = sanitizeMessageContentPart(part);
4078
4272
  return sanitizedPart ? [sanitizedPart] : [];
4079
4273
  }) : typeof message?.content === "string" ? message.content : void 0;
4274
+ const tokensBefore = Number(message?.tokensBefore);
4275
+ const estimatedTokensAfter = message?.estimatedTokensAfter == null ? NaN : Number(message.estimatedTokensAfter);
4080
4276
  return {
4081
4277
  ...role ? { role } : {},
4082
4278
  ...sanitizedContent !== void 0 ? { content: sanitizedContent } : {},
@@ -4087,7 +4283,8 @@ function sanitizeSessionMessage(message) {
4087
4283
  ...message?.queued || metadata?.queued ? { queued: true } : {},
4088
4284
  ...message?.streamingBehavior === "steer" || message?.streamingBehavior === "followUp" ? { streamingBehavior: message.streamingBehavior } : message?.deliverAs === "steer" || message?.deliverAs === "followUp" ? { streamingBehavior: message.deliverAs } : metadata?.streamingBehavior === "steer" || metadata?.streamingBehavior === "followUp" ? { streamingBehavior: metadata.streamingBehavior } : metadata?.deliverAs === "steer" || metadata?.deliverAs === "followUp" ? { streamingBehavior: metadata.deliverAs } : {},
4089
4285
  ...typeof message?.summary === "string" ? { summary: message.summary } : {},
4090
- ...Number.isFinite(Number(message?.tokensBefore)) ? { tokensBefore: Number(message.tokensBefore) } : {},
4286
+ ...Number.isFinite(tokensBefore) ? { tokensBefore } : {},
4287
+ ...Number.isFinite(estimatedTokensAfter) ? { estimatedTokensAfter } : {},
4091
4288
  ...typeof message?.toolCallId === "string" ? { toolCallId: message.toolCallId } : {},
4092
4289
  ...Object.prototype.hasOwnProperty.call(message, "details") ? { details: message.details } : {},
4093
4290
  ...message?.isError ? { isError: true } : {}
@@ -4246,6 +4443,43 @@ function normalizeClientPendingId(value) {
4246
4443
  if (pendingId.length > 128) return void 0;
4247
4444
  return pendingId;
4248
4445
  }
4446
+ function normalizeClientPromptRequestId(value) {
4447
+ if (typeof value !== "string") return void 0;
4448
+ const requestId = value.trim();
4449
+ if (!requestId.startsWith("prompt:")) return void 0;
4450
+ if (requestId.length > 160) return void 0;
4451
+ return requestId;
4452
+ }
4453
+ function parsePositiveInteger(value) {
4454
+ if (!value) return void 0;
4455
+ const parsed = Number(value);
4456
+ if (!Number.isSafeInteger(parsed) || parsed < 0) return void 0;
4457
+ return parsed;
4458
+ }
4459
+ function terminalWebSocketIdFromRequest(request) {
4460
+ const pathname = new URL(request.url).pathname;
4461
+ const match = /^\/api\/terminal\/([^/]+)\/ws\/?$/.exec(pathname);
4462
+ if (!match?.[1]) throw new Response("Terminal WebSocket route not found.", { status: 404 });
4463
+ try {
4464
+ return decodeURIComponent(match[1]);
4465
+ } catch {
4466
+ throw new Response("Invalid terminal id.", { status: 400 });
4467
+ }
4468
+ }
4469
+ function terminalWebSocketCleanupFromPeer(peer) {
4470
+ const cleanup = peer.context.terminalCleanup;
4471
+ return typeof cleanup === "function" ? cleanup : void 0;
4472
+ }
4473
+ function terminalWebSocketContextFromPeer(peer) {
4474
+ const { terminalId, terminalLastSeq, terminalScopeKey } = peer.context;
4475
+ if (typeof terminalId !== "string" || !terminalId) throw new Error("Terminal WebSocket is missing a terminal id.");
4476
+ if (typeof terminalScopeKey !== "string" || !terminalScopeKey) throw new Error("Terminal WebSocket is missing a terminal scope.");
4477
+ return {
4478
+ terminalId,
4479
+ terminalLastSeq: typeof terminalLastSeq === "number" ? terminalLastSeq : void 0,
4480
+ terminalScopeKey
4481
+ };
4482
+ }
4249
4483
  function normalizePromptDraftOwnerKey(value) {
4250
4484
  if (typeof value !== "string") return "";
4251
4485
  const ownerKey = value.trim();
@@ -4274,6 +4508,7 @@ var PicoRuntime = class {
4274
4508
  encoder = new TextEncoder();
4275
4509
  contexts = /* @__PURE__ */ new Map();
4276
4510
  sessionEntries = /* @__PURE__ */ new Map();
4511
+ contextCleanupTimeout;
4277
4512
  sessionTreeLeafOverrides = /* @__PURE__ */ new Map();
4278
4513
  servicesByCwd = /* @__PURE__ */ new Map();
4279
4514
  pendingUiRequests = /* @__PURE__ */ new Map();
@@ -4429,6 +4664,20 @@ var PicoRuntime = class {
4429
4664
  });
4430
4665
  });
4431
4666
  }
4667
+ findPicoDefaultModel(entry) {
4668
+ return entry.services.modelRegistry.getAvailable().find((model) => model.provider === PICO_DEFAULT_MODEL_PROVIDER && model.id === PICO_DEFAULT_MODEL_ID);
4669
+ }
4670
+ async applyPicoDefaultSessionPreferences(entry) {
4671
+ const settingsManager = entry.services.settingsManager;
4672
+ const configuredProvider = settingsManager.getDefaultProvider?.()?.trim();
4673
+ const configuredModel = settingsManager.getDefaultModel?.()?.trim();
4674
+ const configuredThinkingLevel = settingsManager.getDefaultThinkingLevel?.()?.trim();
4675
+ if (!configuredProvider && !configuredModel) {
4676
+ const defaultModel = this.findPicoDefaultModel(entry);
4677
+ if (defaultModel && (entry.session.model?.provider !== defaultModel.provider || entry.session.model?.id !== defaultModel.id)) await entry.session.setModel(defaultModel);
4678
+ }
4679
+ if (!configuredThinkingLevel && entry.session.thinkingLevel !== PICO_DEFAULT_THINKING_LEVEL && entry.session.getAvailableThinkingLevels().includes(PICO_DEFAULT_THINKING_LEVEL)) entry.session.setThinkingLevel(PICO_DEFAULT_THINKING_LEVEL);
4680
+ }
4432
4681
  listAvailableModels(entry) {
4433
4682
  return entry.services.modelRegistry.getAvailable().flatMap((model) => {
4434
4683
  const serializedModel = serializeModel(model);
@@ -4459,7 +4708,10 @@ var PicoRuntime = class {
4459
4708
  draftKey: void 0,
4460
4709
  sessionScope: process.cwd(),
4461
4710
  unreadFinished: /* @__PURE__ */ new Set(),
4462
- sidebarBootstrapDirectories: []
4711
+ sidebarBootstrapDirectories: [],
4712
+ nextSseEventId: 1,
4713
+ replayEvents: [],
4714
+ inactiveSince: void 0
4463
4715
  };
4464
4716
  this.contexts.set(id, next);
4465
4717
  return next;
@@ -4521,11 +4773,27 @@ var PicoRuntime = class {
4521
4773
  });
4522
4774
  await Promise.all(candidates.map((entry) => this.disposeSessionEntry(entry)));
4523
4775
  }
4776
+ scheduleInactiveContextCleanup() {
4777
+ if (this.contextCleanupTimeout) return;
4778
+ this.contextCleanupTimeout = setTimeout(() => {
4779
+ this.contextCleanupTimeout = void 0;
4780
+ this.cleanupInactiveContexts();
4781
+ }, CONTEXT_INACTIVE_GRACE_MS + 100);
4782
+ }
4524
4783
  async cleanupInactiveContexts() {
4784
+ const now = performance.now();
4525
4785
  for (const context of this.contexts.values()) {
4526
- if (context.clients.size > 0) continue;
4786
+ if (context.clients.size > 0) {
4787
+ context.inactiveSince = void 0;
4788
+ continue;
4789
+ }
4527
4790
  const activeEntry = this.getActiveEntry(context);
4528
4791
  if (activeEntry && this.isSessionBusyForDone(activeEntry)) continue;
4792
+ context.inactiveSince ??= now;
4793
+ if (now - context.inactiveSince < CONTEXT_INACTIVE_GRACE_MS) {
4794
+ this.scheduleInactiveContextCleanup();
4795
+ continue;
4796
+ }
4529
4797
  this.contexts.delete(context.id);
4530
4798
  }
4531
4799
  this.syncGitWatchDirectories();
@@ -4568,6 +4836,49 @@ var PicoRuntime = class {
4568
4836
  }
4569
4837
  return lastValue;
4570
4838
  }
4839
+ stateConversationItems(entry) {
4840
+ const branchableMessages = extractBranchableMessages(entry);
4841
+ const forkableMessages = branchableMessages.length === 0 ? extractForkableUserMessages(entry) : [];
4842
+ if (branchableMessages.length === 0 && forkableMessages.length === 0) return entry.retainedConversationItems;
4843
+ let branchableIndex = 0;
4844
+ let forkableIndex = 0;
4845
+ let changed = false;
4846
+ const nextBranchableMessage = (role) => {
4847
+ const matchedIndex = branchableMessages.findIndex((message, index) => index >= branchableIndex && message.role === role);
4848
+ if (matchedIndex < 0) return void 0;
4849
+ branchableIndex = matchedIndex + 1;
4850
+ return branchableMessages[matchedIndex];
4851
+ };
4852
+ const nextForkableUserEntryId = (text) => {
4853
+ const normalizedText = text.trim();
4854
+ if (!normalizedText) return void 0;
4855
+ const matchedIndex = forkableMessages.findIndex((message, index) => index >= forkableIndex && message.text.trim() === normalizedText);
4856
+ if (matchedIndex < 0) return void 0;
4857
+ forkableIndex = matchedIndex + 1;
4858
+ return forkableMessages[matchedIndex]?.entryId;
4859
+ };
4860
+ const items = entry.retainedConversationItems.map((item) => {
4861
+ if (item.kind === "user") {
4862
+ const entryId = nextBranchableMessage("user")?.entryId ?? nextForkableUserEntryId(item.text);
4863
+ if (!entryId || item.forkEntryId === entryId) return item;
4864
+ changed = true;
4865
+ return {
4866
+ ...item,
4867
+ forkEntryId: entryId
4868
+ };
4869
+ }
4870
+ if (item.streaming) return item;
4871
+ const entryId = nextBranchableMessage("assistant")?.entryId;
4872
+ if (!entryId || item.branchEntryId === entryId) return item;
4873
+ changed = true;
4874
+ return {
4875
+ ...item,
4876
+ branchEntryId: entryId
4877
+ };
4878
+ });
4879
+ if (changed) entry.retainedConversationItems = items;
4880
+ return entry.retainedConversationItems;
4881
+ }
4571
4882
  currentStatePayload(entry, context) {
4572
4883
  const draft = this.isDraftEntry(entry);
4573
4884
  const historyTotalCount = entry.session.messages.length;
@@ -4575,7 +4886,7 @@ var PicoRuntime = class {
4575
4886
  type: "state_sync",
4576
4887
  activationRevision: context.activeRevision,
4577
4888
  sessionKey: entry.key,
4578
- items: entry.retainedConversationItems,
4889
+ items: this.stateConversationItems(entry),
4579
4890
  pendingUserMessages: entry.pendingUserMessages.map((message) => clonePendingUserMessage(message)),
4580
4891
  draft,
4581
4892
  streaming: this.getEntryStreamingState(entry),
@@ -4873,8 +5184,32 @@ var PicoRuntime = class {
4873
5184
  messages
4874
5185
  };
4875
5186
  }
5187
+ shouldReplayPayload(payload) {
5188
+ if (!payload || typeof payload !== "object") return false;
5189
+ const type = payload.type;
5190
+ return type === "session_done" || type === "request_error" || type === "extension_error" || type === "extension_ui_request" || type === "auto_session_naming_error" || type === "git_changed";
5191
+ }
5192
+ rememberReplayPayload(context, payload) {
5193
+ if (!this.shouldReplayPayload(payload)) return void 0;
5194
+ const eventId = context.nextSseEventId;
5195
+ context.nextSseEventId += 1;
5196
+ context.replayEvents.push({
5197
+ id: eventId,
5198
+ text: formatSsePayloadText(payload, { id: String(eventId) })
5199
+ });
5200
+ while (context.replayEvents.length > SSE_REPLAY_MAX_EVENTS) context.replayEvents.shift();
5201
+ return String(eventId);
5202
+ }
5203
+ replayEventsToClient(context, client, lastEventId) {
5204
+ if (lastEventId === void 0 || context.replayEvents.length === 0) return;
5205
+ for (const event of context.replayEvents) {
5206
+ if (event.id <= lastEventId) continue;
5207
+ this.writeRawToClient(context, client, event.text);
5208
+ }
5209
+ }
4876
5210
  sendToContext(context, payload) {
4877
- for (const client of context.clients) this.sendPayloadToClient(context, client, payload);
5211
+ const eventId = this.rememberReplayPayload(context, payload);
5212
+ for (const client of context.clients) this.sendPayloadToClient(context, client, payload, { eventId });
4878
5213
  }
4879
5214
  sendStatePayloadToClient(context, client, payload, options) {
4880
5215
  const debug = isSessionLoadDebugEnabled();
@@ -5388,6 +5723,7 @@ var PicoRuntime = class {
5388
5723
  retainedConversationItems: createRetainedConversationState(session.messages.map((message) => sanitizeSessionMessage(message))).items,
5389
5724
  pendingUserMessages: [],
5390
5725
  pendingQueueMutation: false,
5726
+ pausedPendingUserMessageIds: /* @__PURE__ */ new Set(),
5391
5727
  canceledPendingUserMessageIds: /* @__PURE__ */ new Set(),
5392
5728
  firstMessageHint: "",
5393
5729
  modifiedAt: void 0,
@@ -5402,6 +5738,7 @@ var PicoRuntime = class {
5402
5738
  disposed: false
5403
5739
  },
5404
5740
  promptRequestChain: Promise.resolve(),
5741
+ promptRequestRecords: /* @__PURE__ */ new Map(),
5405
5742
  doneNotificationSuppressed: false
5406
5743
  };
5407
5744
  this.sessionEntries.set(key, entry);
@@ -5437,10 +5774,12 @@ var PicoRuntime = class {
5437
5774
  async createNewSessionEntry(cwd, options) {
5438
5775
  const sessionManager = (await this.getSdk()).SessionManager.create(cwd, options?.sessionDir);
5439
5776
  if (options?.newSessionOptions && sessionManager.newSession) sessionManager.newSession(options.newSessionOptions);
5440
- return await this.createSessionEntry(sessionManager, {
5777
+ const entry = await this.createSessionEntry(sessionManager, {
5441
5778
  draft: options?.draft,
5442
5779
  sessionStartEvent: options?.sessionStartEvent
5443
5780
  });
5781
+ await this.applyPicoDefaultSessionPreferences(entry);
5782
+ return entry;
5444
5783
  }
5445
5784
  async ensureSessionEntryByPath(sessionPath) {
5446
5785
  const existing = this.sessionEntries.get(sessionPath);
@@ -5570,6 +5909,18 @@ var PicoRuntime = class {
5570
5909
  refinementReason: input.refinementReason
5571
5910
  });
5572
5911
  }
5912
+ applyHeuristicSessionNameAttempt(entry, text, imageCount) {
5913
+ const heuristic = deriveHeuristicSessionNameAttempt(text, imageCount);
5914
+ if (heuristic.name) this.applyManagedSessionName(entry, heuristic.name, "initial");
5915
+ return heuristic;
5916
+ }
5917
+ applySessionNameFallbackAfterRefinementFailure(entry, text, imageCount) {
5918
+ if (this.getCurrentSessionName(entry)) return true;
5919
+ if (this.applyHeuristicSessionNameAttempt(entry, text, imageCount).name && this.getCurrentSessionName(entry)) return true;
5920
+ const fallbackName = cleanupSessionNameCandidate(normalizeSessionListTitle(text, SESSION_NAME_MAX_LENGTH));
5921
+ if (!fallbackName) return false;
5922
+ return this.applyManagedSessionName(entry, fallbackName, "initial");
5923
+ }
5573
5924
  scheduleSessionNameRefinement(entry, text, imageCount, heuristicReason) {
5574
5925
  if (entry.sessionNaming.pendingGeneration) return;
5575
5926
  entry.sessionNaming.pendingGeneration = true;
@@ -5579,10 +5930,11 @@ var PicoRuntime = class {
5579
5930
  try {
5580
5931
  const generated = await generateSessionNameWithLlm({
5581
5932
  cwd: entry.cwd,
5933
+ model: entry.session.model,
5582
5934
  services: { modelRegistry: entry.services.modelRegistry }
5583
5935
  }, text, imageCount);
5584
5936
  if (!generated?.name) {
5585
- if (!entry.sessionNaming.disposed && entry.sessionNaming.nonce === nonce && sessionId === entry.session.sessionId && !this.getCurrentSessionName(entry)) this.emitAutoSessionNamingFailure(entry, {
5937
+ if (!entry.sessionNaming.disposed && entry.sessionNaming.nonce === nonce && sessionId === entry.session.sessionId && !this.applySessionNameFallbackAfterRefinementFailure(entry, text, imageCount)) this.emitAutoSessionNamingFailure(entry, {
5586
5938
  heuristicReason,
5587
5939
  refinementReason: generated?.reason || "session name refinement returned no usable title",
5588
5940
  text,
@@ -5593,7 +5945,7 @@ var PicoRuntime = class {
5593
5945
  if (entry.sessionNaming.disposed || entry.sessionNaming.nonce !== nonce || sessionId !== entry.session.sessionId) return;
5594
5946
  this.applyManagedSessionName(entry, generated.name, "refine");
5595
5947
  } catch (error) {
5596
- if (!entry.sessionNaming.disposed && entry.sessionNaming.nonce === nonce && sessionId === entry.session.sessionId && !this.getCurrentSessionName(entry)) this.emitAutoSessionNamingFailure(entry, {
5948
+ if (!entry.sessionNaming.disposed && entry.sessionNaming.nonce === nonce && sessionId === entry.session.sessionId && !this.applySessionNameFallbackAfterRefinementFailure(entry, text, imageCount)) this.emitAutoSessionNamingFailure(entry, {
5597
5949
  heuristicReason,
5598
5950
  refinementReason: formatError(error),
5599
5951
  text,
@@ -5606,21 +5958,28 @@ var PicoRuntime = class {
5606
5958
  }
5607
5959
  startAutoSessionNaming(entry, text, imageCount) {
5608
5960
  if (this.getCurrentSessionName(entry)) return;
5609
- const heuristic = deriveHeuristicSessionNameAttempt(text, imageCount);
5610
- if (heuristic.name) this.applyManagedSessionName(entry, heuristic.name, "initial");
5961
+ const heuristic = this.applyHeuristicSessionNameAttempt(entry, text, imageCount);
5611
5962
  this.scheduleSessionNameRefinement(entry, text, imageCount, heuristic.reason);
5612
5963
  }
5613
- maybeAutoNameSession(entry) {
5614
- if (this.getCurrentSessionName(entry)) return;
5615
- for (const message of entry.session.messages) {
5964
+ getFirstPromptForNamingFromMessages(messages, firstMessageHint = "") {
5965
+ for (const message of messages) {
5616
5966
  if (message?.role !== "user") continue;
5617
5967
  const firstPrompt = summarizePromptContent(message.content);
5618
- if (firstPrompt.text || firstPrompt.imageCount > 0) {
5619
- this.startAutoSessionNaming(entry, firstPrompt.text, firstPrompt.imageCount);
5620
- return;
5621
- }
5968
+ if (firstPrompt.text || firstPrompt.imageCount > 0) return firstPrompt;
5622
5969
  }
5623
- if (entry.firstMessageHint.trim()) this.startAutoSessionNaming(entry, entry.firstMessageHint.trim(), 0);
5970
+ const trimmedHint = firstMessageHint.trim();
5971
+ return trimmedHint ? {
5972
+ text: trimmedHint,
5973
+ imageCount: 0
5974
+ } : null;
5975
+ }
5976
+ getSessionFirstPromptForNaming(entry) {
5977
+ return this.getFirstPromptForNamingFromMessages(entry.session.messages, entry.firstMessageHint);
5978
+ }
5979
+ maybeAutoNameSession(entry) {
5980
+ if (this.getCurrentSessionName(entry)) return;
5981
+ const firstPrompt = this.getSessionFirstPromptForNaming(entry);
5982
+ if (firstPrompt) this.startAutoSessionNaming(entry, firstPrompt.text, firstPrompt.imageCount);
5624
5983
  }
5625
5984
  reconcilePendingUserMessages(entry, queueState) {
5626
5985
  const nextQueueState = queueState ?? {
@@ -5629,7 +5988,10 @@ var PicoRuntime = class {
5629
5988
  };
5630
5989
  const steering = [...nextQueueState.steering].filter((text) => typeof text === "string");
5631
5990
  const followUp = [...nextQueueState.followUp].filter((text) => typeof text === "string");
5632
- const pending = entry.pendingUserMessages.map((message) => clonePendingUserMessage(message));
5991
+ const allPending = entry.pendingUserMessages.map((message) => clonePendingUserMessage(message));
5992
+ const pausedPending = allPending.filter((message) => entry.pausedPendingUserMessageIds.has(message.pendingId));
5993
+ const pending = allPending.filter((message) => !entry.pausedPendingUserMessageIds.has(message.pendingId));
5994
+ entry.pausedPendingUserMessageIds = new Set(pausedPending.map((message) => message.pendingId));
5633
5995
  const steeringCounts = /* @__PURE__ */ new Map();
5634
5996
  const followUpCounts = /* @__PURE__ */ new Map();
5635
5997
  for (const text of steering) steeringCounts.set(text, (steeringCounts.get(text) ?? 0) + 1);
@@ -5655,7 +6017,7 @@ var PicoRuntime = class {
5655
6017
  nextPending.push(createPendingUserMessage(text, [], "followUp"));
5656
6018
  followUpCounts.set(text, count - 1);
5657
6019
  }
5658
- entry.pendingUserMessages = sortPendingUserMessages(nextPending);
6020
+ entry.pendingUserMessages = sortPendingUserMessages([...pausedPending, ...nextPending]);
5659
6021
  return entry.pendingUserMessages;
5660
6022
  }
5661
6023
  pendingMessagesFromClientUpdate(entry, pendingMessagesUpdate) {
@@ -5726,6 +6088,75 @@ var PicoRuntime = class {
5726
6088
  await this.broadcastEntryState(entry);
5727
6089
  return entry.pendingUserMessages;
5728
6090
  }
6091
+ drainPendingUserMessagesWhenIdle(entry) {
6092
+ if (entry.pendingQueueDrainPromise) return entry.pendingQueueDrainPromise;
6093
+ const drainPromise = this.runSerializedPromptRequest(entry, async () => {
6094
+ if (this.getEntryStreamingState(entry) || entry.session.isStreaming || this.getEntryCompactingState(entry) || entry.session.isCompacting) return;
6095
+ const pendingMessages = sortPendingUserMessages(entry.pendingUserMessages.map((message) => clonePendingUserMessage(message)));
6096
+ const pendingMessage = pendingMessages[0];
6097
+ if (!pendingMessage) return;
6098
+ const remainingMessages = pendingMessages.slice(1);
6099
+ const message = pendingMessage.text;
6100
+ const images = normalizePromptImages(pendingMessage.images);
6101
+ if (!message.trim() && images.length === 0) {
6102
+ await this.replacePendingUserMessages(entry, remainingMessages);
6103
+ return;
6104
+ }
6105
+ entry.streamingState = true;
6106
+ this.markSessionUserMessage(entry);
6107
+ entry.pendingUserMessages = remainingMessages;
6108
+ await this.broadcastEntryState(entry);
6109
+ await this.broadcastSessionsAll();
6110
+ this.broadcastToViewers(entry.key, {
6111
+ type: "user_message",
6112
+ message,
6113
+ images,
6114
+ queued: false
6115
+ });
6116
+ let promptPreflightComplete;
6117
+ let promptPreflightSettled = false;
6118
+ const promptPreflight = new Promise((resolve) => {
6119
+ promptPreflightComplete = resolve;
6120
+ });
6121
+ const settlePromptPreflight = () => {
6122
+ if (promptPreflightSettled) return;
6123
+ promptPreflightSettled = true;
6124
+ promptPreflightComplete?.();
6125
+ };
6126
+ const promptOptions = images.length > 0 ? { images } : void 0;
6127
+ const promptPromise = entry.session.prompt(message, {
6128
+ ...promptOptions,
6129
+ preflightResult: settlePromptPreflight
6130
+ });
6131
+ const finishPromptIfIdle = async () => {
6132
+ if (!entry.streamingState || entry.session.isStreaming) return;
6133
+ entry.streamingState = false;
6134
+ this.reconcilePendingUserMessages(entry);
6135
+ await this.broadcastEntryState(entry);
6136
+ await this.broadcastSessionsAll();
6137
+ };
6138
+ promptPromise.then(() => {}).catch(async (error) => {
6139
+ await finishPromptIfIdle();
6140
+ console.error("[pico] queued prompt error", error);
6141
+ this.broadcastToViewers(entry.key, {
6142
+ type: "request_error",
6143
+ scope: "prompt",
6144
+ message,
6145
+ error: formatError(error)
6146
+ });
6147
+ }).finally(() => {
6148
+ settlePromptPreflight();
6149
+ });
6150
+ await promptPreflight;
6151
+ if (remainingMessages.length > 0) await this.replacePendingUserMessages(entry, remainingMessages);
6152
+ });
6153
+ let trackedPromise = Promise.resolve();
6154
+ trackedPromise = drainPromise.finally(() => {
6155
+ if (entry.pendingQueueDrainPromise === trackedPromise) entry.pendingQueueDrainPromise = void 0;
6156
+ });
6157
+ entry.pendingQueueDrainPromise = trackedPromise;
6158
+ return trackedPromise;
6159
+ }
5729
6160
  refreshRetainedConversationItems(entry) {
5730
6161
  entry.retainedConversationItems = createRetainedConversationState(entry.session.messages.map((message) => sanitizeSessionMessage(message))).items;
5731
6162
  }
@@ -5928,7 +6359,8 @@ var PicoRuntime = class {
5928
6359
  const state = createRetainedConversationState(entry.session.messages.map((message) => sanitizeSessionMessage(message)));
5929
6360
  const summary = result.summary;
5930
6361
  const tokensBefore = result.tokensBefore;
5931
- state.items = [...state.items, createCompactionSummaryItem(summary, tokensBefore, `compaction:live:${state.items.length}`)];
6362
+ const estimatedTokensAfter = result.estimatedTokensAfter;
6363
+ state.items = [...state.items, createCompactionSummaryItem(summary, tokensBefore, `compaction:live:${state.items.length}`, estimatedTokensAfter)];
5932
6364
  entry.retainedConversationItems = state.items;
5933
6365
  return;
5934
6366
  }
@@ -5961,6 +6393,7 @@ var PicoRuntime = class {
5961
6393
  followUp: Array.isArray(event.followUp) ? event.followUp : []
5962
6394
  });
5963
6395
  }
6396
+ if (type === "session_info_changed") this.touchSessionEntry(entry);
5964
6397
  if (type === "message_end" && event.message?.role === "user") {
5965
6398
  this.touchSessionEntry(entry);
5966
6399
  this.reconcilePendingUserMessages(entry);
@@ -5973,6 +6406,7 @@ var PicoRuntime = class {
5973
6406
  const willRetry = Boolean(event.willRetry);
5974
6407
  if (compactionReason === "manual" && compactionSucceeded) this.scheduleSessionDoneCheck(entry, "manual_compaction");
5975
6408
  else if (entry.pendingDoneReason === "agent" && !willRetry) this.scheduleSessionDoneCheck(entry, "agent", entry.pendingDoneOutcome ?? "success");
6409
+ this.drainPendingUserMessagesWhenIdle(entry);
5976
6410
  }
5977
6411
  if (type === "auto_retry_end") {
5978
6412
  if (entry.pendingDoneReason === "agent" && event.success === false) this.scheduleSessionDoneCheck(entry, "agent", "error");
@@ -5991,7 +6425,7 @@ var PicoRuntime = class {
5991
6425
  if (statusChanged) this.broadcastSessionStatusAll(entry);
5992
6426
  this.rememberSessionTreeLeaf(entry, true);
5993
6427
  await this.broadcastEntryState(entry);
5994
- if (type === "agent_start" || type === "agent_end" || type === "message_end" || type === "compaction_end" || type === "queue_update") await this.broadcastSessionsAll();
6428
+ if (type === "agent_start" || type === "agent_end" || type === "message_end" || type === "compaction_end" || type === "queue_update" || type === "session_info_changed") await this.broadcastSessionsAll();
5995
6429
  if (type === "agent_end" || type === "compaction_end") await this.cleanupInactiveContexts();
5996
6430
  }
5997
6431
  writeRawToClient(context, client, text) {
@@ -6003,12 +6437,14 @@ var PicoRuntime = class {
6003
6437
  closeSseClient: (activeContext, activeClient) => this.closeSseClient(activeContext, activeClient)
6004
6438
  });
6005
6439
  }
6006
- sendPayloadToClient(context, client, payload) {
6440
+ sendPayloadToClient(context, client, payload, options) {
6007
6441
  return sendPayloadToClient({
6008
6442
  encoder: this.encoder,
6009
6443
  context,
6010
6444
  client,
6011
6445
  payload,
6446
+ eventId: options?.eventId,
6447
+ retry: options?.retry,
6012
6448
  closeSseClient: (activeContext, activeClient) => this.closeSseClient(activeContext, activeClient)
6013
6449
  });
6014
6450
  }
@@ -6016,6 +6452,10 @@ var PicoRuntime = class {
6016
6452
  if (client.closed) return;
6017
6453
  client.closed = true;
6018
6454
  context.clients.delete(client);
6455
+ if (context.clients.size === 0) {
6456
+ context.inactiveSince = performance.now();
6457
+ this.scheduleInactiveContextCleanup();
6458
+ }
6019
6459
  this.syncGitWatchDirectories();
6020
6460
  try {
6021
6461
  client.controller.close();
@@ -6025,6 +6465,7 @@ var PicoRuntime = class {
6025
6465
  const startedAt = performance.now();
6026
6466
  const { url, context, activeEntry } = await this.resolveRequest(request, { preferActiveDraft: false });
6027
6467
  context.sidebarBootstrapDirectories = normalizeRequestedDirectories(url.searchParams.getAll("sidebarDirectory"));
6468
+ const lastEventId = parsePositiveInteger(request.headers.get("last-event-id") || url.searchParams.get("lastEventId"));
6028
6469
  this.logSessionLoadDebug("events_response:create", {
6029
6470
  contextId: context.id,
6030
6471
  sidebarBootstrapDirectoryCount: context.sidebarBootstrapDirectories.length,
@@ -6040,9 +6481,11 @@ var PicoRuntime = class {
6040
6481
  controller,
6041
6482
  lastStateSyncSnapshot: void 0
6042
6483
  };
6484
+ context.inactiveSince = void 0;
6043
6485
  context.clients.add(client);
6044
6486
  this.syncGitWatchDirectories();
6045
- this.writeRawToClient(context, client, ": connected\n\n");
6487
+ this.writeRawToClient(context, client, `retry: ${SSE_RETRY_MS}\n: connected\n\n`);
6488
+ this.replayEventsToClient(context, client, lastEventId);
6046
6489
  (async () => {
6047
6490
  const bootstrapStartedAt = performance.now();
6048
6491
  try {
@@ -6102,6 +6545,15 @@ var PicoRuntime = class {
6102
6545
  release?.();
6103
6546
  }
6104
6547
  }
6548
+ prunePromptRequestRecords(entry) {
6549
+ const now = Date.now();
6550
+ for (const [requestId, record] of entry.promptRequestRecords) if (now - record.createdAt > PROMPT_IDEMPOTENCY_TTL_MS) entry.promptRequestRecords.delete(requestId);
6551
+ while (entry.promptRequestRecords.size > PROMPT_IDEMPOTENCY_MAX_RECORDS) {
6552
+ const oldestRequestId = entry.promptRequestRecords.keys().next().value;
6553
+ if (!oldestRequestId) break;
6554
+ entry.promptRequestRecords.delete(oldestRequestId);
6555
+ }
6556
+ }
6105
6557
  async prompt(request, body) {
6106
6558
  const url = new URL(request.url);
6107
6559
  const requestedSessionKey = url.searchParams.get("sessionKey")?.trim() || "";
@@ -6140,104 +6592,132 @@ var PicoRuntime = class {
6140
6592
  if (requestedThinkingLevel && !VALID_THINKING_LEVELS.has(requestedThinkingLevel)) throw new Error(`Invalid thinking level: ${requestedThinkingLevel || "(empty)"}`);
6141
6593
  const streamingBehavior = body.streamingBehavior === "steer" ? "steer" : body.streamingBehavior === "followUp" ? "followUp" : void 0;
6142
6594
  const clientPendingId = normalizeClientPendingId(body.pendingId);
6143
- return await this.runSerializedPromptRequest(activeEntry, async () => {
6144
- if (requestedThinkingLevel && activeEntry.session.thinkingLevel !== requestedThinkingLevel) activeEntry.session.setThinkingLevel(requestedThinkingLevel);
6145
- const promptOptions = images.length > 0 ? { images } : void 0;
6146
- const promotedDraft = this.isDraftEntry(activeEntry);
6147
- const isAlreadyStreaming = this.getEntryStreamingState(activeEntry);
6148
- if (clientPendingId && activeEntry.canceledPendingUserMessageIds.delete(clientPendingId)) {
6149
- await this.broadcastEntryState(activeEntry);
6150
- return {
6151
- ok: true,
6152
- queued: isAlreadyStreaming,
6153
- pendingId: clientPendingId,
6154
- canceled: true
6155
- };
6156
- }
6157
- if (!this.getSessionFirstMessage(activeEntry)) {
6158
- activeEntry.firstMessageHint = message.trim();
6159
- this.startAutoSessionNaming(activeEntry, message.trim(), images.length);
6160
- }
6161
- if (isAlreadyStreaming) {
6162
- const queuedStreamingBehavior = streamingBehavior ?? "steer";
6163
- const pendingMessage = createPendingUserMessage(message, images, queuedStreamingBehavior, clientPendingId);
6164
- this.markSessionUserMessage(activeEntry);
6165
- activeEntry.canceledPendingUserMessageIds.delete(pendingMessage.pendingId);
6166
- activeEntry.pendingUserMessages.push(pendingMessage);
6167
- await this.broadcastEntryState(activeEntry);
6168
- try {
6169
- await activeEntry.session.prompt(message, {
6170
- ...promptOptions,
6171
- streamingBehavior: queuedStreamingBehavior
6172
- });
6173
- } catch (error) {
6174
- activeEntry.pendingUserMessages = activeEntry.pendingUserMessages.filter((entry) => entry.pendingId !== pendingMessage.pendingId);
6595
+ const clientRequestId = normalizeClientPromptRequestId(body.clientRequestId);
6596
+ const executePromptRequest = async () => {
6597
+ return await this.runSerializedPromptRequest(activeEntry, async () => {
6598
+ if (requestedThinkingLevel && activeEntry.session.thinkingLevel !== requestedThinkingLevel) activeEntry.session.setThinkingLevel(requestedThinkingLevel);
6599
+ const promptOptions = images.length > 0 ? { images } : void 0;
6600
+ const promotedDraft = this.isDraftEntry(activeEntry);
6601
+ const isAlreadyStreaming = this.getEntryStreamingState(activeEntry);
6602
+ const isCompactingOnly = !isAlreadyStreaming && Boolean(this.getEntryCompactingState(activeEntry) || activeEntry.session.isCompacting);
6603
+ const isAlreadyBusy = isAlreadyStreaming || isCompactingOnly;
6604
+ if (clientPendingId && activeEntry.canceledPendingUserMessageIds.delete(clientPendingId)) {
6605
+ await this.broadcastEntryState(activeEntry);
6606
+ return {
6607
+ ok: true,
6608
+ queued: isAlreadyBusy,
6609
+ pendingId: clientPendingId,
6610
+ canceled: true
6611
+ };
6612
+ }
6613
+ if (!this.getSessionFirstMessage(activeEntry)) {
6614
+ activeEntry.firstMessageHint = message.trim();
6615
+ this.startAutoSessionNaming(activeEntry, message.trim(), images.length);
6616
+ }
6617
+ if (activeEntry.uiState.editorText) activeEntry.uiState.editorText = "";
6618
+ if (isAlreadyBusy) {
6619
+ const queuedStreamingBehavior = streamingBehavior ?? "steer";
6620
+ const pendingMessage = createPendingUserMessage(message, images, queuedStreamingBehavior, clientPendingId);
6621
+ this.markSessionUserMessage(activeEntry);
6622
+ activeEntry.canceledPendingUserMessageIds.delete(pendingMessage.pendingId);
6623
+ activeEntry.pendingUserMessages.push(pendingMessage);
6624
+ activeEntry.pendingUserMessages = sortPendingUserMessages(activeEntry.pendingUserMessages);
6625
+ await this.broadcastEntryState(activeEntry);
6626
+ if (isCompactingOnly) {
6627
+ await this.broadcastSessionsAll();
6628
+ return {
6629
+ ok: true,
6630
+ queued: true,
6631
+ pendingId: pendingMessage.pendingId
6632
+ };
6633
+ }
6634
+ try {
6635
+ await activeEntry.session.prompt(message, {
6636
+ ...promptOptions,
6637
+ streamingBehavior: queuedStreamingBehavior
6638
+ });
6639
+ } catch (error) {
6640
+ activeEntry.pendingUserMessages = activeEntry.pendingUserMessages.filter((entry) => entry.pendingId !== pendingMessage.pendingId);
6641
+ this.reconcilePendingUserMessages(activeEntry);
6642
+ await this.broadcastEntryState(activeEntry);
6643
+ throw error;
6644
+ }
6175
6645
  this.reconcilePendingUserMessages(activeEntry);
6176
6646
  await this.broadcastEntryState(activeEntry);
6177
- throw error;
6647
+ await this.broadcastSessionsAll();
6648
+ return {
6649
+ ok: true,
6650
+ queued: true,
6651
+ pendingId: pendingMessage.pendingId
6652
+ };
6653
+ }
6654
+ activeEntry.streamingState = true;
6655
+ this.markSessionUserMessage(activeEntry);
6656
+ if (promotedDraft) {
6657
+ activeEntry.draft = false;
6658
+ if (context.draftKey === activeEntry.key) context.draftKey = void 0;
6659
+ await this.broadcastEntryState(activeEntry);
6660
+ await this.broadcastSessionsAll();
6178
6661
  }
6179
- this.reconcilePendingUserMessages(activeEntry);
6180
- await this.broadcastEntryState(activeEntry);
6181
- await this.broadcastSessionsAll();
6182
- return {
6183
- ok: true,
6184
- queued: true,
6185
- pendingId: pendingMessage.pendingId
6186
- };
6187
- }
6188
- activeEntry.streamingState = true;
6189
- this.markSessionUserMessage(activeEntry);
6190
- if (promotedDraft) {
6191
- activeEntry.draft = false;
6192
- if (context.draftKey === activeEntry.key) context.draftKey = void 0;
6193
- await this.broadcastEntryState(activeEntry);
6194
- await this.broadcastSessionsAll();
6195
- }
6196
- this.broadcastToViewers(activeEntry.key, {
6197
- type: "user_message",
6198
- message,
6199
- images,
6200
- queued: false
6201
- });
6202
- let promptPreflightComplete;
6203
- let promptPreflightSettled = false;
6204
- const promptPreflight = new Promise((resolve) => {
6205
- promptPreflightComplete = resolve;
6206
- });
6207
- const settlePromptPreflight = () => {
6208
- if (promptPreflightSettled) return;
6209
- promptPreflightSettled = true;
6210
- promptPreflightComplete?.();
6211
- };
6212
- const promptPromise = activeEntry.session.prompt(message, {
6213
- ...promptOptions,
6214
- preflightResult: settlePromptPreflight
6215
- });
6216
- const finishPromptIfIdle = async () => {
6217
- if (!activeEntry.streamingState || activeEntry.session.isStreaming) return;
6218
- activeEntry.streamingState = false;
6219
- this.reconcilePendingUserMessages(activeEntry);
6220
- await this.broadcastEntryState(activeEntry);
6221
- await this.broadcastSessionsAll();
6222
- };
6223
- promptPromise.then(() => {}).catch(async (error) => {
6224
- await finishPromptIfIdle();
6225
- console.error("[pico] prompt error", error);
6226
6662
  this.broadcastToViewers(activeEntry.key, {
6227
- type: "request_error",
6228
- scope: "prompt",
6663
+ type: "user_message",
6229
6664
  message,
6230
- error: formatError(error)
6665
+ images,
6666
+ queued: false
6231
6667
  });
6232
- }).finally(() => {
6233
- settlePromptPreflight();
6668
+ let promptPreflightComplete;
6669
+ let promptPreflightSettled = false;
6670
+ const promptPreflight = new Promise((resolve) => {
6671
+ promptPreflightComplete = resolve;
6672
+ });
6673
+ const settlePromptPreflight = () => {
6674
+ if (promptPreflightSettled) return;
6675
+ promptPreflightSettled = true;
6676
+ promptPreflightComplete?.();
6677
+ };
6678
+ const promptPromise = activeEntry.session.prompt(message, {
6679
+ ...promptOptions,
6680
+ preflightResult: settlePromptPreflight
6681
+ });
6682
+ const finishPromptIfIdle = async () => {
6683
+ if (!activeEntry.streamingState || activeEntry.session.isStreaming) return;
6684
+ activeEntry.streamingState = false;
6685
+ this.reconcilePendingUserMessages(activeEntry);
6686
+ await this.broadcastEntryState(activeEntry);
6687
+ await this.broadcastSessionsAll();
6688
+ };
6689
+ promptPromise.then(() => {}).catch(async (error) => {
6690
+ await finishPromptIfIdle();
6691
+ console.error("[pico] prompt error", error);
6692
+ this.broadcastToViewers(activeEntry.key, {
6693
+ type: "request_error",
6694
+ scope: "prompt",
6695
+ message,
6696
+ error: formatError(error)
6697
+ });
6698
+ }).finally(() => {
6699
+ settlePromptPreflight();
6700
+ });
6701
+ await promptPreflight;
6702
+ return {
6703
+ ok: true,
6704
+ queued: false
6705
+ };
6234
6706
  });
6235
- await promptPreflight;
6236
- return {
6237
- ok: true,
6238
- queued: false
6239
- };
6707
+ };
6708
+ if (!clientRequestId) return await executePromptRequest();
6709
+ const existingRequest = activeEntry.promptRequestRecords.get(clientRequestId);
6710
+ if (existingRequest) return await existingRequest.promise;
6711
+ this.prunePromptRequestRecords(activeEntry);
6712
+ const promptPromise = executePromptRequest().catch((error) => {
6713
+ activeEntry.promptRequestRecords.delete(clientRequestId);
6714
+ throw error;
6240
6715
  });
6716
+ activeEntry.promptRequestRecords.set(clientRequestId, {
6717
+ createdAt: Date.now(),
6718
+ promise: promptPromise
6719
+ });
6720
+ return await promptPromise;
6241
6721
  }
6242
6722
  async reorderPendingMessages(request, body) {
6243
6723
  const { activeEntry } = await this.resolveRequest(request);
@@ -6274,6 +6754,7 @@ var PicoRuntime = class {
6274
6754
  throw new Error("Pending prompt not found");
6275
6755
  }
6276
6756
  activeEntry.canceledPendingUserMessageIds.delete(pendingId);
6757
+ activeEntry.pausedPendingUserMessageIds.delete(pendingId);
6277
6758
  pendingMessages.splice(pendingIndex, 1);
6278
6759
  await this.replacePendingUserMessages(activeEntry, pendingMessages);
6279
6760
  return {
@@ -6281,13 +6762,42 @@ var PicoRuntime = class {
6281
6762
  pendingId
6282
6763
  };
6283
6764
  }
6765
+ async startPendingQueue(request) {
6766
+ const { activeEntry } = await this.resolveRequest(request);
6767
+ if (activeEntry.pendingUserMessages.length === 0) return {
6768
+ ok: true,
6769
+ pendingMessages: []
6770
+ };
6771
+ if (this.getEntryStreamingState(activeEntry) || activeEntry.session.isStreaming || this.getEntryCompactingState(activeEntry) || activeEntry.session.isCompacting) throw new Error("Wait for the current response to finish before starting the queue.");
6772
+ activeEntry.pausedPendingUserMessageIds.clear();
6773
+ await this.drainPendingUserMessagesWhenIdle(activeEntry);
6774
+ return {
6775
+ ok: true,
6776
+ pendingMessages: activeEntry.pendingUserMessages.map((message) => ({
6777
+ pendingId: message.pendingId,
6778
+ text: message.text,
6779
+ images: message.images,
6780
+ streamingBehavior: message.streamingBehavior
6781
+ }))
6782
+ };
6783
+ }
6284
6784
  async abort(request) {
6285
6785
  const { activeEntry } = await this.resolveRequest(request);
6286
6786
  activeEntry.doneNotificationSuppressed = true;
6287
6787
  this.clearPendingSessionDone(activeEntry);
6788
+ const pendingMessages = activeEntry.pendingUserMessages.map((message) => clonePendingUserMessage(message));
6789
+ if (pendingMessages.length > 0) for (const message of pendingMessages) activeEntry.pausedPendingUserMessageIds.add(message.pendingId);
6790
+ activeEntry.pendingQueueMutation = true;
6791
+ try {
6792
+ activeEntry.session.clearQueue?.();
6793
+ activeEntry.pendingUserMessages = pendingMessages;
6794
+ } finally {
6795
+ activeEntry.pendingQueueMutation = false;
6796
+ }
6288
6797
  activeEntry.session.abortCompaction?.();
6289
6798
  activeEntry.session.abortBranchSummary?.();
6290
6799
  await activeEntry.session.abort();
6800
+ await this.broadcastEntryState(activeEntry);
6291
6801
  return { ok: true };
6292
6802
  }
6293
6803
  async createNewSession(request, body) {
@@ -6398,7 +6908,8 @@ var PicoRuntime = class {
6398
6908
  return {
6399
6909
  ok: true,
6400
6910
  model: serializeModel(activeEntry.session.model),
6401
- thinkingLevel: activeEntry.session.thinkingLevel
6911
+ thinkingLevel: activeEntry.session.thinkingLevel,
6912
+ availableThinkingLevels: activeEntry.session.getAvailableThinkingLevels()
6402
6913
  };
6403
6914
  }
6404
6915
  async setThinking(request, body) {
@@ -6566,13 +7077,18 @@ var PicoRuntime = class {
6566
7077
  const { context, activeEntry } = await this.resolveRequest(request);
6567
7078
  const entryId = typeof body.entryId === "string" ? body.entryId.trim() : "";
6568
7079
  if (!entryId) throw new Error("entryId is required");
7080
+ const forkPosition = body.position === "at" ? "at" : "before";
7081
+ const isEditFork = forkPosition === "before";
6569
7082
  const currentManager = activeEntry.session.sessionManager;
6570
7083
  const selectedEntry = currentManager.getEntry?.(entryId);
6571
7084
  const selectedParentId = typeof selectedEntry?.parentId === "string" ? selectedEntry.parentId : void 0;
6572
- if (!selectedEntry || selectedEntry.type !== "message" || selectedEntry.message?.role !== "user") throw new Error("Invalid entry ID for forking");
6573
- const selectedText = extractMessageText(selectedEntry.message);
7085
+ const selectedRole = typeof selectedEntry?.message?.role === "string" ? selectedEntry.message.role : "";
7086
+ if (!selectedEntry || selectedEntry.type !== "message" || (isEditFork ? selectedRole !== "user" : selectedRole !== "assistant")) throw new Error("Invalid entry ID for forking");
7087
+ const selectedText = isEditFork ? extractMessageText(selectedEntry.message) : "";
6574
7088
  const previousSessionFile = activeEntry.session.sessionFile;
6575
7089
  const sourceSessionDir = currentManager.getSessionDir?.();
7090
+ const forkLeafId = isEditFork ? selectedParentId : entryId;
7091
+ const draft = isEditFork ? !selectedParentId : false;
6576
7092
  let nextEntry;
6577
7093
  if (currentManager.isPersisted?.()) {
6578
7094
  const sdk = await this.getSdk();
@@ -6581,7 +7097,7 @@ var PicoRuntime = class {
6581
7097
  const sourceManager = sdk.SessionManager.open(currentSessionFile, sourceSessionDir, activeEntry.cwd);
6582
7098
  const runtime = await this.createSessionRuntime(sourceManager);
6583
7099
  try {
6584
- const result = await runtime.fork(entryId, { position: "before" });
7100
+ const result = await runtime.fork(entryId, { position: forkPosition });
6585
7101
  if (result.cancelled) {
6586
7102
  await runtime.dispose();
6587
7103
  return {
@@ -6589,8 +7105,8 @@ var PicoRuntime = class {
6589
7105
  cancelled: true
6590
7106
  };
6591
7107
  }
6592
- nextEntry = await this.createSessionEntryFromRuntime(runtime, { draft: !selectedParentId });
6593
- if (result.selectedText) nextEntry.uiState.editorText = result.selectedText;
7108
+ nextEntry = await this.createSessionEntryFromRuntime(runtime, { draft });
7109
+ if (isEditFork && result.selectedText) nextEntry.uiState.editorText = result.selectedText;
6594
7110
  } catch (error) {
6595
7111
  await runtime.dispose().catch(() => {});
6596
7112
  throw error;
@@ -6601,30 +7117,102 @@ var PicoRuntime = class {
6601
7117
  reason: "fork",
6602
7118
  previousSessionFile
6603
7119
  };
6604
- const sessionManager = await this.createForkedInMemorySessionManager(currentManager, selectedParentId, previousSessionFile);
7120
+ const sessionManager = await this.createForkedInMemorySessionManager(currentManager, forkLeafId, previousSessionFile);
6605
7121
  nextEntry = await this.createSessionEntry(sessionManager, {
6606
- draft: !selectedParentId,
7122
+ draft,
6607
7123
  sessionStartEvent
6608
7124
  });
6609
7125
  }
6610
- if (!nextEntry.uiState.editorText) nextEntry.uiState.editorText = selectedText;
7126
+ if (isEditFork && !nextEntry.uiState.editorText) nextEntry.uiState.editorText = selectedText;
6611
7127
  const baseName = cleanupSessionNameCandidate(activeEntry.session.sessionName) || getSessionListTitle({
6612
7128
  name: activeEntry.session.sessionName,
6613
7129
  firstMessage: this.getSessionFirstMessage(activeEntry)
6614
7130
  });
6615
- if (baseName) nextEntry.session.setSessionName(clampSessionNameLength(`${baseName} fork`));
6616
- nextEntry.uiState.editorText = selectedText;
6617
- if (!selectedParentId) nextEntry.firstMessageHint = selectedText;
7131
+ if (baseName) nextEntry.session.setSessionName(clampSessionNameLength(`${baseName} ${isEditFork ? "fork" : "branch"}`));
7132
+ if (isEditFork) {
7133
+ nextEntry.uiState.editorText = selectedText;
7134
+ if (!selectedParentId) nextEntry.firstMessageHint = selectedText;
7135
+ } else nextEntry.uiState.editorText = "";
6618
7136
  this.touchSessionEntry(nextEntry);
6619
7137
  await this.activateContextSession(context, nextEntry);
6620
7138
  await this.broadcastSessionsAll();
6621
7139
  return {
6622
7140
  ok: true,
6623
7141
  draft: this.isDraftEntry(nextEntry),
7142
+ sessionKey: nextEntry.key,
6624
7143
  sessionId: nextEntry.session.sessionId,
6625
7144
  sessionFile: nextEntry.session.sessionFile
6626
7145
  };
6627
7146
  }
7147
+ async generateSessionName(request, body) {
7148
+ const sessionPath = typeof body.path === "string" ? body.path.trim() : "";
7149
+ let source;
7150
+ let temporaryRuntime;
7151
+ try {
7152
+ if (sessionPath) {
7153
+ const targetEntry = this.sessionEntries.get(sessionPath);
7154
+ if (targetEntry) {
7155
+ const firstPrompt = this.getSessionFirstPromptForNaming(targetEntry);
7156
+ if (firstPrompt) source = {
7157
+ cwd: targetEntry.cwd,
7158
+ model: targetEntry.session.model,
7159
+ modelRegistry: targetEntry.services.modelRegistry,
7160
+ firstPrompt
7161
+ };
7162
+ } else {
7163
+ const sessionManager = (await this.getSdk()).SessionManager.open(sessionPath);
7164
+ this.restoreSessionTreeLeafOverride(sessionManager, sessionPath);
7165
+ temporaryRuntime = await this.createSessionRuntime(sessionManager);
7166
+ const firstPrompt = this.getFirstPromptForNamingFromMessages(temporaryRuntime.session.messages);
7167
+ if (firstPrompt) source = {
7168
+ cwd: temporaryRuntime.cwd,
7169
+ model: temporaryRuntime.session.model,
7170
+ modelRegistry: temporaryRuntime.services.modelRegistry,
7171
+ firstPrompt
7172
+ };
7173
+ }
7174
+ } else {
7175
+ const { activeEntry } = await this.resolveRequest(request);
7176
+ const firstPrompt = this.getSessionFirstPromptForNaming(activeEntry);
7177
+ if (firstPrompt) source = {
7178
+ cwd: activeEntry.cwd,
7179
+ model: activeEntry.session.model,
7180
+ modelRegistry: activeEntry.services.modelRegistry,
7181
+ firstPrompt
7182
+ };
7183
+ }
7184
+ if (!source) throw new Error("Session has no user message to name from");
7185
+ const heuristic = deriveHeuristicSessionNameAttempt(source.firstPrompt.text, source.firstPrompt.imageCount);
7186
+ const generated = await generateSessionNameWithLlm({
7187
+ cwd: source.cwd,
7188
+ model: source.model,
7189
+ services: { modelRegistry: source.modelRegistry }
7190
+ }, source.firstPrompt.text, source.firstPrompt.imageCount);
7191
+ if (generated.name) return {
7192
+ ok: true,
7193
+ name: clampSessionNameLength(generated.name),
7194
+ source: "llm"
7195
+ };
7196
+ if (heuristic.name) return {
7197
+ ok: true,
7198
+ name: clampSessionNameLength(heuristic.name),
7199
+ source: "heuristic",
7200
+ reason: generated.reason || heuristic.reason
7201
+ };
7202
+ const fallbackName = cleanupSessionNameCandidate(normalizeSessionListTitle(source.firstPrompt.text, SESSION_NAME_MAX_LENGTH));
7203
+ if (fallbackName) return {
7204
+ ok: true,
7205
+ name: clampSessionNameLength(fallbackName),
7206
+ source: "heuristic",
7207
+ reason: generated.reason || heuristic.reason
7208
+ };
7209
+ throw new Error(generated.reason || heuristic.reason || "Could not generate a session name from the first user message");
7210
+ } finally {
7211
+ await temporaryRuntime?.dispose().catch((error) => {
7212
+ console.error("[pico] temporary session dispose error:", error);
7213
+ });
7214
+ }
7215
+ }
6628
7216
  async renameSession(body) {
6629
7217
  const sessionPath = typeof body.path === "string" ? body.path : "";
6630
7218
  const nextName = typeof body.name === "string" ? body.name.trim() : "";
@@ -6864,6 +7452,7 @@ var PicoRuntime = class {
6864
7452
  }
6865
7453
  async getAuthProviders(request) {
6866
7454
  const { activeEntry } = await this.resolveRequest(request);
7455
+ this.refreshModelRegistries();
6867
7456
  const modelRegistry = activeEntry.services.modelRegistry;
6868
7457
  const authStorage = this.requireAuthStorage(activeEntry);
6869
7458
  const oauthProviders = authStorage.getOAuthProviders();
@@ -6908,7 +7497,8 @@ var PicoRuntime = class {
6908
7497
  ...authStatus(providerId),
6909
7498
  configured: true
6910
7499
  };
6911
- }).sort((left, right) => left.name.localeCompare(right.name))
7500
+ }).sort((left, right) => left.name.localeCompare(right.name)),
7501
+ availableModels: this.listAvailableModels(activeEntry)
6912
7502
  };
6913
7503
  }
6914
7504
  async saveProviderApiKey(request, body) {
@@ -7077,6 +7667,7 @@ var PicoRuntime = class {
7077
7667
  activeEntry.compactingState = false;
7078
7668
  await this.broadcastEntryState(activeEntry);
7079
7669
  await this.broadcastSessionsAll();
7670
+ this.drainPendingUserMessagesWhenIdle(activeEntry);
7080
7671
  }
7081
7672
  }
7082
7673
  async resolveUiRequest(id, body) {
@@ -7098,11 +7689,52 @@ var PicoRuntime = class {
7098
7689
  }
7099
7690
  async createTerminalEventsResponse(request, id) {
7100
7691
  const { context, activeEntry } = await this.resolveRequest(request);
7101
- return this.terminalManager.createEventsResponse(id, this.getTerminalScopeKey(activeEntry, context), request.signal);
7692
+ const lastSeq = parsePositiveInteger(request.headers.get("last-event-id") || new URL(request.url).searchParams.get("lastSeq"));
7693
+ return this.terminalManager.createEventsResponse(id, this.getTerminalScopeKey(activeEntry, context), request.signal, lastSeq);
7694
+ }
7695
+ async createTerminalWebSocketContext(request) {
7696
+ try {
7697
+ const id = terminalWebSocketIdFromRequest(request);
7698
+ const { context, activeEntry } = await this.resolveRequest(request);
7699
+ const scopeKey = this.getTerminalScopeKey(activeEntry, context);
7700
+ const lastSeq = parsePositiveInteger(request.headers.get("last-event-id") || new URL(request.url).searchParams.get("lastSeq"));
7701
+ this.terminalManager.validateTerminal(id, scopeKey);
7702
+ return {
7703
+ terminalId: id,
7704
+ terminalLastSeq: lastSeq,
7705
+ terminalScopeKey: scopeKey
7706
+ };
7707
+ } catch (error) {
7708
+ if (error instanceof Response) throw error;
7709
+ throw new Response(safeErrorMessage(error), { status: 400 });
7710
+ }
7711
+ }
7712
+ openTerminalWebSocket(peer) {
7713
+ const context = terminalWebSocketContextFromPeer(peer);
7714
+ const cleanup = this.terminalManager.connectWebSocket({
7715
+ id: context.terminalId,
7716
+ lastSeq: context.terminalLastSeq,
7717
+ peer,
7718
+ scopeKey: context.terminalScopeKey
7719
+ });
7720
+ peer.context.terminalCleanup = cleanup;
7721
+ }
7722
+ handleTerminalWebSocketMessage(peer, message) {
7723
+ const context = terminalWebSocketContextFromPeer(peer);
7724
+ this.terminalManager.handleWebSocketMessage({
7725
+ id: context.terminalId,
7726
+ message,
7727
+ peer,
7728
+ scopeKey: context.terminalScopeKey
7729
+ });
7730
+ }
7731
+ closeTerminalWebSocket(peer) {
7732
+ terminalWebSocketCleanupFromPeer(peer)?.();
7733
+ delete peer.context.terminalCleanup;
7102
7734
  }
7103
7735
  async writeTerminalInput(request, id, body) {
7104
7736
  const { context, activeEntry } = await this.resolveRequest(request);
7105
- this.terminalManager.writeTerminal(id, this.getTerminalScopeKey(activeEntry, context), body.data);
7737
+ this.terminalManager.writeTerminal(id, this.getTerminalScopeKey(activeEntry, context), body.data, body.inputSeq);
7106
7738
  return { ok: true };
7107
7739
  }
7108
7740
  async resizeTerminal(request, id, body) {
@@ -7180,7 +7812,7 @@ function getPicoRuntime() {
7180
7812
  globalThis.__picoRuntime ??= new PicoRuntime();
7181
7813
  return globalThis.__picoRuntime;
7182
7814
  }
7183
- var Route$57 = createFileRoute("/events")({ server: { handlers: { GET: ({ request }) => getPicoRuntime().createEventsResponse(request) } } });
7815
+ var Route$60 = createFileRoute("/events")({ server: { handlers: { GET: ({ request }) => getPicoRuntime().createEventsResponse(request) } } });
7184
7816
  function jsonResponse(data, init = 200) {
7185
7817
  const responseInit = typeof init === "number" ? { status: init } : init;
7186
7818
  const headers = new Headers(responseInit.headers);
@@ -7216,7 +7848,7 @@ function routeErrorResponse(error, fallbackMessage) {
7216
7848
  async function readRequestJson(request) {
7217
7849
  return await request.json().catch(() => ({}));
7218
7850
  }
7219
- var Route$56 = createFileRoute("/api/thinking")({ server: { handlers: { POST: async ({ request }) => {
7851
+ var Route$59 = createFileRoute("/api/thinking")({ server: { handlers: { POST: async ({ request }) => {
7220
7852
  try {
7221
7853
  const body = await readRequestJson(request);
7222
7854
  return jsonResponse(await getPicoRuntime().setThinking(request, body));
@@ -7224,7 +7856,7 @@ var Route$56 = createFileRoute("/api/thinking")({ server: { handlers: { POST: as
7224
7856
  return routeErrorResponse(error, "Failed to update thinking level");
7225
7857
  }
7226
7858
  } } } });
7227
- var Route$55 = createFileRoute("/api/terminal")({ server: { handlers: { POST: async ({ request }) => {
7859
+ var Route$58 = createFileRoute("/api/terminal")({ server: { handlers: { POST: async ({ request }) => {
7228
7860
  try {
7229
7861
  const body = await readRequestJson(request);
7230
7862
  return jsonResponse(await getPicoRuntime().createTerminal(request, body));
@@ -7232,7 +7864,7 @@ var Route$55 = createFileRoute("/api/terminal")({ server: { handlers: { POST: as
7232
7864
  return routeErrorResponse(error, "Failed to create terminal");
7233
7865
  }
7234
7866
  } } } });
7235
- var Route$54 = createFileRoute("/api/slash-command")({ server: { handlers: { POST: async ({ request }) => {
7867
+ var Route$57 = createFileRoute("/api/slash-command")({ server: { handlers: { POST: async ({ request }) => {
7236
7868
  try {
7237
7869
  const body = await readRequestJson(request);
7238
7870
  return jsonResponse(await getPicoRuntime().runSlashCommand(request, body));
@@ -7240,7 +7872,7 @@ var Route$54 = createFileRoute("/api/slash-command")({ server: { handlers: { POS
7240
7872
  return routeErrorResponse(error, "Failed to run slash command");
7241
7873
  }
7242
7874
  } } } });
7243
- var Route$53 = createFileRoute("/api/provider-usage")({ server: { handlers: { GET: async ({ request }) => {
7875
+ var Route$56 = createFileRoute("/api/provider-usage")({ server: { handlers: { GET: async ({ request }) => {
7244
7876
  const provider = new URL(request.url).searchParams.get("provider") || void 0;
7245
7877
  try {
7246
7878
  return jsonResponse({
@@ -7251,7 +7883,7 @@ var Route$53 = createFileRoute("/api/provider-usage")({ server: { handlers: { GE
7251
7883
  return routeErrorResponse(error, "Failed to read provider usage");
7252
7884
  }
7253
7885
  } } } });
7254
- var Route$52 = createFileRoute("/api/prompt")({ server: { handlers: { POST: async ({ request }) => {
7886
+ var Route$55 = createFileRoute("/api/prompt")({ server: { handlers: { POST: async ({ request }) => {
7255
7887
  try {
7256
7888
  const body = await readRequestJson(request);
7257
7889
  return jsonResponse(await getPicoRuntime().prompt(request, body));
@@ -7806,7 +8438,7 @@ async function listFileReferenceEntries(query, baseCwd, { isQuotedPrefix = false
7806
8438
  };
7807
8439
  });
7808
8440
  }
7809
- var Route$51 = createFileRoute("/api/path-completions")({ server: { handlers: { POST: async ({ request }) => {
8441
+ var Route$54 = createFileRoute("/api/path-completions")({ server: { handlers: { POST: async ({ request }) => {
7810
8442
  try {
7811
8443
  const body = await readRequestJson(request);
7812
8444
  const { context, activeEntry } = await getPicoRuntime().resolveRequest(request);
@@ -7822,7 +8454,7 @@ var Route$51 = createFileRoute("/api/path-completions")({ server: { handlers: {
7822
8454
  return routeErrorResponse(error, "Failed to list path completions");
7823
8455
  }
7824
8456
  } } } });
7825
- var Route$50 = createFileRoute("/api/model")({ server: { handlers: { POST: async ({ request }) => {
8457
+ var Route$53 = createFileRoute("/api/model")({ server: { handlers: { POST: async ({ request }) => {
7826
8458
  try {
7827
8459
  const body = await readRequestJson(request);
7828
8460
  return jsonResponse(await getPicoRuntime().setModel(request, body));
@@ -7830,7 +8462,7 @@ var Route$50 = createFileRoute("/api/model")({ server: { handlers: { POST: async
7830
8462
  return routeErrorResponse(error, "Failed to update model");
7831
8463
  }
7832
8464
  } } } });
7833
- var Route$49 = createFileRoute("/api/highlight")({ server: { handlers: { POST: async ({ request }) => {
8465
+ var Route$52 = createFileRoute("/api/highlight")({ server: { handlers: { POST: async ({ request }) => {
7834
8466
  try {
7835
8467
  const body = await readRequestJson(request);
7836
8468
  return jsonResponse(await getPicoRuntime().highlightCode(body.code, body.language));
@@ -7838,7 +8470,7 @@ var Route$49 = createFileRoute("/api/highlight")({ server: { handlers: { POST: a
7838
8470
  return routeErrorResponse(error, "Failed to highlight code");
7839
8471
  }
7840
8472
  } } } });
7841
- var Route$48 = createFileRoute("/api/git-status")({ server: { handlers: { GET: async ({ request }) => {
8473
+ var Route$51 = createFileRoute("/api/git-status")({ server: { handlers: { GET: async ({ request }) => {
7842
8474
  const requestedCwd = new URL(request.url).searchParams.get("cwd") || "";
7843
8475
  if (!requestedCwd.trim()) return errorResponse("cwd is required");
7844
8476
  try {
@@ -7853,7 +8485,7 @@ var Route$48 = createFileRoute("/api/git-status")({ server: { handlers: { GET: a
7853
8485
  return routeErrorResponse(error, "Failed to read git status");
7854
8486
  }
7855
8487
  } } } });
7856
- var Route$47 = createFileRoute("/api/git-stage")({ server: { handlers: { POST: async ({ request }) => {
8488
+ var Route$50 = createFileRoute("/api/git-stage")({ server: { handlers: { POST: async ({ request }) => {
7857
8489
  let errorFallback = "Failed to stage changes";
7858
8490
  try {
7859
8491
  const body = await readRequestJson(request);
@@ -7878,7 +8510,7 @@ var Route$47 = createFileRoute("/api/git-stage")({ server: { handlers: { POST: a
7878
8510
  return routeErrorResponse(error, errorFallback);
7879
8511
  }
7880
8512
  } } } });
7881
- var Route$46 = createFileRoute("/api/git-review")({ server: { handlers: { GET: async ({ request }) => {
8513
+ var Route$49 = createFileRoute("/api/git-review")({ server: { handlers: { GET: async ({ request }) => {
7882
8514
  const url = new URL(request.url);
7883
8515
  const requestedCwd = url.searchParams.get("cwd") || "";
7884
8516
  const requestedPath = url.searchParams.get("path") || "";
@@ -7901,7 +8533,7 @@ var Route$46 = createFileRoute("/api/git-review")({ server: { handlers: { GET: a
7901
8533
  return routeErrorResponse(error, "Failed to read git review file");
7902
8534
  }
7903
8535
  } } } });
7904
- var Route$45 = createFileRoute("/api/git-push")({ server: { handlers: { POST: async ({ request }) => {
8536
+ var Route$48 = createFileRoute("/api/git-push")({ server: { handlers: { POST: async ({ request }) => {
7905
8537
  try {
7906
8538
  const body = await readRequestJson(request);
7907
8539
  const requestedCwd = typeof body.cwd === "string" ? body.cwd : "";
@@ -7920,7 +8552,7 @@ var Route$45 = createFileRoute("/api/git-push")({ server: { handlers: { POST: as
7920
8552
  return routeErrorResponse(error, "Failed to push changes");
7921
8553
  }
7922
8554
  } } } });
7923
- var Route$44 = createFileRoute("/api/git-pull")({ server: { handlers: { POST: async ({ request }) => {
8555
+ var Route$47 = createFileRoute("/api/git-pull")({ server: { handlers: { POST: async ({ request }) => {
7924
8556
  try {
7925
8557
  const body = await readRequestJson(request);
7926
8558
  const requestedCwd = typeof body.cwd === "string" ? body.cwd : "";
@@ -7938,7 +8570,7 @@ var Route$44 = createFileRoute("/api/git-pull")({ server: { handlers: { POST: as
7938
8570
  return routeErrorResponse(error, "Failed to pull changes");
7939
8571
  }
7940
8572
  } } } });
7941
- var Route$43 = createFileRoute("/api/git-discard")({ server: { handlers: { POST: async ({ request }) => {
8573
+ var Route$46 = createFileRoute("/api/git-discard")({ server: { handlers: { POST: async ({ request }) => {
7942
8574
  try {
7943
8575
  const body = await readRequestJson(request);
7944
8576
  const action = typeof body.action === "string" ? body.action : "";
@@ -7962,7 +8594,7 @@ var Route$43 = createFileRoute("/api/git-discard")({ server: { handlers: { POST:
7962
8594
  return routeErrorResponse(error, "Failed to discard changes");
7963
8595
  }
7964
8596
  } } } });
7965
- var Route$42 = createFileRoute("/api/git-diff")({ server: { handlers: { GET: async ({ request }) => {
8597
+ var Route$45 = createFileRoute("/api/git-diff")({ server: { handlers: { GET: async ({ request }) => {
7966
8598
  const url = new URL(request.url);
7967
8599
  const requestedCwd = url.searchParams.get("cwd") || "";
7968
8600
  const requestedPath = url.searchParams.get("path") || "";
@@ -7982,7 +8614,7 @@ var Route$42 = createFileRoute("/api/git-diff")({ server: { handlers: { GET: asy
7982
8614
  return routeErrorResponse(error, "Failed to read git diff");
7983
8615
  }
7984
8616
  } } } });
7985
- var Route$41 = createFileRoute("/api/git-commit-remote-url")({ server: { handlers: { GET: async ({ request }) => {
8617
+ var Route$44 = createFileRoute("/api/git-commit-remote-url")({ server: { handlers: { GET: async ({ request }) => {
7986
8618
  try {
7987
8619
  const url = new URL(request.url);
7988
8620
  const requestedCwd = url.searchParams.get("cwd") || "";
@@ -8003,7 +8635,7 @@ var Route$41 = createFileRoute("/api/git-commit-remote-url")({ server: { handler
8003
8635
  return routeErrorResponse(error, "Failed to read commit remote URL");
8004
8636
  }
8005
8637
  } } } });
8006
- var Route$40 = createFileRoute("/api/git-commit-message")({ server: { handlers: { POST: async ({ request }) => {
8638
+ var Route$43 = createFileRoute("/api/git-commit-message")({ server: { handlers: { POST: async ({ request }) => {
8007
8639
  try {
8008
8640
  const body = await readRequestJson(request);
8009
8641
  const requestedCwd = typeof body.cwd === "string" ? body.cwd : "";
@@ -8022,7 +8654,7 @@ var Route$40 = createFileRoute("/api/git-commit-message")({ server: { handlers:
8022
8654
  return routeErrorResponse(error, "Failed to generate commit message");
8023
8655
  }
8024
8656
  } } } });
8025
- var Route$39 = createFileRoute("/api/git-commit-files")({ server: { handlers: { GET: async ({ request }) => {
8657
+ var Route$42 = createFileRoute("/api/git-commit-files")({ server: { handlers: { GET: async ({ request }) => {
8026
8658
  try {
8027
8659
  const url = new URL(request.url);
8028
8660
  const requestedCwd = url.searchParams.get("cwd") || "";
@@ -8045,7 +8677,7 @@ function normalizeGitCommitDiffMode(value) {
8045
8677
  if (value === "head" || value === "previous") return value;
8046
8678
  return "commit";
8047
8679
  }
8048
- var Route$38 = createFileRoute("/api/git-commit-diff")({ server: { handlers: { GET: async ({ request }) => {
8680
+ var Route$41 = createFileRoute("/api/git-commit-diff")({ server: { handlers: { GET: async ({ request }) => {
8049
8681
  try {
8050
8682
  const url = new URL(request.url);
8051
8683
  const requestedCwd = url.searchParams.get("cwd") || "";
@@ -8082,7 +8714,7 @@ var gitCommitActions = new Set([
8082
8714
  "drop",
8083
8715
  "squash"
8084
8716
  ]);
8085
- var Route$37 = createFileRoute("/api/git-commit-action")({ server: { handlers: { POST: async ({ request }) => {
8717
+ var Route$40 = createFileRoute("/api/git-commit-action")({ server: { handlers: { POST: async ({ request }) => {
8086
8718
  try {
8087
8719
  const body = await readRequestJson(request);
8088
8720
  const requestedCwd = typeof body.cwd === "string" ? body.cwd : "";
@@ -8108,7 +8740,7 @@ var Route$37 = createFileRoute("/api/git-commit-action")({ server: { handlers: {
8108
8740
  return routeErrorResponse(error, "Failed to run commit action");
8109
8741
  }
8110
8742
  } } } });
8111
- var Route$36 = createFileRoute("/api/git-commit")({ server: { handlers: { POST: async ({ request }) => {
8743
+ var Route$39 = createFileRoute("/api/git-commit")({ server: { handlers: { POST: async ({ request }) => {
8112
8744
  try {
8113
8745
  const body = await readRequestJson(request);
8114
8746
  const requestedCwd = typeof body.cwd === "string" ? body.cwd : "";
@@ -8133,7 +8765,7 @@ var Route$36 = createFileRoute("/api/git-commit")({ server: { handlers: { POST:
8133
8765
  return routeErrorResponse(error, "Failed to commit changes");
8134
8766
  }
8135
8767
  } } } });
8136
- var Route$35 = createFileRoute("/api/git-checkout")({ server: { handlers: { POST: async ({ request }) => {
8768
+ var Route$38 = createFileRoute("/api/git-checkout")({ server: { handlers: { POST: async ({ request }) => {
8137
8769
  try {
8138
8770
  const body = await readRequestJson(request);
8139
8771
  const requestedCwd = typeof body.cwd === "string" ? body.cwd : "";
@@ -8183,7 +8815,7 @@ function getRuntimeRequestForGitChanges(request, url) {
8183
8815
  method: request.method
8184
8816
  });
8185
8817
  }
8186
- var Route$34 = createFileRoute("/api/git-changes")({ server: { handlers: { GET: async ({ request }) => {
8818
+ var Route$37 = createFileRoute("/api/git-changes")({ server: { handlers: { GET: async ({ request }) => {
8187
8819
  const url = new URL(request.url);
8188
8820
  const requestedCwd = url.searchParams.get("cwd") || "";
8189
8821
  const gitScope = getGitChangesScope(url);
@@ -8253,7 +8885,7 @@ var Route$34 = createFileRoute("/api/git-changes")({ server: { handlers: { GET:
8253
8885
  return routeErrorResponse(error, "Failed to read git changes");
8254
8886
  }
8255
8887
  } } } });
8256
- var Route$33 = createFileRoute("/api/file-completions")({ server: { handlers: { POST: async ({ request }) => {
8888
+ var Route$36 = createFileRoute("/api/file-completions")({ server: { handlers: { POST: async ({ request }) => {
8257
8889
  try {
8258
8890
  const body = await readRequestJson(request);
8259
8891
  const { context, activeEntry } = await getPicoRuntime().resolveRequest(request);
@@ -8270,7 +8902,7 @@ var Route$33 = createFileRoute("/api/file-completions")({ server: { handlers: {
8270
8902
  return routeErrorResponse(error, "Failed to list file completions");
8271
8903
  }
8272
8904
  } } } });
8273
- var Route$32 = createFileRoute("/api/directory-sessions-indexes")({ server: { handlers: { GET: async ({ request }) => {
8905
+ var Route$35 = createFileRoute("/api/directory-sessions-indexes")({ server: { handlers: { GET: async ({ request }) => {
8274
8906
  const directories = new URL(request.url).searchParams.getAll("directory");
8275
8907
  if (directories.length === 0) return errorResponse("at least one directory is required");
8276
8908
  try {
@@ -8279,7 +8911,7 @@ var Route$32 = createFileRoute("/api/directory-sessions-indexes")({ server: { ha
8279
8911
  return routeErrorResponse(error, "Failed to list directory session indexes");
8280
8912
  }
8281
8913
  } } } });
8282
- var Route$31 = createFileRoute("/api/directory-sessions-index")({ server: { handlers: { GET: async ({ request }) => {
8914
+ var Route$34 = createFileRoute("/api/directory-sessions-index")({ server: { handlers: { GET: async ({ request }) => {
8283
8915
  const directory = new URL(request.url).searchParams.get("directory") || "";
8284
8916
  if (!directory.trim()) return errorResponse("directory is required");
8285
8917
  try {
@@ -8288,7 +8920,7 @@ var Route$31 = createFileRoute("/api/directory-sessions-index")({ server: { hand
8288
8920
  return routeErrorResponse(error, "Failed to list directory session index");
8289
8921
  }
8290
8922
  } } } });
8291
- var Route$30 = createFileRoute("/api/directory-sessions")({ server: { handlers: { GET: async ({ request }) => {
8923
+ var Route$33 = createFileRoute("/api/directory-sessions")({ server: { handlers: { GET: async ({ request }) => {
8292
8924
  const url = new URL(request.url);
8293
8925
  const directory = url.searchParams.get("directory") || "";
8294
8926
  const offset = Number.parseInt(url.searchParams.get("offset") || "0", 10);
@@ -8303,7 +8935,7 @@ var Route$30 = createFileRoute("/api/directory-sessions")({ server: { handlers:
8303
8935
  return routeErrorResponse(error, "Failed to list directory sessions");
8304
8936
  }
8305
8937
  } } } });
8306
- var Route$29 = createFileRoute("/api/directory-search")({ server: { handlers: { POST: async ({ request }) => {
8938
+ var Route$32 = createFileRoute("/api/directory-search")({ server: { handlers: { POST: async ({ request }) => {
8307
8939
  try {
8308
8940
  const body = await readRequestJson(request);
8309
8941
  const { context, activeEntry } = await getPicoRuntime().resolveRequest(request);
@@ -8319,14 +8951,14 @@ var Route$29 = createFileRoute("/api/directory-search")({ server: { handlers: {
8319
8951
  return routeErrorResponse(error, "Failed to search directories");
8320
8952
  }
8321
8953
  } } } });
8322
- var Route$28 = createFileRoute("/api/abort")({ server: { handlers: { POST: async ({ request }) => {
8954
+ var Route$31 = createFileRoute("/api/abort")({ server: { handlers: { POST: async ({ request }) => {
8323
8955
  try {
8324
8956
  return jsonResponse(await getPicoRuntime().abort(request));
8325
8957
  } catch (error) {
8326
8958
  return routeErrorResponse(error, "Failed to abort session");
8327
8959
  }
8328
8960
  } } } });
8329
- var Route$27 = createFileRoute("/api/ui/$id")({ server: { handlers: { POST: async ({ request, params }) => {
8961
+ var Route$30 = createFileRoute("/api/ui/$id")({ server: { handlers: { POST: async ({ request, params }) => {
8330
8962
  try {
8331
8963
  const body = await readRequestJson(request);
8332
8964
  return jsonResponse(await getPicoRuntime().resolveUiRequest(params.id, body));
@@ -8334,7 +8966,7 @@ var Route$27 = createFileRoute("/api/ui/$id")({ server: { handlers: { POST: asyn
8334
8966
  return routeErrorResponse(error, "Failed to resolve UI request");
8335
8967
  }
8336
8968
  } } } });
8337
- var Route$26 = createFileRoute("/api/settings/hide-thinking")({ server: { handlers: { POST: async ({ request }) => {
8969
+ var Route$29 = createFileRoute("/api/settings/hide-thinking")({ server: { handlers: { POST: async ({ request }) => {
8338
8970
  try {
8339
8971
  const body = await readRequestJson(request);
8340
8972
  return jsonResponse(await getPicoRuntime().setHideThinking(body));
@@ -8342,7 +8974,7 @@ var Route$26 = createFileRoute("/api/settings/hide-thinking")({ server: { handle
8342
8974
  return routeErrorResponse(error, "Failed to update hide-thinking setting");
8343
8975
  }
8344
8976
  } } } });
8345
- var Route$25 = createFileRoute("/api/sessions/delete")({ server: { handlers: { POST: async ({ request }) => {
8977
+ var Route$28 = createFileRoute("/api/sessions/delete")({ server: { handlers: { POST: async ({ request }) => {
8346
8978
  try {
8347
8979
  const body = await readRequestJson(request);
8348
8980
  return jsonResponse(await getPicoRuntime().deleteSessions(request, body));
@@ -8350,7 +8982,7 @@ var Route$25 = createFileRoute("/api/sessions/delete")({ server: { handlers: { P
8350
8982
  return routeErrorResponse(error, "Failed to delete sessions");
8351
8983
  }
8352
8984
  } } } });
8353
- var Route$24 = createFileRoute("/api/session/tree")({ server: { handlers: {
8985
+ var Route$27 = createFileRoute("/api/session/tree")({ server: { handlers: {
8354
8986
  GET: async ({ request }) => {
8355
8987
  try {
8356
8988
  return jsonResponse(await getPicoRuntime().getSessionTreeForRequest(request));
@@ -8367,14 +8999,14 @@ var Route$24 = createFileRoute("/api/session/tree")({ server: { handlers: {
8367
8999
  }
8368
9000
  }
8369
9001
  } } });
8370
- var Route$23 = createFileRoute("/api/session/select")({ server: { handlers: { POST: async ({ request }) => {
9002
+ var Route$26 = createFileRoute("/api/session/select")({ server: { handlers: { POST: async ({ request }) => {
8371
9003
  try {
8372
9004
  return jsonResponse(await getPicoRuntime().selectSession(request));
8373
9005
  } catch (error) {
8374
9006
  return routeErrorResponse(error, "Failed to select session");
8375
9007
  }
8376
9008
  } } } });
8377
- var Route$22 = createFileRoute("/api/session/rename")({ server: { handlers: { POST: async ({ request }) => {
9009
+ var Route$25 = createFileRoute("/api/session/rename")({ server: { handlers: { POST: async ({ request }) => {
8378
9010
  try {
8379
9011
  const body = await readRequestJson(request);
8380
9012
  return jsonResponse(await getPicoRuntime().renameSession(body));
@@ -8382,7 +9014,7 @@ var Route$22 = createFileRoute("/api/session/rename")({ server: { handlers: { PO
8382
9014
  return routeErrorResponse(error, "Failed to rename session");
8383
9015
  }
8384
9016
  } } } });
8385
- var Route$21 = createFileRoute("/api/session/read-state")({ server: { handlers: { POST: async ({ request }) => {
9017
+ var Route$24 = createFileRoute("/api/session/read-state")({ server: { handlers: { POST: async ({ request }) => {
8386
9018
  try {
8387
9019
  const body = await readRequestJson(request);
8388
9020
  return jsonResponse(await getPicoRuntime().setSessionReadState(request, body));
@@ -8390,7 +9022,7 @@ var Route$21 = createFileRoute("/api/session/read-state")({ server: { handlers:
8390
9022
  return routeErrorResponse(error, "Failed to update session read state");
8391
9023
  }
8392
9024
  } } } });
8393
- var Route$20 = createFileRoute("/api/session/new")({ server: { handlers: { POST: async ({ request }) => {
9025
+ var Route$23 = createFileRoute("/api/session/new")({ server: { handlers: { POST: async ({ request }) => {
8394
9026
  try {
8395
9027
  const body = await readRequestJson(request);
8396
9028
  return jsonResponse(await getPicoRuntime().createNewSession(request, body));
@@ -8398,7 +9030,15 @@ var Route$20 = createFileRoute("/api/session/new")({ server: { handlers: { POST:
8398
9030
  return routeErrorResponse(error, "Failed to create session");
8399
9031
  }
8400
9032
  } } } });
8401
- var Route$19 = createFileRoute("/api/session/move")({ server: { handlers: { POST: async ({ request }) => {
9033
+ var Route$22 = createFileRoute("/api/session/name")({ server: { handlers: { POST: async ({ request }) => {
9034
+ try {
9035
+ const body = await readRequestJson(request);
9036
+ return jsonResponse(await getPicoRuntime().generateSessionName(request, body));
9037
+ } catch (error) {
9038
+ return routeErrorResponse(error, "Failed to generate session name");
9039
+ }
9040
+ } } } });
9041
+ var Route$21 = createFileRoute("/api/session/move")({ server: { handlers: { POST: async ({ request }) => {
8402
9042
  try {
8403
9043
  const body = await readRequestJson(request);
8404
9044
  return jsonResponse(await getPicoRuntime().moveSession(request, body));
@@ -8406,7 +9046,7 @@ var Route$19 = createFileRoute("/api/session/move")({ server: { handlers: { POST
8406
9046
  return routeErrorResponse(error, "Failed to move session");
8407
9047
  }
8408
9048
  } } } });
8409
- var Route$18 = createFileRoute("/api/session/history")({ server: { handlers: { GET: async ({ request }) => {
9049
+ var Route$20 = createFileRoute("/api/session/history")({ server: { handlers: { GET: async ({ request }) => {
8410
9050
  try {
8411
9051
  const url = new URL(request.url);
8412
9052
  const before = Number(url.searchParams.get("before"));
@@ -8419,7 +9059,7 @@ var Route$18 = createFileRoute("/api/session/history")({ server: { handlers: { G
8419
9059
  return routeErrorResponse(error, "Failed to load session history");
8420
9060
  }
8421
9061
  } } } });
8422
- var Route$17 = createFileRoute("/api/session/fork")({ server: { handlers: {
9062
+ var Route$19 = createFileRoute("/api/session/fork")({ server: { handlers: {
8423
9063
  GET: async ({ request }) => {
8424
9064
  try {
8425
9065
  return jsonResponse(await getPicoRuntime().getForkableMessages(request));
@@ -8436,7 +9076,7 @@ var Route$17 = createFileRoute("/api/session/fork")({ server: { handlers: {
8436
9076
  }
8437
9077
  }
8438
9078
  } } });
8439
- var Route$16 = createFileRoute("/api/session/delete")({ server: { handlers: { POST: async ({ request }) => {
9079
+ var Route$18 = createFileRoute("/api/session/delete")({ server: { handlers: { POST: async ({ request }) => {
8440
9080
  try {
8441
9081
  const body = await readRequestJson(request);
8442
9082
  return jsonResponse(await getPicoRuntime().deleteSession(request, body));
@@ -8444,14 +9084,21 @@ var Route$16 = createFileRoute("/api/session/delete")({ server: { handlers: { PO
8444
9084
  return routeErrorResponse(error, "Failed to delete session");
8445
9085
  }
8446
9086
  } } } });
8447
- var Route$15 = createFileRoute("/api/session/clone")({ server: { handlers: { POST: async ({ request }) => {
9087
+ var Route$17 = createFileRoute("/api/session/clone")({ server: { handlers: { POST: async ({ request }) => {
8448
9088
  try {
8449
9089
  return jsonResponse(await getPicoRuntime().cloneSession(request));
8450
9090
  } catch (error) {
8451
9091
  return routeErrorResponse(error, "Failed to clone session");
8452
9092
  }
8453
9093
  } } } });
8454
- var Route$14 = createFileRoute("/api/pending-messages/reorder")({ server: { handlers: { POST: async ({ request }) => {
9094
+ var Route$16 = createFileRoute("/api/pending-messages/start")({ server: { handlers: { POST: async ({ request }) => {
9095
+ try {
9096
+ return jsonResponse(await getPicoRuntime().startPendingQueue(request));
9097
+ } catch (error) {
9098
+ return routeErrorResponse(error, "Failed to start queued prompts");
9099
+ }
9100
+ } } } });
9101
+ var Route$15 = createFileRoute("/api/pending-messages/reorder")({ server: { handlers: { POST: async ({ request }) => {
8455
9102
  try {
8456
9103
  const body = await readRequestJson(request);
8457
9104
  return jsonResponse(await getPicoRuntime().reorderPendingMessages(request, body));
@@ -8459,7 +9106,7 @@ var Route$14 = createFileRoute("/api/pending-messages/reorder")({ server: { hand
8459
9106
  return routeErrorResponse(error, "Failed to update pending prompts");
8460
9107
  }
8461
9108
  } } } });
8462
- var Route$13 = createFileRoute("/api/pending-message/remove")({ server: { handlers: { POST: async ({ request }) => {
9109
+ var Route$14 = createFileRoute("/api/pending-message/remove")({ server: { handlers: { POST: async ({ request }) => {
8463
9110
  try {
8464
9111
  const body = await readRequestJson(request);
8465
9112
  return jsonResponse(await getPicoRuntime().removePendingMessage(request, body));
@@ -8467,7 +9114,7 @@ var Route$13 = createFileRoute("/api/pending-message/remove")({ server: { handle
8467
9114
  return routeErrorResponse(error, "Failed to remove pending prompt");
8468
9115
  }
8469
9116
  } } } });
8470
- var Route$12 = createFileRoute("/api/files/tree")({ server: { handlers: { GET: async ({ request }) => {
9117
+ var Route$13 = createFileRoute("/api/files/tree")({ server: { handlers: { GET: async ({ request }) => {
8471
9118
  const requestedCwd = new URL(request.url).searchParams.get("cwd") || "";
8472
9119
  if (!requestedCwd.trim()) return errorResponse("cwd is required");
8473
9120
  try {
@@ -8484,7 +9131,7 @@ var Route$12 = createFileRoute("/api/files/tree")({ server: { handlers: { GET: a
8484
9131
  return routeErrorResponse(error, "Failed to read file tree");
8485
9132
  }
8486
9133
  } } } });
8487
- var Route$11 = createFileRoute("/api/files/read")({ server: { handlers: { GET: async ({ request }) => {
9134
+ var Route$12 = createFileRoute("/api/files/read")({ server: { handlers: { GET: async ({ request }) => {
8488
9135
  const url = new URL(request.url);
8489
9136
  const requestedCwd = url.searchParams.get("cwd") || "";
8490
9137
  const requestedPath = url.searchParams.get("path") || "";
@@ -8504,7 +9151,7 @@ var Route$11 = createFileRoute("/api/files/read")({ server: { handlers: { GET: a
8504
9151
  return routeErrorResponse(error, "Failed to read file");
8505
9152
  }
8506
9153
  } } } });
8507
- var Route$10 = createFileRoute("/api/directory/resolve")({ server: { handlers: { POST: async ({ request }) => {
9154
+ var Route$11 = createFileRoute("/api/directory/resolve")({ server: { handlers: { POST: async ({ request }) => {
8508
9155
  try {
8509
9156
  const body = await readRequestJson(request);
8510
9157
  const { context, activeEntry } = await getPicoRuntime().resolveRequest(request);
@@ -8516,7 +9163,7 @@ var Route$10 = createFileRoute("/api/directory/resolve")({ server: { handlers: {
8516
9163
  return routeErrorResponse(error, "Failed to resolve directory");
8517
9164
  }
8518
9165
  } } } });
8519
- var Route$9 = createFileRoute("/api/directory-sessions/cleanup")({ server: { handlers: { POST: async ({ request }) => {
9166
+ var Route$10 = createFileRoute("/api/directory-sessions/cleanup")({ server: { handlers: { POST: async ({ request }) => {
8520
9167
  try {
8521
9168
  const body = await readRequestJson(request);
8522
9169
  return jsonResponse(await getPicoRuntime().deleteOldDirectorySessions(request, body));
@@ -8524,6 +9171,99 @@ var Route$9 = createFileRoute("/api/directory-sessions/cleanup")({ server: { han
8524
9171
  return routeErrorResponse(error, "Failed to clean up sessions");
8525
9172
  }
8526
9173
  } } } });
9174
+ var API_CONTRACT_VERSION = 1;
9175
+ function readPicoPackageVersion() {
9176
+ let currentDir = dirname(fileURLToPath(import.meta.url));
9177
+ while (currentDir && currentDir !== dirname(currentDir)) {
9178
+ const packageJsonPath = join(currentDir, "package.json");
9179
+ if (existsSync(packageJsonPath)) try {
9180
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
9181
+ if (packageJson.name === "@alivault/pico" && typeof packageJson.version === "string") return packageJson.version;
9182
+ } catch {
9183
+ return "0.0.0";
9184
+ }
9185
+ currentDir = dirname(currentDir);
9186
+ }
9187
+ return "0.0.0";
9188
+ }
9189
+ function buildClientManifest() {
9190
+ return {
9191
+ ok: true,
9192
+ name: "@alivault/pico",
9193
+ version: readPicoPackageVersion(),
9194
+ displayName: "Pico",
9195
+ apiContractVersion: API_CONTRACT_VERSION,
9196
+ pairingRequired: false,
9197
+ authentication: { type: "none" },
9198
+ transport: {
9199
+ sse: true,
9200
+ httpsRequired: false,
9201
+ localHttpAllowed: true
9202
+ },
9203
+ capabilities: {
9204
+ events: [
9205
+ "state_sync",
9206
+ "sessions",
9207
+ "session_status",
9208
+ "session_done",
9209
+ "request_error",
9210
+ "extension_error",
9211
+ "extension_ui_request",
9212
+ "user_message",
9213
+ "auto_session_naming_error",
9214
+ "git_changed"
9215
+ ],
9216
+ endpoints: [
9217
+ "/events",
9218
+ "/api/prompt",
9219
+ "/api/abort",
9220
+ "/api/session/new",
9221
+ "/api/session/select",
9222
+ "/api/session/fork",
9223
+ "/api/session/name",
9224
+ "/api/session/rename",
9225
+ "/api/session/delete",
9226
+ "/api/session/read-state",
9227
+ "/api/directory-sessions-indexes",
9228
+ "/api/directory/resolve",
9229
+ "/api/directory-search",
9230
+ "/api/path-completions",
9231
+ "/api/model",
9232
+ "/api/thinking",
9233
+ "/api/auth/providers",
9234
+ "/api/auth/api-key",
9235
+ "/api/auth/oauth",
9236
+ "/api/auth/logout",
9237
+ "/api/ui/$id",
9238
+ "/api/settings/hide-thinking",
9239
+ "/api/pending-message/remove",
9240
+ "/api/pending-messages/reorder",
9241
+ "/api/pending-messages/start"
9242
+ ],
9243
+ features: [
9244
+ "sse",
9245
+ "session-browser",
9246
+ "conversation",
9247
+ "prompt-composer",
9248
+ "queue-and-steer",
9249
+ "model-selection",
9250
+ "thinking-selection",
9251
+ "provider-authentication",
9252
+ "native-directory-browser",
9253
+ "post-compaction-token-estimates",
9254
+ "edit-user-message-fork",
9255
+ "assistant-message-actions"
9256
+ ]
9257
+ }
9258
+ };
9259
+ }
9260
+ var Route$9 = createFileRoute("/api/client/manifest")({ server: { handlers: { GET: async () => {
9261
+ try {
9262
+ return jsonResponse(buildClientManifest());
9263
+ } catch (error) {
9264
+ return routeErrorResponse(error, "Failed to build client manifest");
9265
+ }
9266
+ } } } });
8527
9267
  var Route$8 = createFileRoute("/api/auth/providers")({ server: { handlers: { GET: async ({ request }) => {
8528
9268
  try {
8529
9269
  return jsonResponse(await getPicoRuntime().getAuthProviders(request));
@@ -8593,275 +9333,290 @@ var Route = createFileRoute("/api/session/tree/label")({ server: { handlers: { P
8593
9333
  return routeErrorResponse(error, "Failed to update tree label");
8594
9334
  }
8595
9335
  } } } });
8596
- var EventsRoute = Route$57.update({
9336
+ var EventsRoute = Route$60.update({
8597
9337
  id: "/events",
8598
9338
  path: "/events",
8599
- getParentRoute: () => Route$58
9339
+ getParentRoute: () => Route$61
8600
9340
  });
8601
- var IndexRoute = Route$59.update({
9341
+ var IndexRoute = Route$62.update({
8602
9342
  id: "/",
8603
9343
  path: "/",
8604
- getParentRoute: () => Route$58
9344
+ getParentRoute: () => Route$61
8605
9345
  });
8606
- var ApiThinkingRoute = Route$56.update({
9346
+ var ApiThinkingRoute = Route$59.update({
8607
9347
  id: "/api/thinking",
8608
9348
  path: "/api/thinking",
8609
- getParentRoute: () => Route$58
9349
+ getParentRoute: () => Route$61
8610
9350
  });
8611
- var ApiTerminalRoute = Route$55.update({
9351
+ var ApiTerminalRoute = Route$58.update({
8612
9352
  id: "/api/terminal",
8613
9353
  path: "/api/terminal",
8614
- getParentRoute: () => Route$58
9354
+ getParentRoute: () => Route$61
8615
9355
  });
8616
- var ApiSlashCommandRoute = Route$54.update({
9356
+ var ApiSlashCommandRoute = Route$57.update({
8617
9357
  id: "/api/slash-command",
8618
9358
  path: "/api/slash-command",
8619
- getParentRoute: () => Route$58
9359
+ getParentRoute: () => Route$61
8620
9360
  });
8621
- var ApiProviderUsageRoute = Route$53.update({
9361
+ var ApiProviderUsageRoute = Route$56.update({
8622
9362
  id: "/api/provider-usage",
8623
9363
  path: "/api/provider-usage",
8624
- getParentRoute: () => Route$58
9364
+ getParentRoute: () => Route$61
8625
9365
  });
8626
- var ApiPromptRoute = Route$52.update({
9366
+ var ApiPromptRoute = Route$55.update({
8627
9367
  id: "/api/prompt",
8628
9368
  path: "/api/prompt",
8629
- getParentRoute: () => Route$58
9369
+ getParentRoute: () => Route$61
8630
9370
  });
8631
- var ApiPathCompletionsRoute = Route$51.update({
9371
+ var ApiPathCompletionsRoute = Route$54.update({
8632
9372
  id: "/api/path-completions",
8633
9373
  path: "/api/path-completions",
8634
- getParentRoute: () => Route$58
9374
+ getParentRoute: () => Route$61
8635
9375
  });
8636
- var ApiModelRoute = Route$50.update({
9376
+ var ApiModelRoute = Route$53.update({
8637
9377
  id: "/api/model",
8638
9378
  path: "/api/model",
8639
- getParentRoute: () => Route$58
9379
+ getParentRoute: () => Route$61
8640
9380
  });
8641
- var ApiHighlightRoute = Route$49.update({
9381
+ var ApiHighlightRoute = Route$52.update({
8642
9382
  id: "/api/highlight",
8643
9383
  path: "/api/highlight",
8644
- getParentRoute: () => Route$58
9384
+ getParentRoute: () => Route$61
8645
9385
  });
8646
- var ApiGitStatusRoute = Route$48.update({
9386
+ var ApiGitStatusRoute = Route$51.update({
8647
9387
  id: "/api/git-status",
8648
9388
  path: "/api/git-status",
8649
- getParentRoute: () => Route$58
9389
+ getParentRoute: () => Route$61
8650
9390
  });
8651
- var ApiGitStageRoute = Route$47.update({
9391
+ var ApiGitStageRoute = Route$50.update({
8652
9392
  id: "/api/git-stage",
8653
9393
  path: "/api/git-stage",
8654
- getParentRoute: () => Route$58
9394
+ getParentRoute: () => Route$61
8655
9395
  });
8656
- var ApiGitReviewRoute = Route$46.update({
9396
+ var ApiGitReviewRoute = Route$49.update({
8657
9397
  id: "/api/git-review",
8658
9398
  path: "/api/git-review",
8659
- getParentRoute: () => Route$58
9399
+ getParentRoute: () => Route$61
8660
9400
  });
8661
- var ApiGitPushRoute = Route$45.update({
9401
+ var ApiGitPushRoute = Route$48.update({
8662
9402
  id: "/api/git-push",
8663
9403
  path: "/api/git-push",
8664
- getParentRoute: () => Route$58
9404
+ getParentRoute: () => Route$61
8665
9405
  });
8666
- var ApiGitPullRoute = Route$44.update({
9406
+ var ApiGitPullRoute = Route$47.update({
8667
9407
  id: "/api/git-pull",
8668
9408
  path: "/api/git-pull",
8669
- getParentRoute: () => Route$58
9409
+ getParentRoute: () => Route$61
8670
9410
  });
8671
- var ApiGitDiscardRoute = Route$43.update({
9411
+ var ApiGitDiscardRoute = Route$46.update({
8672
9412
  id: "/api/git-discard",
8673
9413
  path: "/api/git-discard",
8674
- getParentRoute: () => Route$58
9414
+ getParentRoute: () => Route$61
8675
9415
  });
8676
- var ApiGitDiffRoute = Route$42.update({
9416
+ var ApiGitDiffRoute = Route$45.update({
8677
9417
  id: "/api/git-diff",
8678
9418
  path: "/api/git-diff",
8679
- getParentRoute: () => Route$58
9419
+ getParentRoute: () => Route$61
8680
9420
  });
8681
- var ApiGitCommitRemoteUrlRoute = Route$41.update({
9421
+ var ApiGitCommitRemoteUrlRoute = Route$44.update({
8682
9422
  id: "/api/git-commit-remote-url",
8683
9423
  path: "/api/git-commit-remote-url",
8684
- getParentRoute: () => Route$58
9424
+ getParentRoute: () => Route$61
8685
9425
  });
8686
- var ApiGitCommitMessageRoute = Route$40.update({
9426
+ var ApiGitCommitMessageRoute = Route$43.update({
8687
9427
  id: "/api/git-commit-message",
8688
9428
  path: "/api/git-commit-message",
8689
- getParentRoute: () => Route$58
9429
+ getParentRoute: () => Route$61
8690
9430
  });
8691
- var ApiGitCommitFilesRoute = Route$39.update({
9431
+ var ApiGitCommitFilesRoute = Route$42.update({
8692
9432
  id: "/api/git-commit-files",
8693
9433
  path: "/api/git-commit-files",
8694
- getParentRoute: () => Route$58
9434
+ getParentRoute: () => Route$61
8695
9435
  });
8696
- var ApiGitCommitDiffRoute = Route$38.update({
9436
+ var ApiGitCommitDiffRoute = Route$41.update({
8697
9437
  id: "/api/git-commit-diff",
8698
9438
  path: "/api/git-commit-diff",
8699
- getParentRoute: () => Route$58
9439
+ getParentRoute: () => Route$61
8700
9440
  });
8701
- var ApiGitCommitActionRoute = Route$37.update({
9441
+ var ApiGitCommitActionRoute = Route$40.update({
8702
9442
  id: "/api/git-commit-action",
8703
9443
  path: "/api/git-commit-action",
8704
- getParentRoute: () => Route$58
9444
+ getParentRoute: () => Route$61
8705
9445
  });
8706
- var ApiGitCommitRoute = Route$36.update({
9446
+ var ApiGitCommitRoute = Route$39.update({
8707
9447
  id: "/api/git-commit",
8708
9448
  path: "/api/git-commit",
8709
- getParentRoute: () => Route$58
9449
+ getParentRoute: () => Route$61
8710
9450
  });
8711
- var ApiGitCheckoutRoute = Route$35.update({
9451
+ var ApiGitCheckoutRoute = Route$38.update({
8712
9452
  id: "/api/git-checkout",
8713
9453
  path: "/api/git-checkout",
8714
- getParentRoute: () => Route$58
9454
+ getParentRoute: () => Route$61
8715
9455
  });
8716
- var ApiGitChangesRoute = Route$34.update({
9456
+ var ApiGitChangesRoute = Route$37.update({
8717
9457
  id: "/api/git-changes",
8718
9458
  path: "/api/git-changes",
8719
- getParentRoute: () => Route$58
9459
+ getParentRoute: () => Route$61
8720
9460
  });
8721
- var ApiFileCompletionsRoute = Route$33.update({
9461
+ var ApiFileCompletionsRoute = Route$36.update({
8722
9462
  id: "/api/file-completions",
8723
9463
  path: "/api/file-completions",
8724
- getParentRoute: () => Route$58
9464
+ getParentRoute: () => Route$61
8725
9465
  });
8726
- var ApiDirectorySessionsIndexesRoute = Route$32.update({
9466
+ var ApiDirectorySessionsIndexesRoute = Route$35.update({
8727
9467
  id: "/api/directory-sessions-indexes",
8728
9468
  path: "/api/directory-sessions-indexes",
8729
- getParentRoute: () => Route$58
9469
+ getParentRoute: () => Route$61
8730
9470
  });
8731
- var ApiDirectorySessionsIndexRoute = Route$31.update({
9471
+ var ApiDirectorySessionsIndexRoute = Route$34.update({
8732
9472
  id: "/api/directory-sessions-index",
8733
9473
  path: "/api/directory-sessions-index",
8734
- getParentRoute: () => Route$58
9474
+ getParentRoute: () => Route$61
8735
9475
  });
8736
- var ApiDirectorySessionsRoute = Route$30.update({
9476
+ var ApiDirectorySessionsRoute = Route$33.update({
8737
9477
  id: "/api/directory-sessions",
8738
9478
  path: "/api/directory-sessions",
8739
- getParentRoute: () => Route$58
9479
+ getParentRoute: () => Route$61
8740
9480
  });
8741
- var ApiDirectorySearchRoute = Route$29.update({
9481
+ var ApiDirectorySearchRoute = Route$32.update({
8742
9482
  id: "/api/directory-search",
8743
9483
  path: "/api/directory-search",
8744
- getParentRoute: () => Route$58
9484
+ getParentRoute: () => Route$61
8745
9485
  });
8746
- var ApiAbortRoute = Route$28.update({
9486
+ var ApiAbortRoute = Route$31.update({
8747
9487
  id: "/api/abort",
8748
9488
  path: "/api/abort",
8749
- getParentRoute: () => Route$58
9489
+ getParentRoute: () => Route$61
8750
9490
  });
8751
- var ApiUiIdRoute = Route$27.update({
9491
+ var ApiUiIdRoute = Route$30.update({
8752
9492
  id: "/api/ui/$id",
8753
9493
  path: "/api/ui/$id",
8754
- getParentRoute: () => Route$58
9494
+ getParentRoute: () => Route$61
8755
9495
  });
8756
- var ApiSettingsHideThinkingRoute = Route$26.update({
9496
+ var ApiSettingsHideThinkingRoute = Route$29.update({
8757
9497
  id: "/api/settings/hide-thinking",
8758
9498
  path: "/api/settings/hide-thinking",
8759
- getParentRoute: () => Route$58
9499
+ getParentRoute: () => Route$61
8760
9500
  });
8761
- var ApiSessionsDeleteRoute = Route$25.update({
9501
+ var ApiSessionsDeleteRoute = Route$28.update({
8762
9502
  id: "/api/sessions/delete",
8763
9503
  path: "/api/sessions/delete",
8764
- getParentRoute: () => Route$58
9504
+ getParentRoute: () => Route$61
8765
9505
  });
8766
- var ApiSessionTreeRoute = Route$24.update({
9506
+ var ApiSessionTreeRoute = Route$27.update({
8767
9507
  id: "/api/session/tree",
8768
9508
  path: "/api/session/tree",
8769
- getParentRoute: () => Route$58
9509
+ getParentRoute: () => Route$61
8770
9510
  });
8771
- var ApiSessionSelectRoute = Route$23.update({
9511
+ var ApiSessionSelectRoute = Route$26.update({
8772
9512
  id: "/api/session/select",
8773
9513
  path: "/api/session/select",
8774
- getParentRoute: () => Route$58
9514
+ getParentRoute: () => Route$61
8775
9515
  });
8776
- var ApiSessionRenameRoute = Route$22.update({
9516
+ var ApiSessionRenameRoute = Route$25.update({
8777
9517
  id: "/api/session/rename",
8778
9518
  path: "/api/session/rename",
8779
- getParentRoute: () => Route$58
9519
+ getParentRoute: () => Route$61
8780
9520
  });
8781
- var ApiSessionReadStateRoute = Route$21.update({
9521
+ var ApiSessionReadStateRoute = Route$24.update({
8782
9522
  id: "/api/session/read-state",
8783
9523
  path: "/api/session/read-state",
8784
- getParentRoute: () => Route$58
9524
+ getParentRoute: () => Route$61
8785
9525
  });
8786
- var ApiSessionNewRoute = Route$20.update({
9526
+ var ApiSessionNewRoute = Route$23.update({
8787
9527
  id: "/api/session/new",
8788
9528
  path: "/api/session/new",
8789
- getParentRoute: () => Route$58
9529
+ getParentRoute: () => Route$61
9530
+ });
9531
+ var ApiSessionNameRoute = Route$22.update({
9532
+ id: "/api/session/name",
9533
+ path: "/api/session/name",
9534
+ getParentRoute: () => Route$61
8790
9535
  });
8791
- var ApiSessionMoveRoute = Route$19.update({
9536
+ var ApiSessionMoveRoute = Route$21.update({
8792
9537
  id: "/api/session/move",
8793
9538
  path: "/api/session/move",
8794
- getParentRoute: () => Route$58
9539
+ getParentRoute: () => Route$61
8795
9540
  });
8796
- var ApiSessionHistoryRoute = Route$18.update({
9541
+ var ApiSessionHistoryRoute = Route$20.update({
8797
9542
  id: "/api/session/history",
8798
9543
  path: "/api/session/history",
8799
- getParentRoute: () => Route$58
9544
+ getParentRoute: () => Route$61
8800
9545
  });
8801
- var ApiSessionForkRoute = Route$17.update({
9546
+ var ApiSessionForkRoute = Route$19.update({
8802
9547
  id: "/api/session/fork",
8803
9548
  path: "/api/session/fork",
8804
- getParentRoute: () => Route$58
9549
+ getParentRoute: () => Route$61
8805
9550
  });
8806
- var ApiSessionDeleteRoute = Route$16.update({
9551
+ var ApiSessionDeleteRoute = Route$18.update({
8807
9552
  id: "/api/session/delete",
8808
9553
  path: "/api/session/delete",
8809
- getParentRoute: () => Route$58
9554
+ getParentRoute: () => Route$61
8810
9555
  });
8811
- var ApiSessionCloneRoute = Route$15.update({
9556
+ var ApiSessionCloneRoute = Route$17.update({
8812
9557
  id: "/api/session/clone",
8813
9558
  path: "/api/session/clone",
8814
- getParentRoute: () => Route$58
9559
+ getParentRoute: () => Route$61
8815
9560
  });
8816
- var ApiPendingMessagesReorderRoute = Route$14.update({
9561
+ var ApiPendingMessagesStartRoute = Route$16.update({
9562
+ id: "/api/pending-messages/start",
9563
+ path: "/api/pending-messages/start",
9564
+ getParentRoute: () => Route$61
9565
+ });
9566
+ var ApiPendingMessagesReorderRoute = Route$15.update({
8817
9567
  id: "/api/pending-messages/reorder",
8818
9568
  path: "/api/pending-messages/reorder",
8819
- getParentRoute: () => Route$58
9569
+ getParentRoute: () => Route$61
8820
9570
  });
8821
- var ApiPendingMessageRemoveRoute = Route$13.update({
9571
+ var ApiPendingMessageRemoveRoute = Route$14.update({
8822
9572
  id: "/api/pending-message/remove",
8823
9573
  path: "/api/pending-message/remove",
8824
- getParentRoute: () => Route$58
9574
+ getParentRoute: () => Route$61
8825
9575
  });
8826
- var ApiFilesTreeRoute = Route$12.update({
9576
+ var ApiFilesTreeRoute = Route$13.update({
8827
9577
  id: "/api/files/tree",
8828
9578
  path: "/api/files/tree",
8829
- getParentRoute: () => Route$58
9579
+ getParentRoute: () => Route$61
8830
9580
  });
8831
- var ApiFilesReadRoute = Route$11.update({
9581
+ var ApiFilesReadRoute = Route$12.update({
8832
9582
  id: "/api/files/read",
8833
9583
  path: "/api/files/read",
8834
- getParentRoute: () => Route$58
9584
+ getParentRoute: () => Route$61
8835
9585
  });
8836
- var ApiDirectoryResolveRoute = Route$10.update({
9586
+ var ApiDirectoryResolveRoute = Route$11.update({
8837
9587
  id: "/api/directory/resolve",
8838
9588
  path: "/api/directory/resolve",
8839
- getParentRoute: () => Route$58
9589
+ getParentRoute: () => Route$61
8840
9590
  });
8841
- var ApiDirectorySessionsCleanupRoute = Route$9.update({
9591
+ var ApiDirectorySessionsCleanupRoute = Route$10.update({
8842
9592
  id: "/cleanup",
8843
9593
  path: "/cleanup",
8844
9594
  getParentRoute: () => ApiDirectorySessionsRoute
8845
9595
  });
9596
+ var ApiClientManifestRoute = Route$9.update({
9597
+ id: "/api/client/manifest",
9598
+ path: "/api/client/manifest",
9599
+ getParentRoute: () => Route$61
9600
+ });
8846
9601
  var ApiAuthProvidersRoute = Route$8.update({
8847
9602
  id: "/api/auth/providers",
8848
9603
  path: "/api/auth/providers",
8849
- getParentRoute: () => Route$58
9604
+ getParentRoute: () => Route$61
8850
9605
  });
8851
9606
  var ApiAuthOauthRoute = Route$7.update({
8852
9607
  id: "/api/auth/oauth",
8853
9608
  path: "/api/auth/oauth",
8854
- getParentRoute: () => Route$58
9609
+ getParentRoute: () => Route$61
8855
9610
  });
8856
9611
  var ApiAuthLogoutRoute = Route$6.update({
8857
9612
  id: "/api/auth/logout",
8858
9613
  path: "/api/auth/logout",
8859
- getParentRoute: () => Route$58
9614
+ getParentRoute: () => Route$61
8860
9615
  });
8861
9616
  var ApiAuthApiKeyRoute = Route$5.update({
8862
9617
  id: "/api/auth/api-key",
8863
9618
  path: "/api/auth/api-key",
8864
- getParentRoute: () => Route$58
9619
+ getParentRoute: () => Route$61
8865
9620
  });
8866
9621
  var ApiTerminalIdResizeRoute = Route$4.update({
8867
9622
  id: "/$id/resize",
@@ -8934,16 +9689,19 @@ var rootRouteChildren = {
8934
9689
  ApiAuthLogoutRoute,
8935
9690
  ApiAuthOauthRoute,
8936
9691
  ApiAuthProvidersRoute,
9692
+ ApiClientManifestRoute,
8937
9693
  ApiDirectoryResolveRoute,
8938
9694
  ApiFilesReadRoute,
8939
9695
  ApiFilesTreeRoute,
8940
9696
  ApiPendingMessageRemoveRoute,
8941
9697
  ApiPendingMessagesReorderRoute,
9698
+ ApiPendingMessagesStartRoute,
8942
9699
  ApiSessionCloneRoute,
8943
9700
  ApiSessionDeleteRoute,
8944
9701
  ApiSessionForkRoute,
8945
9702
  ApiSessionHistoryRoute,
8946
9703
  ApiSessionMoveRoute,
9704
+ ApiSessionNameRoute,
8947
9705
  ApiSessionNewRoute,
8948
9706
  ApiSessionReadStateRoute,
8949
9707
  ApiSessionRenameRoute,
@@ -8953,7 +9711,7 @@ var rootRouteChildren = {
8953
9711
  ApiSettingsHideThinkingRoute,
8954
9712
  ApiUiIdRoute
8955
9713
  };
8956
- var routeTree = Route$58._addFileChildren(rootRouteChildren)._addFileTypes();
9714
+ var routeTree = Route$61._addFileChildren(rootRouteChildren)._addFileTypes();
8957
9715
  function getRouter() {
8958
9716
  const queryClient = new QueryClient({ defaultOptions: { queries: {
8959
9717
  retry: 1,