@alivault/pico 0.9.0 → 0.10.1

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 (38) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/{index-BXfOqRXc.js → index-7ls3Zqfs.js} +10 -10
  3. package/.output/public/assets/routes-CNpFhMfH.js +2161 -0
  4. package/.output/public/assets/styles-oRWMaOB0.css +2 -0
  5. package/.output/server/_libs/@base-ui/react+[...].mjs +106 -1
  6. package/.output/server/_libs/@dnd-kit/core+[...].mjs +1 -1
  7. package/.output/server/_libs/@pierre/diffs+[...].mjs +2 -2
  8. package/.output/server/_libs/@pierre/trees+[...].mjs +1 -1
  9. package/.output/server/_libs/@radix-ui/react-dialog+[...].mjs +1 -1
  10. package/.output/server/_libs/@radix-ui/react-primitive+[...].mjs +1 -1
  11. package/.output/server/_libs/@tanstack/react-router+[...].mjs +1 -1
  12. package/.output/server/_libs/@tanstack/react-router-ssr-query+[...].mjs +1 -1
  13. package/.output/server/_libs/cmdk.mjs +1 -1
  14. package/.output/server/_libs/dnd-kit__accessibility.mjs +1 -1
  15. package/.output/server/_libs/dnd-kit__sortable.mjs +1 -1
  16. package/.output/server/_libs/hast-util-from-parse5+[...].mjs +1 -1
  17. package/.output/server/_libs/hast-util-raw+[...].mjs +1 -1
  18. package/.output/server/_libs/hast-util-to-jsx-runtime+[...].mjs +1 -1
  19. package/.output/server/_libs/lucide-react.mjs +104 -11
  20. package/.output/server/_libs/mdast-util-gfm+[...].mjs +1 -1
  21. package/.output/server/_libs/next-themes.mjs +1 -1
  22. package/.output/server/_libs/radix-ui__react-compose-refs.mjs +1 -1
  23. package/.output/server/_libs/radix-ui__react-context.mjs +1 -1
  24. package/.output/server/_libs/react-markdown+[...].mjs +1 -1
  25. package/.output/server/_libs/sonner.mjs +1 -1
  26. package/.output/server/_libs/tanstack__react-hotkeys.mjs +1 -1
  27. package/.output/server/_libs/tanstack__react-query.mjs +1 -1
  28. package/.output/server/_libs/tanstack__react-store.mjs +1 -1
  29. package/.output/server/_libs/vaul.mjs +1 -1
  30. package/.output/server/_ssr/{router-DIdiRf-y.mjs → router-DhVJ7gcL.mjs} +1137 -423
  31. package/.output/server/_ssr/{routes-bPw2DezY.mjs → routes-DbGwM-bj.mjs} +51 -32
  32. package/.output/server/_ssr/{routes-7x8dHcec.mjs → routes-NB2wmfpS.mjs} +6227 -2516
  33. package/.output/server/_ssr/ssr.mjs +3 -3
  34. package/.output/server/{_tanstack-start-manifest_v-CMhyz9t-.mjs → _tanstack-start-manifest_v-DrOsJYxt.mjs} +9 -4
  35. package/.output/server/index.mjs +474 -474
  36. package/package.json +1 -1
  37. package/.output/public/assets/routes-CnNf4Tfg.js +0 -2160
  38. package/.output/public/assets/styles-DueiniTy.css +0 -2
@@ -1,8 +1,8 @@
1
1
  import { i as __toESM } from "../_runtime.mjs";
2
- import { X as require_react, Y as require_jsx_runtime } from "../_libs/@base-ui/react+[...].mjs";
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
- import { a as TriangleAlert, g as OctagonX, j as CircleCheck, y as Info } from "../_libs/lucide-react.mjs";
5
- import { B as extractMessageText$1, I as createCompactionSummaryItem, N as assistantBlocksFromMessage, P as buildItemsFromSync, V as extractToolText, W as mergeAssistantBlocksForStreaming, _ as Route$49, bt as toolCategoryFromTool, k as TooltipProvider, n as APPLIED_THEME_STORAGE_KEY, t as APPLIED_THEME_CLASSES, x as Spinner, z as extractMessageImages } from "./routes-bPw2DezY.mjs";
4
+ import { I as CircleCheck, S as Info, _ as OctagonX, a as TriangleAlert } from "../_libs/lucide-react.mjs";
5
+ import { A as TooltipProvider, B as extractMessageImages, F as buildItemsFromSync, G as mergeAssistantBlocksForStreaming, H as extractToolText, L as createCompactionSummaryItem, P as assistantBlocksFromMessage, S as Spinner, St as toolCategoryFromTool, V as extractMessageText$1, n as APPLIED_THEME_STORAGE_KEY, t as APPLIED_THEME_CLASSES, v as Route$54 } from "./routes-DbGwM-bj.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";
@@ -20,7 +20,7 @@ import os, { homedir } from "node:os";
20
20
  import { performance } from "node:perf_hooks";
21
21
  import { isMainThread } from "node:worker_threads";
22
22
  import { execSync, spawn } from "node:child_process";
23
- //#region node_modules/.nitro/vite/services/ssr/assets/router-DIdiRf-y.js
23
+ //#region node_modules/.nitro/vite/services/ssr/assets/router-DhVJ7gcL.js
24
24
  var import_react = /* @__PURE__ */ __toESM(require_react());
25
25
  var import_jsx_runtime = require_jsx_runtime();
26
26
  var Toaster$1 = ({ ...props }) => {
@@ -71,7 +71,7 @@ function AppProviders({ children }) {
71
71
  })
72
72
  });
73
73
  }
74
- var styles_default = "/assets/styles-DueiniTy.css";
74
+ var styles_default = "/assets/styles-oRWMaOB0.css";
75
75
  var TANSTACK_DEVTOOLS_SETTINGS_KEY = "tanstack_devtools_settings";
76
76
  var TANSTACK_DEVTOOLS_DEFAULTS_STORAGE_KEY = "pico-tanstack-devtools-defaults-v1";
77
77
  var TANSTACK_DEVTOOLS_CONFIG = {
@@ -79,7 +79,7 @@ var TANSTACK_DEVTOOLS_CONFIG = {
79
79
  openHotkey: ["Control", "`"],
80
80
  triggerHidden: true
81
81
  };
82
- var Route$48 = createRootRoute({
82
+ var Route$53 = createRootRoute({
83
83
  head: () => ({
84
84
  meta: [
85
85
  { charSet: "utf-8" },
@@ -142,25 +142,30 @@ function RootDocument({ children }) {
142
142
  })]
143
143
  });
144
144
  }
145
+ function subscribeTanStackDevtoolsReady() {
146
+ return () => {};
147
+ }
148
+ function getTanStackDevtoolsReadySnapshot() {
149
+ try {
150
+ if (window.localStorage.getItem(TANSTACK_DEVTOOLS_DEFAULTS_STORAGE_KEY) !== "true") {
151
+ const rawSettings = window.localStorage.getItem(TANSTACK_DEVTOOLS_SETTINGS_KEY);
152
+ const parsedSettings = rawSettings ? JSON.parse(rawSettings) : void 0;
153
+ const existingSettings = parsedSettings && typeof parsedSettings === "object" && !Array.isArray(parsedSettings) ? parsedSettings : {};
154
+ window.localStorage.setItem(TANSTACK_DEVTOOLS_SETTINGS_KEY, JSON.stringify({
155
+ ...existingSettings,
156
+ openHotkey: TANSTACK_DEVTOOLS_CONFIG.openHotkey,
157
+ triggerHidden: TANSTACK_DEVTOOLS_CONFIG.triggerHidden
158
+ }));
159
+ window.localStorage.setItem(TANSTACK_DEVTOOLS_DEFAULTS_STORAGE_KEY, "true");
160
+ }
161
+ } catch {}
162
+ return true;
163
+ }
164
+ function getTanStackDevtoolsServerSnapshot() {
165
+ return false;
166
+ }
145
167
  function AppTanStackDevtools() {
146
- const [isReady, setIsReady] = import_react.useState(false);
147
- import_react.useEffect(() => {
148
- try {
149
- if (window.localStorage.getItem(TANSTACK_DEVTOOLS_DEFAULTS_STORAGE_KEY) !== "true") {
150
- const rawSettings = window.localStorage.getItem(TANSTACK_DEVTOOLS_SETTINGS_KEY);
151
- const parsedSettings = rawSettings ? JSON.parse(rawSettings) : void 0;
152
- const existingSettings = parsedSettings && typeof parsedSettings === "object" && !Array.isArray(parsedSettings) ? parsedSettings : {};
153
- window.localStorage.setItem(TANSTACK_DEVTOOLS_SETTINGS_KEY, JSON.stringify({
154
- ...existingSettings,
155
- openHotkey: TANSTACK_DEVTOOLS_CONFIG.openHotkey,
156
- triggerHidden: TANSTACK_DEVTOOLS_CONFIG.triggerHidden
157
- }));
158
- window.localStorage.setItem(TANSTACK_DEVTOOLS_DEFAULTS_STORAGE_KEY, "true");
159
- }
160
- } catch {}
161
- setIsReady(true);
162
- }, []);
163
- if (!isReady) return null;
168
+ if (!import_react.useSyncExternalStore(subscribeTanStackDevtoolsReady, getTanStackDevtoolsReadySnapshot, getTanStackDevtoolsServerSnapshot)) return null;
164
169
  }
165
170
  var PI_CODING_AGENT_PACKAGES = ["@earendil-works/pi-coding-agent", "@mariozechner/pi-coding-agent"];
166
171
  function normalizeSdkDir(candidate) {
@@ -455,7 +460,7 @@ async function generateSessionNameWithLlm(entry, text, imageCount) {
455
460
  headers: auth.headers,
456
461
  reasoningEffort: "minimal"
457
462
  });
458
- const raw = Array.isArray(response?.content) ? response.content.filter((block) => block?.type === "text" && typeof block.text === "string").map((block) => block.text).join(" ") : "";
463
+ const raw = Array.isArray(response?.content) ? response.content.flatMap((block) => block?.type === "text" && typeof block.text === "string" ? [block.text] : []).join(" ") : "";
459
464
  if (!raw.trim()) return { reason: `refinement model ${model.provider}/${model.id} returned no text` };
460
465
  const cleaned = cleanupSessionNameCandidate(raw);
461
466
  if (!cleaned) return { reason: `refinement model ${model.provider}/${model.id} returned no usable title` };
@@ -524,15 +529,15 @@ async function resolveRequestedEntry(options) {
524
529
  const requestedEntry = getSessionEntryByKey(requestedSessionKey);
525
530
  if (requestedEntry) return await activateRequestedEntry(requestedEntry);
526
531
  }
527
- if (preferActiveDraft && context.draftKey) {
528
- const draftEntry = getSessionEntryByKey(context.draftKey);
529
- if (draftEntry && isDraftEntry(draftEntry)) return await activateRequestedEntry(draftEntry);
530
- }
531
532
  const requestedSessionId = url.searchParams.get("session");
532
533
  if (requestedSessionId) {
533
534
  const requestedEntry = await ensureSessionEntryById(requestedSessionId);
534
535
  if (requestedEntry) return await activateRequestedEntry(requestedEntry);
535
536
  }
537
+ if (preferActiveDraft && context.draftKey) {
538
+ const draftEntry = getSessionEntryByKey(context.draftKey);
539
+ if (draftEntry && isDraftEntry(draftEntry)) return await activateRequestedEntry(draftEntry);
540
+ }
536
541
  const activeEntry = getActiveEntry(context);
537
542
  if (activeEntry) return await activateRequestedEntry(activeEntry);
538
543
  return await getOrCreateDraftEntry(context);
@@ -743,7 +748,14 @@ function updateToolBlock(state, callId, update) {
743
748
  for (let itemIndex = state.items.length - 1; itemIndex >= 0; itemIndex -= 1) {
744
749
  const item = state.items[itemIndex];
745
750
  if (item?.kind !== "assistant") continue;
746
- const blockIndex = item.blocks.findIndex((block) => block.type === "tool" && block.callId === callId);
751
+ let blockIndex = -1;
752
+ for (let index = item.blocks.length - 1; index >= 0; index -= 1) {
753
+ const block = item.blocks[index];
754
+ if (block?.type === "tool" && block.callId === callId) {
755
+ blockIndex = index;
756
+ break;
757
+ }
758
+ }
747
759
  if (blockIndex < 0) continue;
748
760
  const block = item.blocks[blockIndex];
749
761
  if (block?.type !== "tool") return;
@@ -936,7 +948,8 @@ function applyRetainedConversationEvent(state, event) {
936
948
  }];
937
949
  return;
938
950
  }
939
- state.items = state.items.filter((entry) => !(entry.kind === "assistant" && entry.streaming && entry.blocks.length === 0)).map((entry) => {
951
+ state.items = state.items.flatMap((entry) => {
952
+ if (entry.kind === "assistant" && entry.streaming && entry.blocks.length === 0) return [];
940
953
  if (entry.kind !== "assistant") return entry;
941
954
  if (!entry.streaming && entry.done !== false) return entry;
942
955
  return {
@@ -967,7 +980,7 @@ function extractSessionListMessageText(message) {
967
980
  const content = message.content;
968
981
  if (typeof content === "string") return content;
969
982
  if (!Array.isArray(content)) return "";
970
- return content.filter((part) => isRecord(part) && part.type === "text").map((part) => typeof part.text === "string" ? part.text : "").join("\n");
983
+ return content.flatMap((part) => isRecord(part) && part.type === "text" ? [typeof part.text === "string" ? part.text : ""] : []).join("\n");
971
984
  }
972
985
  function extractSessionListImagePreview(message) {
973
986
  const content = message.content;
@@ -1100,40 +1113,85 @@ function countFullTurnUserAndAssistantMessages(messages) {
1100
1113
  finishTurn(false);
1101
1114
  return count;
1102
1115
  }
1103
- async function readSessionListMetrics(sessionPath) {
1104
- try {
1105
- const content = await readFile(sessionPath, "utf8");
1106
- let lastTimestamp = 0;
1107
- let lastValue;
1108
- const messages = [];
1109
- for (const line of content.split("\n")) {
1110
- if (!line.trim()) continue;
1111
- let entry;
1112
- try {
1113
- entry = JSON.parse(line);
1114
- } catch {
1115
- continue;
1116
- }
1117
- if (!isRecord(entry) || entry.type !== "message") continue;
1118
- const message = isRecord(entry.message) ? entry.message : void 0;
1119
- if (!message) continue;
1120
- const messageWithTimestamp = {
1121
- ...message,
1122
- timestamp: message.timestamp ?? entry.timestamp
1123
- };
1124
- if (messageWithTimestamp.role === "user") {
1125
- const normalized = normalizeModifiedTimestamp(messageWithTimestamp.timestamp);
1126
- const timestamp = modifiedTimestampValue(normalized);
1127
- if (timestamp && timestamp >= lastTimestamp) {
1128
- lastTimestamp = timestamp;
1129
- lastValue = normalized;
1130
- }
1116
+ function readSessionListMetricEntries(content) {
1117
+ const entries = [];
1118
+ for (const line of content.split("\n")) {
1119
+ if (!line.trim()) continue;
1120
+ let entry;
1121
+ try {
1122
+ entry = JSON.parse(line);
1123
+ } catch {
1124
+ continue;
1125
+ }
1126
+ if (!isRecord(entry) || entry.type === "session") continue;
1127
+ entries.push(entry);
1128
+ }
1129
+ return entries;
1130
+ }
1131
+ function sessionListMetricEntryId(entry) {
1132
+ return typeof entry.id === "string" && entry.id ? entry.id : "";
1133
+ }
1134
+ function sessionListMetricEntryParentId(entry) {
1135
+ return typeof entry.parentId === "string" && entry.parentId ? entry.parentId : "";
1136
+ }
1137
+ function sessionListMetricBranchEntries(entries, leafId) {
1138
+ if (leafId === void 0) return entries;
1139
+ if (leafId === null) return [];
1140
+ const byId = new Map(entries.flatMap((entry) => {
1141
+ const id = sessionListMetricEntryId(entry);
1142
+ return id ? [[id, entry]] : [];
1143
+ }));
1144
+ const leaf = byId.get(leafId);
1145
+ if (!leaf) return entries;
1146
+ const branch = [];
1147
+ let current = leaf;
1148
+ const visited = /* @__PURE__ */ new Set();
1149
+ while (current) {
1150
+ const id = sessionListMetricEntryId(current);
1151
+ if (id) {
1152
+ if (visited.has(id)) break;
1153
+ visited.add(id);
1154
+ }
1155
+ branch.unshift(current);
1156
+ const parentId = sessionListMetricEntryParentId(current);
1157
+ current = parentId ? byId.get(parentId) : void 0;
1158
+ }
1159
+ return branch;
1160
+ }
1161
+ function collectSessionListMetricMessages(entries) {
1162
+ let lastTimestamp = 0;
1163
+ let lastValue;
1164
+ const messages = [];
1165
+ for (const entry of entries) {
1166
+ if (entry.type !== "message") continue;
1167
+ const message = isRecord(entry.message) ? entry.message : void 0;
1168
+ if (!message) continue;
1169
+ const messageWithTimestamp = {
1170
+ ...message,
1171
+ timestamp: message.timestamp ?? entry.timestamp
1172
+ };
1173
+ if (messageWithTimestamp.role === "user") {
1174
+ const normalized = normalizeModifiedTimestamp(messageWithTimestamp.timestamp);
1175
+ const timestamp = modifiedTimestampValue(normalized);
1176
+ if (timestamp && timestamp >= lastTimestamp) {
1177
+ lastTimestamp = timestamp;
1178
+ lastValue = normalized;
1131
1179
  }
1132
- if (messageWithTimestamp.role === "user" || messageWithTimestamp.role === "assistant") messages.push(messageWithTimestamp);
1133
1180
  }
1181
+ if (messageWithTimestamp.role === "user" || messageWithTimestamp.role === "assistant") messages.push(messageWithTimestamp);
1182
+ }
1183
+ return {
1184
+ lastUserMessageAt: lastValue,
1185
+ messages
1186
+ };
1187
+ }
1188
+ async function readSessionListMetrics(sessionPath, options) {
1189
+ try {
1190
+ const entries = readSessionListMetricEntries(await readFile(sessionPath, "utf8"));
1191
+ const { lastUserMessageAt, messages } = collectSessionListMetricMessages(Object.prototype.hasOwnProperty.call(options ?? {}, "leafId") ? sessionListMetricBranchEntries(entries, options?.leafId) : entries);
1134
1192
  const lastMessage = getSessionLastCompleteMessageInfo(messages);
1135
1193
  return {
1136
- lastUserMessageAt: lastValue,
1194
+ lastUserMessageAt,
1137
1195
  lastMessageAt: lastMessage.timestamp,
1138
1196
  lastMessagePreview: lastMessage.preview,
1139
1197
  messageCount: countFullTurnUserAndAssistantMessages(messages)
@@ -1209,12 +1267,12 @@ function extractMessageText(message) {
1209
1267
  const content = message.content;
1210
1268
  if (typeof content === "string") return content.trim();
1211
1269
  if (!Array.isArray(content)) return "";
1212
- return content.filter((part) => part?.type === "text" && typeof part.text === "string").map((part) => part.text).join(" ").trim();
1270
+ return content.flatMap((part) => part?.type === "text" && typeof part.text === "string" ? [part.text] : []).join(" ").trim();
1213
1271
  }
1214
1272
  function extractSessionContentText(content) {
1215
1273
  if (typeof content === "string") return content.trim();
1216
1274
  if (!Array.isArray(content)) return "";
1217
- const text = content.filter((part) => Boolean(part) && typeof part === "object" && part.type === "text" && typeof part.text === "string").map((part) => part.text).join(" ").trim();
1275
+ const text = content.flatMap((part) => part && typeof part === "object" && part.type === "text" && typeof part.text === "string" ? [part.text] : []).join(" ").trim();
1218
1276
  if (text) return text;
1219
1277
  const imageCount = content.filter((part) => part && typeof part === "object" && (part.type === "image" || part.type === "input_image")).length;
1220
1278
  if (imageCount > 0) return `${imageCount} image${imageCount === 1 ? "" : "s"}`;
@@ -1318,15 +1376,22 @@ function serializeSessionTreeNode(node) {
1318
1376
  if (entry.type === "custom") serialized.entry.customType = typeof entry.customType === "string" ? entry.customType : "custom";
1319
1377
  if (entry.type === "label") serialized.entry.label = typeof entry.label === "string" ? entry.label : void 0;
1320
1378
  if (entry.type === "session_info") serialized.entry.name = typeof entry.name === "string" ? entry.name : "";
1321
- serialized.children = (Array.isArray(node.children) ? node.children : []).map((child) => serializeSessionTreeNode(child)).filter((child) => Boolean(child));
1379
+ serialized.children = (Array.isArray(node.children) ? node.children : []).flatMap((child) => {
1380
+ const treeNode = serializeSessionTreeNode(child);
1381
+ return treeNode ? [treeNode] : [];
1382
+ });
1322
1383
  return serialized;
1323
1384
  }
1324
1385
  function extractForkableUserMessages(entry) {
1325
1386
  const messages = entry.session.getUserMessagesForForking?.();
1326
- if (Array.isArray(messages) && messages.length > 0) return messages.map((message) => ({
1327
- entryId: typeof message.entryId === "string" ? message.entryId.trim() : "",
1328
- text: typeof message.text === "string" ? message.text.trim() : ""
1329
- })).filter((message) => Boolean(message.entryId && message.text));
1387
+ if (Array.isArray(messages) && messages.length > 0) return messages.flatMap((message) => {
1388
+ const entryId = typeof message.entryId === "string" ? message.entryId.trim() : "";
1389
+ const text = typeof message.text === "string" ? message.text.trim() : "";
1390
+ return entryId && text ? [{
1391
+ entryId,
1392
+ text
1393
+ }] : [];
1394
+ });
1330
1395
  const stack = [...entry.session.sessionManager.getTree?.() ?? []];
1331
1396
  const results = [];
1332
1397
  while (stack.length > 0) {
@@ -1351,7 +1416,7 @@ async function createForkedInMemorySessionManager(options) {
1351
1416
  if (!leafId) return nextManager;
1352
1417
  const branchEntries = sourceManager.getBranch?.(leafId);
1353
1418
  if (!Array.isArray(branchEntries) || branchEntries.length === 0) throw new Error(`Entry ${leafId} not found`);
1354
- const pathWithoutLabels = branchEntries.filter((branchEntry) => branchEntry?.type !== "label").map((branchEntry) => cloneSessionData(branchEntry));
1419
+ const pathWithoutLabels = branchEntries.flatMap((branchEntry) => branchEntry?.type !== "label" ? [cloneSessionData(branchEntry)] : []);
1355
1420
  const header = cloneSessionData(sourceManager.getHeader?.() ?? nextManager.fileEntries?.[0]);
1356
1421
  if (!header) throw new Error("Failed to initialize forked in-memory session.");
1357
1422
  nextManager.fileEntries = [header, ...pathWithoutLabels];
@@ -1471,6 +1536,9 @@ async function createPicoEditToolDefinition(sdk, cwd) {
1471
1536
  }
1472
1537
  };
1473
1538
  }
1539
+ function gitTextContains(text, query) {
1540
+ return text.indexOf(query) >= 0;
1541
+ }
1474
1542
  var GIT_STATUS_CACHE_TTL_MS = 5e3;
1475
1543
  var GIT_CHANGES_CACHE_TTL_MS = 5e3;
1476
1544
  var GIT_COMMITS_DEFAULT_LIMIT = 50;
@@ -1655,7 +1723,10 @@ function parseCommandNullList(output) {
1655
1723
  return output.split("\0").filter((entry) => typeof entry === "string" && entry.length > 0);
1656
1724
  }
1657
1725
  function parseCommandLines(output, { trim = false } = {}) {
1658
- return output.split(/\r?\n/).map((entry) => trim ? entry.trim() : entry).filter((entry) => Boolean(trim ? entry : entry.length));
1726
+ return output.split(/\r?\n/).flatMap((entry) => {
1727
+ const line = trim ? entry.trim() : entry;
1728
+ return trim ? line ? [line] : [] : line.length ? [line] : [];
1729
+ });
1659
1730
  }
1660
1731
  function isGitCommitGraphLine(line) {
1661
1732
  const tabIndex = line.indexOf(" ");
@@ -1711,7 +1782,10 @@ function enrichGitCommitGraphLinesWithStats(lines, statsByHash) {
1711
1782
  });
1712
1783
  }
1713
1784
  function gitCommandErrorMessage(fallback, result) {
1714
- const details = [result.stderr, result.stdout].map((value) => value.trim()).filter(Boolean).join("\n");
1785
+ const details = [result.stderr, result.stdout].flatMap((value) => {
1786
+ const detail = value.trim();
1787
+ return detail ? [detail] : [];
1788
+ }).join("\n");
1715
1789
  if (result.timedOut) return `${fallback}: command timed out`;
1716
1790
  if (details) return `${fallback}: ${details}`;
1717
1791
  return fallback;
@@ -1743,7 +1817,10 @@ function deriveHeuristicCommitMessage(files) {
1743
1817
  }
1744
1818
  if (addedCount > modifiedCount && addedCount >= deletedCount) return `Add ${files.length} files`;
1745
1819
  if (deletedCount > modifiedCount && deletedCount >= addedCount) return `Remove ${files.length} files`;
1746
- const commonDirectory = files.map((file) => file.path.split("/").slice(0, -1).join("/")).filter(Boolean).reduce((common, directory) => {
1820
+ const commonDirectory = files.flatMap((file) => {
1821
+ const directory = file.path.split("/").slice(0, -1).join("/");
1822
+ return directory ? [directory] : [];
1823
+ }).reduce((common, directory) => {
1747
1824
  if (common === void 0) return directory;
1748
1825
  return common === directory ? common : "";
1749
1826
  }, void 0);
@@ -1771,12 +1848,31 @@ function parseGitStatusEntries(output) {
1771
1848
  }
1772
1849
  return files;
1773
1850
  }
1851
+ function parseGitNameStatusEntries(output) {
1852
+ const entries = parseCommandNullList(output);
1853
+ const files = [];
1854
+ for (let index = 0; index < entries.length; index += 1) {
1855
+ const status = entries[index];
1856
+ if (typeof status !== "string" || !status) continue;
1857
+ const isRenamedOrCopied = status.startsWith("R") || status.startsWith("C");
1858
+ const previousPath = isRenamedOrCopied ? entries[index + 1] : void 0;
1859
+ const filePath = isRenamedOrCopied ? entries[index + 2] : entries[index + 1];
1860
+ index += isRenamedOrCopied ? 2 : 1;
1861
+ if (typeof filePath !== "string" || !filePath) continue;
1862
+ files.push({
1863
+ status: /^[RC]\d+/.test(status) ? status.slice(0, 1) : status,
1864
+ path: filePath,
1865
+ ...typeof previousPath === "string" && previousPath ? { previousPath } : {}
1866
+ });
1867
+ }
1868
+ return files;
1869
+ }
1774
1870
  function parseGitNumstatEntries(output) {
1775
1871
  const entries = parseCommandNullList(output);
1776
1872
  const diffs = /* @__PURE__ */ new Map();
1777
1873
  for (let index = 0; index < entries.length; index += 1) {
1778
1874
  const entry = entries[index];
1779
- if (typeof entry !== "string" || !entry.includes(" ")) continue;
1875
+ if (typeof entry !== "string" || !gitTextContains(entry, " ")) continue;
1780
1876
  const [addedRaw = "", deletedRaw = "", ...pathParts] = entry.split(" ");
1781
1877
  let filePath = pathParts.join(" ");
1782
1878
  let previousPath;
@@ -1797,7 +1893,10 @@ function parseGitNumstatEntries(output) {
1797
1893
  return diffs;
1798
1894
  }
1799
1895
  function parseGitRefRows(output) {
1800
- return parseCommandLines(output).map((line) => line.split("\0")).filter((fields) => fields.some((field) => typeof field === "string" && field.length > 0));
1896
+ return parseCommandLines(output).flatMap((line) => {
1897
+ const fields = line.split("\0");
1898
+ return fields.some((field) => typeof field === "string" && field.length > 0) ? [fields] : [];
1899
+ });
1801
1900
  }
1802
1901
  function parseGitBranchTrack(track, upstream) {
1803
1902
  const trackValue = typeof track === "string" ? track.trim() : "";
@@ -1852,12 +1951,28 @@ function assertGitFilePath(path) {
1852
1951
  if (normalizedPath.split(/[\\/]+/).some((part) => part === "..")) throw new Error("file path must stay inside the repository");
1853
1952
  return normalizedPath.replaceAll("\\", "/");
1854
1953
  }
1954
+ function assertGitCommitHash(commit) {
1955
+ const normalizedCommit = typeof commit === "string" ? commit.trim() : "";
1956
+ if (!normalizedCommit) throw new Error("commit is required");
1957
+ if (!/^[0-9a-f]{5,40}$/i.test(normalizedCommit)) throw new Error("commit must be a valid hash");
1958
+ return normalizedCommit;
1959
+ }
1960
+ function assertGitTagName(tagName) {
1961
+ const normalizedTagName = typeof tagName === "string" ? tagName.trim() : "";
1962
+ if (!normalizedTagName) throw new Error("tag name is required");
1963
+ if (normalizedTagName.startsWith("-") || /\s/.test(normalizedTagName)) throw new Error("Invalid tag name");
1964
+ return normalizedTagName;
1965
+ }
1966
+ function normalizeGitResetMode(mode) {
1967
+ if (mode === "soft" || mode === "mixed" || mode === "hard") return mode;
1968
+ throw new Error("reset mode must be soft, mixed, or hard");
1969
+ }
1855
1970
  function parseGitRemoteBranches(output) {
1856
1971
  const branches = [];
1857
1972
  for (const fields of parseGitRefRows(output)) {
1858
1973
  const [name = "", hash = "", subject = "", relativeDate = "", committerDate = ""] = fields;
1859
1974
  const branchName = typeof name === "string" ? name.trim() : "";
1860
- if (!branchName || !branchName.includes("/") || /\/HEAD$/i.test(branchName)) continue;
1975
+ if (!branchName || !gitTextContains(branchName, "/") || /\/HEAD$/i.test(branchName)) continue;
1861
1976
  branches.push({
1862
1977
  name: branchName,
1863
1978
  hash: typeof hash === "string" && hash.trim() ? hash.trim() : void 0,
@@ -2086,11 +2201,12 @@ async function readDirectoryGitFileReview(cwd, path, previousPath) {
2086
2201
  const normalizedPreviousPath = previousPath ? assertGitFilePath(previousPath) : void 0;
2087
2202
  if (!normalizedCwd) return null;
2088
2203
  if (!await isInsideWorkTree(normalizedCwd)) return null;
2204
+ const [oldContent, newContent] = await Promise.all([readGitHeadFileContent(normalizedCwd, normalizedPreviousPath || normalizedPath), readWorktreeFileContent(normalizedCwd, normalizedPath)]);
2089
2205
  return {
2090
2206
  path: normalizedPath,
2091
2207
  previousPath: normalizedPreviousPath,
2092
- oldContent: await readGitHeadFileContent(normalizedCwd, normalizedPreviousPath || normalizedPath),
2093
- newContent: await readWorktreeFileContent(normalizedCwd, normalizedPath)
2208
+ oldContent,
2209
+ newContent
2094
2210
  };
2095
2211
  }
2096
2212
  async function readDirectoryGitFileDiff(cwd, path) {
@@ -2409,14 +2525,20 @@ async function readUnpushedCommitMessages(cwd) {
2409
2525
  });
2410
2526
  return result.code === 0 ? parseCommandLines(result.stdout, { trim: true }).filter(Boolean) : [];
2411
2527
  }
2528
+ async function runGitPushCommand(cwd, context) {
2529
+ return await runCommand("git", context.force ? ["push", "--force-with-lease"] : ["push"], {
2530
+ cwd,
2531
+ timeoutMs: GIT_ACTION_TIMEOUT_MS
2532
+ });
2533
+ }
2412
2534
  async function pushDirectoryGitChanges(cwd, { force = false } = {}) {
2413
2535
  const normalizedCwd = normalizeGitCwd(cwd);
2414
2536
  if (!normalizedCwd) throw new Error("cwd is required");
2415
2537
  if (!await isInsideWorkTree(normalizedCwd)) throw new Error("No git repository detected");
2416
2538
  const pushedCommitMessages = await readUnpushedCommitMessages(normalizedCwd);
2417
- const result = await runCommand("git", force ? ["push", "--force-with-lease"] : ["push"], {
2418
- cwd: normalizedCwd,
2419
- timeoutMs: GIT_ACTION_TIMEOUT_MS
2539
+ const result = await runGitPushCommand(normalizedCwd, {
2540
+ force,
2541
+ pushedCommitMessages
2420
2542
  });
2421
2543
  if (result.code !== 0) throw new Error(gitCommandErrorMessage(force ? "Failed to force push changes" : "Failed to push changes", result));
2422
2544
  invalidateDirectoryGitCaches(normalizedCwd);
@@ -2467,6 +2589,294 @@ async function checkoutDirectoryGitBranch(cwd, branch, options = {}) {
2467
2589
  stderr: result.stderr
2468
2590
  };
2469
2591
  }
2592
+ function gitCommitActionLabel(action) {
2593
+ switch (action) {
2594
+ case "checkout": return "checkout commit";
2595
+ case "cherry-pick": return "cherry-pick commit";
2596
+ case "revert": return "revert commit";
2597
+ case "tag": return "tag commit";
2598
+ case "reset": return "reset branch";
2599
+ case "rebase": return "rebase branch";
2600
+ case "drop": return "drop commit";
2601
+ case "squash": return "squash commits";
2602
+ }
2603
+ }
2604
+ function remoteBaseUrlFromGitUrl(remoteUrl) {
2605
+ const trimmed = remoteUrl.trim();
2606
+ if (!trimmed) return "";
2607
+ const scpLikeMatch = trimmed.match(/^git@([^:]+):(.+)$/);
2608
+ if (scpLikeMatch) {
2609
+ const host = scpLikeMatch[1];
2610
+ const path = scpLikeMatch[2]?.replace(/\.git$/i, "");
2611
+ return host && path ? `https://${host}/${path}` : "";
2612
+ }
2613
+ try {
2614
+ const url = new URL(trimmed);
2615
+ if (url.protocol === "ssh:" && url.username === "git") return `https://${url.host}${url.pathname.replace(/\.git$/i, "")}`;
2616
+ if (url.protocol === "http:" || url.protocol === "https:") {
2617
+ url.username = "";
2618
+ url.password = "";
2619
+ url.pathname = url.pathname.replace(/\.git$/i, "");
2620
+ url.search = "";
2621
+ url.hash = "";
2622
+ return url.toString().replace(/\/$/, "");
2623
+ }
2624
+ } catch {}
2625
+ return "";
2626
+ }
2627
+ function commitUrlPathForHost(host, commit) {
2628
+ return host.toLowerCase().includes("gitlab") ? `/-/commit/${commit}` : `/commit/${commit}`;
2629
+ }
2630
+ async function readPrimaryRemoteName(cwd) {
2631
+ const branchResult = await runCommand("git", [
2632
+ "symbolic-ref",
2633
+ "--quiet",
2634
+ "--short",
2635
+ "HEAD"
2636
+ ], {
2637
+ cwd,
2638
+ timeoutMs: 1500
2639
+ });
2640
+ const branch = branchResult.code === 0 ? branchResult.stdout.trim() : "";
2641
+ if (branch) {
2642
+ const branchRemoteResult = await runCommand("git", [
2643
+ "config",
2644
+ "--get",
2645
+ `branch.${branch}.remote`
2646
+ ], {
2647
+ cwd,
2648
+ timeoutMs: 1500
2649
+ });
2650
+ const branchRemote = branchRemoteResult.stdout.trim();
2651
+ if (branchRemoteResult.code === 0 && branchRemote) return branchRemote;
2652
+ }
2653
+ const pushDefaultResult = await runCommand("git", [
2654
+ "config",
2655
+ "--get",
2656
+ "remote.pushDefault"
2657
+ ], {
2658
+ cwd,
2659
+ timeoutMs: 1500
2660
+ });
2661
+ const pushDefault = pushDefaultResult.stdout.trim();
2662
+ if (pushDefaultResult.code === 0 && pushDefault) return pushDefault;
2663
+ return "origin";
2664
+ }
2665
+ async function readDirectoryGitCommitFiles(cwd, commit) {
2666
+ const normalizedCwd = normalizeGitCwd(cwd);
2667
+ const normalizedCommit = assertGitCommitHash(commit);
2668
+ if (!normalizedCwd) return null;
2669
+ if (!await isInsideWorkTree(normalizedCwd)) return null;
2670
+ const [statusResult, numstatResult] = await Promise.all([runCommand("git", [
2671
+ "show",
2672
+ "--name-status",
2673
+ "-z",
2674
+ "--find-renames=50%",
2675
+ "--format=",
2676
+ normalizedCommit
2677
+ ], {
2678
+ cwd: normalizedCwd,
2679
+ timeoutMs: 1e4
2680
+ }), runCommand("git", [
2681
+ "show",
2682
+ "--numstat",
2683
+ "-z",
2684
+ "--find-renames=50%",
2685
+ "--format=",
2686
+ normalizedCommit
2687
+ ], {
2688
+ cwd: normalizedCwd,
2689
+ timeoutMs: 1e4
2690
+ })]);
2691
+ if (statusResult.code !== 0) throw new Error(gitCommandErrorMessage("Failed to read commit files", statusResult));
2692
+ if (numstatResult.code !== 0) throw new Error(gitCommandErrorMessage("Failed to read commit file stats", numstatResult));
2693
+ const stats = parseGitNumstatEntries(numstatResult.stdout);
2694
+ return parseGitNameStatusEntries(statusResult.stdout).map((file) => ({
2695
+ ...file,
2696
+ ...stats.get(file.path)
2697
+ }));
2698
+ }
2699
+ async function readDirectoryGitCommitDiff(cwd, commit, mode = "commit", path, previousPath) {
2700
+ const normalizedCwd = normalizeGitCwd(cwd);
2701
+ const normalizedCommit = assertGitCommitHash(commit);
2702
+ const normalizedPath = path ? assertGitFilePath(path) : void 0;
2703
+ const normalizedPreviousPath = previousPath ? assertGitFilePath(previousPath) : void 0;
2704
+ if (!normalizedCwd) return null;
2705
+ if (!await isInsideWorkTree(normalizedCwd)) return null;
2706
+ const pathArgs = normalizedPath ? ["--", ...uniqueGitActionPaths([normalizedPreviousPath, normalizedPath])] : [];
2707
+ const result = await runCommand("git", mode === "head" ? [
2708
+ "diff",
2709
+ "--patch",
2710
+ "--no-color",
2711
+ "--no-ext-diff",
2712
+ "--find-renames=50%",
2713
+ `${normalizedCommit}..HEAD`,
2714
+ ...pathArgs
2715
+ ] : mode === "previous" ? [
2716
+ "diff",
2717
+ "--patch",
2718
+ "--no-color",
2719
+ "--no-ext-diff",
2720
+ "--find-renames=50%",
2721
+ `${normalizedCommit}^`,
2722
+ normalizedCommit,
2723
+ ...pathArgs
2724
+ ] : [
2725
+ "show",
2726
+ "--patch",
2727
+ "--no-color",
2728
+ "--no-ext-diff",
2729
+ "--find-renames=50%",
2730
+ "--format=",
2731
+ normalizedCommit,
2732
+ ...pathArgs
2733
+ ], {
2734
+ cwd: normalizedCwd,
2735
+ timeoutMs: 3e4
2736
+ });
2737
+ if (result.code !== 0) throw new Error(gitCommandErrorMessage("Failed to read commit diff", result));
2738
+ const title = mode === "head" ? `${normalizedCommit.slice(0, 7)}..HEAD` : mode === "previous" ? `${normalizedCommit.slice(0, 7)}^..${normalizedCommit.slice(0, 7)}` : normalizedCommit.slice(0, 7);
2739
+ return {
2740
+ commit: normalizedCommit,
2741
+ mode,
2742
+ title: normalizedPath ? `${title}:${normalizedPath}` : title,
2743
+ ...normalizedPath ? { path: normalizedPath } : {},
2744
+ ...normalizedPreviousPath ? { previousPath: normalizedPreviousPath } : {},
2745
+ patch: result.stdout.trim()
2746
+ };
2747
+ }
2748
+ async function readDirectoryGitCommitRemoteUrl(cwd, commit) {
2749
+ const normalizedCwd = normalizeGitCwd(cwd);
2750
+ const normalizedCommit = assertGitCommitHash(commit);
2751
+ if (!normalizedCwd) return null;
2752
+ if (!await isInsideWorkTree(normalizedCwd)) return null;
2753
+ const remoteResult = await runCommand("git", [
2754
+ "remote",
2755
+ "get-url",
2756
+ await readPrimaryRemoteName(normalizedCwd)
2757
+ ], {
2758
+ cwd: normalizedCwd,
2759
+ timeoutMs: 1500
2760
+ });
2761
+ if (remoteResult.code !== 0 || !remoteResult.stdout.trim()) throw new Error("No remote URL found");
2762
+ const baseUrl = remoteBaseUrlFromGitUrl(remoteResult.stdout);
2763
+ if (!baseUrl) throw new Error("Remote URL cannot be opened in the browser");
2764
+ return {
2765
+ commit: normalizedCommit,
2766
+ remoteUrl: `${baseUrl}${commitUrlPathForHost(new URL(baseUrl).host, normalizedCommit)}`
2767
+ };
2768
+ }
2769
+ async function runDirectoryGitCommitAction(cwd, action, commit, options = {}) {
2770
+ const normalizedCwd = normalizeGitCwd(cwd);
2771
+ const normalizedCommit = assertGitCommitHash(commit);
2772
+ if (!normalizedCwd) throw new Error("cwd is required");
2773
+ if (!await isInsideWorkTree(normalizedCwd)) throw new Error("No git repository detected");
2774
+ let result;
2775
+ switch (action) {
2776
+ case "checkout":
2777
+ result = await runCommand("git", [
2778
+ "switch",
2779
+ "--detach",
2780
+ normalizedCommit
2781
+ ], {
2782
+ cwd: normalizedCwd,
2783
+ timeoutMs: GIT_ACTION_TIMEOUT_MS
2784
+ });
2785
+ break;
2786
+ case "cherry-pick":
2787
+ result = await runCommand("git", ["cherry-pick", normalizedCommit], {
2788
+ cwd: normalizedCwd,
2789
+ timeoutMs: GIT_ACTION_TIMEOUT_MS
2790
+ });
2791
+ break;
2792
+ case "revert":
2793
+ result = await runCommand("git", [
2794
+ "revert",
2795
+ "--no-edit",
2796
+ normalizedCommit
2797
+ ], {
2798
+ cwd: normalizedCwd,
2799
+ timeoutMs: GIT_ACTION_TIMEOUT_MS
2800
+ });
2801
+ break;
2802
+ case "tag":
2803
+ result = await runCommand("git", [
2804
+ "tag",
2805
+ assertGitTagName(options.tagName || ""),
2806
+ normalizedCommit
2807
+ ], {
2808
+ cwd: normalizedCwd,
2809
+ timeoutMs: GIT_ACTION_TIMEOUT_MS
2810
+ });
2811
+ break;
2812
+ case "reset":
2813
+ result = await runCommand("git", [
2814
+ "reset",
2815
+ `--${normalizeGitResetMode(options.resetMode)}`,
2816
+ normalizedCommit
2817
+ ], {
2818
+ cwd: normalizedCwd,
2819
+ timeoutMs: GIT_ACTION_TIMEOUT_MS
2820
+ });
2821
+ break;
2822
+ case "rebase":
2823
+ result = await runCommand("git", ["rebase", normalizedCommit], {
2824
+ cwd: normalizedCwd,
2825
+ timeoutMs: GIT_ACTION_TIMEOUT_MS
2826
+ });
2827
+ break;
2828
+ case "drop":
2829
+ result = await runCommand("git", [
2830
+ "rebase",
2831
+ "--onto",
2832
+ `${normalizedCommit}^`,
2833
+ normalizedCommit,
2834
+ "HEAD"
2835
+ ], {
2836
+ cwd: normalizedCwd,
2837
+ timeoutMs: GIT_ACTION_TIMEOUT_MS
2838
+ });
2839
+ break;
2840
+ case "squash": {
2841
+ const message = cleanupCommitMessageCandidate(options.message || "");
2842
+ if (!message) throw new Error("commit message is required");
2843
+ const resetResult = await runCommand("git", [
2844
+ "reset",
2845
+ "--soft",
2846
+ normalizedCommit
2847
+ ], {
2848
+ cwd: normalizedCwd,
2849
+ timeoutMs: GIT_ACTION_TIMEOUT_MS
2850
+ });
2851
+ if (resetResult.code !== 0) throw new Error(gitCommandErrorMessage("Failed to squash commits", resetResult));
2852
+ if ((await runCommand("git", [
2853
+ "diff",
2854
+ "--cached",
2855
+ "--quiet"
2856
+ ], {
2857
+ cwd: normalizedCwd,
2858
+ timeoutMs: 5e3
2859
+ })).code === 0) throw new Error("No commits after this commit to squash");
2860
+ result = await runCommand("git", [
2861
+ "commit",
2862
+ "-m",
2863
+ message
2864
+ ], {
2865
+ cwd: normalizedCwd,
2866
+ timeoutMs: GIT_ACTION_TIMEOUT_MS
2867
+ });
2868
+ result.stdout = [resetResult.stdout, result.stdout].filter(Boolean).join("\n");
2869
+ result.stderr = [resetResult.stderr, result.stderr].filter(Boolean).join("\n");
2870
+ break;
2871
+ }
2872
+ }
2873
+ if (result.code !== 0) throw new Error(gitCommandErrorMessage(`Failed to ${gitCommitActionLabel(action)}`, result));
2874
+ invalidateDirectoryGitCaches(normalizedCwd);
2875
+ return {
2876
+ stdout: result.stdout,
2877
+ stderr: result.stderr
2878
+ };
2879
+ }
2470
2880
  async function generateDirectoryGitCommitMessage(cwd, modelRegistry) {
2471
2881
  const normalizedCwd = normalizeGitCwd(cwd);
2472
2882
  if (!normalizedCwd) throw new Error("cwd is required");
@@ -2544,7 +2954,7 @@ async function generateDirectoryGitCommitMessage(cwd, modelRegistry) {
2544
2954
  headers: auth.headers,
2545
2955
  reasoningEffort: "low"
2546
2956
  });
2547
- const message = cleanupCommitMessageCandidate(Array.isArray(response?.content) ? response.content.filter((block) => block?.type === "text" && typeof block.text === "string").map((block) => block.text).join("\n") : "");
2957
+ const message = cleanupCommitMessageCandidate(Array.isArray(response?.content) ? response.content.flatMap((block) => block?.type === "text" && typeof block.text === "string" ? [block.text] : []).join("\n") : "");
2548
2958
  if (message) return {
2549
2959
  message,
2550
2960
  source: "ai"
@@ -2710,10 +3120,11 @@ var GitWatchManager = class {
2710
3120
  }
2711
3121
  async resolveDirectory(state) {
2712
3122
  const generation = state.generation;
3123
+ if (this.directories.get(state.cwd) !== state) return;
2713
3124
  const repository = await resolveDirectoryGitRepository(state.cwd).catch(() => null);
3125
+ if (!repository) return;
2714
3126
  if (this.directories.get(state.cwd) !== state) return;
2715
3127
  if (state.generation !== generation) return;
2716
- if (!repository) return;
2717
3128
  const repoKey = repositoryWatchKey(repository);
2718
3129
  state.repoKey = repoKey;
2719
3130
  (this.repositories.get(repoKey) ?? this.createRepositoryWatch(repoKey, repository)).subscribers.add(state.cwd);
@@ -3067,6 +3478,7 @@ function getApiKeyProviderDisplayName(providerId) {
3067
3478
  return API_KEY_LOGIN_PROVIDER_NAMES[providerId] ?? providerId;
3068
3479
  }
3069
3480
  var STATE_SYNC_SCALAR_FIELDS = [
3481
+ "activationRevision",
3070
3482
  "sessionKey",
3071
3483
  "draft",
3072
3484
  "streaming",
@@ -3144,7 +3556,10 @@ function sanitizeMessageContentPart(part) {
3144
3556
  function sanitizeSessionMessage(message) {
3145
3557
  const role = typeof message?.role === "string" ? message.role : "";
3146
3558
  const metadata = message?.metadata && typeof message.metadata === "object" ? message.metadata : void 0;
3147
- const sanitizedContent = Array.isArray(message?.content) ? message.content.map((part) => sanitizeMessageContentPart(part)).filter((part) => Boolean(part)) : typeof message?.content === "string" ? message.content : void 0;
3559
+ const sanitizedContent = Array.isArray(message?.content) ? message.content.flatMap((part) => {
3560
+ const sanitizedPart = sanitizeMessageContentPart(part);
3561
+ return sanitizedPart ? [sanitizedPart] : [];
3562
+ }) : typeof message?.content === "string" ? message.content : void 0;
3148
3563
  return {
3149
3564
  ...role ? { role } : {},
3150
3565
  ...sanitizedContent !== void 0 ? { content: sanitizedContent } : {},
@@ -3216,10 +3631,11 @@ function createStateSyncPatch(previous, next) {
3216
3631
  let changed = false;
3217
3632
  const patch = {
3218
3633
  type: "state_sync",
3634
+ activationRevision: next.activationRevision,
3219
3635
  sessionKey: next.sessionKey
3220
3636
  };
3221
3637
  for (const field of STATE_SYNC_SCALAR_FIELDS) {
3222
- if (field === "sessionKey") continue;
3638
+ if (field === "activationRevision" || field === "sessionKey") continue;
3223
3639
  const nextValue = next[field];
3224
3640
  if (Object.is(previous.scalarValues[field], nextValue)) continue;
3225
3641
  patch[field] = nextValue;
@@ -3254,17 +3670,17 @@ function createInitialUiState() {
3254
3670
  }
3255
3671
  function normalizePromptImages(rawImages) {
3256
3672
  if (!Array.isArray(rawImages)) return [];
3257
- return rawImages.map((image) => {
3258
- if (!image || typeof image !== "object") return void 0;
3673
+ return rawImages.flatMap((image) => {
3674
+ if (!image || typeof image !== "object") return [];
3259
3675
  const mimeType = typeof image.mimeType === "string" ? image.mimeType.trim() : "";
3260
3676
  const data = typeof image.data === "string" ? image.data.trim() : "";
3261
- if (!mimeType || !/^image\//i.test(mimeType) || !data) return void 0;
3262
- return {
3677
+ if (!mimeType || !/^image\//i.test(mimeType) || !data) return [];
3678
+ return [{
3263
3679
  type: "image",
3264
3680
  mimeType,
3265
3681
  data
3266
- };
3267
- }).filter((image) => Boolean(image)).slice(0, 8);
3682
+ }];
3683
+ }).slice(0, 8);
3268
3684
  }
3269
3685
  function serializeModel(model) {
3270
3686
  if (!model) return void 0;
@@ -3317,6 +3733,7 @@ var PicoRuntime = class {
3317
3733
  encoder = new TextEncoder();
3318
3734
  contexts = /* @__PURE__ */ new Map();
3319
3735
  sessionEntries = /* @__PURE__ */ new Map();
3736
+ sessionTreeLeafOverrides = /* @__PURE__ */ new Map();
3320
3737
  servicesByCwd = /* @__PURE__ */ new Map();
3321
3738
  pendingUiRequests = /* @__PURE__ */ new Map();
3322
3739
  activeOAuthLogins = /* @__PURE__ */ new Map();
@@ -3471,7 +3888,10 @@ var PicoRuntime = class {
3471
3888
  });
3472
3889
  }
3473
3890
  listAvailableModels(entry) {
3474
- return entry.services.modelRegistry.getAvailable().map((model) => serializeModel(model)).filter((model) => Boolean(model)).sort((left, right) => {
3891
+ return entry.services.modelRegistry.getAvailable().flatMap((model) => {
3892
+ const serializedModel = serializeModel(model);
3893
+ return serializedModel ? [serializedModel] : [];
3894
+ }).sort((left, right) => {
3475
3895
  const providerCompare = (left.provider || "").localeCompare(right.provider || "");
3476
3896
  if (providerCompare !== 0) return providerCompare;
3477
3897
  return (left.name || left.id).localeCompare(right.name || right.id);
@@ -3491,6 +3911,8 @@ var PicoRuntime = class {
3491
3911
  const next = {
3492
3912
  id,
3493
3913
  clients: /* @__PURE__ */ new Set(),
3914
+ activeRevision: 0,
3915
+ selectionNonce: 0,
3494
3916
  activeKey: void 0,
3495
3917
  draftKey: void 0,
3496
3918
  sessionScope: process.cwd(),
@@ -3503,6 +3925,31 @@ var PicoRuntime = class {
3503
3925
  getSessionPath(entry) {
3504
3926
  return entry.session.sessionFile ?? entry.key;
3505
3927
  }
3928
+ getSessionTreeLeafOverrideKey(entry) {
3929
+ return entry.session.sessionFile || "";
3930
+ }
3931
+ rememberSessionTreeLeaf(entry, onlyExisting = false) {
3932
+ const key = this.getSessionTreeLeafOverrideKey(entry);
3933
+ if (!key) return;
3934
+ if (onlyExisting && !this.sessionTreeLeafOverrides.has(key)) return;
3935
+ const leafId = entry.session.sessionManager.getLeafId?.() ?? null;
3936
+ if (this.sessionTreeLeafOverrides.get(key) === leafId) return;
3937
+ this.sessionTreeLeafOverrides.set(key, leafId);
3938
+ this.invalidateSessionIndexCache();
3939
+ }
3940
+ restoreSessionTreeLeafOverride(manager, sessionPath) {
3941
+ if (!this.sessionTreeLeafOverrides.has(sessionPath)) return;
3942
+ const leafId = this.sessionTreeLeafOverrides.get(sessionPath) ?? null;
3943
+ if (leafId === null) {
3944
+ manager.resetLeaf?.();
3945
+ return;
3946
+ }
3947
+ if (!manager.getEntry?.(leafId)) {
3948
+ this.sessionTreeLeafOverrides.delete(sessionPath);
3949
+ return;
3950
+ }
3951
+ manager.branch?.(leafId);
3952
+ }
3506
3953
  getActiveEntry(context) {
3507
3954
  return context.activeKey ? this.sessionEntries.get(context.activeKey) : void 0;
3508
3955
  }
@@ -3524,13 +3971,13 @@ var PicoRuntime = class {
3524
3971
  return false;
3525
3972
  }
3526
3973
  async disposeUnreferencedSessionEntries() {
3527
- const candidates = [...this.sessionEntries.values()];
3528
- for (const entry of candidates) {
3529
- if (this.sessionEntries.get(entry.key) !== entry) continue;
3530
- if (this.isSessionEntryReferenced(entry)) continue;
3531
- if (this.isSessionBusyForDone(entry)) continue;
3532
- await this.disposeSessionEntry(entry);
3533
- }
3974
+ const candidates = [...this.sessionEntries.values()].filter((entry) => {
3975
+ if (this.sessionEntries.get(entry.key) !== entry) return false;
3976
+ if (this.isSessionEntryReferenced(entry)) return false;
3977
+ if (this.isSessionBusyForDone(entry)) return false;
3978
+ return true;
3979
+ });
3980
+ await Promise.all(candidates.map((entry) => this.disposeSessionEntry(entry)));
3534
3981
  }
3535
3982
  async cleanupInactiveContexts() {
3536
3983
  for (const context of this.contexts.values()) {
@@ -3579,11 +4026,12 @@ var PicoRuntime = class {
3579
4026
  }
3580
4027
  return lastValue;
3581
4028
  }
3582
- currentStatePayload(entry) {
4029
+ currentStatePayload(entry, context) {
3583
4030
  const draft = this.isDraftEntry(entry);
3584
4031
  const historyTotalCount = entry.session.messages.length;
3585
4032
  return {
3586
4033
  type: "state_sync",
4034
+ activationRevision: context.activeRevision,
3587
4035
  sessionKey: entry.key,
3588
4036
  items: entry.retainedConversationItems,
3589
4037
  pendingUserMessages: entry.pendingUserMessages.map((message) => clonePendingUserMessage(message)),
@@ -3647,13 +4095,17 @@ var PicoRuntime = class {
3647
4095
  const sessions = allSessions.filter((entry) => (entry.messageCount ?? 0) > 0);
3648
4096
  const metricsStartedAt = performance.now();
3649
4097
  const withMetrics = await Promise.all(sessions.map(async (entry) => {
3650
- const metrics = entry.path ? await readSessionListMetrics(entry.path) : void 0;
4098
+ let metrics;
4099
+ if (entry.path) {
4100
+ const hasLeafOverride = this.sessionTreeLeafOverrides.has(entry.path);
4101
+ metrics = await readSessionListMetrics(entry.path, hasLeafOverride ? { leafId: this.sessionTreeLeafOverrides.get(entry.path) ?? null } : void 0);
4102
+ }
3651
4103
  return {
3652
4104
  ...entry,
3653
- lastUserMessageAt: metrics?.lastUserMessageAt ?? entry.lastUserMessageAt,
3654
- lastMessageAt: metrics?.lastMessageAt ?? entry.lastMessageAt,
3655
- lastMessagePreview: metrics?.lastMessagePreview ?? entry.lastMessagePreview,
3656
- messageCount: metrics?.messageCount ?? entry.messageCount
4105
+ lastUserMessageAt: metrics ? metrics.lastUserMessageAt : entry.lastUserMessageAt,
4106
+ lastMessageAt: metrics ? metrics.lastMessageAt : entry.lastMessageAt,
4107
+ lastMessagePreview: metrics ? metrics.lastMessagePreview : entry.lastMessagePreview,
4108
+ messageCount: metrics ? metrics.messageCount : entry.messageCount
3657
4109
  };
3658
4110
  }));
3659
4111
  this.logSessionLoadDebug("session_index:metrics", {
@@ -3708,12 +4160,12 @@ var PicoRuntime = class {
3708
4160
  });
3709
4161
  }
3710
4162
  async listEntriesForDirectory(allSessions, directoryPath) {
3711
- const sessions = allSessions.filter((entry) => entry.cwd === directoryPath).map((entry) => ({ ...entry }));
3712
- const byPath = new Map(sessions.filter((entry) => entry.path).map((entry) => [entry.path, entry]));
3713
- const byId = new Map(sessions.filter((entry) => entry.id).map((entry) => [entry.id, entry]));
3714
- for (const entry of this.sessionEntries.values()) {
3715
- if (entry.cwd !== directoryPath || !this.hasVisibleSessionContent(entry)) continue;
3716
- const fallback = await this.sessionFallbackInfo(entry);
4163
+ const sessions = allSessions.flatMap((entry) => entry.cwd === directoryPath ? [{ ...entry }] : []);
4164
+ const byPath = new Map(sessions.flatMap((entry) => entry.path ? [[entry.path, entry]] : []));
4165
+ const byId = new Map(sessions.flatMap((entry) => entry.id ? [[entry.id, entry]] : []));
4166
+ const fallbackEntries = [...this.sessionEntries.values()].filter((entry) => entry.cwd === directoryPath && this.hasVisibleSessionContent(entry));
4167
+ const fallbacks = await Promise.all(fallbackEntries.map((entry) => this.sessionFallbackInfo(entry)));
4168
+ for (const fallback of fallbacks) {
3717
4169
  const existing = (fallback.path ? byPath.get(fallback.path) : void 0) || (fallback.id ? byId.get(fallback.id) : void 0);
3718
4170
  if (existing) {
3719
4171
  this.mergeSessionListEntry(existing, fallback);
@@ -3726,7 +4178,7 @@ var PicoRuntime = class {
3726
4178
  return sessions.sort((left, right) => this.compareSessionListEntriesByLastUserMessage(left, right));
3727
4179
  }
3728
4180
  buildStreamingPaths() {
3729
- return new Set([...this.sessionEntries.values()].filter((entry) => this.getEntryStreamingState(entry)).map((entry) => this.getSessionPath(entry)));
4181
+ return new Set([...this.sessionEntries.values()].flatMap((entry) => this.getEntryStreamingState(entry) ? [this.getSessionPath(entry)] : []));
3730
4182
  }
3731
4183
  async collectDirectoryEntries(allSessions, directories) {
3732
4184
  const entriesByDirectory = /* @__PURE__ */ new Map();
@@ -3928,7 +4380,7 @@ var PicoRuntime = class {
3928
4380
  if (!entry) return;
3929
4381
  const startedAt = performance.now();
3930
4382
  const payloadStartedAt = performance.now();
3931
- const payload = this.currentStatePayload(entry);
4383
+ const payload = this.currentStatePayload(entry, context);
3932
4384
  const payloadDurationMs = roundedDurationMs(payloadStartedAt);
3933
4385
  let sentCount = 0;
3934
4386
  for (const client of context.clients) if (this.sendStatePayloadToClient(context, client, payload, options)) sentCount += 1;
@@ -3986,6 +4438,24 @@ var PicoRuntime = class {
3986
4438
  }
3987
4439
  await Promise.all(connectedContexts.map((context) => this.sendSessionsToContext(context)));
3988
4440
  }
4441
+ async activateContextsSessionAndBroadcast(contexts, entry) {
4442
+ await Promise.all(contexts.map((context) => this.activateContextSession(context, entry)));
4443
+ await this.broadcastSessionsAll();
4444
+ }
4445
+ async bindSessionEntryAndBroadcast(entry) {
4446
+ await this.bindSessionEntry(entry);
4447
+ await Promise.all([this.broadcastEntryState(entry), this.broadcastSessionsAll()]);
4448
+ }
4449
+ scheduleTreeNavigationSessionListChange(entry, previousLeafId, result) {
4450
+ if (result.cancelled || result.aborted) return;
4451
+ const nextLeafId = entry.session.sessionManager.getLeafId?.() ?? null;
4452
+ if (!result.summaryEntry && previousLeafId === nextLeafId) return;
4453
+ this.rememberSessionTreeLeaf(entry);
4454
+ this.touchSessionEntry(entry);
4455
+ this.broadcastSessionsAll().catch((error) => {
4456
+ console.error("[pico] failed to broadcast tree navigation sessions:", error);
4457
+ });
4458
+ }
3989
4459
  syncGitWatchDirectories() {
3990
4460
  const cwds = /* @__PURE__ */ new Set();
3991
4461
  for (const context of this.contexts.values()) {
@@ -4157,6 +4627,7 @@ var PicoRuntime = class {
4157
4627
  async activateContextSession(context, entry, options) {
4158
4628
  const previousActiveKey = context.activeKey;
4159
4629
  const startedAt = performance.now();
4630
+ context.activeRevision += 1;
4160
4631
  await activateContextSession({
4161
4632
  context,
4162
4633
  entry,
@@ -4185,6 +4656,7 @@ var PicoRuntime = class {
4185
4656
  this.syncGitWatchDirectories();
4186
4657
  this.logSessionLoadDebug("context_session:activate", {
4187
4658
  contextId: context.id,
4659
+ activeRevision: context.activeRevision,
4188
4660
  previousActiveKey,
4189
4661
  notify: options?.notify !== false,
4190
4662
  statusDurationMs,
@@ -4407,6 +4879,7 @@ var PicoRuntime = class {
4407
4879
  const sdk = await this.getSdk();
4408
4880
  const openStartedAt = performance.now();
4409
4881
  const sessionManager = sdk.SessionManager.open(sessionPath);
4882
+ this.restoreSessionTreeLeafOverride(sessionManager, sessionPath);
4410
4883
  this.logSessionLoadDebug("session_manager:open", {
4411
4884
  sessionPath,
4412
4885
  cwd: sessionManager.getCwd(),
@@ -4423,7 +4896,9 @@ var PicoRuntime = class {
4423
4896
  if (currentManager.isPersisted?.()) {
4424
4897
  const currentSessionFile = entry.session.sessionFile;
4425
4898
  if (!currentSessionFile) throw new Error("Persisted session is missing a session file");
4426
- return (await this.getSdk()).SessionManager.open(currentSessionFile, currentManager.getSessionDir?.(), entry.cwd);
4899
+ const sessionManager = (await this.getSdk()).SessionManager.open(currentSessionFile, currentManager.getSessionDir?.(), entry.cwd);
4900
+ this.restoreSessionTreeLeafOverride(sessionManager, currentSessionFile);
4901
+ return sessionManager;
4427
4902
  }
4428
4903
  return await this.createForkedInMemorySessionManager(currentManager, currentManager.getLeafId?.(), entry.session.sessionFile);
4429
4904
  }
@@ -4609,25 +5084,26 @@ var PicoRuntime = class {
4609
5084
  }
4610
5085
  pendingMessagesFromClientUpdate(entry, pendingMessagesUpdate) {
4611
5086
  const pendingMessages = entry.pendingUserMessages.map((message) => clonePendingUserMessage(message));
4612
- const normalizedUpdates = Array.isArray(pendingMessagesUpdate) ? pendingMessagesUpdate.map((message) => {
5087
+ const normalizedUpdates = Array.isArray(pendingMessagesUpdate) ? pendingMessagesUpdate.flatMap((message) => {
5088
+ const pendingId = typeof message?.pendingId === "string" ? message.pendingId : "";
5089
+ if (!pendingId) return [];
4613
5090
  const hasImages = hasOwnProperty(message, "images");
4614
5091
  const hasText = hasOwnProperty(message, "text");
4615
- return {
4616
- pendingId: typeof message?.pendingId === "string" ? message.pendingId : "",
5092
+ return [{
5093
+ pendingId,
4617
5094
  hasImages,
4618
5095
  hasText,
4619
5096
  images: hasImages ? normalizePromptImages(message?.images) : [],
4620
5097
  streamingBehavior: normalizePendingStreamingBehavior(message?.streamingBehavior),
4621
5098
  text: hasText && typeof message?.text === "string" ? message.text : ""
4622
- };
4623
- }).filter((message) => Boolean(message.pendingId)) : [];
4624
- if (normalizedUpdates.length !== pendingMessages.length) throw new Error("pendingMessages must include every pending prompt exactly once.");
5099
+ }];
5100
+ }) : [];
4625
5101
  const pendingMessagesById = new Map(pendingMessages.map((message) => [message.pendingId, message]));
4626
5102
  if (pendingMessagesById.size !== pendingMessages.length) throw new Error("Pending prompt order is out of date. Refresh and try again.");
4627
5103
  const nextPendingMessages = [];
4628
5104
  for (const update of normalizedUpdates) {
4629
5105
  const existing = pendingMessagesById.get(update.pendingId);
4630
- if (!existing) throw new Error("pendingMessages must include every pending prompt exactly once.");
5106
+ if (!existing) continue;
4631
5107
  const text = update.hasText ? update.text : existing.text;
4632
5108
  const images = update.hasImages ? update.images : existing.images;
4633
5109
  if (!text.trim() && images.length === 0) throw new Error("Pending prompt text or image is required.");
@@ -4642,23 +5118,30 @@ var PicoRuntime = class {
4642
5118
  if (pendingMessagesById.size > 0) throw new Error("pendingMessages must include every pending prompt exactly once.");
4643
5119
  return sortPendingUserMessages(nextPendingMessages);
4644
5120
  }
5121
+ async replayPendingUserMessages(entry, pendingMessages, index = 0) {
5122
+ const pendingMessage = pendingMessages[index];
5123
+ if (!pendingMessage) return;
5124
+ const text = pendingMessage.text;
5125
+ const images = normalizePromptImages(pendingMessage.images);
5126
+ if (text.trim() || images.length > 0) await entry.session.prompt(text, {
5127
+ ...images.length > 0 ? { images } : {},
5128
+ streamingBehavior: pendingMessage.streamingBehavior
5129
+ });
5130
+ await this.replayPendingUserMessages(entry, pendingMessages, index + 1);
5131
+ }
4645
5132
  async replacePendingUserMessages(entry, pendingMessages) {
4646
5133
  const nextPending = sortPendingUserMessages(pendingMessages);
4647
5134
  const canReplayPending = Boolean(entry.session.isStreaming);
4648
- if (!canReplayPending && nextPending.length > 0) throw new Error("Pending prompts can only be changed while the session is streaming.");
5135
+ if (!canReplayPending) {
5136
+ entry.pendingUserMessages = nextPending;
5137
+ await this.broadcastEntryState(entry);
5138
+ return entry.pendingUserMessages;
5139
+ }
4649
5140
  entry.pendingQueueMutation = true;
4650
5141
  try {
4651
5142
  entry.session.clearQueue();
4652
5143
  entry.pendingUserMessages = [];
4653
- if (canReplayPending) for (const pendingMessage of nextPending) {
4654
- const text = pendingMessage.text;
4655
- const images = normalizePromptImages(pendingMessage.images);
4656
- if (!text.trim() && images.length === 0) continue;
4657
- await entry.session.prompt(text, {
4658
- ...images.length > 0 ? { images } : {},
4659
- streamingBehavior: pendingMessage.streamingBehavior
4660
- });
4661
- }
5144
+ if (canReplayPending) await this.replayPendingUserMessages(entry, nextPending);
4662
5145
  entry.pendingUserMessages = nextPending;
4663
5146
  this.reconcilePendingUserMessages(entry);
4664
5147
  } finally {
@@ -4779,11 +5262,9 @@ var PicoRuntime = class {
4779
5262
  sessionId: void 0,
4780
5263
  sessionFile: void 0
4781
5264
  };
4782
- for (const context of viewers()) {
4783
- context.draftKey = result.entry.key;
4784
- await this.activateContextSession(context, result.entry);
4785
- }
4786
- await this.broadcastSessionsAll();
5265
+ const activeViewers = viewers();
5266
+ for (const context of activeViewers) context.draftKey = result.entry.key;
5267
+ await this.activateContextsSessionAndBroadcast(activeViewers, result.entry);
4787
5268
  return {
4788
5269
  sessionId: void 0,
4789
5270
  sessionFile: void 0
@@ -4793,22 +5274,19 @@ var PicoRuntime = class {
4793
5274
  const branchedPath = session.sessionManager.createBranchedSession?.(branchEntryId);
4794
5275
  if (!branchedPath) return { cancelled: true };
4795
5276
  const nextEntry = await this.ensureSessionEntryByPath(branchedPath);
4796
- for (const context of viewers()) await this.activateContextSession(context, nextEntry);
4797
- await this.broadcastSessionsAll();
5277
+ await this.activateContextsSessionAndBroadcast(viewers(), nextEntry);
4798
5278
  return {
4799
5279
  sessionId: nextEntry.session.sessionId,
4800
5280
  sessionFile: nextEntry.session.sessionFile
4801
5281
  };
4802
5282
  },
4803
5283
  navigateTree: async (targetId, navigateOptions) => {
5284
+ const previousLeafId = session.sessionManager.getLeafId?.() ?? null;
4804
5285
  const result = await session.navigateTree(targetId, navigateOptions);
4805
5286
  this.refreshRetainedConversationItems(entry);
4806
5287
  if (result.editorText != null) entry.uiState.editorText = result.editorText;
4807
5288
  await this.broadcastEntryState(entry);
4808
- if (result.summaryEntry) {
4809
- this.touchSessionEntry(entry);
4810
- await this.broadcastSessionsAll();
4811
- }
5289
+ this.scheduleTreeNavigationSessionListChange(entry, previousLeafId, result);
4812
5290
  return {
4813
5291
  cancelled: Boolean(result.cancelled),
4814
5292
  aborted: Boolean(result.aborted)
@@ -4825,8 +5303,7 @@ var PicoRuntime = class {
4825
5303
  sessionId: entry.session.sessionId,
4826
5304
  sessionFile: entry.session.sessionFile
4827
5305
  };
4828
- for (const context of viewers()) await this.activateContextSession(context, nextEntry);
4829
- await this.broadcastSessionsAll();
5306
+ await this.activateContextsSessionAndBroadcast(viewers(), nextEntry);
4830
5307
  return {
4831
5308
  sessionId: nextEntry.session.sessionId,
4832
5309
  sessionFile: nextEntry.session.sessionFile
@@ -4834,9 +5311,7 @@ var PicoRuntime = class {
4834
5311
  },
4835
5312
  reload: async () => {
4836
5313
  await session.reload?.();
4837
- await this.bindSessionEntry(entry);
4838
- await this.broadcastEntryState(entry);
4839
- await this.broadcastSessionsAll();
5314
+ await this.bindSessionEntryAndBroadcast(entry);
4840
5315
  }
4841
5316
  },
4842
5317
  shutdownHandler: () => {
@@ -4938,6 +5413,7 @@ var PicoRuntime = class {
4938
5413
  statusChanged = true;
4939
5414
  }
4940
5415
  if (statusChanged) this.broadcastSessionStatusAll(entry);
5416
+ this.rememberSessionTreeLeaf(entry, true);
4941
5417
  await this.broadcastEntryState(entry);
4942
5418
  if (type === "agent_start" || type === "agent_end" || type === "message_end" || type === "compaction_end" || type === "queue_update") await this.broadcastSessionsAll();
4943
5419
  if (type === "agent_end" || type === "compaction_end") await this.cleanupInactiveContexts();
@@ -4995,12 +5471,13 @@ var PicoRuntime = class {
4995
5471
  const bootstrapStartedAt = performance.now();
4996
5472
  try {
4997
5473
  const stateStartedAt = performance.now();
4998
- this.sendStatePayloadToClient(context, client, this.currentStatePayload(activeEntry), { forceFull: true });
5474
+ const bootstrapEntry = this.getActiveEntry(context) ?? activeEntry;
5475
+ this.sendStatePayloadToClient(context, client, this.currentStatePayload(bootstrapEntry, context), { forceFull: true });
4999
5476
  this.logSessionLoadDebug("events_response:bootstrap_state", {
5000
5477
  contextId: context.id,
5001
5478
  clientId: client.id,
5002
5479
  durationMs: roundedDurationMs(stateStartedAt),
5003
- ...this.sessionDebugDetails(activeEntry)
5480
+ ...this.sessionDebugDetails(bootstrapEntry)
5004
5481
  });
5005
5482
  if (client.closed || request.signal.aborted) return;
5006
5483
  const sessionsStartedAt = performance.now();
@@ -5162,7 +5639,6 @@ var PicoRuntime = class {
5162
5639
  const pendingMessages = Array.isArray(body.pendingMessages) ? body.pendingMessages : Array.isArray(body.pendingIds) ? body.pendingIds.map((pendingId) => ({ pendingId })) : null;
5163
5640
  if (!pendingMessages) throw new Error("pendingMessages must be an array");
5164
5641
  const nextPendingMessages = this.pendingMessagesFromClientUpdate(activeEntry, pendingMessages);
5165
- if (!activeEntry.session.isStreaming && nextPendingMessages.length > 0) throw new Error("Pending prompts can only be changed while the session is streaming.");
5166
5642
  await this.replacePendingUserMessages(activeEntry, nextPendingMessages);
5167
5643
  return {
5168
5644
  ok: true,
@@ -5181,7 +5657,9 @@ var PicoRuntime = class {
5181
5657
  const pendingMessages = activeEntry.pendingUserMessages.map((message) => clonePendingUserMessage(message));
5182
5658
  const pendingIndex = pendingMessages.findIndex((message) => message.pendingId === pendingId);
5183
5659
  if (pendingIndex === -1) {
5184
- if (normalizeClientPendingId(pendingId)) {
5660
+ const isKnownClientPendingId = Boolean(normalizeClientPendingId(pendingId));
5661
+ const isKnownServerPendingId = pendingId.startsWith("pending:");
5662
+ if (isKnownClientPendingId || isKnownServerPendingId) {
5185
5663
  activeEntry.canceledPendingUserMessageIds.add(pendingId);
5186
5664
  return {
5187
5665
  ok: true,
@@ -5192,7 +5670,6 @@ var PicoRuntime = class {
5192
5670
  }
5193
5671
  activeEntry.canceledPendingUserMessageIds.delete(pendingId);
5194
5672
  pendingMessages.splice(pendingIndex, 1);
5195
- if (!activeEntry.session.isStreaming && pendingMessages.length > 0) throw new Error("Pending prompts can only be changed while the session is streaming.");
5196
5673
  await this.replacePendingUserMessages(activeEntry, pendingMessages);
5197
5674
  return {
5198
5675
  ok: true,
@@ -5232,11 +5709,16 @@ var PicoRuntime = class {
5232
5709
  const url = new URL(request.url);
5233
5710
  const requestedSessionId = url.searchParams.get("session")?.trim() || "";
5234
5711
  const requestedSessionPath = url.searchParams.get("sessionPath")?.trim() || "";
5712
+ const rawSelectionNonce = Number(url.searchParams.get("selectionNonce"));
5713
+ const selectionNonce = Number.isSafeInteger(rawSelectionNonce) && rawSelectionNonce > 0 ? rawSelectionNonce : 0;
5235
5714
  const context = this.ensureContext(url.searchParams.get("context") || "default");
5236
5715
  context.sessionScope = normalizeSessionScope(url.searchParams.get("scope"), process.cwd());
5716
+ if (selectionNonce > context.selectionNonce) context.selectionNonce = selectionNonce;
5237
5717
  this.logSessionLoadDebug("select_session:start", {
5238
5718
  requestedSessionId,
5239
5719
  requestedSessionPath,
5720
+ selectionNonce,
5721
+ latestSelectionNonce: context.selectionNonce,
5240
5722
  contextId: context.id
5241
5723
  });
5242
5724
  try {
@@ -5252,6 +5734,26 @@ var PicoRuntime = class {
5252
5734
  ...this.sessionDebugDetails(nextEntry)
5253
5735
  });
5254
5736
  if (!nextEntry) throw new Error(`Unknown session: ${requestedSessionId}`);
5737
+ if (request.signal.aborted) {
5738
+ this.logSessionLoadDebug("select_session:skip_aborted", {
5739
+ requestedSessionId,
5740
+ requestedSessionPath,
5741
+ selectionNonce,
5742
+ latestSelectionNonce: context.selectionNonce,
5743
+ ...this.sessionDebugDetails(nextEntry)
5744
+ });
5745
+ return { ok: true };
5746
+ }
5747
+ if (selectionNonce > 0 && selectionNonce < context.selectionNonce) {
5748
+ this.logSessionLoadDebug("select_session:skip_stale", {
5749
+ requestedSessionId,
5750
+ requestedSessionPath,
5751
+ selectionNonce,
5752
+ latestSelectionNonce: context.selectionNonce,
5753
+ ...this.sessionDebugDetails(nextEntry)
5754
+ });
5755
+ return { ok: true };
5756
+ }
5255
5757
  const activateStartedAt = performance.now();
5256
5758
  await this.activateContextSession(context, nextEntry);
5257
5759
  this.logSessionLoadDebug("select_session:activate", {
@@ -5332,7 +5834,10 @@ var PicoRuntime = class {
5332
5834
  return {
5333
5835
  leafId,
5334
5836
  streamingEntryId,
5335
- tree: (manager.getTree() || []).map((node) => serializeSessionTreeNode(node)).filter((node) => Boolean(node)).map((node) => markStreamingNode(node))
5837
+ tree: (manager.getTree() || []).flatMap((node) => {
5838
+ const treeNode = serializeSessionTreeNode(node);
5839
+ return treeNode ? [markStreamingNode(treeNode)] : [];
5840
+ })
5336
5841
  };
5337
5842
  }
5338
5843
  async getSessionTreeForRequest(request) {
@@ -5372,6 +5877,7 @@ var PicoRuntime = class {
5372
5877
  const targetId = typeof body.targetId === "string" ? body.targetId.trim() : "";
5373
5878
  if (!targetId) throw new Error("targetId is required");
5374
5879
  if (this.getEntryStreamingState(activeEntry) || activeEntry.session.isStreaming) throw new Error("Abort the current response before navigating the tree.");
5880
+ const previousLeafId = activeEntry.session.sessionManager.getLeafId?.() ?? null;
5375
5881
  const result = await activeEntry.session.navigateTree(targetId, {
5376
5882
  summarize: Boolean(body.summarize),
5377
5883
  customInstructions: typeof body.customInstructions === "string" ? body.customInstructions : void 0,
@@ -5380,11 +5886,8 @@ var PicoRuntime = class {
5380
5886
  });
5381
5887
  this.refreshRetainedConversationItems(activeEntry);
5382
5888
  if (result.editorText != null) activeEntry.uiState.editorText = result.editorText;
5383
- if (result.summaryEntry) {
5384
- this.touchSessionEntry(activeEntry);
5385
- await this.broadcastSessionsAll();
5386
- }
5387
5889
  await this.broadcastEntryState(activeEntry);
5890
+ this.scheduleTreeNavigationSessionListChange(activeEntry, previousLeafId, result);
5388
5891
  return {
5389
5892
  ok: true,
5390
5893
  cancelled: Boolean(result.cancelled),
@@ -5532,6 +6035,27 @@ var PicoRuntime = class {
5532
6035
  name: nextName
5533
6036
  };
5534
6037
  }
6038
+ async setSessionReadState(request, body) {
6039
+ const { context } = await this.resolveRequest(request);
6040
+ const sessionPath = typeof body.path === "string" ? body.path.trim() : "";
6041
+ const unread = typeof body.unread === "boolean" ? body.unread : false;
6042
+ if (!sessionPath) throw new Error("path is required");
6043
+ if (unread) context.unreadFinished.add(sessionPath);
6044
+ else context.unreadFinished.delete(sessionPath);
6045
+ const loadedEntry = [...this.sessionEntries.values()].find((entry) => this.getSessionPath(entry) === sessionPath);
6046
+ if (loadedEntry) this.sendSessionStatusToContext(context, loadedEntry);
6047
+ else this.sendToContext(context, {
6048
+ type: "session_status",
6049
+ sessionPath,
6050
+ unread
6051
+ });
6052
+ await this.sendSessionsToContext(context);
6053
+ return {
6054
+ ok: true,
6055
+ path: sessionPath,
6056
+ unread
6057
+ };
6058
+ }
5535
6059
  async writeMovedSessionFile(sessionPath, nextCwd, targetSessionDirectory) {
5536
6060
  const sourcePath = resolve(sessionPath);
5537
6061
  const targetPath = resolve(await uniqueSessionMovePath(sourcePath, targetSessionDirectory));
@@ -5563,20 +6087,23 @@ var PicoRuntime = class {
5563
6087
  const previousPath = resolve(sessionPath);
5564
6088
  const previousEntry = loadedEntries[0];
5565
6089
  const previousCwd = previousEntry?.cwd || "";
5566
- const affectedContexts = [...this.contexts.values()].map((context) => ({
5567
- context,
5568
- wasActive: loadedEntries.some((entry) => context.activeKey === entry.key),
5569
- wasDraft: loadedEntries.some((entry) => context.draftKey === entry.key)
5570
- })).filter((entry) => entry.wasActive || entry.wasDraft);
6090
+ const affectedContexts = [...this.contexts.values()].flatMap((context) => {
6091
+ const wasActive = loadedEntries.some((entry) => context.activeKey === entry.key);
6092
+ const wasDraft = loadedEntries.some((entry) => context.draftKey === entry.key);
6093
+ return wasActive || wasDraft ? [{
6094
+ context,
6095
+ wasActive,
6096
+ wasDraft
6097
+ }] : [];
6098
+ });
5571
6099
  const nextPath = await this.writeMovedSessionFile(previousPath, nextCwd, targetSessionDirectory);
5572
- for (const loadedEntry of loadedEntries) await this.disposeSessionEntry(loadedEntry);
6100
+ await Promise.all(loadedEntries.map((loadedEntry) => this.disposeSessionEntry(loadedEntry)));
5573
6101
  let nextEntry;
5574
6102
  if (affectedContexts.some((entry) => entry.wasActive)) {
5575
6103
  nextEntry = await this.ensureSessionEntryByPath(nextPath);
5576
- for (const affected of affectedContexts) {
5577
- if (affected.wasDraft) affected.context.draftKey = void 0;
5578
- if (affected.wasActive) await this.activateContextSession(affected.context, nextEntry, { refreshSessions: false });
5579
- }
6104
+ const activeNextEntry = nextEntry;
6105
+ for (const affected of affectedContexts) if (affected.wasDraft) affected.context.draftKey = void 0;
6106
+ await Promise.all(affectedContexts.flatMap((affected) => affected.wasActive ? [this.activateContextSession(affected.context, activeNextEntry, { refreshSessions: false })] : []));
5580
6107
  } else for (const affected of affectedContexts) if (affected.wasDraft) affected.context.draftKey = void 0;
5581
6108
  for (const context of this.contexts.values()) if (context.unreadFinished.delete(previousPath)) context.unreadFinished.add(nextPath);
5582
6109
  this.invalidateSessionIndexCache();
@@ -5621,21 +6148,20 @@ var PicoRuntime = class {
5621
6148
  const allSessions = await this.listSessionIndexEntries();
5622
6149
  const directorySessions = await this.listEntriesForDirectory(allSessions, directory);
5623
6150
  const streamingPaths = this.buildStreamingPaths();
5624
- const activeKeys = new Set([...this.contexts.values()].map((ctx) => ctx.activeKey).filter(Boolean));
5625
- const activePaths = new Set([...this.sessionEntries.values()].filter((entry) => activeKeys.has(entry.key)).map((entry) => this.getSessionPath(entry)));
6151
+ const activeKeys = new Set([...this.contexts.values()].flatMap((ctx) => ctx.activeKey ? [ctx.activeKey] : []));
6152
+ const activePaths = new Set([...this.sessionEntries.values()].flatMap((entry) => activeKeys.has(entry.key) ? [this.getSessionPath(entry)] : []));
5626
6153
  const includeActiveSession = body.includeActiveSession === true;
5627
- const matchingSessions = directorySessions.map((entry) => {
6154
+ const matchingSessions = directorySessions.flatMap((entry) => {
5628
6155
  const activityAt = normalizeModifiedTimestamp(entry.lastUserMessageAt) || normalizeModifiedTimestamp(entry.modified);
5629
- return {
6156
+ const serializedEntry = {
5630
6157
  ...this.serializeSessionListEntry(entry, context, streamingPaths),
5631
6158
  activityAt
5632
6159
  };
5633
- }).filter((entry) => {
5634
- if (!entry.path) return false;
5635
- if (streamingPaths.has(entry.path)) return false;
5636
- if (!includeActiveSession && activePaths.has(entry.path)) return false;
5637
- const activityTime = modifiedTimestampValue(entry.activityAt);
5638
- return activityTime > 0 && activityTime < cutoffTime;
6160
+ if (!serializedEntry.path) return [];
6161
+ if (streamingPaths.has(serializedEntry.path)) return [];
6162
+ if (!includeActiveSession && activePaths.has(serializedEntry.path)) return [];
6163
+ const activityTime = modifiedTimestampValue(serializedEntry.activityAt);
6164
+ return activityTime > 0 && activityTime < cutoffTime ? [serializedEntry] : [];
5639
6165
  });
5640
6166
  if (body.dryRun !== false) return {
5641
6167
  ok: true,
@@ -5645,10 +6171,10 @@ var PicoRuntime = class {
5645
6171
  deletedSessionIds: [],
5646
6172
  matchingSessions
5647
6173
  };
5648
- const deletedSessionIds = matchingSessions.map((session) => session.id).filter((id) => Boolean(id));
5649
- const matchingPaths = matchingSessions.map((session) => session.path).filter((path) => Boolean(path));
6174
+ const deletedSessionIds = matchingSessions.flatMap((session) => session.id ? [session.id] : []);
6175
+ const matchingPaths = matchingSessions.flatMap((session) => session.path ? [session.path] : []);
5650
6176
  const matchingPathSet = new Set(matchingPaths);
5651
- await Promise.all([...this.sessionEntries.values()].filter((entry) => matchingPathSet.has(this.getSessionPath(entry))).map((entry) => this.disposeSessionEntry(entry)));
6177
+ await Promise.all([...this.sessionEntries.values()].flatMap((entry) => matchingPathSet.has(this.getSessionPath(entry)) ? [this.disposeSessionEntry(entry)] : []));
5652
6178
  for (const ctx of this.contexts.values()) for (const sessionPath of matchingPaths) ctx.unreadFinished.delete(sessionPath);
5653
6179
  await Promise.all(matchingPaths.map((sessionPath) => this.trashOrDeleteSessionFile(sessionPath)));
5654
6180
  this.invalidateSessionIndexCache();
@@ -5664,23 +6190,32 @@ var PicoRuntime = class {
5664
6190
  }
5665
6191
  async deleteSessions(request, body) {
5666
6192
  const { context } = await this.resolveRequest(request);
5667
- const paths = Array.isArray(body.paths) ? body.paths.filter((path) => typeof path === "string").map((path) => path.trim()).filter(Boolean) : [];
6193
+ const paths = Array.isArray(body.paths) ? body.paths.flatMap((path) => {
6194
+ if (typeof path !== "string") return [];
6195
+ const trimmedPath = path.trim();
6196
+ return trimmedPath ? [trimmedPath] : [];
6197
+ }) : [];
5668
6198
  const sessionPaths = [...new Set(paths)];
5669
6199
  if (sessionPaths.length === 0) throw new Error("paths are required");
5670
6200
  const sessionPathSet = new Set(sessionPaths);
5671
6201
  const loadedEntries = [...this.sessionEntries.values()].filter((entry) => sessionPathSet.has(this.getSessionPath(entry)));
5672
- let replacementEntry;
5673
- for (const loadedEntry of loadedEntries) {
6202
+ const replacementRequests = loadedEntries.flatMap((loadedEntry) => {
5674
6203
  const affectedContexts = [...this.contexts.values()].filter((ctx) => ctx.activeKey === loadedEntry.key);
5675
- if (affectedContexts.length > 0) {
5676
- const nextReplacementEntry = await this.createNewSessionEntry(loadedEntry.cwd, { draft: true });
5677
- replacementEntry = replacementEntry ?? nextReplacementEntry;
5678
- for (const affected of affectedContexts) {
5679
- affected.draftKey = nextReplacementEntry.key;
5680
- await this.activateContextSession(affected, nextReplacementEntry);
5681
- }
5682
- }
6204
+ return affectedContexts.length > 0 ? [{
6205
+ affectedContexts,
6206
+ cwd: loadedEntry.cwd
6207
+ }] : [];
6208
+ });
6209
+ const replacementResults = await Promise.all(replacementRequests.map(async (request) => ({
6210
+ affectedContexts: request.affectedContexts,
6211
+ entry: await this.createNewSessionEntry(request.cwd, { draft: true })
6212
+ })));
6213
+ let replacementEntry;
6214
+ for (const result of replacementResults) {
6215
+ replacementEntry = replacementEntry ?? result.entry;
6216
+ for (const affected of result.affectedContexts) affected.draftKey = result.entry.key;
5683
6217
  }
6218
+ await Promise.all(replacementResults.flatMap((result) => result.affectedContexts.map((affected) => this.activateContextSession(affected, result.entry))));
5684
6219
  for (const ctx of this.contexts.values()) for (const sessionPath of sessionPaths) ctx.unreadFinished.delete(sessionPath);
5685
6220
  await Promise.all(loadedEntries.map((entry) => this.disposeSessionEntry(entry)));
5686
6221
  await Promise.all(sessionPaths.map((sessionPath) => this.trashOrDeleteSessionFile(sessionPath)));
@@ -5742,15 +6277,15 @@ var PicoRuntime = class {
5742
6277
  authType: "oauth",
5743
6278
  ...authStatus(provider.id)
5744
6279
  })).sort((left, right) => left.name.localeCompare(right.name));
5745
- const apiKeyOptions = Array.from(providerIds).filter((providerId) => {
5746
- if (BUILT_IN_API_KEY_LOGIN_PROVIDERS.has(providerId)) return true;
5747
- return !oauthProviderIds.has(providerId);
5748
- }).map((providerId) => ({
5749
- id: providerId,
5750
- name: getApiKeyProviderDisplayName(providerId),
5751
- authType: "api_key",
5752
- ...authStatus(providerId)
5753
- })).sort((left, right) => left.name.localeCompare(right.name));
6280
+ const apiKeyOptions = Array.from(providerIds).flatMap((providerId) => {
6281
+ if (!BUILT_IN_API_KEY_LOGIN_PROVIDERS.has(providerId) && oauthProviderIds.has(providerId)) return [];
6282
+ return [{
6283
+ id: providerId,
6284
+ name: getApiKeyProviderDisplayName(providerId),
6285
+ authType: "api_key",
6286
+ ...authStatus(providerId)
6287
+ }];
6288
+ }).sort((left, right) => left.name.localeCompare(right.name));
5754
6289
  const nameByProvider = new Map([...oauthOptions, ...apiKeyOptions].map((option) => [option.id, option.name]));
5755
6290
  const authTypeByProvider = new Map([...oauthOptions, ...apiKeyOptions].map((option) => [option.id, option.authType]));
5756
6291
  return {
@@ -5997,14 +6532,14 @@ var PicoRuntime = class {
5997
6532
  pending.resolve({ cancelled: true });
5998
6533
  } catch {}
5999
6534
  this.pendingUiRequests.clear();
6000
- for (const entry of this.sessionEntries.values()) await this.disposeSessionEntry(entry);
6535
+ await Promise.all([...this.sessionEntries.values()].map((entry) => this.disposeSessionEntry(entry)));
6001
6536
  }
6002
6537
  };
6003
6538
  function getPicoRuntime() {
6004
6539
  globalThis.__picoRuntime ??= new PicoRuntime();
6005
6540
  return globalThis.__picoRuntime;
6006
6541
  }
6007
- var Route$47 = createFileRoute("/events")({ server: { handlers: { GET: ({ request }) => getPicoRuntime().createEventsResponse(request) } } });
6542
+ var Route$52 = createFileRoute("/events")({ server: { handlers: { GET: ({ request }) => getPicoRuntime().createEventsResponse(request) } } });
6008
6543
  function jsonResponse(data, init = 200) {
6009
6544
  const responseInit = typeof init === "number" ? { status: init } : init;
6010
6545
  const headers = new Headers(responseInit.headers);
@@ -6040,7 +6575,7 @@ function routeErrorResponse(error, fallbackMessage) {
6040
6575
  async function readRequestJson(request) {
6041
6576
  return await request.json().catch(() => ({}));
6042
6577
  }
6043
- var Route$46 = createFileRoute("/api/thinking")({ server: { handlers: { POST: async ({ request }) => {
6578
+ var Route$51 = createFileRoute("/api/thinking")({ server: { handlers: { POST: async ({ request }) => {
6044
6579
  try {
6045
6580
  const body = await readRequestJson(request);
6046
6581
  return jsonResponse(await getPicoRuntime().setThinking(request, body));
@@ -6048,7 +6583,7 @@ var Route$46 = createFileRoute("/api/thinking")({ server: { handlers: { POST: as
6048
6583
  return routeErrorResponse(error, "Failed to update thinking level");
6049
6584
  }
6050
6585
  } } } });
6051
- var Route$45 = createFileRoute("/api/slash-command")({ server: { handlers: { POST: async ({ request }) => {
6586
+ var Route$50 = createFileRoute("/api/slash-command")({ server: { handlers: { POST: async ({ request }) => {
6052
6587
  try {
6053
6588
  const body = await readRequestJson(request);
6054
6589
  return jsonResponse(await getPicoRuntime().runSlashCommand(request, body));
@@ -6056,7 +6591,7 @@ var Route$45 = createFileRoute("/api/slash-command")({ server: { handlers: { POS
6056
6591
  return routeErrorResponse(error, "Failed to run slash command");
6057
6592
  }
6058
6593
  } } } });
6059
- var Route$44 = createFileRoute("/api/provider-usage")({ server: { handlers: { GET: async ({ request }) => {
6594
+ var Route$49 = createFileRoute("/api/provider-usage")({ server: { handlers: { GET: async ({ request }) => {
6060
6595
  const provider = new URL(request.url).searchParams.get("provider") || void 0;
6061
6596
  try {
6062
6597
  return jsonResponse({
@@ -6067,7 +6602,7 @@ var Route$44 = createFileRoute("/api/provider-usage")({ server: { handlers: { GE
6067
6602
  return routeErrorResponse(error, "Failed to read provider usage");
6068
6603
  }
6069
6604
  } } } });
6070
- var Route$43 = createFileRoute("/api/prompt")({ server: { handlers: { POST: async ({ request }) => {
6605
+ var Route$48 = createFileRoute("/api/prompt")({ server: { handlers: { POST: async ({ request }) => {
6071
6606
  try {
6072
6607
  const body = await readRequestJson(request);
6073
6608
  return jsonResponse(await getPicoRuntime().prompt(request, body));
@@ -6097,6 +6632,9 @@ var DIRECTORY_SEARCH_IGNORE_DIRECTORIES = new Set([
6097
6632
  function displayPath(value = "") {
6098
6633
  return String(value).replace(/\\/g, "/");
6099
6634
  }
6635
+ function projectPathTextContains(text, query) {
6636
+ return text.indexOf(query) >= 0;
6637
+ }
6100
6638
  function expandHomeDirectory(inputPath) {
6101
6639
  if (inputPath === "~") return os.homedir();
6102
6640
  if (inputPath.startsWith("~/")) return `${os.homedir()}${inputPath.slice(1)}`;
@@ -6165,13 +6703,12 @@ async function listPathCompletionEntries(prefix, baseCwd) {
6165
6703
  return [];
6166
6704
  }
6167
6705
  const normalizedSearchPrefix = searchPrefix.toLowerCase();
6168
- const suggestions = [];
6169
- for (const entry of entries) {
6170
- if (!entry.name.toLowerCase().startsWith(normalizedSearchPrefix)) continue;
6706
+ const matchingEntries = entries.filter((entry) => entry.name.toLowerCase().startsWith(normalizedSearchPrefix));
6707
+ const suggestions = await Promise.all(matchingEntries.map(async (entry) => {
6171
6708
  const isDirectory = await completionEntryIsDirectory(entry, join(searchDir, entry.name));
6172
6709
  let completionPath;
6173
6710
  if (displayPrefix.endsWith("/") || displayPrefix.endsWith("\\")) completionPath = `${displayPrefix}${entry.name}`;
6174
- else if (displayPrefix.includes("/") || displayPrefix.includes("\\")) if (displayPrefix === "~") completionPath = `~/${entry.name}`;
6711
+ else if (projectPathTextContains(displayPrefix, "/") || projectPathTextContains(displayPrefix, "\\")) if (displayPrefix === "~") completionPath = `~/${entry.name}`;
6175
6712
  else if (displayPrefix.startsWith("~/")) {
6176
6713
  const parentDir = dirname(displayPrefix.slice(2));
6177
6714
  completionPath = parentDir === "." ? `~/${entry.name}` : `~/${displayPath(parentDir)}/${entry.name}`;
@@ -6184,13 +6721,12 @@ async function listPathCompletionEntries(prefix, baseCwd) {
6184
6721
  }
6185
6722
  else if (displayPrefix.startsWith("~")) completionPath = `~/${entry.name}`;
6186
6723
  else completionPath = entry.name;
6187
- const value = isDirectory ? `${displayPath(completionPath)}/` : displayPath(completionPath);
6188
- suggestions.push({
6189
- value,
6724
+ return {
6725
+ value: isDirectory ? `${displayPath(completionPath)}/` : displayPath(completionPath),
6190
6726
  label: `${entry.name}${isDirectory ? "/" : ""}`,
6191
6727
  isDirectory
6192
- });
6193
- }
6728
+ };
6729
+ }));
6194
6730
  suggestions.sort((left, right) => {
6195
6731
  if (left.isDirectory && !right.isDirectory) return -1;
6196
6732
  if (!left.isDirectory && right.isDirectory) return 1;
@@ -6257,7 +6793,7 @@ async function runFdSearch(command, args) {
6257
6793
  const displayedLine = displayPath(line);
6258
6794
  const hasTrailingSeparator = displayedLine.endsWith("/");
6259
6795
  const normalizedPath = hasTrailingSeparator ? displayedLine.slice(0, -1) : displayedLine;
6260
- if (normalizedPath === ".git" || normalizedPath.startsWith(".git/") || normalizedPath.includes("/.git/")) continue;
6796
+ if (normalizedPath === ".git" || normalizedPath.startsWith(".git/") || projectPathTextContains(normalizedPath, "/.git/")) continue;
6261
6797
  results.push({
6262
6798
  path: displayedLine,
6263
6799
  isDirectory: hasTrailingSeparator
@@ -6267,52 +6803,72 @@ async function runFdSearch(command, args) {
6267
6803
  });
6268
6804
  });
6269
6805
  }
6806
+ async function runFdSearchCandidates(args) {
6807
+ return (await Promise.all(["fd", "fdfind"].map((command) => runFdSearch(command, args)))).find((result) => result !== void 0);
6808
+ }
6270
6809
  function escapePathQueryRegex(value = "") {
6271
6810
  return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6272
6811
  }
6273
6812
  function buildFdPathQuery(query = "") {
6274
6813
  const normalized = displayPath(query);
6275
- if (!normalized.includes("/")) return normalized;
6814
+ if (!projectPathTextContains(normalized, "/")) return normalized;
6276
6815
  const hasTrailingSeparator = normalized.endsWith("/");
6277
6816
  const trimmed = normalized.replace(/^\/+|\/+$/g, "");
6278
6817
  if (!trimmed) return normalized;
6279
6818
  const separatorPattern = "[\\\\/]";
6280
- const segments = trimmed.split("/").filter(Boolean).map((segment) => escapePathQueryRegex(segment));
6819
+ const segments = trimmed.split("/").flatMap((segment) => segment ? [escapePathQueryRegex(segment)] : []);
6281
6820
  if (segments.length === 0) return normalized;
6282
6821
  let pattern = segments.join(separatorPattern);
6283
6822
  if (hasTrailingSeparator) pattern += separatorPattern;
6284
6823
  return pattern;
6285
6824
  }
6825
+ async function readDirectoryEntries(directory) {
6826
+ try {
6827
+ return await readdir(directory, {
6828
+ withFileTypes: true,
6829
+ encoding: "utf8"
6830
+ });
6831
+ } catch {
6832
+ return [];
6833
+ }
6834
+ }
6835
+ async function walkDirectoryFallbackLayer({ baseDir, normalizedQuery, currentDirs, matches, maxResults }) {
6836
+ if (currentDirs.length === 0 || matches.length >= maxResults) return;
6837
+ const directoryEntries = await Promise.all(currentDirs.map(async (currentDir) => ({
6838
+ currentDir,
6839
+ entries: await readDirectoryEntries(currentDir)
6840
+ })));
6841
+ const nextDirs = [];
6842
+ for (const { currentDir, entries } of directoryEntries) for (const entry of entries) {
6843
+ if (matches.length >= maxResults) break;
6844
+ if (WALK_IGNORE_DIRECTORIES.has(entry.name)) continue;
6845
+ const fullPath = join(currentDir, entry.name);
6846
+ const relativePath = displayPath(path.relative(baseDir, fullPath));
6847
+ const isDirectory = entry.isDirectory();
6848
+ const displayValue = isDirectory ? `${relativePath}/` : relativePath;
6849
+ if (!normalizedQuery || projectPathTextContains(displayValue.toLowerCase(), normalizedQuery)) matches.push({
6850
+ path: displayValue,
6851
+ isDirectory
6852
+ });
6853
+ if (isDirectory) nextDirs.push(fullPath);
6854
+ }
6855
+ await walkDirectoryFallbackLayer({
6856
+ baseDir,
6857
+ normalizedQuery,
6858
+ currentDirs: nextDirs,
6859
+ matches,
6860
+ maxResults
6861
+ });
6862
+ }
6286
6863
  async function walkDirectoryWithFallback(baseDir, query = "", maxResults = 100) {
6287
- const queue = [baseDir];
6288
6864
  const matches = [];
6289
- const normalizedQuery = displayPath(query).toLowerCase();
6290
- while (queue.length > 0 && matches.length < maxResults) {
6291
- const currentDir = queue.shift();
6292
- if (!currentDir) continue;
6293
- let entries;
6294
- try {
6295
- entries = await readdir(currentDir, {
6296
- withFileTypes: true,
6297
- encoding: "utf8"
6298
- });
6299
- } catch {
6300
- continue;
6301
- }
6302
- for (const entry of entries) {
6303
- if (matches.length >= maxResults) break;
6304
- if (WALK_IGNORE_DIRECTORIES.has(entry.name)) continue;
6305
- const fullPath = join(currentDir, entry.name);
6306
- const relativePath = displayPath(path.relative(baseDir, fullPath));
6307
- const isDirectory = entry.isDirectory();
6308
- const displayValue = isDirectory ? `${relativePath}/` : relativePath;
6309
- if (!normalizedQuery || displayValue.toLowerCase().includes(normalizedQuery)) matches.push({
6310
- path: displayValue,
6311
- isDirectory
6312
- });
6313
- if (isDirectory) queue.push(fullPath);
6314
- }
6315
- }
6865
+ await walkDirectoryFallbackLayer({
6866
+ baseDir,
6867
+ normalizedQuery: displayPath(query).toLowerCase(),
6868
+ currentDirs: [baseDir],
6869
+ matches,
6870
+ maxResults
6871
+ });
6316
6872
  return matches;
6317
6873
  }
6318
6874
  async function walkDirectory(baseDir, query = "", maxResults = 100) {
@@ -6345,10 +6901,8 @@ async function walkDirectory(baseDir, query = "", maxResults = 100) {
6345
6901
  "node_modules"
6346
6902
  ];
6347
6903
  if (query) args.push(buildFdPathQuery(query));
6348
- for (const command of ["fd", "fdfind"]) {
6349
- const results = await runFdSearch(command, args);
6350
- if (results !== void 0) return results;
6351
- }
6904
+ const results = await runFdSearchCandidates(args);
6905
+ if (results !== void 0) return results;
6352
6906
  return await walkDirectoryWithFallback(baseDir, query, maxResults);
6353
6907
  }
6354
6908
  async function resolveScopedFileReferenceQuery(rawQuery, baseCwd) {
@@ -6394,7 +6948,11 @@ function resolveProjectFilePath(baseCwd, filePath) {
6394
6948
  };
6395
6949
  }
6396
6950
  async function listProjectFileTreePaths(baseCwd) {
6397
- return (await walkDirectory(baseCwd, "", PROJECT_FILE_TREE_LIMIT)).filter((entry) => !entry.isDirectory).map((entry) => displayPath(entry.path).replace(/\/+$/g, "")).filter(Boolean).sort((left, right) => left.localeCompare(right));
6951
+ return (await walkDirectory(baseCwd, "", PROJECT_FILE_TREE_LIMIT)).flatMap((entry) => {
6952
+ if (entry.isDirectory) return [];
6953
+ const path = displayPath(entry.path).replace(/\/+$/g, "");
6954
+ return path ? [path] : [];
6955
+ }).sort((left, right) => left.localeCompare(right));
6398
6956
  }
6399
6957
  async function readProjectFileContent(baseCwd, filePath) {
6400
6958
  const resolved = resolveProjectFilePath(baseCwd, filePath);
@@ -6423,46 +6981,54 @@ function directorySearchScore({ absolutePath, query, root }) {
6423
6981
  if (score === 0) return 0;
6424
6982
  return score - depth * 5;
6425
6983
  }
6984
+ async function searchDirectoriesFallbackLayer({ root, normalizedQuery, currentDirectories, results, maxResults }) {
6985
+ const eligibleDirectories = currentDirectories.filter((current) => current.depth < DIRECTORY_SEARCH_MAX_DEPTH);
6986
+ if (eligibleDirectories.length === 0 || results.length >= maxResults) return;
6987
+ const directoryEntries = await Promise.all(eligibleDirectories.map(async (current) => ({
6988
+ current,
6989
+ entries: await readDirectoryEntries(current.directory)
6990
+ })));
6991
+ const nextDirectories = [];
6992
+ for (const { current, entries } of directoryEntries) for (const entry of entries) {
6993
+ if (results.length >= maxResults) break;
6994
+ if (!entry.isDirectory()) continue;
6995
+ if (DIRECTORY_SEARCH_IGNORE_DIRECTORIES.has(entry.name)) continue;
6996
+ if (!normalizedQuery.startsWith(".") && entry.name.startsWith(".")) continue;
6997
+ const fullPath = join(current.directory, entry.name);
6998
+ const relativePath = `${displayPath(path.relative(root, fullPath))}/`;
6999
+ if (projectPathTextContains(relativePath.toLowerCase(), normalizedQuery)) results.push({
7000
+ path: relativePath,
7001
+ isDirectory: true
7002
+ });
7003
+ nextDirectories.push({
7004
+ directory: fullPath,
7005
+ depth: current.depth + 1
7006
+ });
7007
+ }
7008
+ await searchDirectoriesFallbackLayer({
7009
+ root,
7010
+ normalizedQuery,
7011
+ currentDirectories: nextDirectories,
7012
+ results,
7013
+ maxResults
7014
+ });
7015
+ }
6426
7016
  async function searchDirectoriesWithFallback({ root, query, maxResults }) {
6427
- const normalizedQuery = query.trim().toLowerCase();
6428
7017
  const results = [];
6429
- const queue = [{
6430
- directory: root,
6431
- depth: 0
6432
- }];
6433
- while (queue.length > 0 && results.length < maxResults) {
6434
- const current = queue.shift();
6435
- if (!current || current.depth >= DIRECTORY_SEARCH_MAX_DEPTH) continue;
6436
- let entries;
6437
- try {
6438
- entries = await readdir(current.directory, {
6439
- withFileTypes: true,
6440
- encoding: "utf8"
6441
- });
6442
- } catch {
6443
- continue;
6444
- }
6445
- for (const entry of entries) {
6446
- if (results.length >= maxResults) break;
6447
- if (!entry.isDirectory()) continue;
6448
- if (DIRECTORY_SEARCH_IGNORE_DIRECTORIES.has(entry.name)) continue;
6449
- if (!normalizedQuery.startsWith(".") && entry.name.startsWith(".")) continue;
6450
- const fullPath = join(current.directory, entry.name);
6451
- const relativePath = `${displayPath(path.relative(root, fullPath))}/`;
6452
- if (relativePath.toLowerCase().includes(normalizedQuery)) results.push({
6453
- path: relativePath,
6454
- isDirectory: true
6455
- });
6456
- queue.push({
6457
- directory: fullPath,
6458
- depth: current.depth + 1
6459
- });
6460
- }
6461
- }
7018
+ await searchDirectoriesFallbackLayer({
7019
+ root,
7020
+ normalizedQuery: query.trim().toLowerCase(),
7021
+ currentDirectories: [{
7022
+ directory: root,
7023
+ depth: 0
7024
+ }],
7025
+ results,
7026
+ maxResults
7027
+ });
6462
7028
  return results;
6463
7029
  }
6464
7030
  async function searchDirectoriesInRoot({ root, query, maxResults }) {
6465
- const args = [
7031
+ const results = await runFdSearchCandidates([
6466
7032
  "--base-directory",
6467
7033
  root,
6468
7034
  "--max-depth",
@@ -6498,11 +7064,8 @@ async function searchDirectoriesInRoot({ root, query, maxResults }) {
6498
7064
  "--exclude",
6499
7065
  "node_modules",
6500
7066
  buildFdPathQuery(query)
6501
- ];
6502
- for (const command of ["fd", "fdfind"]) {
6503
- const results = await runFdSearch(command, args);
6504
- if (results !== void 0) return results.filter((entry) => entry.isDirectory);
6505
- }
7067
+ ]);
7068
+ if (results !== void 0) return results.filter((entry) => entry.isDirectory);
6506
7069
  return await searchDirectoriesWithFallback({
6507
7070
  root,
6508
7071
  query,
@@ -6521,37 +7084,40 @@ async function searchDirectoryEntries(query, baseCwd) {
6521
7084
  if (baseCwd && !pathIsInside(baseCwd, homeDirectory)) roots.push(baseCwd);
6522
7085
  const seen = /* @__PURE__ */ new Set();
6523
7086
  const matches = [];
6524
- for (const root of roots) {
7087
+ const rootEntries = await Promise.all(roots.map(async (root) => {
6525
7088
  let rootStats;
6526
7089
  try {
6527
7090
  rootStats = await stat(root);
6528
7091
  } catch {
6529
- continue;
7092
+ return [];
6530
7093
  }
6531
- if (!rootStats.isDirectory()) continue;
6532
- const entries = await searchDirectoriesInRoot({
7094
+ if (!rootStats.isDirectory()) return [];
7095
+ return (await searchDirectoriesInRoot({
6533
7096
  root,
6534
7097
  query: normalizedQuery,
6535
7098
  maxResults: DIRECTORY_SEARCH_LIMIT * 4
7099
+ })).map((entry) => ({
7100
+ entry,
7101
+ root
7102
+ }));
7103
+ }));
7104
+ for (const { entry, root } of rootEntries.flat()) {
7105
+ const relativePath = displayPath(entry.path).replace(/\/+$/g, "");
7106
+ if (!relativePath) continue;
7107
+ const absolutePath = resolve(root, relativePath);
7108
+ if (seen.has(absolutePath)) continue;
7109
+ seen.add(absolutePath);
7110
+ const score = directorySearchScore({
7111
+ absolutePath,
7112
+ query: normalizedQuery,
7113
+ root
7114
+ });
7115
+ if (score <= 0) continue;
7116
+ matches.push({
7117
+ absolutePath,
7118
+ root,
7119
+ score
6536
7120
  });
6537
- for (const entry of entries) {
6538
- const relativePath = displayPath(entry.path).replace(/\/+$/g, "");
6539
- if (!relativePath) continue;
6540
- const absolutePath = resolve(root, relativePath);
6541
- if (seen.has(absolutePath)) continue;
6542
- seen.add(absolutePath);
6543
- const score = directorySearchScore({
6544
- absolutePath,
6545
- query: normalizedQuery,
6546
- root
6547
- });
6548
- if (score <= 0) continue;
6549
- matches.push({
6550
- absolutePath,
6551
- root,
6552
- score
6553
- });
6554
- }
6555
7121
  }
6556
7122
  return matches.sort((left, right) => {
6557
7123
  if (right.score !== left.score) return right.score - left.score;
@@ -6571,10 +7137,13 @@ async function listFileReferenceEntries(query, baseCwd, { isQuotedPrefix = false
6571
7137
  const scopedQuery = await resolveScopedFileReferenceQuery(normalizedQuery, baseCwd);
6572
7138
  const searchBaseDir = scopedQuery?.searchBaseDir ?? baseCwd;
6573
7139
  const searchQuery = scopedQuery?.query ?? normalizedQuery;
6574
- return (await walkDirectory(searchBaseDir, searchQuery, 100)).map((entry) => ({
6575
- ...entry,
6576
- score: scoreFileReferenceEntry(entry.path, searchQuery, entry.isDirectory)
6577
- })).filter((entry) => entry.score > 0).sort((left, right) => right.score - left.score).slice(0, 20).map((entry) => {
7140
+ return (await walkDirectory(searchBaseDir, searchQuery, 100)).flatMap((entry) => {
7141
+ const score = scoreFileReferenceEntry(entry.path, searchQuery, entry.isDirectory);
7142
+ return score > 0 ? [{
7143
+ ...entry,
7144
+ score
7145
+ }] : [];
7146
+ }).sort((left, right) => right.score - left.score).slice(0, 20).map((entry) => {
6578
7147
  const pathWithoutSlash = entry.isDirectory ? entry.path.slice(0, -1) : entry.path;
6579
7148
  const displayValue = scopedQuery ? scopedDisplayPath(scopedQuery.displayBase, pathWithoutSlash) : pathWithoutSlash;
6580
7149
  return {
@@ -6588,7 +7157,7 @@ async function listFileReferenceEntries(query, baseCwd, { isQuotedPrefix = false
6588
7157
  };
6589
7158
  });
6590
7159
  }
6591
- var Route$42 = createFileRoute("/api/path-completions")({ server: { handlers: { POST: async ({ request }) => {
7160
+ var Route$47 = createFileRoute("/api/path-completions")({ server: { handlers: { POST: async ({ request }) => {
6592
7161
  try {
6593
7162
  const body = await readRequestJson(request);
6594
7163
  const { context, activeEntry } = await getPicoRuntime().resolveRequest(request);
@@ -6604,7 +7173,7 @@ var Route$42 = createFileRoute("/api/path-completions")({ server: { handlers: {
6604
7173
  return routeErrorResponse(error, "Failed to list path completions");
6605
7174
  }
6606
7175
  } } } });
6607
- var Route$41 = createFileRoute("/api/model")({ server: { handlers: { POST: async ({ request }) => {
7176
+ var Route$46 = createFileRoute("/api/model")({ server: { handlers: { POST: async ({ request }) => {
6608
7177
  try {
6609
7178
  const body = await readRequestJson(request);
6610
7179
  return jsonResponse(await getPicoRuntime().setModel(request, body));
@@ -6612,7 +7181,7 @@ var Route$41 = createFileRoute("/api/model")({ server: { handlers: { POST: async
6612
7181
  return routeErrorResponse(error, "Failed to update model");
6613
7182
  }
6614
7183
  } } } });
6615
- var Route$40 = createFileRoute("/api/highlight")({ server: { handlers: { POST: async ({ request }) => {
7184
+ var Route$45 = createFileRoute("/api/highlight")({ server: { handlers: { POST: async ({ request }) => {
6616
7185
  try {
6617
7186
  const body = await readRequestJson(request);
6618
7187
  return jsonResponse(await getPicoRuntime().highlightCode(body.code, body.language));
@@ -6620,7 +7189,7 @@ var Route$40 = createFileRoute("/api/highlight")({ server: { handlers: { POST: a
6620
7189
  return routeErrorResponse(error, "Failed to highlight code");
6621
7190
  }
6622
7191
  } } } });
6623
- var Route$39 = createFileRoute("/api/git-status")({ server: { handlers: { GET: async ({ request }) => {
7192
+ var Route$44 = createFileRoute("/api/git-status")({ server: { handlers: { GET: async ({ request }) => {
6624
7193
  const requestedCwd = new URL(request.url).searchParams.get("cwd") || "";
6625
7194
  if (!requestedCwd.trim()) return errorResponse("cwd is required");
6626
7195
  try {
@@ -6635,7 +7204,7 @@ var Route$39 = createFileRoute("/api/git-status")({ server: { handlers: { GET: a
6635
7204
  return routeErrorResponse(error, "Failed to read git status");
6636
7205
  }
6637
7206
  } } } });
6638
- var Route$38 = createFileRoute("/api/git-stage")({ server: { handlers: { POST: async ({ request }) => {
7207
+ var Route$43 = createFileRoute("/api/git-stage")({ server: { handlers: { POST: async ({ request }) => {
6639
7208
  try {
6640
7209
  const body = await readRequestJson(request);
6641
7210
  const requestedCwd = typeof body.cwd === "string" ? body.cwd : "";
@@ -6656,7 +7225,7 @@ var Route$38 = createFileRoute("/api/git-stage")({ server: { handlers: { POST: a
6656
7225
  return routeErrorResponse(error, "Failed to stage changes");
6657
7226
  }
6658
7227
  } } } });
6659
- var Route$37 = createFileRoute("/api/git-review")({ server: { handlers: { GET: async ({ request }) => {
7228
+ var Route$42 = createFileRoute("/api/git-review")({ server: { handlers: { GET: async ({ request }) => {
6660
7229
  const url = new URL(request.url);
6661
7230
  const requestedCwd = url.searchParams.get("cwd") || "";
6662
7231
  const requestedPath = url.searchParams.get("path") || "";
@@ -6679,7 +7248,7 @@ var Route$37 = createFileRoute("/api/git-review")({ server: { handlers: { GET: a
6679
7248
  return routeErrorResponse(error, "Failed to read git review file");
6680
7249
  }
6681
7250
  } } } });
6682
- var Route$36 = createFileRoute("/api/git-push")({ server: { handlers: { POST: async ({ request }) => {
7251
+ var Route$41 = createFileRoute("/api/git-push")({ server: { handlers: { POST: async ({ request }) => {
6683
7252
  try {
6684
7253
  const body = await readRequestJson(request);
6685
7254
  const requestedCwd = typeof body.cwd === "string" ? body.cwd : "";
@@ -6698,7 +7267,7 @@ var Route$36 = createFileRoute("/api/git-push")({ server: { handlers: { POST: as
6698
7267
  return routeErrorResponse(error, "Failed to push changes");
6699
7268
  }
6700
7269
  } } } });
6701
- var Route$35 = createFileRoute("/api/git-pull")({ server: { handlers: { POST: async ({ request }) => {
7270
+ var Route$40 = createFileRoute("/api/git-pull")({ server: { handlers: { POST: async ({ request }) => {
6702
7271
  try {
6703
7272
  const body = await readRequestJson(request);
6704
7273
  const requestedCwd = typeof body.cwd === "string" ? body.cwd : "";
@@ -6716,7 +7285,7 @@ var Route$35 = createFileRoute("/api/git-pull")({ server: { handlers: { POST: as
6716
7285
  return routeErrorResponse(error, "Failed to pull changes");
6717
7286
  }
6718
7287
  } } } });
6719
- var Route$34 = createFileRoute("/api/git-discard")({ server: { handlers: { POST: async ({ request }) => {
7288
+ var Route$39 = createFileRoute("/api/git-discard")({ server: { handlers: { POST: async ({ request }) => {
6720
7289
  try {
6721
7290
  const body = await readRequestJson(request);
6722
7291
  const requestedCwd = typeof body.cwd === "string" ? body.cwd : "";
@@ -6738,7 +7307,7 @@ var Route$34 = createFileRoute("/api/git-discard")({ server: { handlers: { POST:
6738
7307
  return routeErrorResponse(error, "Failed to discard changes");
6739
7308
  }
6740
7309
  } } } });
6741
- var Route$33 = createFileRoute("/api/git-diff")({ server: { handlers: { GET: async ({ request }) => {
7310
+ var Route$38 = createFileRoute("/api/git-diff")({ server: { handlers: { GET: async ({ request }) => {
6742
7311
  const url = new URL(request.url);
6743
7312
  const requestedCwd = url.searchParams.get("cwd") || "";
6744
7313
  const requestedPath = url.searchParams.get("path") || "";
@@ -6758,7 +7327,28 @@ var Route$33 = createFileRoute("/api/git-diff")({ server: { handlers: { GET: asy
6758
7327
  return routeErrorResponse(error, "Failed to read git diff");
6759
7328
  }
6760
7329
  } } } });
6761
- var Route$32 = createFileRoute("/api/git-commit-message")({ server: { handlers: { POST: async ({ request }) => {
7330
+ var Route$37 = createFileRoute("/api/git-commit-remote-url")({ server: { handlers: { GET: async ({ request }) => {
7331
+ try {
7332
+ const url = new URL(request.url);
7333
+ const requestedCwd = url.searchParams.get("cwd") || "";
7334
+ const commit = url.searchParams.get("commit") || "";
7335
+ if (!requestedCwd.trim()) throw new Error("cwd is required");
7336
+ if (!commit.trim()) throw new Error("commit is required");
7337
+ const { context, activeEntry } = await getPicoRuntime().resolveRequest(request);
7338
+ const cwd = await resolveDirectoryPath(requestedCwd, getPicoRuntime().getBaseCwd(activeEntry, context));
7339
+ const result = await readDirectoryGitCommitRemoteUrl(cwd, commit);
7340
+ if (!result?.remoteUrl) throw new Error("No remote URL found");
7341
+ return jsonResponse({
7342
+ ok: true,
7343
+ cwd,
7344
+ commit: result.commit,
7345
+ remoteUrl: result.remoteUrl
7346
+ });
7347
+ } catch (error) {
7348
+ return routeErrorResponse(error, "Failed to read commit remote URL");
7349
+ }
7350
+ } } } });
7351
+ var Route$36 = createFileRoute("/api/git-commit-message")({ server: { handlers: { POST: async ({ request }) => {
6762
7352
  try {
6763
7353
  const body = await readRequestJson(request);
6764
7354
  const requestedCwd = typeof body.cwd === "string" ? body.cwd : "";
@@ -6777,7 +7367,93 @@ var Route$32 = createFileRoute("/api/git-commit-message")({ server: { handlers:
6777
7367
  return routeErrorResponse(error, "Failed to generate commit message");
6778
7368
  }
6779
7369
  } } } });
6780
- var Route$31 = createFileRoute("/api/git-commit")({ server: { handlers: { POST: async ({ request }) => {
7370
+ var Route$35 = createFileRoute("/api/git-commit-files")({ server: { handlers: { GET: async ({ request }) => {
7371
+ try {
7372
+ const url = new URL(request.url);
7373
+ const requestedCwd = url.searchParams.get("cwd") || "";
7374
+ const commit = url.searchParams.get("commit") || "";
7375
+ if (!requestedCwd.trim()) throw new Error("cwd is required");
7376
+ if (!commit.trim()) throw new Error("commit is required");
7377
+ const { context, activeEntry } = await getPicoRuntime().resolveRequest(request);
7378
+ const cwd = await resolveDirectoryPath(requestedCwd, getPicoRuntime().getBaseCwd(activeEntry, context));
7379
+ return jsonResponse({
7380
+ ok: true,
7381
+ cwd,
7382
+ commit,
7383
+ files: await readDirectoryGitCommitFiles(cwd, commit) || []
7384
+ });
7385
+ } catch (error) {
7386
+ return routeErrorResponse(error, "Failed to read commit files");
7387
+ }
7388
+ } } } });
7389
+ function normalizeGitCommitDiffMode(value) {
7390
+ if (value === "head" || value === "previous") return value;
7391
+ return "commit";
7392
+ }
7393
+ var Route$34 = createFileRoute("/api/git-commit-diff")({ server: { handlers: { GET: async ({ request }) => {
7394
+ try {
7395
+ const url = new URL(request.url);
7396
+ const requestedCwd = url.searchParams.get("cwd") || "";
7397
+ const commit = url.searchParams.get("commit") || "";
7398
+ const mode = normalizeGitCommitDiffMode(url.searchParams.get("mode"));
7399
+ const path = url.searchParams.get("path") || void 0;
7400
+ const previousPath = url.searchParams.get("previousPath") || void 0;
7401
+ if (!requestedCwd.trim()) throw new Error("cwd is required");
7402
+ if (!commit.trim()) throw new Error("commit is required");
7403
+ const { context, activeEntry } = await getPicoRuntime().resolveRequest(request);
7404
+ const cwd = await resolveDirectoryPath(requestedCwd, getPicoRuntime().getBaseCwd(activeEntry, context));
7405
+ const result = await readDirectoryGitCommitDiff(cwd, commit, mode, path, previousPath);
7406
+ return jsonResponse({
7407
+ ok: true,
7408
+ cwd,
7409
+ commit: result?.commit || commit,
7410
+ mode,
7411
+ title: result?.title || commit,
7412
+ ...result?.path ? { path: result.path } : {},
7413
+ ...result?.previousPath ? { previousPath: result.previousPath } : {},
7414
+ patch: result?.patch || "No git repository detected."
7415
+ });
7416
+ } catch (error) {
7417
+ return routeErrorResponse(error, "Failed to read commit diff");
7418
+ }
7419
+ } } } });
7420
+ var gitCommitActions = new Set([
7421
+ "checkout",
7422
+ "cherry-pick",
7423
+ "revert",
7424
+ "tag",
7425
+ "reset",
7426
+ "rebase",
7427
+ "drop",
7428
+ "squash"
7429
+ ]);
7430
+ var Route$33 = createFileRoute("/api/git-commit-action")({ server: { handlers: { POST: async ({ request }) => {
7431
+ try {
7432
+ const body = await readRequestJson(request);
7433
+ const requestedCwd = typeof body.cwd === "string" ? body.cwd : "";
7434
+ const action = typeof body.action === "string" ? body.action : "";
7435
+ const commit = typeof body.commit === "string" ? body.commit : "";
7436
+ if (!requestedCwd.trim()) throw new Error("cwd is required");
7437
+ if (!gitCommitActions.has(action)) throw new Error("action is required");
7438
+ if (!commit.trim()) throw new Error("commit is required");
7439
+ const { context, activeEntry } = await getPicoRuntime().resolveRequest(request);
7440
+ const cwd = await resolveDirectoryPath(requestedCwd, getPicoRuntime().getBaseCwd(activeEntry, context));
7441
+ const result = await runDirectoryGitCommitAction(cwd, action, commit, {
7442
+ tagName: typeof body.tagName === "string" ? body.tagName : "",
7443
+ resetMode: typeof body.resetMode === "string" ? body.resetMode : "",
7444
+ message: typeof body.message === "string" ? body.message : ""
7445
+ });
7446
+ return jsonResponse({
7447
+ ok: true,
7448
+ cwd,
7449
+ stdout: result.stdout,
7450
+ stderr: result.stderr
7451
+ });
7452
+ } catch (error) {
7453
+ return routeErrorResponse(error, "Failed to run commit action");
7454
+ }
7455
+ } } } });
7456
+ var Route$32 = createFileRoute("/api/git-commit")({ server: { handlers: { POST: async ({ request }) => {
6781
7457
  try {
6782
7458
  const body = await readRequestJson(request);
6783
7459
  const requestedCwd = typeof body.cwd === "string" ? body.cwd : "";
@@ -6802,7 +7478,7 @@ var Route$31 = createFileRoute("/api/git-commit")({ server: { handlers: { POST:
6802
7478
  return routeErrorResponse(error, "Failed to commit changes");
6803
7479
  }
6804
7480
  } } } });
6805
- var Route$30 = createFileRoute("/api/git-checkout")({ server: { handlers: { POST: async ({ request }) => {
7481
+ var Route$31 = createFileRoute("/api/git-checkout")({ server: { handlers: { POST: async ({ request }) => {
6806
7482
  try {
6807
7483
  const body = await readRequestJson(request);
6808
7484
  const requestedCwd = typeof body.cwd === "string" ? body.cwd : "";
@@ -6852,7 +7528,7 @@ function getRuntimeRequestForGitChanges(request, url) {
6852
7528
  method: request.method
6853
7529
  });
6854
7530
  }
6855
- var Route$29 = createFileRoute("/api/git-changes")({ server: { handlers: { GET: async ({ request }) => {
7531
+ var Route$30 = createFileRoute("/api/git-changes")({ server: { handlers: { GET: async ({ request }) => {
6856
7532
  const url = new URL(request.url);
6857
7533
  const requestedCwd = url.searchParams.get("cwd") || "";
6858
7534
  const gitScope = getGitChangesScope(url);
@@ -6922,7 +7598,7 @@ var Route$29 = createFileRoute("/api/git-changes")({ server: { handlers: { GET:
6922
7598
  return routeErrorResponse(error, "Failed to read git changes");
6923
7599
  }
6924
7600
  } } } });
6925
- var Route$28 = createFileRoute("/api/file-completions")({ server: { handlers: { POST: async ({ request }) => {
7601
+ var Route$29 = createFileRoute("/api/file-completions")({ server: { handlers: { POST: async ({ request }) => {
6926
7602
  try {
6927
7603
  const body = await readRequestJson(request);
6928
7604
  const { context, activeEntry } = await getPicoRuntime().resolveRequest(request);
@@ -6939,7 +7615,7 @@ var Route$28 = createFileRoute("/api/file-completions")({ server: { handlers: {
6939
7615
  return routeErrorResponse(error, "Failed to list file completions");
6940
7616
  }
6941
7617
  } } } });
6942
- var Route$27 = createFileRoute("/api/directory-sessions-indexes")({ server: { handlers: { GET: async ({ request }) => {
7618
+ var Route$28 = createFileRoute("/api/directory-sessions-indexes")({ server: { handlers: { GET: async ({ request }) => {
6943
7619
  const directories = new URL(request.url).searchParams.getAll("directory");
6944
7620
  if (directories.length === 0) return errorResponse("at least one directory is required");
6945
7621
  try {
@@ -6948,7 +7624,7 @@ var Route$27 = createFileRoute("/api/directory-sessions-indexes")({ server: { ha
6948
7624
  return routeErrorResponse(error, "Failed to list directory session indexes");
6949
7625
  }
6950
7626
  } } } });
6951
- var Route$26 = createFileRoute("/api/directory-sessions-index")({ server: { handlers: { GET: async ({ request }) => {
7627
+ var Route$27 = createFileRoute("/api/directory-sessions-index")({ server: { handlers: { GET: async ({ request }) => {
6952
7628
  const directory = new URL(request.url).searchParams.get("directory") || "";
6953
7629
  if (!directory.trim()) return errorResponse("directory is required");
6954
7630
  try {
@@ -6957,7 +7633,7 @@ var Route$26 = createFileRoute("/api/directory-sessions-index")({ server: { hand
6957
7633
  return routeErrorResponse(error, "Failed to list directory session index");
6958
7634
  }
6959
7635
  } } } });
6960
- var Route$25 = createFileRoute("/api/directory-sessions")({ server: { handlers: { GET: async ({ request }) => {
7636
+ var Route$26 = createFileRoute("/api/directory-sessions")({ server: { handlers: { GET: async ({ request }) => {
6961
7637
  const url = new URL(request.url);
6962
7638
  const directory = url.searchParams.get("directory") || "";
6963
7639
  const offset = Number.parseInt(url.searchParams.get("offset") || "0", 10);
@@ -6972,7 +7648,7 @@ var Route$25 = createFileRoute("/api/directory-sessions")({ server: { handlers:
6972
7648
  return routeErrorResponse(error, "Failed to list directory sessions");
6973
7649
  }
6974
7650
  } } } });
6975
- var Route$24 = createFileRoute("/api/directory-search")({ server: { handlers: { POST: async ({ request }) => {
7651
+ var Route$25 = createFileRoute("/api/directory-search")({ server: { handlers: { POST: async ({ request }) => {
6976
7652
  try {
6977
7653
  const body = await readRequestJson(request);
6978
7654
  const { context, activeEntry } = await getPicoRuntime().resolveRequest(request);
@@ -6988,14 +7664,14 @@ var Route$24 = createFileRoute("/api/directory-search")({ server: { handlers: {
6988
7664
  return routeErrorResponse(error, "Failed to search directories");
6989
7665
  }
6990
7666
  } } } });
6991
- var Route$23 = createFileRoute("/api/abort")({ server: { handlers: { POST: async ({ request }) => {
7667
+ var Route$24 = createFileRoute("/api/abort")({ server: { handlers: { POST: async ({ request }) => {
6992
7668
  try {
6993
7669
  return jsonResponse(await getPicoRuntime().abort(request));
6994
7670
  } catch (error) {
6995
7671
  return routeErrorResponse(error, "Failed to abort session");
6996
7672
  }
6997
7673
  } } } });
6998
- var Route$22 = createFileRoute("/api/ui/$id")({ server: { handlers: { POST: async ({ request, params }) => {
7674
+ var Route$23 = createFileRoute("/api/ui/$id")({ server: { handlers: { POST: async ({ request, params }) => {
6999
7675
  try {
7000
7676
  const body = await readRequestJson(request);
7001
7677
  return jsonResponse(await getPicoRuntime().resolveUiRequest(params.id, body));
@@ -7003,7 +7679,7 @@ var Route$22 = createFileRoute("/api/ui/$id")({ server: { handlers: { POST: asyn
7003
7679
  return routeErrorResponse(error, "Failed to resolve UI request");
7004
7680
  }
7005
7681
  } } } });
7006
- var Route$21 = createFileRoute("/api/settings/hide-thinking")({ server: { handlers: { POST: async ({ request }) => {
7682
+ var Route$22 = createFileRoute("/api/settings/hide-thinking")({ server: { handlers: { POST: async ({ request }) => {
7007
7683
  try {
7008
7684
  const body = await readRequestJson(request);
7009
7685
  return jsonResponse(await getPicoRuntime().setHideThinking(body));
@@ -7011,7 +7687,7 @@ var Route$21 = createFileRoute("/api/settings/hide-thinking")({ server: { handle
7011
7687
  return routeErrorResponse(error, "Failed to update hide-thinking setting");
7012
7688
  }
7013
7689
  } } } });
7014
- var Route$20 = createFileRoute("/api/sessions/delete")({ server: { handlers: { POST: async ({ request }) => {
7690
+ var Route$21 = createFileRoute("/api/sessions/delete")({ server: { handlers: { POST: async ({ request }) => {
7015
7691
  try {
7016
7692
  const body = await readRequestJson(request);
7017
7693
  return jsonResponse(await getPicoRuntime().deleteSessions(request, body));
@@ -7019,7 +7695,7 @@ var Route$20 = createFileRoute("/api/sessions/delete")({ server: { handlers: { P
7019
7695
  return routeErrorResponse(error, "Failed to delete sessions");
7020
7696
  }
7021
7697
  } } } });
7022
- var Route$19 = createFileRoute("/api/session/tree")({ server: { handlers: {
7698
+ var Route$20 = createFileRoute("/api/session/tree")({ server: { handlers: {
7023
7699
  GET: async ({ request }) => {
7024
7700
  try {
7025
7701
  return jsonResponse(await getPicoRuntime().getSessionTreeForRequest(request));
@@ -7036,14 +7712,14 @@ var Route$19 = createFileRoute("/api/session/tree")({ server: { handlers: {
7036
7712
  }
7037
7713
  }
7038
7714
  } } });
7039
- var Route$18 = createFileRoute("/api/session/select")({ server: { handlers: { POST: async ({ request }) => {
7715
+ var Route$19 = createFileRoute("/api/session/select")({ server: { handlers: { POST: async ({ request }) => {
7040
7716
  try {
7041
7717
  return jsonResponse(await getPicoRuntime().selectSession(request));
7042
7718
  } catch (error) {
7043
7719
  return routeErrorResponse(error, "Failed to select session");
7044
7720
  }
7045
7721
  } } } });
7046
- var Route$17 = createFileRoute("/api/session/rename")({ server: { handlers: { POST: async ({ request }) => {
7722
+ var Route$18 = createFileRoute("/api/session/rename")({ server: { handlers: { POST: async ({ request }) => {
7047
7723
  try {
7048
7724
  const body = await readRequestJson(request);
7049
7725
  return jsonResponse(await getPicoRuntime().renameSession(body));
@@ -7051,6 +7727,14 @@ var Route$17 = createFileRoute("/api/session/rename")({ server: { handlers: { PO
7051
7727
  return routeErrorResponse(error, "Failed to rename session");
7052
7728
  }
7053
7729
  } } } });
7730
+ var Route$17 = createFileRoute("/api/session/read-state")({ server: { handlers: { POST: async ({ request }) => {
7731
+ try {
7732
+ const body = await readRequestJson(request);
7733
+ return jsonResponse(await getPicoRuntime().setSessionReadState(request, body));
7734
+ } catch (error) {
7735
+ return routeErrorResponse(error, "Failed to update session read state");
7736
+ }
7737
+ } } } });
7054
7738
  var Route$16 = createFileRoute("/api/session/new")({ server: { handlers: { POST: async ({ request }) => {
7055
7739
  try {
7056
7740
  const body = await readRequestJson(request);
@@ -7224,220 +7908,245 @@ var Route = createFileRoute("/api/session/tree/label")({ server: { handlers: { P
7224
7908
  return routeErrorResponse(error, "Failed to update tree label");
7225
7909
  }
7226
7910
  } } } });
7227
- var EventsRoute = Route$47.update({
7911
+ var EventsRoute = Route$52.update({
7228
7912
  id: "/events",
7229
7913
  path: "/events",
7230
- getParentRoute: () => Route$48
7914
+ getParentRoute: () => Route$53
7231
7915
  });
7232
- var IndexRoute = Route$49.update({
7916
+ var IndexRoute = Route$54.update({
7233
7917
  id: "/",
7234
7918
  path: "/",
7235
- getParentRoute: () => Route$48
7919
+ getParentRoute: () => Route$53
7236
7920
  });
7237
- var ApiThinkingRoute = Route$46.update({
7921
+ var ApiThinkingRoute = Route$51.update({
7238
7922
  id: "/api/thinking",
7239
7923
  path: "/api/thinking",
7240
- getParentRoute: () => Route$48
7924
+ getParentRoute: () => Route$53
7241
7925
  });
7242
- var ApiSlashCommandRoute = Route$45.update({
7926
+ var ApiSlashCommandRoute = Route$50.update({
7243
7927
  id: "/api/slash-command",
7244
7928
  path: "/api/slash-command",
7245
- getParentRoute: () => Route$48
7929
+ getParentRoute: () => Route$53
7246
7930
  });
7247
- var ApiProviderUsageRoute = Route$44.update({
7931
+ var ApiProviderUsageRoute = Route$49.update({
7248
7932
  id: "/api/provider-usage",
7249
7933
  path: "/api/provider-usage",
7250
- getParentRoute: () => Route$48
7934
+ getParentRoute: () => Route$53
7251
7935
  });
7252
- var ApiPromptRoute = Route$43.update({
7936
+ var ApiPromptRoute = Route$48.update({
7253
7937
  id: "/api/prompt",
7254
7938
  path: "/api/prompt",
7255
- getParentRoute: () => Route$48
7939
+ getParentRoute: () => Route$53
7256
7940
  });
7257
- var ApiPathCompletionsRoute = Route$42.update({
7941
+ var ApiPathCompletionsRoute = Route$47.update({
7258
7942
  id: "/api/path-completions",
7259
7943
  path: "/api/path-completions",
7260
- getParentRoute: () => Route$48
7944
+ getParentRoute: () => Route$53
7261
7945
  });
7262
- var ApiModelRoute = Route$41.update({
7946
+ var ApiModelRoute = Route$46.update({
7263
7947
  id: "/api/model",
7264
7948
  path: "/api/model",
7265
- getParentRoute: () => Route$48
7949
+ getParentRoute: () => Route$53
7266
7950
  });
7267
- var ApiHighlightRoute = Route$40.update({
7951
+ var ApiHighlightRoute = Route$45.update({
7268
7952
  id: "/api/highlight",
7269
7953
  path: "/api/highlight",
7270
- getParentRoute: () => Route$48
7954
+ getParentRoute: () => Route$53
7271
7955
  });
7272
- var ApiGitStatusRoute = Route$39.update({
7956
+ var ApiGitStatusRoute = Route$44.update({
7273
7957
  id: "/api/git-status",
7274
7958
  path: "/api/git-status",
7275
- getParentRoute: () => Route$48
7959
+ getParentRoute: () => Route$53
7276
7960
  });
7277
- var ApiGitStageRoute = Route$38.update({
7961
+ var ApiGitStageRoute = Route$43.update({
7278
7962
  id: "/api/git-stage",
7279
7963
  path: "/api/git-stage",
7280
- getParentRoute: () => Route$48
7964
+ getParentRoute: () => Route$53
7281
7965
  });
7282
- var ApiGitReviewRoute = Route$37.update({
7966
+ var ApiGitReviewRoute = Route$42.update({
7283
7967
  id: "/api/git-review",
7284
7968
  path: "/api/git-review",
7285
- getParentRoute: () => Route$48
7969
+ getParentRoute: () => Route$53
7286
7970
  });
7287
- var ApiGitPushRoute = Route$36.update({
7971
+ var ApiGitPushRoute = Route$41.update({
7288
7972
  id: "/api/git-push",
7289
7973
  path: "/api/git-push",
7290
- getParentRoute: () => Route$48
7974
+ getParentRoute: () => Route$53
7291
7975
  });
7292
- var ApiGitPullRoute = Route$35.update({
7976
+ var ApiGitPullRoute = Route$40.update({
7293
7977
  id: "/api/git-pull",
7294
7978
  path: "/api/git-pull",
7295
- getParentRoute: () => Route$48
7979
+ getParentRoute: () => Route$53
7296
7980
  });
7297
- var ApiGitDiscardRoute = Route$34.update({
7981
+ var ApiGitDiscardRoute = Route$39.update({
7298
7982
  id: "/api/git-discard",
7299
7983
  path: "/api/git-discard",
7300
- getParentRoute: () => Route$48
7984
+ getParentRoute: () => Route$53
7301
7985
  });
7302
- var ApiGitDiffRoute = Route$33.update({
7986
+ var ApiGitDiffRoute = Route$38.update({
7303
7987
  id: "/api/git-diff",
7304
7988
  path: "/api/git-diff",
7305
- getParentRoute: () => Route$48
7989
+ getParentRoute: () => Route$53
7306
7990
  });
7307
- var ApiGitCommitMessageRoute = Route$32.update({
7991
+ var ApiGitCommitRemoteUrlRoute = Route$37.update({
7992
+ id: "/api/git-commit-remote-url",
7993
+ path: "/api/git-commit-remote-url",
7994
+ getParentRoute: () => Route$53
7995
+ });
7996
+ var ApiGitCommitMessageRoute = Route$36.update({
7308
7997
  id: "/api/git-commit-message",
7309
7998
  path: "/api/git-commit-message",
7310
- getParentRoute: () => Route$48
7999
+ getParentRoute: () => Route$53
8000
+ });
8001
+ var ApiGitCommitFilesRoute = Route$35.update({
8002
+ id: "/api/git-commit-files",
8003
+ path: "/api/git-commit-files",
8004
+ getParentRoute: () => Route$53
8005
+ });
8006
+ var ApiGitCommitDiffRoute = Route$34.update({
8007
+ id: "/api/git-commit-diff",
8008
+ path: "/api/git-commit-diff",
8009
+ getParentRoute: () => Route$53
7311
8010
  });
7312
- var ApiGitCommitRoute = Route$31.update({
8011
+ var ApiGitCommitActionRoute = Route$33.update({
8012
+ id: "/api/git-commit-action",
8013
+ path: "/api/git-commit-action",
8014
+ getParentRoute: () => Route$53
8015
+ });
8016
+ var ApiGitCommitRoute = Route$32.update({
7313
8017
  id: "/api/git-commit",
7314
8018
  path: "/api/git-commit",
7315
- getParentRoute: () => Route$48
8019
+ getParentRoute: () => Route$53
7316
8020
  });
7317
- var ApiGitCheckoutRoute = Route$30.update({
8021
+ var ApiGitCheckoutRoute = Route$31.update({
7318
8022
  id: "/api/git-checkout",
7319
8023
  path: "/api/git-checkout",
7320
- getParentRoute: () => Route$48
8024
+ getParentRoute: () => Route$53
7321
8025
  });
7322
- var ApiGitChangesRoute = Route$29.update({
8026
+ var ApiGitChangesRoute = Route$30.update({
7323
8027
  id: "/api/git-changes",
7324
8028
  path: "/api/git-changes",
7325
- getParentRoute: () => Route$48
8029
+ getParentRoute: () => Route$53
7326
8030
  });
7327
- var ApiFileCompletionsRoute = Route$28.update({
8031
+ var ApiFileCompletionsRoute = Route$29.update({
7328
8032
  id: "/api/file-completions",
7329
8033
  path: "/api/file-completions",
7330
- getParentRoute: () => Route$48
8034
+ getParentRoute: () => Route$53
7331
8035
  });
7332
- var ApiDirectorySessionsIndexesRoute = Route$27.update({
8036
+ var ApiDirectorySessionsIndexesRoute = Route$28.update({
7333
8037
  id: "/api/directory-sessions-indexes",
7334
8038
  path: "/api/directory-sessions-indexes",
7335
- getParentRoute: () => Route$48
8039
+ getParentRoute: () => Route$53
7336
8040
  });
7337
- var ApiDirectorySessionsIndexRoute = Route$26.update({
8041
+ var ApiDirectorySessionsIndexRoute = Route$27.update({
7338
8042
  id: "/api/directory-sessions-index",
7339
8043
  path: "/api/directory-sessions-index",
7340
- getParentRoute: () => Route$48
8044
+ getParentRoute: () => Route$53
7341
8045
  });
7342
- var ApiDirectorySessionsRoute = Route$25.update({
8046
+ var ApiDirectorySessionsRoute = Route$26.update({
7343
8047
  id: "/api/directory-sessions",
7344
8048
  path: "/api/directory-sessions",
7345
- getParentRoute: () => Route$48
8049
+ getParentRoute: () => Route$53
7346
8050
  });
7347
- var ApiDirectorySearchRoute = Route$24.update({
8051
+ var ApiDirectorySearchRoute = Route$25.update({
7348
8052
  id: "/api/directory-search",
7349
8053
  path: "/api/directory-search",
7350
- getParentRoute: () => Route$48
8054
+ getParentRoute: () => Route$53
7351
8055
  });
7352
- var ApiAbortRoute = Route$23.update({
8056
+ var ApiAbortRoute = Route$24.update({
7353
8057
  id: "/api/abort",
7354
8058
  path: "/api/abort",
7355
- getParentRoute: () => Route$48
8059
+ getParentRoute: () => Route$53
7356
8060
  });
7357
- var ApiUiIdRoute = Route$22.update({
8061
+ var ApiUiIdRoute = Route$23.update({
7358
8062
  id: "/api/ui/$id",
7359
8063
  path: "/api/ui/$id",
7360
- getParentRoute: () => Route$48
8064
+ getParentRoute: () => Route$53
7361
8065
  });
7362
- var ApiSettingsHideThinkingRoute = Route$21.update({
8066
+ var ApiSettingsHideThinkingRoute = Route$22.update({
7363
8067
  id: "/api/settings/hide-thinking",
7364
8068
  path: "/api/settings/hide-thinking",
7365
- getParentRoute: () => Route$48
8069
+ getParentRoute: () => Route$53
7366
8070
  });
7367
- var ApiSessionsDeleteRoute = Route$20.update({
8071
+ var ApiSessionsDeleteRoute = Route$21.update({
7368
8072
  id: "/api/sessions/delete",
7369
8073
  path: "/api/sessions/delete",
7370
- getParentRoute: () => Route$48
8074
+ getParentRoute: () => Route$53
7371
8075
  });
7372
- var ApiSessionTreeRoute = Route$19.update({
8076
+ var ApiSessionTreeRoute = Route$20.update({
7373
8077
  id: "/api/session/tree",
7374
8078
  path: "/api/session/tree",
7375
- getParentRoute: () => Route$48
8079
+ getParentRoute: () => Route$53
7376
8080
  });
7377
- var ApiSessionSelectRoute = Route$18.update({
8081
+ var ApiSessionSelectRoute = Route$19.update({
7378
8082
  id: "/api/session/select",
7379
8083
  path: "/api/session/select",
7380
- getParentRoute: () => Route$48
8084
+ getParentRoute: () => Route$53
7381
8085
  });
7382
- var ApiSessionRenameRoute = Route$17.update({
8086
+ var ApiSessionRenameRoute = Route$18.update({
7383
8087
  id: "/api/session/rename",
7384
8088
  path: "/api/session/rename",
7385
- getParentRoute: () => Route$48
8089
+ getParentRoute: () => Route$53
8090
+ });
8091
+ var ApiSessionReadStateRoute = Route$17.update({
8092
+ id: "/api/session/read-state",
8093
+ path: "/api/session/read-state",
8094
+ getParentRoute: () => Route$53
7386
8095
  });
7387
8096
  var ApiSessionNewRoute = Route$16.update({
7388
8097
  id: "/api/session/new",
7389
8098
  path: "/api/session/new",
7390
- getParentRoute: () => Route$48
8099
+ getParentRoute: () => Route$53
7391
8100
  });
7392
8101
  var ApiSessionMoveRoute = Route$15.update({
7393
8102
  id: "/api/session/move",
7394
8103
  path: "/api/session/move",
7395
- getParentRoute: () => Route$48
8104
+ getParentRoute: () => Route$53
7396
8105
  });
7397
8106
  var ApiSessionHistoryRoute = Route$14.update({
7398
8107
  id: "/api/session/history",
7399
8108
  path: "/api/session/history",
7400
- getParentRoute: () => Route$48
8109
+ getParentRoute: () => Route$53
7401
8110
  });
7402
8111
  var ApiSessionForkRoute = Route$13.update({
7403
8112
  id: "/api/session/fork",
7404
8113
  path: "/api/session/fork",
7405
- getParentRoute: () => Route$48
8114
+ getParentRoute: () => Route$53
7406
8115
  });
7407
8116
  var ApiSessionDeleteRoute = Route$12.update({
7408
8117
  id: "/api/session/delete",
7409
8118
  path: "/api/session/delete",
7410
- getParentRoute: () => Route$48
8119
+ getParentRoute: () => Route$53
7411
8120
  });
7412
8121
  var ApiSessionCloneRoute = Route$11.update({
7413
8122
  id: "/api/session/clone",
7414
8123
  path: "/api/session/clone",
7415
- getParentRoute: () => Route$48
8124
+ getParentRoute: () => Route$53
7416
8125
  });
7417
8126
  var ApiPendingMessagesReorderRoute = Route$10.update({
7418
8127
  id: "/api/pending-messages/reorder",
7419
8128
  path: "/api/pending-messages/reorder",
7420
- getParentRoute: () => Route$48
8129
+ getParentRoute: () => Route$53
7421
8130
  });
7422
8131
  var ApiPendingMessageRemoveRoute = Route$9.update({
7423
8132
  id: "/api/pending-message/remove",
7424
8133
  path: "/api/pending-message/remove",
7425
- getParentRoute: () => Route$48
8134
+ getParentRoute: () => Route$53
7426
8135
  });
7427
8136
  var ApiFilesTreeRoute = Route$8.update({
7428
8137
  id: "/api/files/tree",
7429
8138
  path: "/api/files/tree",
7430
- getParentRoute: () => Route$48
8139
+ getParentRoute: () => Route$53
7431
8140
  });
7432
8141
  var ApiFilesReadRoute = Route$7.update({
7433
8142
  id: "/api/files/read",
7434
8143
  path: "/api/files/read",
7435
- getParentRoute: () => Route$48
8144
+ getParentRoute: () => Route$53
7436
8145
  });
7437
8146
  var ApiDirectoryResolveRoute = Route$6.update({
7438
8147
  id: "/api/directory/resolve",
7439
8148
  path: "/api/directory/resolve",
7440
- getParentRoute: () => Route$48
8149
+ getParentRoute: () => Route$53
7441
8150
  });
7442
8151
  var ApiDirectorySessionsCleanupRoute = Route$5.update({
7443
8152
  id: "/cleanup",
@@ -7447,22 +8156,22 @@ var ApiDirectorySessionsCleanupRoute = Route$5.update({
7447
8156
  var ApiAuthProvidersRoute = Route$4.update({
7448
8157
  id: "/api/auth/providers",
7449
8158
  path: "/api/auth/providers",
7450
- getParentRoute: () => Route$48
8159
+ getParentRoute: () => Route$53
7451
8160
  });
7452
8161
  var ApiAuthOauthRoute = Route$3.update({
7453
8162
  id: "/api/auth/oauth",
7454
8163
  path: "/api/auth/oauth",
7455
- getParentRoute: () => Route$48
8164
+ getParentRoute: () => Route$53
7456
8165
  });
7457
8166
  var ApiAuthLogoutRoute = Route$2.update({
7458
8167
  id: "/api/auth/logout",
7459
8168
  path: "/api/auth/logout",
7460
- getParentRoute: () => Route$48
8169
+ getParentRoute: () => Route$53
7461
8170
  });
7462
8171
  var ApiAuthApiKeyRoute = Route$1.update({
7463
8172
  id: "/api/auth/api-key",
7464
8173
  path: "/api/auth/api-key",
7465
- getParentRoute: () => Route$48
8174
+ getParentRoute: () => Route$53
7466
8175
  });
7467
8176
  var ApiSessionTreeLabelRoute = Route.update({
7468
8177
  id: "/label",
@@ -7484,7 +8193,11 @@ var rootRouteChildren = {
7484
8193
  ApiGitChangesRoute,
7485
8194
  ApiGitCheckoutRoute,
7486
8195
  ApiGitCommitRoute,
8196
+ ApiGitCommitActionRoute,
8197
+ ApiGitCommitDiffRoute,
8198
+ ApiGitCommitFilesRoute,
7487
8199
  ApiGitCommitMessageRoute,
8200
+ ApiGitCommitRemoteUrlRoute,
7488
8201
  ApiGitDiffRoute,
7489
8202
  ApiGitDiscardRoute,
7490
8203
  ApiGitPullRoute,
@@ -7514,6 +8227,7 @@ var rootRouteChildren = {
7514
8227
  ApiSessionHistoryRoute,
7515
8228
  ApiSessionMoveRoute,
7516
8229
  ApiSessionNewRoute,
8230
+ ApiSessionReadStateRoute,
7517
8231
  ApiSessionRenameRoute,
7518
8232
  ApiSessionSelectRoute,
7519
8233
  ApiSessionTreeRoute: ApiSessionTreeRoute._addFileChildren(ApiSessionTreeRouteChildren),
@@ -7521,7 +8235,7 @@ var rootRouteChildren = {
7521
8235
  ApiSettingsHideThinkingRoute,
7522
8236
  ApiUiIdRoute
7523
8237
  };
7524
- var routeTree = Route$48._addFileChildren(rootRouteChildren)._addFileTypes();
8238
+ var routeTree = Route$53._addFileChildren(rootRouteChildren)._addFileTypes();
7525
8239
  function getRouter() {
7526
8240
  const queryClient = new QueryClient({ defaultOptions: { queries: {
7527
8241
  retry: 1,