@alivault/pico 0.10.0 → 0.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,14 +1,14 @@
1
1
  import { i as __toESM } from "../_runtime.mjs";
2
2
  import { $ as require_react, Q as require_jsx_runtime } from "../_libs/@base-ui/react+[...].mjs";
3
3
  import { a as createRouter, c as createRootRoute, n as Scripts, r as HeadContent, s as createFileRoute } from "../_libs/@tanstack/react-router+[...].mjs";
4
- import { F 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, V as extractMessageText$1, n as APPLIED_THEME_STORAGE_KEY, t as APPLIED_THEME_CLASSES, v as Route$54, xt as toolCategoryFromTool } from "./routes-D6yf6dMU.mjs";
4
+ import { I as CircleCheck, S as Info, _ as OctagonX, a as TriangleAlert } from "../_libs/lucide-react.mjs";
5
+ import { B as createCompactionSummaryItem, G as extractToolText, I as appliedThemeClassColorMode, J as mergeAssistantBlocksForStreaming, L as assistantBlocksFromMessage, M as TooltipProvider, R as buildItemsFromSync, T as Spinner, U as extractMessageImages, W as extractMessageText$1, f as PICO_SHIKI_VARIABLE_DEFAULTS, kt as toolCategoryFromTool, n as APPLIED_THEME_STORAGE_KEY, t as APPLIED_THEME_CLASSES, u as PICO_CODE_SHIKI_THEME, x as Route$54 } from "./routes-CHNxptm_.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";
9
9
  import { n as z, t as J } from "../_libs/next-themes.mjs";
10
10
  import { t as Toaster } from "../_libs/sonner.mjs";
11
- import { a as rust_exports, i as python_exports, n as go_exports, o as css_exports, r as c_exports, s as highlight, t as java_exports } from "../_libs/sugar-high.mjs";
11
+ import { c as bundledLanguages, l as bundledLanguagesAlias, s as codeToHtml, u as createCssVariablesTheme } from "../_libs/@pierre/diffs+[...].mjs";
12
12
  import { t as createTwoFilesPatch } from "../_libs/diff.mjs";
13
13
  import { n as Debouncer } from "../_libs/tanstack__pacer.mjs";
14
14
  import { createHash, randomUUID } from "node:crypto";
@@ -20,13 +20,17 @@ 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-BfWi4RfZ.js
23
+ //#region node_modules/.nitro/vite/services/ssr/assets/router-CphHgwo2.js
24
24
  var import_react = /* @__PURE__ */ __toESM(require_react());
25
25
  var import_jsx_runtime = require_jsx_runtime();
26
+ function sonnerThemeFromAppliedTheme(theme) {
27
+ if (theme === "system") return "system";
28
+ return appliedThemeClassColorMode(theme) ?? "system";
29
+ }
26
30
  var Toaster$1 = ({ ...props }) => {
27
31
  const { theme = "system" } = z();
28
32
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Toaster, {
29
- theme,
33
+ theme: sonnerThemeFromAppliedTheme(theme),
30
34
  className: "toaster group",
31
35
  icons: {
32
36
  success: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CircleCheck, { className: "size-4" }),
@@ -39,6 +43,18 @@ var Toaster$1 = ({ ...props }) => {
39
43
  "--normal-bg": "var(--popover)",
40
44
  "--normal-text": "var(--popover-foreground)",
41
45
  "--normal-border": "var(--border)",
46
+ "--success-bg": "color-mix(in oklab, var(--success) 14%, var(--popover))",
47
+ "--success-border": "color-mix(in oklab, var(--success) 35%, var(--border))",
48
+ "--success-text": "var(--success)",
49
+ "--info-bg": "color-mix(in oklab, var(--primary) 14%, var(--popover))",
50
+ "--info-border": "color-mix(in oklab, var(--primary) 35%, var(--border))",
51
+ "--info-text": "var(--primary)",
52
+ "--warning-bg": "color-mix(in oklab, var(--warning) 14%, var(--popover))",
53
+ "--warning-border": "color-mix(in oklab, var(--warning) 35%, var(--border))",
54
+ "--warning-text": "var(--warning)",
55
+ "--error-bg": "color-mix(in oklab, var(--danger) 14%, var(--popover))",
56
+ "--error-border": "color-mix(in oklab, var(--danger) 35%, var(--border))",
57
+ "--error-text": "var(--danger)",
42
58
  "--border-radius": "var(--radius)"
43
59
  },
44
60
  toastOptions: { classNames: { toast: "cn-toast" } },
@@ -71,7 +87,7 @@ function AppProviders({ children }) {
71
87
  })
72
88
  });
73
89
  }
74
- var styles_default = "/assets/styles-DgbW000h.css";
90
+ var styles_default = "/assets/styles-hk0oLlcJ.css";
75
91
  var TANSTACK_DEVTOOLS_SETTINGS_KEY = "tanstack_devtools_settings";
76
92
  var TANSTACK_DEVTOOLS_DEFAULTS_STORAGE_KEY = "pico-tanstack-devtools-defaults-v1";
77
93
  var TANSTACK_DEVTOOLS_CONFIG = {
@@ -519,7 +535,7 @@ async function clearContextDraft(options) {
519
535
  await disposeDraftIfUnused(draftEntry);
520
536
  }
521
537
  async function resolveRequestedEntry(options) {
522
- const { url, context, getSessionEntryByKey, ensureSessionEntryById, getActiveEntry, isDraftEntry, getOrCreateDraftEntry, activateContextSession, notifyOnActivate = true, preferActiveDraft = false } = options;
538
+ const { url, context, getSessionEntryByKey, ensureSessionEntryById, getActiveEntry, isDraftEntry, getOrCreateDraftEntry, activateContextSession, notifyOnActivate = true, preferActiveDraft = false, preferActiveDraftOverRequestedSession = false } = options;
523
539
  const activateRequestedEntry = async (entry) => {
524
540
  await activateContextSession(context, entry, { notify: notifyOnActivate });
525
541
  return entry;
@@ -529,39 +545,52 @@ async function resolveRequestedEntry(options) {
529
545
  const requestedEntry = getSessionEntryByKey(requestedSessionKey);
530
546
  if (requestedEntry) return await activateRequestedEntry(requestedEntry);
531
547
  }
532
- if (preferActiveDraft && context.draftKey) {
533
- const draftEntry = getSessionEntryByKey(context.draftKey);
534
- if (draftEntry && isDraftEntry(draftEntry)) return await activateRequestedEntry(draftEntry);
535
- }
536
548
  const requestedSessionId = url.searchParams.get("session");
537
549
  if (requestedSessionId) {
550
+ if (preferActiveDraft && preferActiveDraftOverRequestedSession && context.draftKey && context.activeKey === context.draftKey) {
551
+ const activeDraftEntry = getSessionEntryByKey(context.draftKey);
552
+ if (activeDraftEntry && isDraftEntry(activeDraftEntry)) return await activateRequestedEntry(activeDraftEntry);
553
+ }
538
554
  const requestedEntry = await ensureSessionEntryById(requestedSessionId);
539
555
  if (requestedEntry) return await activateRequestedEntry(requestedEntry);
540
556
  }
557
+ if (preferActiveDraft && context.draftKey) {
558
+ const draftEntry = getSessionEntryByKey(context.draftKey);
559
+ if (draftEntry && isDraftEntry(draftEntry)) return await activateRequestedEntry(draftEntry);
560
+ }
541
561
  const activeEntry = getActiveEntry(context);
542
562
  if (activeEntry) return await activateRequestedEntry(activeEntry);
543
563
  return await getOrCreateDraftEntry(context);
544
564
  }
565
+ var SPECIAL_SHIKI_LANGUAGES = new Set([
566
+ "ansi",
567
+ "plain",
568
+ "plaintext",
569
+ "text"
570
+ ]);
571
+ var SUPPORTED_SHIKI_LANGUAGES = new Set([...Object.keys(bundledLanguages), ...Object.keys(bundledLanguagesAlias)]);
572
+ var picoCodeTheme = createCssVariablesTheme({
573
+ name: PICO_CODE_SHIKI_THEME,
574
+ variablePrefix: "--sh-",
575
+ variableDefaults: { ...PICO_SHIKI_VARIABLE_DEFAULTS },
576
+ fontStyle: false
577
+ });
545
578
  function normalizeHighlightLanguage(language) {
546
579
  const normalized = typeof language === "string" ? language.trim().toLowerCase().replace(/^language-/, "") : "";
547
580
  if (!normalized) return "";
548
581
  switch (normalized) {
549
- case "js":
550
582
  case "mjs":
551
583
  case "cjs": return "javascript";
552
- case "ts": return "typescript";
553
- case "py": return "python";
554
- case "rs": return "rust";
584
+ case "cts":
585
+ case "mts": return "typescript";
555
586
  case "golang": return "go";
556
587
  case "htm":
557
588
  case "xhtml": return "html";
558
- case "yml": return "yaml";
589
+ case "svg": return "xml";
559
590
  case "shell":
560
- case "shellscript":
561
- case "sh":
562
- case "zsh": return "bash";
591
+ case "shellscript": return "bash";
563
592
  case "plain":
564
- case "text": return "plaintext";
593
+ case "txt": return "text";
565
594
  case "h": return "c";
566
595
  default: return normalized;
567
596
  }
@@ -571,46 +600,34 @@ function countTextLines(text) {
571
600
  for (let index = 0; index < text.length; index += 1) if (text.charCodeAt(index) === 10) lines += 1;
572
601
  return lines;
573
602
  }
574
- function getSugarHighOptions(language) {
575
- switch (language) {
576
- case "javascript":
577
- case "jsx":
578
- case "typescript":
579
- case "tsx":
580
- case "json":
581
- case "jsonc":
582
- case "html":
583
- case "xml":
584
- case "svg":
585
- case "mdx": return null;
586
- case "css": return css_exports;
587
- case "python": return python_exports;
588
- case "rust": return rust_exports;
589
- case "c": return c_exports;
590
- case "go": return go_exports;
591
- case "java": return java_exports;
592
- default: return;
593
- }
594
- }
595
- function buildHighlightPayload(options) {
603
+ function isSupportedShikiLanguage(language) {
604
+ return SPECIAL_SHIKI_LANGUAGES.has(language) || SUPPORTED_SHIKI_LANGUAGES.has(language);
605
+ }
606
+ function shikiCodeInnerHtml(html) {
607
+ return /<pre\b[^>]*><code>([\s\S]*)<\/code><\/pre>/.exec(html)?.[1] ?? html;
608
+ }
609
+ async function buildHighlightPayload(options) {
596
610
  const text = typeof options.code === "string" ? options.code : "";
597
611
  const normalizedLanguage = normalizeHighlightLanguage(options.language);
598
612
  if (!text || !normalizedLanguage) return {
599
613
  skipped: true,
600
614
  language: normalizedLanguage || void 0
601
615
  };
602
- if (normalizedLanguage === "plaintext" || text.length > 1e5 || countTextLines(text) > 1500) return {
616
+ if (normalizedLanguage === "text" || normalizedLanguage === "plaintext" || text.length > 1e5 || countTextLines(text) > 1500) return {
603
617
  skipped: true,
604
618
  language: normalizedLanguage
605
619
  };
606
- const sugarOptions = getSugarHighOptions(normalizedLanguage);
607
- if (sugarOptions === void 0) return {
620
+ if (!isSupportedShikiLanguage(normalizedLanguage)) return {
608
621
  unsupported: true,
609
622
  language: normalizedLanguage
610
623
  };
611
624
  return {
612
625
  language: normalizedLanguage,
613
- html: highlight(text, sugarOptions ?? void 0)
626
+ html: shikiCodeInnerHtml(await codeToHtml(text, {
627
+ lang: normalizedLanguage,
628
+ theme: picoCodeTheme,
629
+ defaultColor: false
630
+ }))
614
631
  };
615
632
  }
616
633
  function messageItemIndex(item) {
@@ -1113,40 +1130,85 @@ function countFullTurnUserAndAssistantMessages(messages) {
1113
1130
  finishTurn(false);
1114
1131
  return count;
1115
1132
  }
1116
- async function readSessionListMetrics(sessionPath) {
1117
- try {
1118
- const content = await readFile(sessionPath, "utf8");
1119
- let lastTimestamp = 0;
1120
- let lastValue;
1121
- const messages = [];
1122
- for (const line of content.split("\n")) {
1123
- if (!line.trim()) continue;
1124
- let entry;
1125
- try {
1126
- entry = JSON.parse(line);
1127
- } catch {
1128
- continue;
1129
- }
1130
- if (!isRecord(entry) || entry.type !== "message") continue;
1131
- const message = isRecord(entry.message) ? entry.message : void 0;
1132
- if (!message) continue;
1133
- const messageWithTimestamp = {
1134
- ...message,
1135
- timestamp: message.timestamp ?? entry.timestamp
1136
- };
1137
- if (messageWithTimestamp.role === "user") {
1138
- const normalized = normalizeModifiedTimestamp(messageWithTimestamp.timestamp);
1139
- const timestamp = modifiedTimestampValue(normalized);
1140
- if (timestamp && timestamp >= lastTimestamp) {
1141
- lastTimestamp = timestamp;
1142
- lastValue = normalized;
1143
- }
1133
+ function readSessionListMetricEntries(content) {
1134
+ const entries = [];
1135
+ for (const line of content.split("\n")) {
1136
+ if (!line.trim()) continue;
1137
+ let entry;
1138
+ try {
1139
+ entry = JSON.parse(line);
1140
+ } catch {
1141
+ continue;
1142
+ }
1143
+ if (!isRecord(entry) || entry.type === "session") continue;
1144
+ entries.push(entry);
1145
+ }
1146
+ return entries;
1147
+ }
1148
+ function sessionListMetricEntryId(entry) {
1149
+ return typeof entry.id === "string" && entry.id ? entry.id : "";
1150
+ }
1151
+ function sessionListMetricEntryParentId(entry) {
1152
+ return typeof entry.parentId === "string" && entry.parentId ? entry.parentId : "";
1153
+ }
1154
+ function sessionListMetricBranchEntries(entries, leafId) {
1155
+ if (leafId === void 0) return entries;
1156
+ if (leafId === null) return [];
1157
+ const byId = new Map(entries.flatMap((entry) => {
1158
+ const id = sessionListMetricEntryId(entry);
1159
+ return id ? [[id, entry]] : [];
1160
+ }));
1161
+ const leaf = byId.get(leafId);
1162
+ if (!leaf) return entries;
1163
+ const branch = [];
1164
+ let current = leaf;
1165
+ const visited = /* @__PURE__ */ new Set();
1166
+ while (current) {
1167
+ const id = sessionListMetricEntryId(current);
1168
+ if (id) {
1169
+ if (visited.has(id)) break;
1170
+ visited.add(id);
1171
+ }
1172
+ branch.unshift(current);
1173
+ const parentId = sessionListMetricEntryParentId(current);
1174
+ current = parentId ? byId.get(parentId) : void 0;
1175
+ }
1176
+ return branch;
1177
+ }
1178
+ function collectSessionListMetricMessages(entries) {
1179
+ let lastTimestamp = 0;
1180
+ let lastValue;
1181
+ const messages = [];
1182
+ for (const entry of entries) {
1183
+ if (entry.type !== "message") continue;
1184
+ const message = isRecord(entry.message) ? entry.message : void 0;
1185
+ if (!message) continue;
1186
+ const messageWithTimestamp = {
1187
+ ...message,
1188
+ timestamp: message.timestamp ?? entry.timestamp
1189
+ };
1190
+ if (messageWithTimestamp.role === "user") {
1191
+ const normalized = normalizeModifiedTimestamp(messageWithTimestamp.timestamp);
1192
+ const timestamp = modifiedTimestampValue(normalized);
1193
+ if (timestamp && timestamp >= lastTimestamp) {
1194
+ lastTimestamp = timestamp;
1195
+ lastValue = normalized;
1144
1196
  }
1145
- if (messageWithTimestamp.role === "user" || messageWithTimestamp.role === "assistant") messages.push(messageWithTimestamp);
1146
1197
  }
1198
+ if (messageWithTimestamp.role === "user" || messageWithTimestamp.role === "assistant") messages.push(messageWithTimestamp);
1199
+ }
1200
+ return {
1201
+ lastUserMessageAt: lastValue,
1202
+ messages
1203
+ };
1204
+ }
1205
+ async function readSessionListMetrics(sessionPath, options) {
1206
+ try {
1207
+ const entries = readSessionListMetricEntries(await readFile(sessionPath, "utf8"));
1208
+ const { lastUserMessageAt, messages } = collectSessionListMetricMessages(Object.prototype.hasOwnProperty.call(options ?? {}, "leafId") ? sessionListMetricBranchEntries(entries, options?.leafId) : entries);
1147
1209
  const lastMessage = getSessionLastCompleteMessageInfo(messages);
1148
1210
  return {
1149
- lastUserMessageAt: lastValue,
1211
+ lastUserMessageAt,
1150
1212
  lastMessageAt: lastMessage.timestamp,
1151
1213
  lastMessagePreview: lastMessage.preview,
1152
1214
  messageCount: countFullTurnUserAndAssistantMessages(messages)
@@ -3688,6 +3750,7 @@ var PicoRuntime = class {
3688
3750
  encoder = new TextEncoder();
3689
3751
  contexts = /* @__PURE__ */ new Map();
3690
3752
  sessionEntries = /* @__PURE__ */ new Map();
3753
+ sessionTreeLeafOverrides = /* @__PURE__ */ new Map();
3691
3754
  servicesByCwd = /* @__PURE__ */ new Map();
3692
3755
  pendingUiRequests = /* @__PURE__ */ new Map();
3693
3756
  activeOAuthLogins = /* @__PURE__ */ new Map();
@@ -3866,6 +3929,7 @@ var PicoRuntime = class {
3866
3929
  id,
3867
3930
  clients: /* @__PURE__ */ new Set(),
3868
3931
  activeRevision: 0,
3932
+ selectionNonce: 0,
3869
3933
  activeKey: void 0,
3870
3934
  draftKey: void 0,
3871
3935
  sessionScope: process.cwd(),
@@ -3878,6 +3942,31 @@ var PicoRuntime = class {
3878
3942
  getSessionPath(entry) {
3879
3943
  return entry.session.sessionFile ?? entry.key;
3880
3944
  }
3945
+ getSessionTreeLeafOverrideKey(entry) {
3946
+ return entry.session.sessionFile || "";
3947
+ }
3948
+ rememberSessionTreeLeaf(entry, onlyExisting = false) {
3949
+ const key = this.getSessionTreeLeafOverrideKey(entry);
3950
+ if (!key) return;
3951
+ if (onlyExisting && !this.sessionTreeLeafOverrides.has(key)) return;
3952
+ const leafId = entry.session.sessionManager.getLeafId?.() ?? null;
3953
+ if (this.sessionTreeLeafOverrides.get(key) === leafId) return;
3954
+ this.sessionTreeLeafOverrides.set(key, leafId);
3955
+ this.invalidateSessionIndexCache();
3956
+ }
3957
+ restoreSessionTreeLeafOverride(manager, sessionPath) {
3958
+ if (!this.sessionTreeLeafOverrides.has(sessionPath)) return;
3959
+ const leafId = this.sessionTreeLeafOverrides.get(sessionPath) ?? null;
3960
+ if (leafId === null) {
3961
+ manager.resetLeaf?.();
3962
+ return;
3963
+ }
3964
+ if (!manager.getEntry?.(leafId)) {
3965
+ this.sessionTreeLeafOverrides.delete(sessionPath);
3966
+ return;
3967
+ }
3968
+ manager.branch?.(leafId);
3969
+ }
3881
3970
  getActiveEntry(context) {
3882
3971
  return context.activeKey ? this.sessionEntries.get(context.activeKey) : void 0;
3883
3972
  }
@@ -3988,16 +4077,19 @@ var PicoRuntime = class {
3988
4077
  return mergeSessionListEntry(target, fallback);
3989
4078
  }
3990
4079
  async sessionFallbackInfo(entry) {
3991
- const firstMessage = this.getSessionFirstMessage(entry);
4080
+ const sessionName = entry.session.sessionName;
4081
+ const waitingForAutoName = Boolean(entry.sessionNaming.pendingGeneration && !cleanupSessionNameCandidate(sessionName));
4082
+ const firstMessage = waitingForAutoName ? "" : this.getSessionFirstMessage(entry);
4083
+ const listName = waitingForAutoName ? "New session" : sessionName;
3992
4084
  const lastMessage = getSessionLastCompleteMessageInfo(entry.session.messages);
3993
4085
  return {
3994
4086
  path: entry.session.sessionFile,
3995
4087
  id: entry.session.sessionId,
3996
4088
  cwd: entry.cwd,
3997
- name: entry.session.sessionName,
4089
+ name: listName,
3998
4090
  firstMessage,
3999
4091
  title: getSessionListTitle({
4000
- name: entry.session.sessionName,
4092
+ name: listName,
4001
4093
  firstMessage
4002
4094
  }),
4003
4095
  modified: await this.sessionEntryModified(entry),
@@ -4023,13 +4115,17 @@ var PicoRuntime = class {
4023
4115
  const sessions = allSessions.filter((entry) => (entry.messageCount ?? 0) > 0);
4024
4116
  const metricsStartedAt = performance.now();
4025
4117
  const withMetrics = await Promise.all(sessions.map(async (entry) => {
4026
- const metrics = entry.path ? await readSessionListMetrics(entry.path) : void 0;
4118
+ let metrics;
4119
+ if (entry.path) {
4120
+ const hasLeafOverride = this.sessionTreeLeafOverrides.has(entry.path);
4121
+ metrics = await readSessionListMetrics(entry.path, hasLeafOverride ? { leafId: this.sessionTreeLeafOverrides.get(entry.path) ?? null } : void 0);
4122
+ }
4027
4123
  return {
4028
4124
  ...entry,
4029
- lastUserMessageAt: metrics?.lastUserMessageAt ?? entry.lastUserMessageAt,
4030
- lastMessageAt: metrics?.lastMessageAt ?? entry.lastMessageAt,
4031
- lastMessagePreview: metrics?.lastMessagePreview ?? entry.lastMessagePreview,
4032
- messageCount: metrics?.messageCount ?? entry.messageCount
4125
+ lastUserMessageAt: metrics ? metrics.lastUserMessageAt : entry.lastUserMessageAt,
4126
+ lastMessageAt: metrics ? metrics.lastMessageAt : entry.lastMessageAt,
4127
+ lastMessagePreview: metrics ? metrics.lastMessagePreview : entry.lastMessagePreview,
4128
+ messageCount: metrics ? metrics.messageCount : entry.messageCount
4033
4129
  };
4034
4130
  }));
4035
4131
  this.logSessionLoadDebug("session_index:metrics", {
@@ -4370,6 +4466,16 @@ var PicoRuntime = class {
4370
4466
  await this.bindSessionEntry(entry);
4371
4467
  await Promise.all([this.broadcastEntryState(entry), this.broadcastSessionsAll()]);
4372
4468
  }
4469
+ scheduleTreeNavigationSessionListChange(entry, previousLeafId, result) {
4470
+ if (result.cancelled || result.aborted) return;
4471
+ const nextLeafId = entry.session.sessionManager.getLeafId?.() ?? null;
4472
+ if (!result.summaryEntry && previousLeafId === nextLeafId) return;
4473
+ this.rememberSessionTreeLeaf(entry);
4474
+ this.touchSessionEntry(entry);
4475
+ this.broadcastSessionsAll().catch((error) => {
4476
+ console.error("[pico] failed to broadcast tree navigation sessions:", error);
4477
+ });
4478
+ }
4373
4479
  syncGitWatchDirectories() {
4374
4480
  const cwds = /* @__PURE__ */ new Set();
4375
4481
  for (const context of this.contexts.values()) {
@@ -4649,7 +4755,8 @@ var PicoRuntime = class {
4649
4755
  getOrCreateDraftEntry: async (activeContext) => await this.getOrCreateDraftEntry(activeContext),
4650
4756
  activateContextSession: async (activeContext, entry, activateOptions) => await this.activateContextSession(activeContext, entry, activateOptions),
4651
4757
  notifyOnActivate: options?.notifyOnActivate,
4652
- preferActiveDraft: options?.preferActiveDraft
4758
+ preferActiveDraft: options?.preferActiveDraft,
4759
+ preferActiveDraftOverRequestedSession: options?.preferActiveDraftOverRequestedSession
4653
4760
  });
4654
4761
  }
4655
4762
  async resolveRequest(request, options) {
@@ -4668,7 +4775,8 @@ var PicoRuntime = class {
4668
4775
  try {
4669
4776
  const activeEntry = await this.resolveRequestedEntry(url, context, {
4670
4777
  notifyOnActivate: options?.notifyOnActivate ?? false,
4671
- preferActiveDraft: options?.preferActiveDraft ?? true
4778
+ preferActiveDraft: options?.preferActiveDraft ?? true,
4779
+ preferActiveDraftOverRequestedSession: options?.preferActiveDraftOverRequestedSession ?? false
4672
4780
  });
4673
4781
  this.logSessionLoadDebug("request_resolve:done", {
4674
4782
  pathname: url.pathname,
@@ -4793,6 +4901,7 @@ var PicoRuntime = class {
4793
4901
  const sdk = await this.getSdk();
4794
4902
  const openStartedAt = performance.now();
4795
4903
  const sessionManager = sdk.SessionManager.open(sessionPath);
4904
+ this.restoreSessionTreeLeafOverride(sessionManager, sessionPath);
4796
4905
  this.logSessionLoadDebug("session_manager:open", {
4797
4906
  sessionPath,
4798
4907
  cwd: sessionManager.getCwd(),
@@ -4809,7 +4918,9 @@ var PicoRuntime = class {
4809
4918
  if (currentManager.isPersisted?.()) {
4810
4919
  const currentSessionFile = entry.session.sessionFile;
4811
4920
  if (!currentSessionFile) throw new Error("Persisted session is missing a session file");
4812
- return (await this.getSdk()).SessionManager.open(currentSessionFile, currentManager.getSessionDir?.(), entry.cwd);
4921
+ const sessionManager = (await this.getSdk()).SessionManager.open(currentSessionFile, currentManager.getSessionDir?.(), entry.cwd);
4922
+ this.restoreSessionTreeLeafOverride(sessionManager, currentSessionFile);
4923
+ return sessionManager;
4813
4924
  }
4814
4925
  return await this.createForkedInMemorySessionManager(currentManager, currentManager.getLeafId?.(), entry.session.sessionFile);
4815
4926
  }
@@ -5192,14 +5303,12 @@ var PicoRuntime = class {
5192
5303
  };
5193
5304
  },
5194
5305
  navigateTree: async (targetId, navigateOptions) => {
5306
+ const previousLeafId = session.sessionManager.getLeafId?.() ?? null;
5195
5307
  const result = await session.navigateTree(targetId, navigateOptions);
5196
5308
  this.refreshRetainedConversationItems(entry);
5197
5309
  if (result.editorText != null) entry.uiState.editorText = result.editorText;
5198
5310
  await this.broadcastEntryState(entry);
5199
- if (result.summaryEntry) {
5200
- this.touchSessionEntry(entry);
5201
- await this.broadcastSessionsAll();
5202
- }
5311
+ this.scheduleTreeNavigationSessionListChange(entry, previousLeafId, result);
5203
5312
  return {
5204
5313
  cancelled: Boolean(result.cancelled),
5205
5314
  aborted: Boolean(result.aborted)
@@ -5326,6 +5435,7 @@ var PicoRuntime = class {
5326
5435
  statusChanged = true;
5327
5436
  }
5328
5437
  if (statusChanged) this.broadcastSessionStatusAll(entry);
5438
+ this.rememberSessionTreeLeaf(entry, true);
5329
5439
  await this.broadcastEntryState(entry);
5330
5440
  if (type === "agent_start" || type === "agent_end" || type === "message_end" || type === "compaction_end" || type === "queue_update") await this.broadcastSessionsAll();
5331
5441
  if (type === "agent_end" || type === "compaction_end") await this.cleanupInactiveContexts();
@@ -5383,12 +5493,13 @@ var PicoRuntime = class {
5383
5493
  const bootstrapStartedAt = performance.now();
5384
5494
  try {
5385
5495
  const stateStartedAt = performance.now();
5386
- this.sendStatePayloadToClient(context, client, this.currentStatePayload(activeEntry, context), { forceFull: true });
5496
+ const bootstrapEntry = this.getActiveEntry(context) ?? activeEntry;
5497
+ this.sendStatePayloadToClient(context, client, this.currentStatePayload(bootstrapEntry, context), { forceFull: true });
5387
5498
  this.logSessionLoadDebug("events_response:bootstrap_state", {
5388
5499
  contextId: context.id,
5389
5500
  clientId: client.id,
5390
5501
  durationMs: roundedDurationMs(stateStartedAt),
5391
- ...this.sessionDebugDetails(activeEntry)
5502
+ ...this.sessionDebugDetails(bootstrapEntry)
5392
5503
  });
5393
5504
  if (client.closed || request.signal.aborted) return;
5394
5505
  const sessionsStartedAt = performance.now();
@@ -5438,7 +5549,10 @@ var PicoRuntime = class {
5438
5549
  }
5439
5550
  }
5440
5551
  async prompt(request, body) {
5441
- const { context, activeEntry } = await this.resolveRequest(request, { preferActiveDraft: true });
5552
+ const { context, activeEntry } = await this.resolveRequest(request, {
5553
+ preferActiveDraft: true,
5554
+ preferActiveDraftOverRequestedSession: true
5555
+ });
5442
5556
  const message = typeof body.message === "string" ? body.message : "";
5443
5557
  const images = normalizePromptImages(body.images);
5444
5558
  if (!message.trim() && images.length === 0) throw new Error("message or image is required");
@@ -5620,11 +5734,16 @@ var PicoRuntime = class {
5620
5734
  const url = new URL(request.url);
5621
5735
  const requestedSessionId = url.searchParams.get("session")?.trim() || "";
5622
5736
  const requestedSessionPath = url.searchParams.get("sessionPath")?.trim() || "";
5737
+ const rawSelectionNonce = Number(url.searchParams.get("selectionNonce"));
5738
+ const selectionNonce = Number.isSafeInteger(rawSelectionNonce) && rawSelectionNonce > 0 ? rawSelectionNonce : 0;
5623
5739
  const context = this.ensureContext(url.searchParams.get("context") || "default");
5624
5740
  context.sessionScope = normalizeSessionScope(url.searchParams.get("scope"), process.cwd());
5741
+ if (selectionNonce > context.selectionNonce) context.selectionNonce = selectionNonce;
5625
5742
  this.logSessionLoadDebug("select_session:start", {
5626
5743
  requestedSessionId,
5627
5744
  requestedSessionPath,
5745
+ selectionNonce,
5746
+ latestSelectionNonce: context.selectionNonce,
5628
5747
  contextId: context.id
5629
5748
  });
5630
5749
  try {
@@ -5640,6 +5759,26 @@ var PicoRuntime = class {
5640
5759
  ...this.sessionDebugDetails(nextEntry)
5641
5760
  });
5642
5761
  if (!nextEntry) throw new Error(`Unknown session: ${requestedSessionId}`);
5762
+ if (request.signal.aborted) {
5763
+ this.logSessionLoadDebug("select_session:skip_aborted", {
5764
+ requestedSessionId,
5765
+ requestedSessionPath,
5766
+ selectionNonce,
5767
+ latestSelectionNonce: context.selectionNonce,
5768
+ ...this.sessionDebugDetails(nextEntry)
5769
+ });
5770
+ return { ok: true };
5771
+ }
5772
+ if (selectionNonce > 0 && selectionNonce < context.selectionNonce) {
5773
+ this.logSessionLoadDebug("select_session:skip_stale", {
5774
+ requestedSessionId,
5775
+ requestedSessionPath,
5776
+ selectionNonce,
5777
+ latestSelectionNonce: context.selectionNonce,
5778
+ ...this.sessionDebugDetails(nextEntry)
5779
+ });
5780
+ return { ok: true };
5781
+ }
5643
5782
  const activateStartedAt = performance.now();
5644
5783
  await this.activateContextSession(context, nextEntry);
5645
5784
  this.logSessionLoadDebug("select_session:activate", {
@@ -5763,6 +5902,7 @@ var PicoRuntime = class {
5763
5902
  const targetId = typeof body.targetId === "string" ? body.targetId.trim() : "";
5764
5903
  if (!targetId) throw new Error("targetId is required");
5765
5904
  if (this.getEntryStreamingState(activeEntry) || activeEntry.session.isStreaming) throw new Error("Abort the current response before navigating the tree.");
5905
+ const previousLeafId = activeEntry.session.sessionManager.getLeafId?.() ?? null;
5766
5906
  const result = await activeEntry.session.navigateTree(targetId, {
5767
5907
  summarize: Boolean(body.summarize),
5768
5908
  customInstructions: typeof body.customInstructions === "string" ? body.customInstructions : void 0,
@@ -5771,11 +5911,8 @@ var PicoRuntime = class {
5771
5911
  });
5772
5912
  this.refreshRetainedConversationItems(activeEntry);
5773
5913
  if (result.editorText != null) activeEntry.uiState.editorText = result.editorText;
5774
- if (result.summaryEntry) {
5775
- this.touchSessionEntry(activeEntry);
5776
- await this.broadcastSessionsAll();
5777
- }
5778
5914
  await this.broadcastEntryState(activeEntry);
5915
+ this.scheduleTreeNavigationSessionListChange(activeEntry, previousLeafId, result);
5779
5916
  return {
5780
5917
  ok: true,
5781
5918
  cancelled: Boolean(result.cancelled),
@@ -6366,7 +6503,7 @@ var PicoRuntime = class {
6366
6503
  async highlightCode(code, language) {
6367
6504
  const text = typeof code === "string" ? code : "";
6368
6505
  try {
6369
- const highlightInput = buildHighlightPayload({
6506
+ const highlightInput = await buildHighlightPayload({
6370
6507
  code,
6371
6508
  language
6372
6509
  });