@adhdev/daemon-standalone 0.8.75 → 0.8.77

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.
package/dist/index.js CHANGED
@@ -9244,7 +9244,7 @@ var require_dist = __commonJS({
9244
9244
  }
9245
9245
  }
9246
9246
  };
9247
- var import_crypto2 = require("crypto");
9247
+ var import_crypto3 = require("crypto");
9248
9248
  var path5 = __toESM2(require("path"));
9249
9249
  function normalizeSlug(input) {
9250
9250
  return input.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
@@ -9360,7 +9360,7 @@ var require_dist = __commonJS({
9360
9360
  var SessionHostRegistry = class {
9361
9361
  sessions = /* @__PURE__ */ new Map();
9362
9362
  createSession(payload) {
9363
- const sessionId = payload.sessionId || (0, import_crypto2.randomUUID)();
9363
+ const sessionId = payload.sessionId || (0, import_crypto3.randomUUID)();
9364
9364
  if (this.sessions.has(sessionId)) {
9365
9365
  throw new Error(`Session already exists: ${sessionId}`);
9366
9366
  }
@@ -26823,9 +26823,9 @@ var init_handler = __esm({
26823
26823
  if (this.fsw.closed) {
26824
26824
  return;
26825
26825
  }
26826
- const dirname3 = sp.dirname(file2);
26826
+ const dirname4 = sp.dirname(file2);
26827
26827
  const basename3 = sp.basename(file2);
26828
- const parent = this.fsw._getWatchedDir(dirname3);
26828
+ const parent = this.fsw._getWatchedDir(dirname4);
26829
26829
  let prevStats = stats;
26830
26830
  if (parent.has(basename3))
26831
26831
  return;
@@ -26852,7 +26852,7 @@ var init_handler = __esm({
26852
26852
  prevStats = newStats2;
26853
26853
  }
26854
26854
  } catch (error48) {
26855
- this.fsw._remove(dirname3, basename3);
26855
+ this.fsw._remove(dirname4, basename3);
26856
26856
  }
26857
26857
  } else if (parent.has(basename3)) {
26858
26858
  const at2 = newStats.atimeMs;
@@ -27927,7 +27927,7 @@ var require_dist2 = __commonJS({
27927
27927
  };
27928
27928
  }
27929
27929
  function generateMachineId() {
27930
- return `${MACHINE_ID_PREFIX}${(0, import_crypto2.randomUUID)().replace(/-/g, "")}`;
27930
+ return `${MACHINE_ID_PREFIX}${(0, import_crypto3.randomUUID)().replace(/-/g, "")}`;
27931
27931
  }
27932
27932
  function isStableMachineId(machineId) {
27933
27933
  return typeof machineId === "string" && machineId.startsWith(MACHINE_ID_PREFIX);
@@ -28046,7 +28046,7 @@ var require_dist2 = __commonJS({
28046
28046
  var import_os2;
28047
28047
  var import_path2;
28048
28048
  var import_fs;
28049
- var import_crypto2;
28049
+ var import_crypto3;
28050
28050
  var DEFAULT_CONFIG;
28051
28051
  var MACHINE_ID_PREFIX;
28052
28052
  var init_config = __esm2({
@@ -28055,7 +28055,7 @@ var require_dist2 = __commonJS({
28055
28055
  import_os2 = require("os");
28056
28056
  import_path2 = require("path");
28057
28057
  import_fs = require("fs");
28058
- import_crypto2 = require("crypto");
28058
+ import_crypto3 = require("crypto");
28059
28059
  DEFAULT_CONFIG = {
28060
28060
  serverUrl: "https://api.adhf.dev",
28061
28061
  allowServerApiProxy: false,
@@ -31510,6 +31510,7 @@ ${data.message || ""}`.trim();
31510
31510
  resolveChatMessageKind: () => resolveChatMessageKind,
31511
31511
  resolveDebugRuntimeConfig: () => resolveDebugRuntimeConfig,
31512
31512
  resolveSessionHostAppName: () => resolveSessionHostAppName2,
31513
+ runAsyncBatch: () => runAsyncBatch2,
31513
31514
  saveConfig: () => saveConfig,
31514
31515
  saveState: () => saveState,
31515
31516
  setDebugRuntimeConfig: () => setDebugRuntimeConfig,
@@ -32228,12 +32229,66 @@ ${data.message || ""}`.trim();
32228
32229
  const availableMem = darwinAvail != null ? darwinAvail : freeMem;
32229
32230
  return { totalMem, freeMem, availableMem };
32230
32231
  }
32232
+ var LIVE_LIFECYCLES = /* @__PURE__ */ new Set(["starting", "running", "stopping", "interrupted"]);
32233
+ function isSessionHostLiveRuntime(record2) {
32234
+ const lifecycle = String(record2?.lifecycle || "").trim();
32235
+ return LIVE_LIFECYCLES.has(lifecycle);
32236
+ }
32237
+ function getSessionHostRecoveryLabel(meta3) {
32238
+ const recoveryState = typeof meta3?.runtimeRecoveryState === "string" ? String(meta3.runtimeRecoveryState).trim() : "";
32239
+ if (!recoveryState) return null;
32240
+ if (recoveryState === "auto_resumed") return "restored after restart";
32241
+ if (recoveryState === "resume_failed") return "restore failed";
32242
+ if (recoveryState === "host_restart_interrupted") return "host restart interrupted";
32243
+ if (recoveryState === "orphan_snapshot") return "snapshot recovered";
32244
+ return recoveryState.replace(/_/g, " ");
32245
+ }
32246
+ function isSessionHostRecoverySnapshot(record2) {
32247
+ if (!record2) return false;
32248
+ if (isSessionHostLiveRuntime(record2)) return false;
32249
+ const lifecycle = String(record2.lifecycle || "").trim();
32250
+ if (lifecycle && lifecycle !== "stopped" && lifecycle !== "failed") {
32251
+ return false;
32252
+ }
32253
+ const meta3 = record2.meta || void 0;
32254
+ if (meta3?.restoredFromStorage === true) return true;
32255
+ return getSessionHostRecoveryLabel(meta3) !== null;
32256
+ }
32257
+ function getSessionHostSurfaceKind(record2) {
32258
+ if (isSessionHostLiveRuntime(record2)) return "live_runtime";
32259
+ if (isSessionHostRecoverySnapshot(record2)) return "recovery_snapshot";
32260
+ return "inactive_record";
32261
+ }
32262
+ function partitionSessionHostRecords(records) {
32263
+ const liveRuntimes = [];
32264
+ const recoverySnapshots = [];
32265
+ const inactiveRecords = [];
32266
+ for (const record2 of records) {
32267
+ const kind = getSessionHostSurfaceKind(record2);
32268
+ if (kind === "live_runtime") {
32269
+ liveRuntimes.push(record2);
32270
+ } else if (kind === "recovery_snapshot") {
32271
+ recoverySnapshots.push(record2);
32272
+ } else {
32273
+ inactiveRecords.push(record2);
32274
+ }
32275
+ }
32276
+ return {
32277
+ liveRuntimes,
32278
+ recoverySnapshots,
32279
+ inactiveRecords
32280
+ };
32281
+ }
32282
+ function partitionSessionHostDiagnosticsSessions(records) {
32283
+ return partitionSessionHostRecords(records || []);
32284
+ }
32231
32285
  var DEFAULT_ACTIVE_CHAT_POLL_STATUSES = /* @__PURE__ */ new Set([
32232
32286
  "generating",
32233
32287
  "waiting_approval",
32234
32288
  "starting"
32235
32289
  ]);
32236
32290
  var DEFAULT_CHAT_TAIL_RECENT_MESSAGE_GRACE_MS = 8e3;
32291
+ var LIVE_RUNTIME_LIFECYCLES = /* @__PURE__ */ new Set(["starting", "running", "stopping", "interrupted"]);
32237
32292
  function parseMessageTimestamp(value) {
32238
32293
  if (typeof value === "number" && Number.isFinite(value)) return value;
32239
32294
  if (typeof value === "string") {
@@ -32242,6 +32297,23 @@ ${data.message || ""}`.trim();
32242
32297
  }
32243
32298
  return 0;
32244
32299
  }
32300
+ function isDefinitelyNonLiveRuntimeSession(session) {
32301
+ const surfaceKind = String(session?.runtimeSurfaceKind || "").trim();
32302
+ if (surfaceKind === "live_runtime") return false;
32303
+ if (surfaceKind === "recovery_snapshot") return true;
32304
+ if (surfaceKind === "inactive_record") return false;
32305
+ const lifecycle = String(session?.runtimeLifecycle || "").trim();
32306
+ if (lifecycle && LIVE_RUNTIME_LIFECYCLES.has(lifecycle)) return false;
32307
+ const inferredSurfaceKind = getSessionHostSurfaceKind({
32308
+ lifecycle: lifecycle || null,
32309
+ meta: {
32310
+ restoredFromStorage: session?.runtimeRestoredFromStorage === true,
32311
+ ...session?.runtimeRecoveryState ? { runtimeRecoveryState: session.runtimeRecoveryState } : {}
32312
+ }
32313
+ });
32314
+ if (inferredSurfaceKind === "recovery_snapshot") return true;
32315
+ return false;
32316
+ }
32245
32317
  function classifyHotChatSessionsForSubscriptionFlush2(sessions, previousHotSessionIds, options = {}) {
32246
32318
  const now = options.now ?? Date.now();
32247
32319
  const recentMessageGraceMs = Math.max(
@@ -32250,9 +32322,14 @@ ${data.message || ""}`.trim();
32250
32322
  );
32251
32323
  const activeStatuses = options.activeStatuses ?? DEFAULT_ACTIVE_CHAT_POLL_STATUSES;
32252
32324
  const active = /* @__PURE__ */ new Set();
32325
+ const excluded = /* @__PURE__ */ new Set();
32253
32326
  for (const session of sessions) {
32254
32327
  const sessionId = typeof session?.id === "string" ? session.id : "";
32255
32328
  if (!sessionId) continue;
32329
+ if (isDefinitelyNonLiveRuntimeSession(session)) {
32330
+ excluded.add(sessionId);
32331
+ continue;
32332
+ }
32256
32333
  const status = String(session?.status || "").toLowerCase();
32257
32334
  const lastMessageAt = parseMessageTimestamp(session?.lastMessageAt);
32258
32335
  const recentlyUpdated = lastMessageAt > 0 && now - lastMessageAt <= recentMessageGraceMs;
@@ -32261,13 +32338,39 @@ ${data.message || ""}`.trim();
32261
32338
  }
32262
32339
  }
32263
32340
  const finalizing = new Set(
32264
- Array.from(previousHotSessionIds).filter((sessionId) => !active.has(sessionId))
32341
+ Array.from(previousHotSessionIds).filter((sessionId) => !active.has(sessionId) && !excluded.has(sessionId))
32265
32342
  );
32266
32343
  return { active, finalizing };
32267
32344
  }
32268
32345
  var import_ws2 = __toESM2(require("ws"));
32269
32346
  var http = __toESM2(require("http"));
32270
32347
  init_logger();
32348
+ function normalizeTitle(value) {
32349
+ return String(value || "").trim().replace(/\s+/g, " ").toLowerCase();
32350
+ }
32351
+ function titlesMatch(lhs, rhs) {
32352
+ const a = normalizeTitle(lhs);
32353
+ const b2 = normalizeTitle(rhs);
32354
+ if (!a || !b2) return false;
32355
+ return a === b2 || a.includes(b2) || b2.includes(a);
32356
+ }
32357
+ function resolveCdpPageTarget(params) {
32358
+ const { pages, pinnedTargetId, previousPageTitle } = params;
32359
+ if (pages.length === 0) return { target: null, retargeted: false };
32360
+ if (!pinnedTargetId) {
32361
+ return { target: pages[0] || null, retargeted: false };
32362
+ }
32363
+ const exact = pages.find((page) => page.id === pinnedTargetId);
32364
+ if (exact) return { target: exact, retargeted: false };
32365
+ const titleMatchesList = pages.filter((page) => titlesMatch(page.title, previousPageTitle));
32366
+ if (titleMatchesList.length === 1) {
32367
+ return { target: titleMatchesList[0], retargeted: true };
32368
+ }
32369
+ if (pages.length === 1) {
32370
+ return { target: pages[0], retargeted: true };
32371
+ }
32372
+ return { target: null, retargeted: false };
32373
+ }
32271
32374
  var DaemonCdpManager = class {
32272
32375
  ws = null;
32273
32376
  browserWs = null;
@@ -32428,18 +32531,28 @@ ${data.message || ""}`.trim();
32428
32531
  resolve11(targets.find((t) => t.webSocketDebuggerUrl) || null);
32429
32532
  return;
32430
32533
  }
32431
- const mainPages = pages.filter((t) => !this.isNonMainTitle(t.title || ""));
32432
- const list = mainPages.length > 0 ? mainPages : pages;
32534
+ const titleFilteredPages = pages.filter((t) => !this.isNonMainTitle(t.title || ""));
32535
+ const mainPages = titleFilteredPages.filter((t) => this.isMainPageUrl(t.url));
32536
+ const list = mainPages.length > 0 ? mainPages : titleFilteredPages.length > 0 ? titleFilteredPages : pages;
32433
32537
  this.log(`[CDP] pages(${list.length}): ${list.map((t) => `"${t.title}"`).join(", ")}`);
32434
- if (this._targetId) {
32435
- const specific = list.find((t) => t.id === this._targetId);
32436
- if (specific) {
32437
- this._pageTitle = specific.title || "";
32438
- resolve11(specific);
32439
- } else {
32440
- this.log(`[CDP] Target ${this._targetId} not found in page list`);
32441
- resolve11(null);
32538
+ const previousTargetId = this._targetId;
32539
+ const selected = resolveCdpPageTarget({
32540
+ pages: list,
32541
+ pinnedTargetId: previousTargetId,
32542
+ previousPageTitle: this._pageTitle
32543
+ });
32544
+ if (selected.target) {
32545
+ if (selected.retargeted && previousTargetId && previousTargetId !== selected.target.id) {
32546
+ this.log(`[CDP] Target ${previousTargetId} rekeyed to ${selected.target.id}`);
32547
+ this._targetId = selected.target.id;
32442
32548
  }
32549
+ this._pageTitle = selected.target.title || "";
32550
+ resolve11(selected.target);
32551
+ return;
32552
+ }
32553
+ if (previousTargetId) {
32554
+ this.log(`[CDP] Target ${previousTargetId} not found in page list`);
32555
+ resolve11(null);
32443
32556
  return;
32444
32557
  }
32445
32558
  this._pageTitle = list[0]?.title || "";
@@ -33867,7 +33980,17 @@ ${cleanBody}`;
33867
33980
  init_chat_message_normalization();
33868
33981
  var HISTORY_DIR = path7.join(os52.homedir(), ".adhdev", "history");
33869
33982
  var RETAIN_DAYS = 30;
33983
+ var SAVED_HISTORY_INDEX_VERSION = 1;
33984
+ var SAVED_HISTORY_INDEX_FILE = ".saved-history-index.json";
33985
+ var SAVED_HISTORY_INDEX_LOCK_SUFFIX = ".lock";
33986
+ var SAVED_HISTORY_INDEX_LOCK_WAIT_MS = 1500;
33987
+ var SAVED_HISTORY_INDEX_LOCK_STALE_MS = 15e3;
33988
+ var SAVED_HISTORY_INDEX_LOCK_POLL_MS = 25;
33989
+ var SAVED_HISTORY_ROLLUP_THRESHOLD_BYTES = 16 * 1024 * 1024;
33870
33990
  var savedHistorySessionCache = /* @__PURE__ */ new Map();
33991
+ var savedHistoryFileSummaryCache = /* @__PURE__ */ new Map();
33992
+ var savedHistoryBackgroundRefresh = /* @__PURE__ */ new Set();
33993
+ var savedHistoryRollupInFlight = /* @__PURE__ */ new Set();
33871
33994
  var CODEX_STARTER_PROMPT_RE = /^(?:[›❯]\s*)?(?:Find and fix a bug in @filename|Improve documentation in @filename|Write tests for @filename|Explain this codebase|Summarize recent commits|Implement \{feature\}|Use \/skills(?: to list available skills)?|Run \/review on my current changes)$/i;
33872
33995
  function normalizeHistoryComparable(text) {
33873
33996
  return String(text || "").replace(/\s+/g, " ").trim();
@@ -33925,6 +34048,68 @@ ${cleanBody}`;
33925
34048
  content
33926
34049
  };
33927
34050
  }
34051
+ function sortSavedHistorySessionSummaries(summaries) {
34052
+ return summaries.slice().sort((a, b2) => b2.lastMessageAt - a.lastMessageAt);
34053
+ }
34054
+ function buildSavedHistorySessionSummaryMapFromEntries(entries) {
34055
+ const summaries = /* @__PURE__ */ new Map();
34056
+ for (const entry of Array.from(entries.values())) {
34057
+ const fileSummary = entry.summary;
34058
+ if (!fileSummary || fileSummary.messageCount <= 0 || !fileSummary.lastMessageAt) continue;
34059
+ const existing = summaries.get(fileSummary.historySessionId);
34060
+ if (!existing) {
34061
+ summaries.set(fileSummary.historySessionId, {
34062
+ historySessionId: fileSummary.historySessionId,
34063
+ sessionTitle: fileSummary.sessionTitle,
34064
+ messageCount: fileSummary.messageCount,
34065
+ firstMessageAt: fileSummary.firstMessageAt,
34066
+ lastMessageAt: fileSummary.lastMessageAt,
34067
+ preview: fileSummary.preview,
34068
+ workspace: fileSummary.workspace
34069
+ });
34070
+ continue;
34071
+ }
34072
+ existing.messageCount += fileSummary.messageCount;
34073
+ if (!existing.firstMessageAt || fileSummary.firstMessageAt < existing.firstMessageAt) {
34074
+ existing.firstMessageAt = fileSummary.firstMessageAt;
34075
+ }
34076
+ if (fileSummary.lastMessageAt >= existing.lastMessageAt) {
34077
+ existing.lastMessageAt = fileSummary.lastMessageAt;
34078
+ if (fileSummary.sessionTitle) existing.sessionTitle = fileSummary.sessionTitle;
34079
+ if (fileSummary.preview) existing.preview = fileSummary.preview;
34080
+ }
34081
+ if (!existing.workspace && fileSummary.workspace) {
34082
+ existing.workspace = fileSummary.workspace;
34083
+ }
34084
+ }
34085
+ return Object.fromEntries(sortSavedHistorySessionSummaries(Array.from(summaries.values())).map((summary) => [summary.historySessionId, summary]));
34086
+ }
34087
+ function readPersistedSavedHistorySessionSummaries(dir) {
34088
+ try {
34089
+ const filePath = getSavedHistoryIndexFilePath(dir);
34090
+ if (!fs32.existsSync(filePath)) return null;
34091
+ const raw = JSON.parse(fs32.readFileSync(filePath, "utf-8"));
34092
+ if (!raw || raw.version !== SAVED_HISTORY_INDEX_VERSION || !raw.sessions || typeof raw.sessions !== "object") {
34093
+ return null;
34094
+ }
34095
+ return sortSavedHistorySessionSummaries(
34096
+ Object.values(raw.sessions).filter((summary) => !!summary && typeof summary.historySessionId === "string" && summary.messageCount > 0 && summary.lastMessageAt > 0).map((summary) => ({
34097
+ historySessionId: summary.historySessionId,
34098
+ sessionTitle: summary.sessionTitle,
34099
+ messageCount: summary.messageCount,
34100
+ firstMessageAt: summary.firstMessageAt,
34101
+ lastMessageAt: summary.lastMessageAt,
34102
+ preview: summary.preview,
34103
+ workspace: summary.workspace
34104
+ }))
34105
+ );
34106
+ } catch {
34107
+ return null;
34108
+ }
34109
+ }
34110
+ function shouldScheduleSavedHistoryRollup(totalBytes) {
34111
+ return Number.isFinite(totalBytes) && totalBytes >= SAVED_HISTORY_ROLLUP_THRESHOLD_BYTES;
34112
+ }
33928
34113
  function sanitizeHistoryFileSegment(value) {
33929
34114
  return String(value || "").replace(/[^a-zA-Z0-9_-]/g, "_");
33930
34115
  }
@@ -33938,71 +34123,386 @@ ${cleanBody}`;
33938
34123
  return true;
33939
34124
  }).sort().reverse();
33940
34125
  }
33941
- function buildSavedHistoryCacheSignature(dir, files) {
33942
- return files.map((file2) => {
34126
+ function normalizeSavedHistorySessionId(agentType, historySessionId) {
34127
+ const normalizedId = String(historySessionId || "").trim();
34128
+ if (!normalizedId) return "";
34129
+ const strictProviderId = normalizeProviderSessionId(agentType, normalizedId);
34130
+ if (strictProviderId) return strictProviderId;
34131
+ return agentType === "hermes-cli" ? "" : normalizedId;
34132
+ }
34133
+ function extractSavedHistorySessionIdFromFile(agentType, file2) {
34134
+ const match = file2.match(/^([A-Za-z0-9_-]+)_\d{4}-\d{2}-\d{2}\.jsonl$/);
34135
+ return normalizeSavedHistorySessionId(agentType, match?.[1] || "");
34136
+ }
34137
+ function buildSavedHistoryFileSignatureMap(dir, files) {
34138
+ return new Map(files.map((file2) => {
33943
34139
  try {
33944
34140
  const stat4 = fs32.statSync(path7.join(dir, file2));
33945
- return `${file2}:${stat4.size}:${Math.trunc(stat4.mtimeMs)}`;
34141
+ return [file2, `${file2}:${stat4.size}:${Math.trunc(stat4.mtimeMs)}`];
33946
34142
  } catch {
33947
- return `${file2}:missing`;
33948
- }
33949
- }).join("|");
33950
- }
33951
- function computeSavedHistorySessionSummaries(agentType, dir, files) {
33952
- const groupedFiles = /* @__PURE__ */ new Map();
33953
- const filePattern = /^([A-Za-z0-9_-]+)_\d{4}-\d{2}-\d{2}\.jsonl$/;
33954
- for (const file2 of files) {
33955
- const match = file2.match(filePattern);
33956
- if (!match?.[1]) continue;
33957
- const historySessionId = match[1];
33958
- const grouped = groupedFiles.get(historySessionId) || [];
33959
- grouped.push(file2);
33960
- groupedFiles.set(historySessionId, grouped);
33961
- }
33962
- const summaries = [];
33963
- for (const [historySessionId, grouped] of groupedFiles.entries()) {
33964
- let messageCount = 0;
33965
- let firstMessageAt = 0;
33966
- let lastMessageAt = 0;
33967
- let sessionTitle = "";
33968
- let preview = "";
33969
- let workspace = "";
33970
- for (const file2 of grouped.sort()) {
33971
- const filePath = path7.join(dir, file2);
33972
- const content = fs32.readFileSync(filePath, "utf-8");
33973
- const lines = content.split("\n").filter(Boolean);
33974
- for (const line of lines) {
33975
- let parsed = null;
34143
+ return [file2, `${file2}:missing`];
34144
+ }
34145
+ }));
34146
+ }
34147
+ function buildSavedHistoryCacheSignature(files, fileSignatures) {
34148
+ return files.map((file2) => fileSignatures.get(file2) || `${file2}:missing`).join("|");
34149
+ }
34150
+ function getSavedHistoryIndexFilePath(dir) {
34151
+ return path7.join(dir, SAVED_HISTORY_INDEX_FILE);
34152
+ }
34153
+ function getSavedHistoryIndexLockPath(dir) {
34154
+ return `${getSavedHistoryIndexFilePath(dir)}${SAVED_HISTORY_INDEX_LOCK_SUFFIX}`;
34155
+ }
34156
+ function sleepBlocking(ms2) {
34157
+ if (ms2 <= 0) return;
34158
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms2);
34159
+ }
34160
+ function loadPersistedSavedHistoryIndexFromFile(dir) {
34161
+ try {
34162
+ const filePath = getSavedHistoryIndexFilePath(dir);
34163
+ if (!fs32.existsSync(filePath)) return /* @__PURE__ */ new Map();
34164
+ const raw = JSON.parse(fs32.readFileSync(filePath, "utf-8"));
34165
+ if (!raw || raw.version !== SAVED_HISTORY_INDEX_VERSION || !raw.files || typeof raw.files !== "object") {
34166
+ return /* @__PURE__ */ new Map();
34167
+ }
34168
+ return new Map(
34169
+ Object.entries(raw.files).filter(([file2, entry]) => !!file2 && !!entry && typeof entry.signature === "string").map(([file2, entry]) => [file2, {
34170
+ signature: entry.signature,
34171
+ summary: entry.summary || null
34172
+ }])
34173
+ );
34174
+ } catch {
34175
+ return /* @__PURE__ */ new Map();
34176
+ }
34177
+ }
34178
+ function writePersistedSavedHistoryIndexFile(dir, entries) {
34179
+ const filePath = getSavedHistoryIndexFilePath(dir);
34180
+ const tempPath = `${filePath}.tmp`;
34181
+ const payload = {
34182
+ version: SAVED_HISTORY_INDEX_VERSION,
34183
+ files: Object.fromEntries(entries.entries()),
34184
+ sessions: buildSavedHistorySessionSummaryMapFromEntries(entries)
34185
+ };
34186
+ fs32.writeFileSync(tempPath, JSON.stringify(payload), "utf-8");
34187
+ fs32.renameSync(tempPath, filePath);
34188
+ }
34189
+ function acquireSavedHistoryIndexLock(dir) {
34190
+ const lockPath = getSavedHistoryIndexLockPath(dir);
34191
+ const deadline = Date.now() + SAVED_HISTORY_INDEX_LOCK_WAIT_MS;
34192
+ while (Date.now() <= deadline) {
34193
+ try {
34194
+ fs32.mkdirSync(lockPath);
34195
+ return () => {
33976
34196
  try {
33977
- parsed = JSON.parse(line);
34197
+ fs32.rmSync(lockPath, { recursive: true, force: true });
33978
34198
  } catch {
33979
- parsed = null;
33980
34199
  }
33981
- if (!parsed || parsed.historySessionId !== historySessionId) continue;
33982
- if (parsed.kind === "session_start") {
33983
- if (!workspace && parsed.workspace) workspace = parsed.workspace;
34200
+ };
34201
+ } catch (error48) {
34202
+ if (error48?.code !== "EEXIST") return null;
34203
+ try {
34204
+ const stat4 = fs32.statSync(lockPath);
34205
+ if (Date.now() - stat4.mtimeMs > SAVED_HISTORY_INDEX_LOCK_STALE_MS) {
34206
+ fs32.rmSync(lockPath, { recursive: true, force: true });
33984
34207
  continue;
33985
34208
  }
33986
- messageCount += 1;
33987
- if (!firstMessageAt || parsed.receivedAt < firstMessageAt) firstMessageAt = parsed.receivedAt;
33988
- if (!lastMessageAt || parsed.receivedAt > lastMessageAt) lastMessageAt = parsed.receivedAt;
33989
- if (parsed.sessionTitle) sessionTitle = parsed.sessionTitle;
33990
- if (parsed.role !== "system" && parsed.content.trim()) preview = parsed.content.trim();
33991
- }
33992
- }
33993
- if (messageCount === 0 || !lastMessageAt) continue;
33994
- summaries.push({
33995
- historySessionId,
33996
- sessionTitle: sessionTitle || void 0,
33997
- messageCount,
33998
- firstMessageAt,
33999
- lastMessageAt,
34000
- preview: preview || void 0,
34001
- workspace: workspace || void 0
34002
- });
34209
+ } catch {
34210
+ continue;
34211
+ }
34212
+ sleepBlocking(SAVED_HISTORY_INDEX_LOCK_POLL_MS);
34213
+ }
34214
+ }
34215
+ return null;
34216
+ }
34217
+ function withLockedPersistedSavedHistoryIndex(dir, callback) {
34218
+ const release2 = acquireSavedHistoryIndexLock(dir);
34219
+ if (!release2) return null;
34220
+ try {
34221
+ const entries = loadPersistedSavedHistoryIndexFromFile(dir);
34222
+ const result = callback(entries);
34223
+ writePersistedSavedHistoryIndexFile(dir, entries);
34224
+ return result;
34225
+ } catch {
34226
+ return null;
34227
+ } finally {
34228
+ release2();
34229
+ }
34230
+ }
34231
+ function loadPersistedSavedHistoryIndex(dir) {
34232
+ return loadPersistedSavedHistoryIndexFromFile(dir);
34233
+ }
34234
+ function savePersistedSavedHistoryIndex(dir, entries) {
34235
+ withLockedPersistedSavedHistoryIndex(dir, (currentEntries) => {
34236
+ const incomingFiles = new Set(Array.from(entries.keys()));
34237
+ for (const [file2, entry] of Array.from(entries.entries())) {
34238
+ const liveSignature = buildSavedHistoryFileSignature(dir, file2);
34239
+ const existingEntry = currentEntries.get(file2);
34240
+ if (existingEntry && existingEntry.signature !== liveSignature && entry.signature !== liveSignature) {
34241
+ continue;
34242
+ }
34243
+ if (entry.signature !== liveSignature && (!existingEntry || existingEntry.signature !== liveSignature)) {
34244
+ continue;
34245
+ }
34246
+ currentEntries.set(file2, entry.signature === liveSignature ? entry : {
34247
+ signature: liveSignature,
34248
+ summary: existingEntry?.summary || entry.summary
34249
+ });
34250
+ }
34251
+ for (const file2 of Array.from(currentEntries.keys())) {
34252
+ if (incomingFiles.has(file2)) continue;
34253
+ if (!fs32.existsSync(path7.join(dir, file2))) {
34254
+ currentEntries.delete(file2);
34255
+ }
34256
+ }
34257
+ });
34258
+ }
34259
+ function invalidatePersistedSavedHistoryIndex(agentType, dir) {
34260
+ try {
34261
+ fs32.rmSync(getSavedHistoryIndexFilePath(dir), { force: true });
34262
+ } catch {
34263
+ }
34264
+ savedHistorySessionCache.delete(agentType.replace(/[^a-zA-Z0-9_-]/g, "_"));
34265
+ }
34266
+ function buildSavedHistoryIndexFileSignature(dir) {
34267
+ try {
34268
+ const stat4 = fs32.statSync(getSavedHistoryIndexFilePath(dir));
34269
+ return `index:${stat4.size}:${Math.trunc(stat4.mtimeMs)}`;
34270
+ } catch {
34271
+ return "index:missing";
34003
34272
  }
34004
- summaries.sort((a, b2) => b2.lastMessageAt - a.lastMessageAt);
34005
- return summaries;
34273
+ }
34274
+ function historyDirectoryHasFilesNewerThanIndex(dir) {
34275
+ try {
34276
+ const indexStat = fs32.statSync(getSavedHistoryIndexFilePath(dir));
34277
+ const files = listHistoryFiles(dir);
34278
+ for (const file2 of files) {
34279
+ const stat4 = fs32.statSync(path7.join(dir, file2));
34280
+ if (stat4.mtimeMs > indexStat.mtimeMs) return true;
34281
+ }
34282
+ return false;
34283
+ } catch {
34284
+ return true;
34285
+ }
34286
+ }
34287
+ function buildSavedHistoryFileSignature(dir, file2) {
34288
+ try {
34289
+ const stat4 = fs32.statSync(path7.join(dir, file2));
34290
+ return `${file2}:${stat4.size}:${Math.trunc(stat4.mtimeMs)}`;
34291
+ } catch {
34292
+ return `${file2}:missing`;
34293
+ }
34294
+ }
34295
+ function persistSavedHistoryFileSummaryEntry(agentType, dir, file2, updater) {
34296
+ const filePath = path7.join(dir, file2);
34297
+ const result = withLockedPersistedSavedHistoryIndex(dir, (entries) => {
34298
+ const currentEntry = entries.get(file2) || null;
34299
+ const nextSummary = updater(currentEntry?.summary || null);
34300
+ const nextEntry = {
34301
+ signature: buildSavedHistoryFileSignature(dir, file2),
34302
+ summary: nextSummary
34303
+ };
34304
+ entries.set(file2, nextEntry);
34305
+ savedHistoryFileSummaryCache.set(filePath, nextEntry);
34306
+ return nextEntry;
34307
+ });
34308
+ if (!result) return;
34309
+ if (result.summary?.historySessionId && shouldScheduleSavedHistoryRollupForSignature(result.signature)) {
34310
+ scheduleSavedHistoryRollup(agentType, result.summary.historySessionId);
34311
+ }
34312
+ }
34313
+ function updateSavedHistoryIndexForSessionStart(agentType, dir, file2, historySessionId, workspace) {
34314
+ const normalizedSessionId = normalizeSavedHistorySessionId(agentType, historySessionId);
34315
+ const normalizedWorkspace = String(workspace || "").trim();
34316
+ if (!normalizedSessionId || !normalizedWorkspace) return;
34317
+ persistSavedHistoryFileSummaryEntry(agentType, dir, file2, (currentSummary) => ({
34318
+ file: file2,
34319
+ historySessionId: normalizedSessionId,
34320
+ messageCount: currentSummary?.messageCount || 0,
34321
+ firstMessageAt: currentSummary?.firstMessageAt || 0,
34322
+ lastMessageAt: currentSummary?.lastMessageAt || 0,
34323
+ sessionTitle: currentSummary?.sessionTitle,
34324
+ preview: currentSummary?.preview,
34325
+ workspace: normalizedWorkspace
34326
+ }));
34327
+ }
34328
+ function updateSavedHistoryIndexForAppendedMessages(agentType, dir, file2, historySessionId, messages) {
34329
+ const normalizedSessionId = normalizeSavedHistorySessionId(agentType, historySessionId || "");
34330
+ if (!normalizedSessionId || messages.length === 0) return;
34331
+ persistSavedHistoryFileSummaryEntry(agentType, dir, file2, (currentSummary) => {
34332
+ const nextSummary = {
34333
+ file: file2,
34334
+ historySessionId: normalizedSessionId,
34335
+ messageCount: currentSummary?.messageCount || 0,
34336
+ firstMessageAt: currentSummary?.firstMessageAt || 0,
34337
+ lastMessageAt: currentSummary?.lastMessageAt || 0,
34338
+ sessionTitle: currentSummary?.sessionTitle,
34339
+ preview: currentSummary?.preview,
34340
+ workspace: currentSummary?.workspace
34341
+ };
34342
+ for (const message of messages) {
34343
+ if (!message || message.historySessionId !== historySessionId) continue;
34344
+ if (message.kind === "session_start") {
34345
+ if (message.workspace) nextSummary.workspace = message.workspace;
34346
+ continue;
34347
+ }
34348
+ nextSummary.messageCount += 1;
34349
+ if (!nextSummary.firstMessageAt || message.receivedAt < nextSummary.firstMessageAt) {
34350
+ nextSummary.firstMessageAt = message.receivedAt;
34351
+ }
34352
+ if (!nextSummary.lastMessageAt || message.receivedAt >= nextSummary.lastMessageAt) {
34353
+ nextSummary.lastMessageAt = message.receivedAt;
34354
+ if (message.sessionTitle) nextSummary.sessionTitle = message.sessionTitle;
34355
+ if (message.role !== "system" && message.content.trim()) nextSummary.preview = message.content.trim();
34356
+ } else if (message.sessionTitle) {
34357
+ nextSummary.sessionTitle = message.sessionTitle;
34358
+ }
34359
+ if (!nextSummary.preview && message.role !== "system" && message.content.trim()) {
34360
+ nextSummary.preview = message.content.trim();
34361
+ }
34362
+ }
34363
+ return nextSummary;
34364
+ });
34365
+ }
34366
+ function computeSavedHistoryFileSummary(agentType, dir, file2) {
34367
+ const historySessionId = extractSavedHistorySessionIdFromFile(agentType, file2);
34368
+ if (!historySessionId) return null;
34369
+ const filePath = path7.join(dir, file2);
34370
+ const content = fs32.readFileSync(filePath, "utf-8");
34371
+ const lines = content.split("\n").filter(Boolean);
34372
+ let messageCount = 0;
34373
+ let firstMessageAt = 0;
34374
+ let lastMessageAt = 0;
34375
+ let sessionTitle = "";
34376
+ let preview = "";
34377
+ let workspace = "";
34378
+ for (const line of lines) {
34379
+ let parsed = null;
34380
+ try {
34381
+ parsed = JSON.parse(line);
34382
+ } catch {
34383
+ parsed = null;
34384
+ }
34385
+ if (!parsed || parsed.historySessionId !== historySessionId) continue;
34386
+ if (parsed.kind === "session_start") {
34387
+ if (!workspace && parsed.workspace) workspace = parsed.workspace;
34388
+ continue;
34389
+ }
34390
+ messageCount += 1;
34391
+ if (!firstMessageAt || parsed.receivedAt < firstMessageAt) firstMessageAt = parsed.receivedAt;
34392
+ if (!lastMessageAt || parsed.receivedAt > lastMessageAt) lastMessageAt = parsed.receivedAt;
34393
+ if (parsed.sessionTitle) sessionTitle = parsed.sessionTitle;
34394
+ if (parsed.role !== "system" && parsed.content.trim()) preview = parsed.content.trim();
34395
+ }
34396
+ if (messageCount === 0 || !lastMessageAt) return null;
34397
+ return {
34398
+ file: file2,
34399
+ historySessionId,
34400
+ messageCount,
34401
+ firstMessageAt,
34402
+ lastMessageAt,
34403
+ sessionTitle: sessionTitle || void 0,
34404
+ preview: preview || void 0,
34405
+ workspace: workspace || void 0
34406
+ };
34407
+ }
34408
+ function shouldScheduleSavedHistoryRollupForSignature(signature) {
34409
+ const parts = String(signature || "").split(":");
34410
+ const size = Number(parts[1] || 0);
34411
+ return shouldScheduleSavedHistoryRollup(size);
34412
+ }
34413
+ function scheduleSavedHistoryRollup(agentType, historySessionId) {
34414
+ const key = `${agentType}:${historySessionId}`;
34415
+ if (!historySessionId || savedHistoryRollupInFlight.has(key)) return;
34416
+ savedHistoryRollupInFlight.add(key);
34417
+ setTimeout(() => {
34418
+ try {
34419
+ new ChatHistoryWriter().compactHistorySession(agentType, historySessionId);
34420
+ } finally {
34421
+ savedHistoryRollupInFlight.delete(key);
34422
+ }
34423
+ }, 0);
34424
+ }
34425
+ function scheduleSavedHistoryBackgroundRefresh(agentType, dir) {
34426
+ const key = `${agentType}:${dir}`;
34427
+ if (savedHistoryBackgroundRefresh.has(key)) return;
34428
+ savedHistoryBackgroundRefresh.add(key);
34429
+ setTimeout(() => {
34430
+ try {
34431
+ if (!fs32.existsSync(dir)) return;
34432
+ const files = listHistoryFiles(dir);
34433
+ const fileSignatures = buildSavedHistoryFileSignatureMap(dir, files);
34434
+ const persistedEntries = loadPersistedSavedHistoryIndex(dir);
34435
+ const computed = computeSavedHistorySessionSummaries(agentType, dir, files, fileSignatures, persistedEntries);
34436
+ savePersistedSavedHistoryIndex(dir, computed.persistedEntries || /* @__PURE__ */ new Map());
34437
+ const refreshedIndexSignature = buildSavedHistoryIndexFileSignature(dir);
34438
+ savedHistorySessionCache.set(agentType.replace(/[^a-zA-Z0-9_-]/g, "_"), {
34439
+ signature: refreshedIndexSignature,
34440
+ summaries: computed.summaries || []
34441
+ });
34442
+ for (const [file2, entry] of Array.from(computed.persistedEntries.entries())) {
34443
+ if (!entry?.summary || !shouldScheduleSavedHistoryRollupForSignature(entry.signature)) continue;
34444
+ scheduleSavedHistoryRollup(agentType, entry.summary.historySessionId);
34445
+ }
34446
+ } catch {
34447
+ } finally {
34448
+ savedHistoryBackgroundRefresh.delete(key);
34449
+ }
34450
+ }, 0);
34451
+ }
34452
+ function computeSavedHistorySessionSummaries(agentType, dir, files, fileSignatures, persistedEntries) {
34453
+ const summaryBySessionId = /* @__PURE__ */ new Map();
34454
+ const nextPersistedEntries = /* @__PURE__ */ new Map();
34455
+ for (const file2 of files.slice().sort()) {
34456
+ const filePath = path7.join(dir, file2);
34457
+ const signature = fileSignatures.get(file2) || `${file2}:missing`;
34458
+ const cached2 = savedHistoryFileSummaryCache.get(filePath);
34459
+ const persisted = persistedEntries.get(file2);
34460
+ const reusableEntry = cached2?.signature === signature ? cached2 : persisted?.signature === signature ? persisted : null;
34461
+ const fileSummary = reusableEntry?.summary || computeSavedHistoryFileSummary(agentType, dir, file2);
34462
+ const nextEntry = reusableEntry || {
34463
+ signature,
34464
+ summary: fileSummary
34465
+ };
34466
+ if (!reusableEntry) {
34467
+ nextEntry.signature = signature;
34468
+ nextEntry.summary = fileSummary;
34469
+ }
34470
+ savedHistoryFileSummaryCache.set(filePath, nextEntry);
34471
+ nextPersistedEntries.set(file2, nextEntry);
34472
+ if (!fileSummary) continue;
34473
+ const existing = summaryBySessionId.get(fileSummary.historySessionId);
34474
+ if (fileSummary.messageCount <= 0 || !fileSummary.lastMessageAt) {
34475
+ continue;
34476
+ }
34477
+ if (!existing) {
34478
+ summaryBySessionId.set(fileSummary.historySessionId, {
34479
+ historySessionId: fileSummary.historySessionId,
34480
+ sessionTitle: fileSummary.sessionTitle,
34481
+ messageCount: fileSummary.messageCount,
34482
+ firstMessageAt: fileSummary.firstMessageAt,
34483
+ lastMessageAt: fileSummary.lastMessageAt,
34484
+ preview: fileSummary.preview,
34485
+ workspace: fileSummary.workspace
34486
+ });
34487
+ continue;
34488
+ }
34489
+ existing.messageCount += fileSummary.messageCount;
34490
+ if (!existing.firstMessageAt || fileSummary.firstMessageAt < existing.firstMessageAt) {
34491
+ existing.firstMessageAt = fileSummary.firstMessageAt;
34492
+ }
34493
+ if (fileSummary.lastMessageAt >= existing.lastMessageAt) {
34494
+ existing.lastMessageAt = fileSummary.lastMessageAt;
34495
+ if (fileSummary.sessionTitle) existing.sessionTitle = fileSummary.sessionTitle;
34496
+ if (fileSummary.preview) existing.preview = fileSummary.preview;
34497
+ }
34498
+ if (!existing.workspace && fileSummary.workspace) {
34499
+ existing.workspace = fileSummary.workspace;
34500
+ }
34501
+ }
34502
+ return {
34503
+ summaries: Array.from(summaryBySessionId.values()).sort((a, b2) => b2.lastMessageAt - a.lastMessageAt),
34504
+ persistedEntries: nextPersistedEntries
34505
+ };
34006
34506
  }
34007
34507
  var ChatHistoryWriter = class {
34008
34508
  /** Last seen message count per agent (deduplication) */
@@ -34077,9 +34577,11 @@ ${cleanBody}`;
34077
34577
  fs32.mkdirSync(dir, { recursive: true });
34078
34578
  const date5 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
34079
34579
  const filePrefix = effectiveHistoryKey ? `${this.sanitize(effectiveHistoryKey)}_` : "";
34080
- const filePath = path7.join(dir, `${filePrefix}${date5}.jsonl`);
34580
+ const fileName = `${filePrefix}${date5}.jsonl`;
34581
+ const filePath = path7.join(dir, fileName);
34081
34582
  const lines = newMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
34082
34583
  fs32.appendFileSync(filePath, lines, "utf-8");
34584
+ updateSavedHistoryIndexForAppendedMessages(agentType, dir, fileName, effectiveHistoryKey, newMessages);
34083
34585
  const prevCount = this.lastSeenCounts.get(dedupKey) || 0;
34084
34586
  if (!historySessionId && messages.length < prevCount * 0.5 && prevCount > 3) {
34085
34587
  seenHashes.clear();
@@ -34170,7 +34672,8 @@ ${cleanBody}`;
34170
34672
  const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
34171
34673
  fs32.mkdirSync(dir, { recursive: true });
34172
34674
  const date5 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
34173
- const filePath = path7.join(dir, `${this.sanitize(id)}_${date5}.jsonl`);
34675
+ const fileName = `${this.sanitize(id)}_${date5}.jsonl`;
34676
+ const filePath = path7.join(dir, fileName);
34174
34677
  const record2 = {
34175
34678
  ts: (/* @__PURE__ */ new Date()).toISOString(),
34176
34679
  receivedAt: Date.now(),
@@ -34183,6 +34686,7 @@ ${cleanBody}`;
34183
34686
  workspace: ws2
34184
34687
  };
34185
34688
  fs32.appendFileSync(filePath, JSON.stringify(record2) + "\n", "utf-8");
34689
+ updateSavedHistoryIndexForSessionStart(agentType, dir, fileName, id, ws2);
34186
34690
  } catch {
34187
34691
  }
34188
34692
  }
@@ -34248,6 +34752,7 @@ ${cleanBody}`;
34248
34752
  }
34249
34753
  fs32.unlinkSync(sourcePath);
34250
34754
  }
34755
+ invalidatePersistedSavedHistoryIndex(agentType, dir);
34251
34756
  } catch {
34252
34757
  }
34253
34758
  }
@@ -34297,6 +34802,7 @@ ${cleanBody}`;
34297
34802
  fs32.writeFileSync(filePath, `${collapsed.map((entry) => JSON.stringify(entry)).join("\n")}
34298
34803
  `, "utf-8");
34299
34804
  }
34805
+ invalidatePersistedSavedHistoryIndex(agentType, dir);
34300
34806
  } catch {
34301
34807
  }
34302
34808
  }
@@ -34316,13 +34822,18 @@ ${cleanBody}`;
34316
34822
  for (const dir of agentDirs) {
34317
34823
  const dirPath = path7.join(HISTORY_DIR, dir.name);
34318
34824
  const files = fs32.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl") || f.endsWith(".terminal.log"));
34825
+ let removedAny = false;
34319
34826
  for (const file2 of files) {
34320
34827
  const filePath = path7.join(dirPath, file2);
34321
34828
  const stat4 = fs32.statSync(filePath);
34322
34829
  if (stat4.mtimeMs < cutoff) {
34323
34830
  fs32.unlinkSync(filePath);
34831
+ removedAny = true;
34324
34832
  }
34325
34833
  }
34834
+ if (removedAny) {
34835
+ invalidatePersistedSavedHistoryIndex(dir.name, dirPath);
34836
+ }
34326
34837
  }
34327
34838
  } catch {
34328
34839
  }
@@ -34388,18 +34899,51 @@ ${cleanBody}`;
34388
34899
  savedHistorySessionCache.delete(sanitized);
34389
34900
  return { sessions: [], hasMore: false };
34390
34901
  }
34391
- const files = listHistoryFiles(dir);
34392
- const signature = buildSavedHistoryCacheSignature(dir, files);
34393
34902
  const cached2 = savedHistorySessionCache.get(sanitized);
34394
- const summaries = cached2?.signature === signature ? cached2.summaries : computeSavedHistorySessionSummaries(agentType, dir, files);
34395
- if (!cached2 || cached2.signature !== signature) {
34903
+ const offset = Math.max(0, options.offset || 0);
34904
+ const limit = Math.max(1, options.limit || 30);
34905
+ const indexSignature = buildSavedHistoryIndexFileSignature(dir);
34906
+ let cacheWasInvalidated = false;
34907
+ if (cached2) {
34908
+ const cacheLooksPersisted = cached2.signature.startsWith("index:");
34909
+ const cacheStillValid = cacheLooksPersisted ? cached2.signature === indexSignature : (() => {
34910
+ const files2 = listHistoryFiles(dir);
34911
+ const fileSignatures2 = buildSavedHistoryFileSignatureMap(dir, files2);
34912
+ return cached2.signature === buildSavedHistoryCacheSignature(files2, fileSignatures2);
34913
+ })();
34914
+ if (cacheStillValid) {
34915
+ const sliced2 = cached2.summaries.slice(offset, offset + limit);
34916
+ return {
34917
+ sessions: sliced2,
34918
+ hasMore: cached2.summaries.length > offset + limit
34919
+ };
34920
+ }
34921
+ cacheWasInvalidated = true;
34922
+ }
34923
+ const persistedSessions = readPersistedSavedHistorySessionSummaries(dir);
34924
+ if (!cacheWasInvalidated && persistedSessions?.length && !historyDirectoryHasFilesNewerThanIndex(dir)) {
34396
34925
  savedHistorySessionCache.set(sanitized, {
34397
- signature,
34398
- summaries
34926
+ signature: indexSignature,
34927
+ summaries: persistedSessions
34399
34928
  });
34929
+ scheduleSavedHistoryBackgroundRefresh(agentType, dir);
34930
+ const sliced2 = persistedSessions.slice(offset, offset + limit);
34931
+ return {
34932
+ sessions: sliced2,
34933
+ hasMore: persistedSessions.length > offset + limit
34934
+ };
34400
34935
  }
34401
- const offset = Math.max(0, options.offset || 0);
34402
- const limit = Math.max(1, options.limit || 30);
34936
+ const files = listHistoryFiles(dir);
34937
+ const fileSignatures = buildSavedHistoryFileSignatureMap(dir, files);
34938
+ const signature = buildSavedHistoryCacheSignature(files, fileSignatures);
34939
+ const persistedEntries = loadPersistedSavedHistoryIndex(dir);
34940
+ const computed = computeSavedHistorySessionSummaries(agentType, dir, files, fileSignatures, persistedEntries);
34941
+ const summaries = computed.summaries || [];
34942
+ savePersistedSavedHistoryIndex(dir, computed.persistedEntries || /* @__PURE__ */ new Map());
34943
+ savedHistorySessionCache.set(sanitized, {
34944
+ signature,
34945
+ summaries
34946
+ });
34403
34947
  const sliced = summaries.slice(offset, offset + limit);
34404
34948
  return {
34405
34949
  sessions: sliced,
@@ -36013,7 +36557,7 @@ ${effect.notification.body || ""}`.trim();
36013
36557
  return profile !== "live";
36014
36558
  }
36015
36559
  function shouldIncludeRuntimeMetadata(profile) {
36016
- return profile !== "live";
36560
+ return true;
36017
36561
  }
36018
36562
  function findCdpManager(cdpManagers, key) {
36019
36563
  const exact = cdpManagers.get(key);
@@ -36128,6 +36672,21 @@ ${effect.notification.body || ""}`.trim();
36128
36672
  lastUpdated: ext.lastUpdated
36129
36673
  };
36130
36674
  }
36675
+ function shouldIncludeExtensionSession(ext) {
36676
+ const status = String(ext.status || "").trim().toLowerCase();
36677
+ const hasActiveChat = !!ext.activeChat;
36678
+ const hasMessages = Array.isArray(ext.activeChat?.messages) && ext.activeChat.messages.length > 0;
36679
+ const hasModal = !!ext.activeChat?.activeModal;
36680
+ const hasStreams = Array.isArray(ext.agentStreams) && ext.agentStreams.length > 0;
36681
+ const hasProviderSessionId = typeof ext.providerSessionId === "string" && ext.providerSessionId.trim().length > 0;
36682
+ const hasControlValues = !!(ext.controlValues && Object.keys(ext.controlValues).length > 0);
36683
+ const hasProviderControls = Array.isArray(ext.providerControls) && ext.providerControls.length > 0;
36684
+ const hasOpenPanelCapability = Array.isArray(ext.sessionCapabilities) && ext.sessionCapabilities.includes("open_panel");
36685
+ const hasSummaryMetadata = !!ext.summaryMetadata;
36686
+ const hasError = typeof ext.errorMessage === "string" && ext.errorMessage.trim().length > 0;
36687
+ const hasInterestingStatus = !!status && !["idle", "panel_hidden", "disconnected", "not_monitored"].includes(status);
36688
+ return hasActiveChat || hasMessages || hasModal || hasStreams || hasProviderSessionId || hasControlValues || hasProviderControls || hasOpenPanelCapability || hasSummaryMetadata || hasError || hasInterestingStatus;
36689
+ }
36131
36690
  function buildCliSession(state, options) {
36132
36691
  const profile = options.profile || "full";
36133
36692
  const activeChat = normalizeActiveChatData(state.activeChat, getActiveChatOptions(profile));
@@ -36153,8 +36712,12 @@ ${effect.notification.body || ""}`.trim();
36153
36712
  runtimeKey: state.runtime?.runtimeKey,
36154
36713
  runtimeDisplayName: state.runtime?.displayName,
36155
36714
  runtimeWorkspaceLabel: state.runtime?.workspaceLabel,
36715
+ runtimeLifecycle: state.runtime?.lifecycle ?? null,
36716
+ runtimeSurfaceKind: state.runtime?.surfaceKind,
36156
36717
  runtimeWriteOwner: state.runtime?.writeOwner || null,
36157
- runtimeAttachedClients: state.runtime?.attachedClients || []
36718
+ runtimeAttachedClients: state.runtime?.attachedClients || [],
36719
+ runtimeRestoredFromStorage: state.runtime?.restoredFromStorage === true,
36720
+ runtimeRecoveryState: state.runtime?.recoveryState ?? null
36158
36721
  },
36159
36722
  mode: state.mode,
36160
36723
  resume: state.resume,
@@ -36211,6 +36774,7 @@ ${effect.notification.body || ""}`.trim();
36211
36774
  for (const state of ideStates) {
36212
36775
  sessions.push(buildIdeWorkspaceSession(state, cdpManagers, options));
36213
36776
  for (const ext of state.extensions) {
36777
+ if (!shouldIncludeExtensionSession(ext)) continue;
36214
36778
  sessions.push(buildExtensionAgentSession(state, ext, options));
36215
36779
  }
36216
36780
  }
@@ -38351,7 +38915,9 @@ ${effect.notification.body || ""}`.trim();
38351
38915
  });
38352
38916
  }
38353
38917
  async function executeProviderScript(h, args, scriptName) {
38354
- const resolvedProviderType = h.currentSession?.providerType || h.currentProviderType || args?.agentType || args?.providerType;
38918
+ const explicitTargetSessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : "";
38919
+ const targetSession = explicitTargetSessionId ? h.ctx.sessionRegistry?.get(explicitTargetSessionId) : void 0;
38920
+ const resolvedProviderType = targetSession?.providerType || h.currentSession?.providerType || h.currentProviderType || args?.agentType || args?.providerType;
38355
38921
  if (!resolvedProviderType) return { success: false, error: "targetSessionId or providerType is required" };
38356
38922
  const loader = h.ctx.providerLoader;
38357
38923
  if (!loader) return { success: false, error: "ProviderLoader not initialized" };
@@ -38394,16 +38960,16 @@ ${effect.notification.body || ""}`.trim();
38394
38960
  const scriptFn = provider.scripts[actualScriptName];
38395
38961
  const scriptCode = scriptFn(normalizedArgs);
38396
38962
  if (!scriptCode) return { success: false, error: `Script '${actualScriptName}' returned null` };
38397
- const cdpKey = provider.category === "ide" ? h.currentSession?.cdpManagerKey || h.currentManagerKey || resolvedProviderType : h.currentSession?.cdpManagerKey || h.currentManagerKey;
38963
+ const cdpKey = provider.category === "ide" ? targetSession?.cdpManagerKey || h.currentSession?.cdpManagerKey || h.currentManagerKey || resolvedProviderType : targetSession?.cdpManagerKey || h.currentSession?.cdpManagerKey || h.currentManagerKey;
38398
38964
  LOG2.info("Command", `[ExtScript] provider=${provider.type} category=${provider.category} cdpKey=${cdpKey}`);
38399
38965
  const cdp = h.getCdp(cdpKey);
38400
38966
  if (!cdp?.isConnected) return { success: false, error: `No CDP connection for ${cdpKey || "any"}` };
38401
38967
  try {
38402
38968
  let result;
38403
38969
  if (provider.category === "extension") {
38404
- const runtimeSessionId = h.currentSession?.sessionId || args?.targetSessionId;
38970
+ const runtimeSessionId = explicitTargetSessionId || h.currentSession?.sessionId;
38405
38971
  if (!runtimeSessionId) return { success: false, error: `No target session found for ${resolvedProviderType}` };
38406
- const parentSessionId = h.currentSession?.parentSessionId;
38972
+ const parentSessionId = targetSession?.parentSessionId || h.currentSession?.parentSessionId;
38407
38973
  if (parentSessionId) {
38408
38974
  await h.agentStream?.setActiveSession(cdp, parentSessionId, runtimeSessionId);
38409
38975
  await h.agentStream?.syncActiveSession(cdp, parentSessionId);
@@ -39398,8 +39964,12 @@ ${effect.notification.body || ""}`.trim();
39398
39964
  runtimeKey: runtime.runtimeKey,
39399
39965
  displayName: runtime.displayName,
39400
39966
  workspaceLabel: runtime.workspaceLabel,
39967
+ lifecycle: runtime.lifecycle ?? null,
39968
+ surfaceKind: runtime.surfaceKind,
39401
39969
  writeOwner: runtime.writeOwner || null,
39402
- attachedClients: runtime.attachedClients || []
39970
+ attachedClients: runtime.attachedClients || [],
39971
+ restoredFromStorage: runtime.restoredFromStorage === true,
39972
+ recoveryState: runtime.recoveryState ?? null
39403
39973
  } : void 0,
39404
39974
  resume: this.provider.resume,
39405
39975
  controlValues: surface.controlValues,
@@ -43681,59 +44251,6 @@ Run 'adhdev doctor' for detailed diagnostics.`
43681
44251
  }
43682
44252
  cleanOldFiles();
43683
44253
  init_logger();
43684
- var LIVE_LIFECYCLES = /* @__PURE__ */ new Set(["starting", "running", "stopping", "interrupted"]);
43685
- function isSessionHostLiveRuntime(record2) {
43686
- const lifecycle = String(record2?.lifecycle || "").trim();
43687
- return LIVE_LIFECYCLES.has(lifecycle);
43688
- }
43689
- function getSessionHostRecoveryLabel(meta3) {
43690
- const recoveryState = typeof meta3?.runtimeRecoveryState === "string" ? String(meta3.runtimeRecoveryState).trim() : "";
43691
- if (!recoveryState) return null;
43692
- if (recoveryState === "auto_resumed") return "restored after restart";
43693
- if (recoveryState === "resume_failed") return "restore failed";
43694
- if (recoveryState === "host_restart_interrupted") return "host restart interrupted";
43695
- if (recoveryState === "orphan_snapshot") return "snapshot recovered";
43696
- return recoveryState.replace(/_/g, " ");
43697
- }
43698
- function isSessionHostRecoverySnapshot(record2) {
43699
- if (!record2) return false;
43700
- if (isSessionHostLiveRuntime(record2)) return false;
43701
- const lifecycle = String(record2.lifecycle || "").trim();
43702
- if (lifecycle && lifecycle !== "stopped" && lifecycle !== "failed") {
43703
- return false;
43704
- }
43705
- const meta3 = record2.meta || void 0;
43706
- if (meta3?.restoredFromStorage === true) return true;
43707
- return getSessionHostRecoveryLabel(meta3) !== null;
43708
- }
43709
- function getSessionHostSurfaceKind(record2) {
43710
- if (isSessionHostLiveRuntime(record2)) return "live_runtime";
43711
- if (isSessionHostRecoverySnapshot(record2)) return "recovery_snapshot";
43712
- return "inactive_record";
43713
- }
43714
- function partitionSessionHostRecords(records) {
43715
- const liveRuntimes = [];
43716
- const recoverySnapshots = [];
43717
- const inactiveRecords = [];
43718
- for (const record2 of records) {
43719
- const kind = getSessionHostSurfaceKind(record2);
43720
- if (kind === "live_runtime") {
43721
- liveRuntimes.push(record2);
43722
- } else if (kind === "recovery_snapshot") {
43723
- recoverySnapshots.push(record2);
43724
- } else {
43725
- inactiveRecords.push(record2);
43726
- }
43727
- }
43728
- return {
43729
- liveRuntimes,
43730
- recoverySnapshots,
43731
- inactiveRecords
43732
- };
43733
- }
43734
- function partitionSessionHostDiagnosticsSessions(records) {
43735
- return partitionSessionHostRecords(records || []);
43736
- }
43737
44254
  var os16 = __toESM2(require("os"));
43738
44255
  init_config();
43739
44256
  init_terminal_screen();
@@ -45277,6 +45794,21 @@ Run 'adhdev doctor' for detailed diagnostics.`
45277
45794
  }
45278
45795
  };
45279
45796
  }
45797
+ async function runAsyncBatch2(items, worker, options = {}) {
45798
+ const list = Array.from(items);
45799
+ if (list.length === 0) return;
45800
+ const concurrency = Math.max(1, Math.min(list.length, Math.floor(options.concurrency || 1)));
45801
+ let nextIndex = 0;
45802
+ const runners = Array.from({ length: concurrency }, async () => {
45803
+ while (true) {
45804
+ const currentIndex = nextIndex;
45805
+ nextIndex += 1;
45806
+ if (currentIndex >= list.length) return;
45807
+ await worker(list[currentIndex], currentIndex);
45808
+ }
45809
+ });
45810
+ await Promise.all(runners);
45811
+ }
45280
45812
  init_read_chat_contract();
45281
45813
  init_chat_message_normalization();
45282
45814
  var ProviderStreamAdapter = class {
@@ -45742,10 +46274,12 @@ Run 'adhdev doctor' for detailed diagnostics.`
45742
46274
  }
45743
46275
  }
45744
46276
  /** Collect active extension session state */
45745
- async collectActiveSession(cdp, parentSessionId) {
46277
+ async collectActiveSession(cdp, parentSessionId, attemptedSessionIds = /* @__PURE__ */ new Set(), originSessionId) {
45746
46278
  if (!this.enabled) return null;
45747
46279
  const activeSessionId = this.getActiveSessionId(parentSessionId);
45748
46280
  if (!activeSessionId) return null;
46281
+ const resolvedOriginSessionId = originSessionId || activeSessionId;
46282
+ attemptedSessionIds.add(activeSessionId);
45749
46283
  let agent = this.managedBySessionId.get(activeSessionId);
45750
46284
  if (!agent) {
45751
46285
  agent = await this.connectManagedSession(cdp, parentSessionId, activeSessionId) || void 0;
@@ -45758,18 +46292,44 @@ Run 'adhdev doctor' for detailed diagnostics.`
45758
46292
  try {
45759
46293
  const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
45760
46294
  const state = await agent.adapter.readChat(evaluate);
45761
- const stateError = this.getStateError(state);
45762
- const selectedModelValue = typeof state.controlValues?.model === "string" ? state.controlValues.model : "";
45763
- LOG2.debug("AgentStream", `[AgentStream] readChat(${type}) result: status=${state.status} msgs=${state.messages?.length || 0} model=${selectedModelValue}${state.status === "error" ? " error=" + JSON.stringify(stateError) : ""}`);
45764
- if (state.status === "error" && this.isRecoverableSessionError(stateError)) {
46295
+ const resolvedProviderSessionId = typeof state.providerSessionId === "string" && state.providerSessionId.trim() ? state.providerSessionId.trim() : typeof state.sessionId === "string" && state.sessionId.trim() && state.sessionId !== agent.runtimeSessionId ? state.sessionId.trim() : void 0;
46296
+ const normalizedState = {
46297
+ ...state,
46298
+ sessionId: agent.runtimeSessionId,
46299
+ ...resolvedProviderSessionId ? { providerSessionId: resolvedProviderSessionId } : {}
46300
+ };
46301
+ const stateError = this.getStateError(normalizedState);
46302
+ const selectedModelValue = typeof normalizedState.controlValues?.model === "string" ? normalizedState.controlValues.model : "";
46303
+ LOG2.debug("AgentStream", `[AgentStream] readChat(${type}) result: status=${normalizedState.status} msgs=${normalizedState.messages?.length || 0} model=${selectedModelValue}${normalizedState.status === "error" ? " error=" + JSON.stringify(stateError) : ""}`);
46304
+ if (normalizedState.status === "error" && this.isRecoverableSessionError(stateError)) {
45765
46305
  throw new Error(stateError);
45766
46306
  }
45767
- agent.lastState = state;
46307
+ agent.lastState = normalizedState;
45768
46308
  agent.lastError = null;
45769
- if (state.status === "panel_hidden") {
46309
+ if (normalizedState.status === "panel_hidden") {
46310
+ const discovered = await cdp.discoverAgentWebviews().catch(() => []);
46311
+ const fallbackTarget = discovered.find((entry) => {
46312
+ if (entry.agentType === type) return false;
46313
+ const fallbackSessionId = this.resolveSessionIdForTarget(parentSessionId, entry.agentType);
46314
+ return !!fallbackSessionId && fallbackSessionId !== activeSessionId && !attemptedSessionIds.has(fallbackSessionId);
46315
+ });
46316
+ if (fallbackTarget) {
46317
+ const fallbackSessionId = this.resolveSessionIdForTarget(parentSessionId, fallbackTarget.agentType);
46318
+ if (fallbackSessionId && fallbackSessionId !== activeSessionId && !attemptedSessionIds.has(fallbackSessionId)) {
46319
+ this.logFn(`[AgentStream] Active session ${type} is hidden; switching to visible agent ${fallbackTarget.agentType} (${parentSessionId})`);
46320
+ await this.setActiveSession(cdp, parentSessionId, fallbackSessionId);
46321
+ await this.syncActiveSession(cdp, parentSessionId);
46322
+ const fallbackState = await this.collectActiveSession(cdp, parentSessionId, attemptedSessionIds, resolvedOriginSessionId);
46323
+ if (fallbackState?.status === "panel_hidden" && resolvedOriginSessionId !== fallbackSessionId) {
46324
+ await this.setActiveSession(cdp, parentSessionId, resolvedOriginSessionId);
46325
+ await this.syncActiveSession(cdp, parentSessionId);
46326
+ }
46327
+ return fallbackState;
46328
+ }
46329
+ }
45770
46330
  agent.lastHiddenCheckTime = Date.now();
45771
46331
  }
45772
- return state;
46332
+ return normalizedState;
45773
46333
  } catch (e) {
45774
46334
  const errorMsg = e?.message || String(e);
45775
46335
  this.logFn(`[AgentStream] readChat(${type}) error: ${errorMsg.slice(0, 200)}`);
@@ -46063,6 +46623,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
46063
46623
  try {
46064
46624
  await agentStreamManager.syncActiveSession(cdp, parentSessionId);
46065
46625
  let stream = await agentStreamManager.collectActiveSession(cdp, parentSessionId);
46626
+ resolvedActiveSessionId = stream?.sessionId || agentStreamManager.getActiveSessionId(parentSessionId) || resolvedActiveSessionId;
46066
46627
  if (stream?.status === "waiting_approval") {
46067
46628
  const autoApprove = providerLoader.getSettings(stream.agentType).autoApprove !== false;
46068
46629
  if (autoApprove && resolvedActiveSessionId) {
@@ -52017,6 +52578,8 @@ data: ${JSON.stringify(msg.data)}
52017
52578
  runtimeKey: record2.runtimeKey,
52018
52579
  displayName: record2.displayName,
52019
52580
  workspaceLabel: record2.workspaceLabel,
52581
+ lifecycle: typeof record2.lifecycle === "string" ? record2.lifecycle : null,
52582
+ surfaceKind: record2.surfaceKind,
52020
52583
  writeOwner: record2.writeOwner ? {
52021
52584
  clientId: record2.writeOwner.clientId,
52022
52585
  ownerType: record2.writeOwner.ownerType
@@ -52619,6 +53182,7 @@ var import_ws = require("ws");
52619
53182
  var path4 = __toESM(require("path"));
52620
53183
  var fs3 = __toESM(require("fs"));
52621
53184
  var os5 = __toESM(require("os"));
53185
+ var import_crypto2 = require("crypto");
52622
53186
  var import_daemon_core2 = __toESM(require_dist2());
52623
53187
 
52624
53188
  // src/session-host.ts
@@ -53124,6 +53688,165 @@ async function getWorkspaceSocketInfo(workspaceName) {
53124
53688
  var import_daemon_core3 = __toESM(require_dist2());
53125
53689
  var DEFAULT_PORT = 3847;
53126
53690
  var STATUS_INTERVAL = 2e3;
53691
+ var STANDALONE_AUTH_SESSION_COOKIE = "adhdev_standalone_session";
53692
+ var STANDALONE_PASSWORD_CONFIG_FILE = "standalone-auth.json";
53693
+ var STANDALONE_BIND_HOST_CONFIG_FILE = "standalone-network.json";
53694
+ var STANDALONE_BIND_HOST_DEFAULT = "127.0.0.1";
53695
+ var PASSWORD_KEYLEN = 64;
53696
+ var DEFAULT_SESSION_TTL_MS = 1e3 * 60 * 60 * 24 * 30;
53697
+ function getStandalonePasswordConfigPath() {
53698
+ const dir = path4.join(os5.homedir(), ".adhdev");
53699
+ if (!fs3.existsSync(dir)) {
53700
+ fs3.mkdirSync(dir, { recursive: true, mode: 448 });
53701
+ }
53702
+ return path4.join(dir, STANDALONE_PASSWORD_CONFIG_FILE);
53703
+ }
53704
+ function getStandaloneConfigJsonPath() {
53705
+ const dir = path4.join(os5.homedir(), ".adhdev");
53706
+ if (!fs3.existsSync(dir)) {
53707
+ fs3.mkdirSync(dir, { recursive: true, mode: 448 });
53708
+ }
53709
+ return path4.join(dir, STANDALONE_BIND_HOST_CONFIG_FILE);
53710
+ }
53711
+ function loadStandaloneBindHostPreference() {
53712
+ try {
53713
+ const configPath = getStandaloneConfigJsonPath();
53714
+ if (!fs3.existsSync(configPath)) return STANDALONE_BIND_HOST_DEFAULT;
53715
+ const parsed = JSON.parse(fs3.readFileSync(configPath, "utf8"));
53716
+ return parsed?.standaloneBindHost === "0.0.0.0" ? "0.0.0.0" : STANDALONE_BIND_HOST_DEFAULT;
53717
+ } catch {
53718
+ return STANDALONE_BIND_HOST_DEFAULT;
53719
+ }
53720
+ }
53721
+ function saveStandaloneBindHostPreference(bindHost) {
53722
+ const configPath = getStandaloneConfigJsonPath();
53723
+ let parsed = {};
53724
+ try {
53725
+ if (fs3.existsSync(configPath)) {
53726
+ const raw = fs3.readFileSync(configPath, "utf8");
53727
+ const next = JSON.parse(raw);
53728
+ if (next && typeof next === "object" && !Array.isArray(next)) parsed = next;
53729
+ }
53730
+ } catch {
53731
+ parsed = {};
53732
+ }
53733
+ parsed.standaloneBindHost = bindHost;
53734
+ fs3.writeFileSync(configPath, JSON.stringify(parsed, null, 2), { encoding: "utf8", mode: 384 });
53735
+ try {
53736
+ fs3.chmodSync(configPath, 384);
53737
+ } catch {
53738
+ }
53739
+ return bindHost;
53740
+ }
53741
+ function createPasswordRecord(password, salt = (0, import_crypto2.randomBytes)(16).toString("hex")) {
53742
+ return {
53743
+ passwordHash: (0, import_crypto2.scryptSync)(`${password || ""}`, salt, PASSWORD_KEYLEN).toString("hex"),
53744
+ passwordSalt: salt,
53745
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
53746
+ };
53747
+ }
53748
+ function verifyPassword(password, config2) {
53749
+ if (!config2?.passwordHash || !config2.passwordSalt) return false;
53750
+ const actual = Buffer.from((0, import_crypto2.scryptSync)(`${password || ""}`, config2.passwordSalt, PASSWORD_KEYLEN).toString("hex"), "utf8");
53751
+ const expected = Buffer.from(config2.passwordHash, "utf8");
53752
+ return actual.length === expected.length && (0, import_crypto2.timingSafeEqual)(actual, expected);
53753
+ }
53754
+ function loadStandalonePasswordConfig(filePath = getStandalonePasswordConfigPath()) {
53755
+ if (!fs3.existsSync(filePath)) return null;
53756
+ try {
53757
+ const parsed = JSON.parse(fs3.readFileSync(filePath, "utf8"));
53758
+ if (!parsed || typeof parsed !== "object") return null;
53759
+ if (typeof parsed.passwordHash !== "string" || typeof parsed.passwordSalt !== "string") return null;
53760
+ return {
53761
+ passwordHash: parsed.passwordHash,
53762
+ passwordSalt: parsed.passwordSalt,
53763
+ updatedAt: typeof parsed.updatedAt === "string" ? parsed.updatedAt : (/* @__PURE__ */ new Date(0)).toISOString()
53764
+ };
53765
+ } catch {
53766
+ return null;
53767
+ }
53768
+ }
53769
+ function saveStandalonePasswordConfig(filePath, config2) {
53770
+ const dir = path4.dirname(filePath);
53771
+ if (!fs3.existsSync(dir)) {
53772
+ fs3.mkdirSync(dir, { recursive: true, mode: 448 });
53773
+ }
53774
+ fs3.writeFileSync(filePath, JSON.stringify(config2, null, 2), { encoding: "utf8", mode: 384 });
53775
+ try {
53776
+ fs3.chmodSync(filePath, 384);
53777
+ } catch {
53778
+ }
53779
+ }
53780
+ function clearStandalonePasswordConfig(filePath = getStandalonePasswordConfigPath()) {
53781
+ if (fs3.existsSync(filePath)) {
53782
+ fs3.rmSync(filePath, { force: true });
53783
+ }
53784
+ }
53785
+ function shouldWarnForPublicUnauthenticatedHost(input) {
53786
+ return input.host === "0.0.0.0" && !input.hasTokenAuth && !input.hasPasswordAuth;
53787
+ }
53788
+ function parseCookies(cookieHeader) {
53789
+ if (!cookieHeader) return {};
53790
+ return Object.fromEntries(
53791
+ cookieHeader.split(";").map((part) => part.trim()).filter(Boolean).map((part) => {
53792
+ const eq = part.indexOf("=");
53793
+ if (eq === -1) return [part, ""];
53794
+ return [part.slice(0, eq), decodeURIComponent(part.slice(eq + 1))];
53795
+ })
53796
+ );
53797
+ }
53798
+ function buildSessionCookie(sessionId, secure, maxAgeMs = DEFAULT_SESSION_TTL_MS) {
53799
+ const parts = [
53800
+ `${STANDALONE_AUTH_SESSION_COOKIE}=${encodeURIComponent(sessionId)}`,
53801
+ "Path=/",
53802
+ "HttpOnly",
53803
+ "SameSite=Lax",
53804
+ `Max-Age=${Math.max(0, Math.floor(maxAgeMs / 1e3))}`
53805
+ ];
53806
+ if (secure) parts.push("Secure");
53807
+ return parts.join("; ");
53808
+ }
53809
+ function buildClearedSessionCookie(secure) {
53810
+ return buildSessionCookie("", secure, 0);
53811
+ }
53812
+ var StandaloneSessionStore = class {
53813
+ sessions = /* @__PURE__ */ new Map();
53814
+ create(ttlMs = DEFAULT_SESSION_TTL_MS) {
53815
+ const id = (0, import_crypto2.randomBytes)(24).toString("hex");
53816
+ this.sessions.set(id, Date.now() + ttlMs);
53817
+ return id;
53818
+ }
53819
+ has(sessionId) {
53820
+ if (!sessionId) return false;
53821
+ const expiresAt = this.sessions.get(sessionId);
53822
+ if (!expiresAt) return false;
53823
+ if (expiresAt <= Date.now()) {
53824
+ this.sessions.delete(sessionId);
53825
+ return false;
53826
+ }
53827
+ return true;
53828
+ }
53829
+ revoke(sessionId) {
53830
+ if (!sessionId) return;
53831
+ this.sessions.delete(sessionId);
53832
+ }
53833
+ clear() {
53834
+ this.sessions.clear();
53835
+ }
53836
+ };
53837
+ function isStandaloneRequestAuthenticated(input) {
53838
+ const hasTokenAuth = !!input.configuredToken;
53839
+ const hasPasswordAuth = !!input.passwordConfig;
53840
+ if (!hasTokenAuth && !hasPasswordAuth) return true;
53841
+ if (hasTokenAuth && (input.bearerToken === input.configuredToken || input.queryToken === input.configuredToken)) {
53842
+ return true;
53843
+ }
53844
+ if (hasPasswordAuth) {
53845
+ const cookies = parseCookies(input.cookieHeader);
53846
+ return input.sessionStore.has(cookies[STANDALONE_AUTH_SESSION_COOKIE]);
53847
+ }
53848
+ return false;
53849
+ }
53127
53850
  var pkgVersion = process.env.ADHDEV_PKG_VERSION || "unknown";
53128
53851
  if (pkgVersion === "unknown") {
53129
53852
  try {
@@ -53172,6 +53895,10 @@ var StandaloneServer = class {
53172
53895
  wsSessionModalSubscriptions = /* @__PURE__ */ new Map();
53173
53896
  wsDaemonMetadataSubscriptions = /* @__PURE__ */ new Map();
53174
53897
  authToken = null;
53898
+ passwordConfigPath = getStandalonePasswordConfigPath();
53899
+ passwordConfig = null;
53900
+ authSessions = new StandaloneSessionStore();
53901
+ listenHost = "127.0.0.1";
53175
53902
  statusTimer = null;
53176
53903
  lastStatusBroadcastAt = 0;
53177
53904
  statusBroadcastPending = false;
@@ -53228,10 +53955,88 @@ var StandaloneServer = class {
53228
53955
  const mode = this.getCliPresentationMode(sessionId);
53229
53956
  return mode === "chat" || mode === "terminal";
53230
53957
  }
53958
+ hasPasswordAuth() {
53959
+ return !!this.passwordConfig;
53960
+ }
53961
+ hasAnyAuth() {
53962
+ return !!this.authToken || this.hasPasswordAuth();
53963
+ }
53964
+ getCookieSecureFlag(req) {
53965
+ const forwardedProto = req.headers["x-forwarded-proto"];
53966
+ return !!req.socket.encrypted || typeof forwardedProto === "string" && forwardedProto.toLowerCase().includes("https");
53967
+ }
53968
+ getRequestTokens(req, rawUrl) {
53969
+ const authHeader = req.headers["authorization"];
53970
+ const bearerToken = typeof authHeader === "string" && authHeader.startsWith("Bearer ") ? authHeader.slice(7) : null;
53971
+ const queryToken = new URL(rawUrl, `http://${req.headers.host || "localhost"}`).searchParams.get("token");
53972
+ return { bearerToken, queryToken };
53973
+ }
53974
+ isRequestAuthenticated(req, rawUrl) {
53975
+ const { bearerToken, queryToken } = this.getRequestTokens(req, rawUrl);
53976
+ return isStandaloneRequestAuthenticated({
53977
+ configuredToken: this.authToken,
53978
+ passwordConfig: this.passwordConfig,
53979
+ bearerToken,
53980
+ queryToken,
53981
+ cookieHeader: typeof req.headers.cookie === "string" ? req.headers.cookie : void 0,
53982
+ sessionStore: this.authSessions
53983
+ });
53984
+ }
53985
+ getRequestSessionId(req) {
53986
+ const cookies = parseCookies(typeof req.headers.cookie === "string" ? req.headers.cookie : void 0);
53987
+ return cookies.adhdev_standalone_session || null;
53988
+ }
53989
+ buildAuthStatus(req, rawUrl) {
53990
+ const required2 = this.hasAnyAuth();
53991
+ return {
53992
+ required: required2,
53993
+ authenticated: this.isRequestAuthenticated(req, rawUrl),
53994
+ hasTokenAuth: !!this.authToken,
53995
+ hasPasswordAuth: this.hasPasswordAuth(),
53996
+ publicHostWarning: shouldWarnForPublicUnauthenticatedHost({
53997
+ host: this.listenHost,
53998
+ hasTokenAuth: !!this.authToken,
53999
+ hasPasswordAuth: this.hasPasswordAuth()
54000
+ }),
54001
+ boundHost: this.listenHost
54002
+ };
54003
+ }
54004
+ isTrustedStandaloneMutationRequest(req) {
54005
+ const originHeader = req.headers.origin;
54006
+ if (typeof originHeader !== "string" || !originHeader.trim()) return true;
54007
+ try {
54008
+ const origin = new URL(originHeader);
54009
+ const host = req.headers.host || "";
54010
+ return origin.host === host;
54011
+ } catch {
54012
+ return false;
54013
+ }
54014
+ }
54015
+ async readJsonBody(req) {
54016
+ return await new Promise((resolve4, reject) => {
54017
+ let body = "";
54018
+ req.on("data", (chunk) => {
54019
+ body += chunk;
54020
+ });
54021
+ req.on("end", () => {
54022
+ try {
54023
+ resolve4(body ? JSON.parse(body) : {});
54024
+ } catch (error48) {
54025
+ reject(error48);
54026
+ }
54027
+ });
54028
+ req.on("error", reject);
54029
+ });
54030
+ }
53231
54031
  async start(options = {}) {
53232
- const port = options.port || DEFAULT_PORT;
53233
- const host = options.host || "127.0.0.1";
54032
+ const persistedStandaloneBindHost = loadStandaloneBindHostPreference();
53234
54033
  const cfg = (0, import_daemon_core2.loadConfig)();
54034
+ if (!options.host && persistedStandaloneBindHost !== STANDALONE_BIND_HOST_DEFAULT) {
54035
+ saveStandaloneBindHostPreference(persistedStandaloneBindHost);
54036
+ }
54037
+ const port = options.port || DEFAULT_PORT;
54038
+ const host = options.host || persistedStandaloneBindHost;
54039
+ this.listenHost = host;
53235
54040
  const sessionHostEndpoint = await ensureSessionHostReady();
53236
54041
  this.sessionHostEndpoint = sessionHostEndpoint;
53237
54042
  const sessionHostControl = new StandaloneSessionHostControlPlane(
@@ -53239,6 +54044,7 @@ var StandaloneServer = class {
53239
54044
  );
53240
54045
  this.sessionHostControl = sessionHostControl;
53241
54046
  this.authToken = options.token || process.env.ADHDEV_TOKEN || null;
54047
+ this.passwordConfig = loadStandalonePasswordConfig(this.passwordConfigPath);
53242
54048
  const statusInstanceId = `standalone_${cfg.machineId || "mach_unknown"}`;
53243
54049
  this.components = await (0, import_daemon_core2.initDaemonComponents)({
53244
54050
  cliManagerDeps: {
@@ -53256,7 +54062,7 @@ var StandaloneServer = class {
53256
54062
  }),
53257
54063
  onStatusChange: () => {
53258
54064
  this.scheduleBroadcastStatus();
53259
- void this.flushWsChatSubscriptions(void 0, { onlyActive: false });
54065
+ void this.flushWsChatSubscriptions(void 0, { onlyActive: true });
53260
54066
  },
53261
54067
  removeAgentTracking: () => {
53262
54068
  },
@@ -53285,7 +54091,7 @@ var StandaloneServer = class {
53285
54091
  statusDaemonMode: false,
53286
54092
  onStatusChange: () => {
53287
54093
  this.scheduleBroadcastStatus();
53288
- void this.flushWsChatSubscriptions(void 0, { onlyActive: false });
54094
+ void this.flushWsChatSubscriptions(void 0, { onlyActive: true });
53289
54095
  },
53290
54096
  sessionHostControl,
53291
54097
  onStreamsUpdated: (ideType, streams) => {
@@ -53312,13 +54118,10 @@ var StandaloneServer = class {
53312
54118
  this.httpServer.on("upgrade", (req, socket, head) => {
53313
54119
  const wsUrl = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
53314
54120
  if (wsUrl.pathname === "/ws") {
53315
- if (this.authToken) {
53316
- const urlToken = wsUrl.searchParams.get("token");
53317
- if (urlToken !== this.authToken) {
53318
- socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
53319
- socket.destroy();
53320
- return;
53321
- }
54121
+ if (!this.isRequestAuthenticated(req, req.url || "/")) {
54122
+ socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
54123
+ socket.destroy();
54124
+ return;
53322
54125
  }
53323
54126
  this.wss.handleUpgrade(req, socket, head, (ws2) => {
53324
54127
  this.handleWsConnection(ws2);
@@ -53351,7 +54154,14 @@ var StandaloneServer = class {
53351
54154
  }
53352
54155
  }
53353
54156
  if (this.authToken) {
53354
- console.log(` \u{1F511} Token: ${this.authToken}`);
54157
+ console.log(" \u{1F511} Token auth: enabled");
54158
+ }
54159
+ if (this.passwordConfig) {
54160
+ console.log(" \u{1F510} Password auth: enabled");
54161
+ }
54162
+ if (shouldWarnForPublicUnauthenticatedHost({ host, hasTokenAuth: !!this.authToken, hasPasswordAuth: !!this.passwordConfig })) {
54163
+ console.warn(" \u26A0\uFE0F Public host mode is enabled without any auth.");
54164
+ console.warn(" Anyone on your LAN can open and control this dashboard until you set a password or token.");
53355
54165
  }
53356
54166
  console.log("");
53357
54167
  const cdpCount = [...this.components.cdpManagers.values()].filter((m) => m.isConnected).length;
@@ -53391,18 +54201,148 @@ var StandaloneServer = class {
53391
54201
  res.end();
53392
54202
  return;
53393
54203
  }
53394
- if (this.authToken && url2.startsWith("/api/")) {
53395
- const authHeader = req.headers["authorization"];
53396
- const bearerToken = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : null;
53397
- const queryToken = new URL(url2, `http://${req.headers.host || "localhost"}`).searchParams.get("token");
53398
- if (bearerToken !== this.authToken && queryToken !== this.authToken) {
53399
- res.writeHead(401, { "Content-Type": "application/json" });
53400
- res.end(JSON.stringify({ error: "Unauthorized. Provide token via Authorization header or ?token= query." }));
53401
- return;
53402
- }
54204
+ const parsedUrl = new URL(url2, `http://${req.headers.host || "localhost"}`);
54205
+ if (parsedUrl.pathname === "/auth/session" && method === "GET") {
54206
+ res.writeHead(200, { "Content-Type": "application/json" });
54207
+ res.end(JSON.stringify(this.buildAuthStatus(req, url2)));
54208
+ return;
54209
+ }
54210
+ if (parsedUrl.pathname === "/auth/login" && method === "POST") {
54211
+ void (async () => {
54212
+ if (!this.passwordConfig) {
54213
+ res.writeHead(400, { "Content-Type": "application/json" });
54214
+ res.end(JSON.stringify({ error: "Password auth is not configured." }));
54215
+ return;
54216
+ }
54217
+ const body = await this.readJsonBody(req);
54218
+ if (!verifyPassword(typeof body.password === "string" ? body.password : "", this.passwordConfig)) {
54219
+ res.writeHead(401, { "Content-Type": "application/json" });
54220
+ res.end(JSON.stringify({ error: "Incorrect password." }));
54221
+ return;
54222
+ }
54223
+ this.authSessions.clear();
54224
+ const sessionId = this.authSessions.create();
54225
+ res.setHeader("Set-Cookie", buildSessionCookie(sessionId, this.getCookieSecureFlag(req)));
54226
+ res.writeHead(200, { "Content-Type": "application/json" });
54227
+ res.end(JSON.stringify({ ...this.buildAuthStatus(req, url2), authenticated: true }));
54228
+ })().catch((error48) => {
54229
+ res.writeHead(400, { "Content-Type": "application/json" });
54230
+ res.end(JSON.stringify({ error: error48?.message || String(error48) }));
54231
+ });
54232
+ return;
54233
+ }
54234
+ if (parsedUrl.pathname === "/auth/logout" && method === "POST") {
54235
+ this.authSessions.revoke(this.getRequestSessionId(req));
54236
+ res.setHeader("Set-Cookie", buildClearedSessionCookie(this.getCookieSecureFlag(req)));
54237
+ res.writeHead(200, { "Content-Type": "application/json" });
54238
+ res.end(JSON.stringify({ success: true }));
54239
+ return;
54240
+ }
54241
+ if (parsedUrl.pathname === "/auth/password" && method === "POST") {
54242
+ void (async () => {
54243
+ if (!this.hasAnyAuth() && !this.isTrustedStandaloneMutationRequest(req)) {
54244
+ res.writeHead(403, { "Content-Type": "application/json" });
54245
+ res.end(JSON.stringify({ error: "Cross-origin standalone settings changes are not allowed without existing auth." }));
54246
+ return;
54247
+ }
54248
+ if (this.hasAnyAuth() && !this.isRequestAuthenticated(req, url2)) {
54249
+ res.writeHead(401, { "Content-Type": "application/json" });
54250
+ res.end(JSON.stringify({ error: "Unauthorized" }));
54251
+ return;
54252
+ }
54253
+ const body = await this.readJsonBody(req);
54254
+ const currentPassword = typeof body.currentPassword === "string" ? body.currentPassword : "";
54255
+ const newPassword = typeof body.newPassword === "string" ? body.newPassword : "";
54256
+ const clearPassword = body.clear === true;
54257
+ if (this.passwordConfig && !verifyPassword(currentPassword, this.passwordConfig)) {
54258
+ res.writeHead(403, { "Content-Type": "application/json" });
54259
+ res.end(JSON.stringify({ error: "Current password is incorrect." }));
54260
+ return;
54261
+ }
54262
+ if (clearPassword) {
54263
+ clearStandalonePasswordConfig(this.passwordConfigPath);
54264
+ this.passwordConfig = null;
54265
+ this.authSessions.clear();
54266
+ res.setHeader("Set-Cookie", buildClearedSessionCookie(this.getCookieSecureFlag(req)));
54267
+ res.writeHead(200, { "Content-Type": "application/json" });
54268
+ res.end(JSON.stringify({ success: true, ...this.buildAuthStatus(req, url2), authenticated: !this.hasAnyAuth() }));
54269
+ return;
54270
+ }
54271
+ if (newPassword.trim().length < 4) {
54272
+ res.writeHead(400, { "Content-Type": "application/json" });
54273
+ res.end(JSON.stringify({ error: "Password must be at least 4 characters." }));
54274
+ return;
54275
+ }
54276
+ const nextConfig = createPasswordRecord(newPassword.trim());
54277
+ saveStandalonePasswordConfig(this.passwordConfigPath, nextConfig);
54278
+ this.passwordConfig = nextConfig;
54279
+ this.authSessions.clear();
54280
+ const sessionId = this.authSessions.create();
54281
+ res.setHeader("Set-Cookie", buildSessionCookie(sessionId, this.getCookieSecureFlag(req)));
54282
+ res.writeHead(200, { "Content-Type": "application/json" });
54283
+ res.end(JSON.stringify({ success: true, ...this.buildAuthStatus(req, url2), authenticated: true }));
54284
+ })().catch((error48) => {
54285
+ res.writeHead(400, { "Content-Type": "application/json" });
54286
+ res.end(JSON.stringify({ error: error48?.message || String(error48) }));
54287
+ });
54288
+ return;
54289
+ }
54290
+ if (parsedUrl.pathname === "/api/v1/standalone/preferences" && method === "GET") {
54291
+ const configuredBindHost = loadStandaloneBindHostPreference();
54292
+ res.writeHead(200, { "Content-Type": "application/json" });
54293
+ res.end(JSON.stringify({
54294
+ standaloneBindHost: configuredBindHost,
54295
+ currentBindHost: this.listenHost,
54296
+ hasPasswordAuth: !!this.passwordConfig,
54297
+ hasTokenAuth: !!this.authToken,
54298
+ publicHostWarning: shouldWarnForPublicUnauthenticatedHost({
54299
+ host: configuredBindHost,
54300
+ hasTokenAuth: !!this.authToken,
54301
+ hasPasswordAuth: !!this.passwordConfig
54302
+ })
54303
+ }));
54304
+ return;
54305
+ }
54306
+ if (parsedUrl.pathname === "/api/v1/standalone/preferences" && method === "POST") {
54307
+ void (async () => {
54308
+ if (!this.hasAnyAuth() && !this.isTrustedStandaloneMutationRequest(req)) {
54309
+ res.writeHead(403, { "Content-Type": "application/json" });
54310
+ res.end(JSON.stringify({ error: "Cross-origin standalone settings changes are not allowed without existing auth." }));
54311
+ return;
54312
+ }
54313
+ if (this.hasAnyAuth() && !this.isRequestAuthenticated(req, url2)) {
54314
+ res.writeHead(401, { "Content-Type": "application/json" });
54315
+ res.end(JSON.stringify({ error: "Unauthorized" }));
54316
+ return;
54317
+ }
54318
+ const body = await this.readJsonBody(req);
54319
+ const nextHost = body?.standaloneBindHost === "0.0.0.0" ? "0.0.0.0" : "127.0.0.1";
54320
+ const savedHost = saveStandaloneBindHostPreference(nextHost);
54321
+ res.writeHead(200, { "Content-Type": "application/json" });
54322
+ res.end(JSON.stringify({
54323
+ success: true,
54324
+ standaloneBindHost: savedHost,
54325
+ currentBindHost: this.listenHost,
54326
+ hasPasswordAuth: !!this.passwordConfig,
54327
+ hasTokenAuth: !!this.authToken,
54328
+ publicHostWarning: shouldWarnForPublicUnauthenticatedHost({
54329
+ host: savedHost,
54330
+ hasTokenAuth: !!this.authToken,
54331
+ hasPasswordAuth: !!this.passwordConfig
54332
+ })
54333
+ }));
54334
+ })().catch((error48) => {
54335
+ res.writeHead(400, { "Content-Type": "application/json" });
54336
+ res.end(JSON.stringify({ error: error48?.message || String(error48) }));
54337
+ });
54338
+ return;
54339
+ }
54340
+ if (url2.startsWith("/api/") && !this.isRequestAuthenticated(req, url2)) {
54341
+ res.writeHead(401, { "Content-Type": "application/json" });
54342
+ res.end(JSON.stringify({ error: "Unauthorized. Provide dashboard session cookie or token auth." }));
54343
+ return;
53403
54344
  }
53404
54345
  const apiPath = url2.startsWith("/api/v1/") ? url2.slice(7) : null;
53405
- const parsedUrl = new URL(url2, `http://${req.headers.host || "localhost"}`);
53406
54346
  if (apiPath === "/status" && method === "GET") {
53407
54347
  const status = this.getStatus(getSharedSnapshot());
53408
54348
  res.writeHead(200, { "Content-Type": "application/json" });
@@ -53885,6 +54825,7 @@ var StandaloneServer = class {
53885
54825
  try {
53886
54826
  const targets = targetWs ? [targetWs] : Array.from(this.clients);
53887
54827
  const hotSessionIds = options.onlyActive ? this.getHotChatSessionIdsForWsFlush() : null;
54828
+ const tasks = [];
53888
54829
  for (const ws2 of targets) {
53889
54830
  if (ws2.readyState !== import_ws.WebSocket.OPEN) continue;
53890
54831
  const subs = this.wsSubscriptions.get(ws2);
@@ -53894,11 +54835,18 @@ var StandaloneServer = class {
53894
54835
  if (hotSessionIds && !hotSessionIds.active.has(targetSessionId) && !hotSessionIds.finalizing.has(targetSessionId)) {
53895
54836
  continue;
53896
54837
  }
54838
+ tasks.push({ ws: ws2, key, sub });
54839
+ }
54840
+ }
54841
+ await (0, import_daemon_core2.runAsyncBatch)(tasks, async ({ ws: ws2, key, sub }) => {
54842
+ try {
53897
54843
  const update = await this.buildChatTailUpdate(sub.request.params, sub, key);
53898
- if (!update || ws2.readyState !== import_ws.WebSocket.OPEN) continue;
54844
+ if (!update || ws2.readyState !== import_ws.WebSocket.OPEN) return;
53899
54845
  ws2.send(JSON.stringify({ type: "topic_update", update }));
54846
+ } catch (error48) {
54847
+ import_daemon_core2.LOG.warn("Standalone", `[chat_tail] skipped session=${sub.request.params.targetSessionId} key=${key} error=${error48?.message || error48}`);
53900
54848
  }
53901
- }
54849
+ }, { concurrency: 4 });
53902
54850
  } finally {
53903
54851
  this.wsChatFlushInFlight = false;
53904
54852
  if (this.pendingWsChatFlush) {
@@ -54282,6 +55230,7 @@ async function main() {
54282
55230
  process.exit(exitCode);
54283
55231
  }
54284
55232
  const options = {};
55233
+ let hostExplicit = false;
54285
55234
  for (let i = 0; i < args.length; i++) {
54286
55235
  if ((args[i] === "--port" || args[i] === "-p") && args[i + 1]) {
54287
55236
  options.port = parseInt(args[i + 1]);
@@ -54289,6 +55238,7 @@ async function main() {
54289
55238
  }
54290
55239
  if (args[i] === "--host" || args[i] === "-H") {
54291
55240
  options.host = "0.0.0.0";
55241
+ hostExplicit = true;
54292
55242
  }
54293
55243
  if (args[i] === "--public" && args[i + 1]) {
54294
55244
  options.publicDir = args[i + 1];
@@ -54330,6 +55280,9 @@ Runtime commands:
54330
55280
  process.exit(0);
54331
55281
  }
54332
55282
  }
55283
+ if (!hostExplicit) {
55284
+ options.host = loadStandaloneBindHostPreference();
55285
+ }
54333
55286
  if (!options.publicDir) {
54334
55287
  const candidates = [
54335
55288
  path4.join(__dirname, "../../web-standalone/dist"),
@@ -54345,6 +55298,9 @@ Runtime commands:
54345
55298
  }
54346
55299
  const server = new StandaloneServer();
54347
55300
  await server.start(options);
55301
+ if (!hostExplicit) {
55302
+ saveStandaloneBindHostPreference(options.host === "0.0.0.0" ? "0.0.0.0" : "127.0.0.1");
55303
+ }
54348
55304
  await new Promise(() => {
54349
55305
  });
54350
55306
  }