@adhdev/daemon-core 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
@@ -3667,6 +3667,7 @@ __export(index_exports, {
3667
3667
  resolveChatMessageKind: () => resolveChatMessageKind,
3668
3668
  resolveDebugRuntimeConfig: () => resolveDebugRuntimeConfig,
3669
3669
  resolveSessionHostAppName: () => resolveSessionHostAppName,
3670
+ runAsyncBatch: () => runAsyncBatch,
3670
3671
  saveConfig: () => saveConfig,
3671
3672
  saveState: () => saveState,
3672
3673
  setDebugRuntimeConfig: () => setDebugRuntimeConfig,
@@ -4408,6 +4409,61 @@ function getHostMemorySnapshot() {
4408
4409
  return { totalMem, freeMem, availableMem };
4409
4410
  }
4410
4411
 
4412
+ // src/session-host/runtime-surface.ts
4413
+ var LIVE_LIFECYCLES = /* @__PURE__ */ new Set(["starting", "running", "stopping", "interrupted"]);
4414
+ function isSessionHostLiveRuntime(record) {
4415
+ const lifecycle = String(record?.lifecycle || "").trim();
4416
+ return LIVE_LIFECYCLES.has(lifecycle);
4417
+ }
4418
+ function getSessionHostRecoveryLabel(meta) {
4419
+ const recoveryState = typeof meta?.runtimeRecoveryState === "string" ? String(meta.runtimeRecoveryState).trim() : "";
4420
+ if (!recoveryState) return null;
4421
+ if (recoveryState === "auto_resumed") return "restored after restart";
4422
+ if (recoveryState === "resume_failed") return "restore failed";
4423
+ if (recoveryState === "host_restart_interrupted") return "host restart interrupted";
4424
+ if (recoveryState === "orphan_snapshot") return "snapshot recovered";
4425
+ return recoveryState.replace(/_/g, " ");
4426
+ }
4427
+ function isSessionHostRecoverySnapshot(record) {
4428
+ if (!record) return false;
4429
+ if (isSessionHostLiveRuntime(record)) return false;
4430
+ const lifecycle = String(record.lifecycle || "").trim();
4431
+ if (lifecycle && lifecycle !== "stopped" && lifecycle !== "failed") {
4432
+ return false;
4433
+ }
4434
+ const meta = record.meta || void 0;
4435
+ if (meta?.restoredFromStorage === true) return true;
4436
+ return getSessionHostRecoveryLabel(meta) !== null;
4437
+ }
4438
+ function getSessionHostSurfaceKind(record) {
4439
+ if (isSessionHostLiveRuntime(record)) return "live_runtime";
4440
+ if (isSessionHostRecoverySnapshot(record)) return "recovery_snapshot";
4441
+ return "inactive_record";
4442
+ }
4443
+ function partitionSessionHostRecords(records) {
4444
+ const liveRuntimes = [];
4445
+ const recoverySnapshots = [];
4446
+ const inactiveRecords = [];
4447
+ for (const record of records) {
4448
+ const kind = getSessionHostSurfaceKind(record);
4449
+ if (kind === "live_runtime") {
4450
+ liveRuntimes.push(record);
4451
+ } else if (kind === "recovery_snapshot") {
4452
+ recoverySnapshots.push(record);
4453
+ } else {
4454
+ inactiveRecords.push(record);
4455
+ }
4456
+ }
4457
+ return {
4458
+ liveRuntimes,
4459
+ recoverySnapshots,
4460
+ inactiveRecords
4461
+ };
4462
+ }
4463
+ function partitionSessionHostDiagnosticsSessions(records) {
4464
+ return partitionSessionHostRecords(records || []);
4465
+ }
4466
+
4411
4467
  // src/status/chat-tail-hot-sessions.ts
4412
4468
  var DEFAULT_ACTIVE_CHAT_POLL_STATUSES = /* @__PURE__ */ new Set([
4413
4469
  "generating",
@@ -4415,6 +4471,7 @@ var DEFAULT_ACTIVE_CHAT_POLL_STATUSES = /* @__PURE__ */ new Set([
4415
4471
  "starting"
4416
4472
  ]);
4417
4473
  var DEFAULT_CHAT_TAIL_RECENT_MESSAGE_GRACE_MS = 8e3;
4474
+ var LIVE_RUNTIME_LIFECYCLES = /* @__PURE__ */ new Set(["starting", "running", "stopping", "interrupted"]);
4418
4475
  function parseMessageTimestamp(value) {
4419
4476
  if (typeof value === "number" && Number.isFinite(value)) return value;
4420
4477
  if (typeof value === "string") {
@@ -4423,6 +4480,23 @@ function parseMessageTimestamp(value) {
4423
4480
  }
4424
4481
  return 0;
4425
4482
  }
4483
+ function isDefinitelyNonLiveRuntimeSession(session) {
4484
+ const surfaceKind = String(session?.runtimeSurfaceKind || "").trim();
4485
+ if (surfaceKind === "live_runtime") return false;
4486
+ if (surfaceKind === "recovery_snapshot") return true;
4487
+ if (surfaceKind === "inactive_record") return false;
4488
+ const lifecycle = String(session?.runtimeLifecycle || "").trim();
4489
+ if (lifecycle && LIVE_RUNTIME_LIFECYCLES.has(lifecycle)) return false;
4490
+ const inferredSurfaceKind = getSessionHostSurfaceKind({
4491
+ lifecycle: lifecycle || null,
4492
+ meta: {
4493
+ restoredFromStorage: session?.runtimeRestoredFromStorage === true,
4494
+ ...session?.runtimeRecoveryState ? { runtimeRecoveryState: session.runtimeRecoveryState } : {}
4495
+ }
4496
+ });
4497
+ if (inferredSurfaceKind === "recovery_snapshot") return true;
4498
+ return false;
4499
+ }
4426
4500
  function classifyHotChatSessionsForSubscriptionFlush(sessions, previousHotSessionIds, options = {}) {
4427
4501
  const now = options.now ?? Date.now();
4428
4502
  const recentMessageGraceMs = Math.max(
@@ -4431,9 +4505,14 @@ function classifyHotChatSessionsForSubscriptionFlush(sessions, previousHotSessio
4431
4505
  );
4432
4506
  const activeStatuses = options.activeStatuses ?? DEFAULT_ACTIVE_CHAT_POLL_STATUSES;
4433
4507
  const active = /* @__PURE__ */ new Set();
4508
+ const excluded = /* @__PURE__ */ new Set();
4434
4509
  for (const session of sessions) {
4435
4510
  const sessionId = typeof session?.id === "string" ? session.id : "";
4436
4511
  if (!sessionId) continue;
4512
+ if (isDefinitelyNonLiveRuntimeSession(session)) {
4513
+ excluded.add(sessionId);
4514
+ continue;
4515
+ }
4437
4516
  const status = String(session?.status || "").toLowerCase();
4438
4517
  const lastMessageAt = parseMessageTimestamp(session?.lastMessageAt);
4439
4518
  const recentlyUpdated = lastMessageAt > 0 && now - lastMessageAt <= recentMessageGraceMs;
@@ -4442,7 +4521,7 @@ function classifyHotChatSessionsForSubscriptionFlush(sessions, previousHotSessio
4442
4521
  }
4443
4522
  }
4444
4523
  const finalizing = new Set(
4445
- Array.from(previousHotSessionIds).filter((sessionId) => !active.has(sessionId))
4524
+ Array.from(previousHotSessionIds).filter((sessionId) => !active.has(sessionId) && !excluded.has(sessionId))
4446
4525
  );
4447
4526
  return { active, finalizing };
4448
4527
  }
@@ -4451,6 +4530,32 @@ function classifyHotChatSessionsForSubscriptionFlush(sessions, previousHotSessio
4451
4530
  var import_ws = __toESM(require("ws"));
4452
4531
  var http = __toESM(require("http"));
4453
4532
  init_logger();
4533
+ function normalizeTitle(value) {
4534
+ return String(value || "").trim().replace(/\s+/g, " ").toLowerCase();
4535
+ }
4536
+ function titlesMatch(lhs, rhs) {
4537
+ const a = normalizeTitle(lhs);
4538
+ const b = normalizeTitle(rhs);
4539
+ if (!a || !b) return false;
4540
+ return a === b || a.includes(b) || b.includes(a);
4541
+ }
4542
+ function resolveCdpPageTarget(params) {
4543
+ const { pages, pinnedTargetId, previousPageTitle } = params;
4544
+ if (pages.length === 0) return { target: null, retargeted: false };
4545
+ if (!pinnedTargetId) {
4546
+ return { target: pages[0] || null, retargeted: false };
4547
+ }
4548
+ const exact = pages.find((page) => page.id === pinnedTargetId);
4549
+ if (exact) return { target: exact, retargeted: false };
4550
+ const titleMatchesList = pages.filter((page) => titlesMatch(page.title, previousPageTitle));
4551
+ if (titleMatchesList.length === 1) {
4552
+ return { target: titleMatchesList[0], retargeted: true };
4553
+ }
4554
+ if (pages.length === 1) {
4555
+ return { target: pages[0], retargeted: true };
4556
+ }
4557
+ return { target: null, retargeted: false };
4558
+ }
4454
4559
  var DaemonCdpManager = class {
4455
4560
  ws = null;
4456
4561
  browserWs = null;
@@ -4611,18 +4716,28 @@ var DaemonCdpManager = class {
4611
4716
  resolve11(targets.find((t) => t.webSocketDebuggerUrl) || null);
4612
4717
  return;
4613
4718
  }
4614
- const mainPages = pages.filter((t) => !this.isNonMainTitle(t.title || ""));
4615
- const list = mainPages.length > 0 ? mainPages : pages;
4719
+ const titleFilteredPages = pages.filter((t) => !this.isNonMainTitle(t.title || ""));
4720
+ const mainPages = titleFilteredPages.filter((t) => this.isMainPageUrl(t.url));
4721
+ const list = mainPages.length > 0 ? mainPages : titleFilteredPages.length > 0 ? titleFilteredPages : pages;
4616
4722
  this.log(`[CDP] pages(${list.length}): ${list.map((t) => `"${t.title}"`).join(", ")}`);
4617
- if (this._targetId) {
4618
- const specific = list.find((t) => t.id === this._targetId);
4619
- if (specific) {
4620
- this._pageTitle = specific.title || "";
4621
- resolve11(specific);
4622
- } else {
4623
- this.log(`[CDP] Target ${this._targetId} not found in page list`);
4624
- resolve11(null);
4723
+ const previousTargetId = this._targetId;
4724
+ const selected = resolveCdpPageTarget({
4725
+ pages: list,
4726
+ pinnedTargetId: previousTargetId,
4727
+ previousPageTitle: this._pageTitle
4728
+ });
4729
+ if (selected.target) {
4730
+ if (selected.retargeted && previousTargetId && previousTargetId !== selected.target.id) {
4731
+ this.log(`[CDP] Target ${previousTargetId} rekeyed to ${selected.target.id}`);
4732
+ this._targetId = selected.target.id;
4625
4733
  }
4734
+ this._pageTitle = selected.target.title || "";
4735
+ resolve11(selected.target);
4736
+ return;
4737
+ }
4738
+ if (previousTargetId) {
4739
+ this.log(`[CDP] Target ${previousTargetId} not found in page list`);
4740
+ resolve11(null);
4626
4741
  return;
4627
4742
  }
4628
4743
  this._pageTitle = list[0]?.title || "";
@@ -6062,7 +6177,17 @@ var os5 = __toESM(require("os"));
6062
6177
  init_chat_message_normalization();
6063
6178
  var HISTORY_DIR = path7.join(os5.homedir(), ".adhdev", "history");
6064
6179
  var RETAIN_DAYS = 30;
6180
+ var SAVED_HISTORY_INDEX_VERSION = 1;
6181
+ var SAVED_HISTORY_INDEX_FILE = ".saved-history-index.json";
6182
+ var SAVED_HISTORY_INDEX_LOCK_SUFFIX = ".lock";
6183
+ var SAVED_HISTORY_INDEX_LOCK_WAIT_MS = 1500;
6184
+ var SAVED_HISTORY_INDEX_LOCK_STALE_MS = 15e3;
6185
+ var SAVED_HISTORY_INDEX_LOCK_POLL_MS = 25;
6186
+ var SAVED_HISTORY_ROLLUP_THRESHOLD_BYTES = 16 * 1024 * 1024;
6065
6187
  var savedHistorySessionCache = /* @__PURE__ */ new Map();
6188
+ var savedHistoryFileSummaryCache = /* @__PURE__ */ new Map();
6189
+ var savedHistoryBackgroundRefresh = /* @__PURE__ */ new Set();
6190
+ var savedHistoryRollupInFlight = /* @__PURE__ */ new Set();
6066
6191
  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;
6067
6192
  function normalizeHistoryComparable(text) {
6068
6193
  return String(text || "").replace(/\s+/g, " ").trim();
@@ -6120,6 +6245,68 @@ function sanitizeHistoryMessage(agentType, message) {
6120
6245
  content
6121
6246
  };
6122
6247
  }
6248
+ function sortSavedHistorySessionSummaries(summaries) {
6249
+ return summaries.slice().sort((a, b) => b.lastMessageAt - a.lastMessageAt);
6250
+ }
6251
+ function buildSavedHistorySessionSummaryMapFromEntries(entries) {
6252
+ const summaries = /* @__PURE__ */ new Map();
6253
+ for (const entry of Array.from(entries.values())) {
6254
+ const fileSummary = entry.summary;
6255
+ if (!fileSummary || fileSummary.messageCount <= 0 || !fileSummary.lastMessageAt) continue;
6256
+ const existing = summaries.get(fileSummary.historySessionId);
6257
+ if (!existing) {
6258
+ summaries.set(fileSummary.historySessionId, {
6259
+ historySessionId: fileSummary.historySessionId,
6260
+ sessionTitle: fileSummary.sessionTitle,
6261
+ messageCount: fileSummary.messageCount,
6262
+ firstMessageAt: fileSummary.firstMessageAt,
6263
+ lastMessageAt: fileSummary.lastMessageAt,
6264
+ preview: fileSummary.preview,
6265
+ workspace: fileSummary.workspace
6266
+ });
6267
+ continue;
6268
+ }
6269
+ existing.messageCount += fileSummary.messageCount;
6270
+ if (!existing.firstMessageAt || fileSummary.firstMessageAt < existing.firstMessageAt) {
6271
+ existing.firstMessageAt = fileSummary.firstMessageAt;
6272
+ }
6273
+ if (fileSummary.lastMessageAt >= existing.lastMessageAt) {
6274
+ existing.lastMessageAt = fileSummary.lastMessageAt;
6275
+ if (fileSummary.sessionTitle) existing.sessionTitle = fileSummary.sessionTitle;
6276
+ if (fileSummary.preview) existing.preview = fileSummary.preview;
6277
+ }
6278
+ if (!existing.workspace && fileSummary.workspace) {
6279
+ existing.workspace = fileSummary.workspace;
6280
+ }
6281
+ }
6282
+ return Object.fromEntries(sortSavedHistorySessionSummaries(Array.from(summaries.values())).map((summary) => [summary.historySessionId, summary]));
6283
+ }
6284
+ function readPersistedSavedHistorySessionSummaries(dir) {
6285
+ try {
6286
+ const filePath = getSavedHistoryIndexFilePath(dir);
6287
+ if (!fs3.existsSync(filePath)) return null;
6288
+ const raw = JSON.parse(fs3.readFileSync(filePath, "utf-8"));
6289
+ if (!raw || raw.version !== SAVED_HISTORY_INDEX_VERSION || !raw.sessions || typeof raw.sessions !== "object") {
6290
+ return null;
6291
+ }
6292
+ return sortSavedHistorySessionSummaries(
6293
+ Object.values(raw.sessions).filter((summary) => !!summary && typeof summary.historySessionId === "string" && summary.messageCount > 0 && summary.lastMessageAt > 0).map((summary) => ({
6294
+ historySessionId: summary.historySessionId,
6295
+ sessionTitle: summary.sessionTitle,
6296
+ messageCount: summary.messageCount,
6297
+ firstMessageAt: summary.firstMessageAt,
6298
+ lastMessageAt: summary.lastMessageAt,
6299
+ preview: summary.preview,
6300
+ workspace: summary.workspace
6301
+ }))
6302
+ );
6303
+ } catch {
6304
+ return null;
6305
+ }
6306
+ }
6307
+ function shouldScheduleSavedHistoryRollup(totalBytes) {
6308
+ return Number.isFinite(totalBytes) && totalBytes >= SAVED_HISTORY_ROLLUP_THRESHOLD_BYTES;
6309
+ }
6123
6310
  function sanitizeHistoryFileSegment(value) {
6124
6311
  return String(value || "").replace(/[^a-zA-Z0-9_-]/g, "_");
6125
6312
  }
@@ -6133,71 +6320,386 @@ function listHistoryFiles(dir, historySessionId) {
6133
6320
  return true;
6134
6321
  }).sort().reverse();
6135
6322
  }
6136
- function buildSavedHistoryCacheSignature(dir, files) {
6137
- return files.map((file) => {
6323
+ function normalizeSavedHistorySessionId(agentType, historySessionId) {
6324
+ const normalizedId = String(historySessionId || "").trim();
6325
+ if (!normalizedId) return "";
6326
+ const strictProviderId = normalizeProviderSessionId(agentType, normalizedId);
6327
+ if (strictProviderId) return strictProviderId;
6328
+ return agentType === "hermes-cli" ? "" : normalizedId;
6329
+ }
6330
+ function extractSavedHistorySessionIdFromFile(agentType, file) {
6331
+ const match = file.match(/^([A-Za-z0-9_-]+)_\d{4}-\d{2}-\d{2}\.jsonl$/);
6332
+ return normalizeSavedHistorySessionId(agentType, match?.[1] || "");
6333
+ }
6334
+ function buildSavedHistoryFileSignatureMap(dir, files) {
6335
+ return new Map(files.map((file) => {
6138
6336
  try {
6139
6337
  const stat = fs3.statSync(path7.join(dir, file));
6140
- return `${file}:${stat.size}:${Math.trunc(stat.mtimeMs)}`;
6338
+ return [file, `${file}:${stat.size}:${Math.trunc(stat.mtimeMs)}`];
6141
6339
  } catch {
6142
- return `${file}:missing`;
6143
- }
6144
- }).join("|");
6145
- }
6146
- function computeSavedHistorySessionSummaries(agentType, dir, files) {
6147
- const groupedFiles = /* @__PURE__ */ new Map();
6148
- const filePattern = /^([A-Za-z0-9_-]+)_\d{4}-\d{2}-\d{2}\.jsonl$/;
6149
- for (const file of files) {
6150
- const match = file.match(filePattern);
6151
- if (!match?.[1]) continue;
6152
- const historySessionId = match[1];
6153
- const grouped = groupedFiles.get(historySessionId) || [];
6154
- grouped.push(file);
6155
- groupedFiles.set(historySessionId, grouped);
6156
- }
6157
- const summaries = [];
6158
- for (const [historySessionId, grouped] of groupedFiles.entries()) {
6159
- let messageCount = 0;
6160
- let firstMessageAt = 0;
6161
- let lastMessageAt = 0;
6162
- let sessionTitle = "";
6163
- let preview = "";
6164
- let workspace = "";
6165
- for (const file of grouped.sort()) {
6166
- const filePath = path7.join(dir, file);
6167
- const content = fs3.readFileSync(filePath, "utf-8");
6168
- const lines = content.split("\n").filter(Boolean);
6169
- for (const line of lines) {
6170
- let parsed = null;
6340
+ return [file, `${file}:missing`];
6341
+ }
6342
+ }));
6343
+ }
6344
+ function buildSavedHistoryCacheSignature(files, fileSignatures) {
6345
+ return files.map((file) => fileSignatures.get(file) || `${file}:missing`).join("|");
6346
+ }
6347
+ function getSavedHistoryIndexFilePath(dir) {
6348
+ return path7.join(dir, SAVED_HISTORY_INDEX_FILE);
6349
+ }
6350
+ function getSavedHistoryIndexLockPath(dir) {
6351
+ return `${getSavedHistoryIndexFilePath(dir)}${SAVED_HISTORY_INDEX_LOCK_SUFFIX}`;
6352
+ }
6353
+ function sleepBlocking(ms) {
6354
+ if (ms <= 0) return;
6355
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
6356
+ }
6357
+ function loadPersistedSavedHistoryIndexFromFile(dir) {
6358
+ try {
6359
+ const filePath = getSavedHistoryIndexFilePath(dir);
6360
+ if (!fs3.existsSync(filePath)) return /* @__PURE__ */ new Map();
6361
+ const raw = JSON.parse(fs3.readFileSync(filePath, "utf-8"));
6362
+ if (!raw || raw.version !== SAVED_HISTORY_INDEX_VERSION || !raw.files || typeof raw.files !== "object") {
6363
+ return /* @__PURE__ */ new Map();
6364
+ }
6365
+ return new Map(
6366
+ Object.entries(raw.files).filter(([file, entry]) => !!file && !!entry && typeof entry.signature === "string").map(([file, entry]) => [file, {
6367
+ signature: entry.signature,
6368
+ summary: entry.summary || null
6369
+ }])
6370
+ );
6371
+ } catch {
6372
+ return /* @__PURE__ */ new Map();
6373
+ }
6374
+ }
6375
+ function writePersistedSavedHistoryIndexFile(dir, entries) {
6376
+ const filePath = getSavedHistoryIndexFilePath(dir);
6377
+ const tempPath = `${filePath}.tmp`;
6378
+ const payload = {
6379
+ version: SAVED_HISTORY_INDEX_VERSION,
6380
+ files: Object.fromEntries(entries.entries()),
6381
+ sessions: buildSavedHistorySessionSummaryMapFromEntries(entries)
6382
+ };
6383
+ fs3.writeFileSync(tempPath, JSON.stringify(payload), "utf-8");
6384
+ fs3.renameSync(tempPath, filePath);
6385
+ }
6386
+ function acquireSavedHistoryIndexLock(dir) {
6387
+ const lockPath = getSavedHistoryIndexLockPath(dir);
6388
+ const deadline = Date.now() + SAVED_HISTORY_INDEX_LOCK_WAIT_MS;
6389
+ while (Date.now() <= deadline) {
6390
+ try {
6391
+ fs3.mkdirSync(lockPath);
6392
+ return () => {
6171
6393
  try {
6172
- parsed = JSON.parse(line);
6394
+ fs3.rmSync(lockPath, { recursive: true, force: true });
6173
6395
  } catch {
6174
- parsed = null;
6175
6396
  }
6176
- if (!parsed || parsed.historySessionId !== historySessionId) continue;
6177
- if (parsed.kind === "session_start") {
6178
- if (!workspace && parsed.workspace) workspace = parsed.workspace;
6397
+ };
6398
+ } catch (error) {
6399
+ if (error?.code !== "EEXIST") return null;
6400
+ try {
6401
+ const stat = fs3.statSync(lockPath);
6402
+ if (Date.now() - stat.mtimeMs > SAVED_HISTORY_INDEX_LOCK_STALE_MS) {
6403
+ fs3.rmSync(lockPath, { recursive: true, force: true });
6179
6404
  continue;
6180
6405
  }
6181
- messageCount += 1;
6182
- if (!firstMessageAt || parsed.receivedAt < firstMessageAt) firstMessageAt = parsed.receivedAt;
6183
- if (!lastMessageAt || parsed.receivedAt > lastMessageAt) lastMessageAt = parsed.receivedAt;
6184
- if (parsed.sessionTitle) sessionTitle = parsed.sessionTitle;
6185
- if (parsed.role !== "system" && parsed.content.trim()) preview = parsed.content.trim();
6186
- }
6187
- }
6188
- if (messageCount === 0 || !lastMessageAt) continue;
6189
- summaries.push({
6190
- historySessionId,
6191
- sessionTitle: sessionTitle || void 0,
6192
- messageCount,
6193
- firstMessageAt,
6194
- lastMessageAt,
6195
- preview: preview || void 0,
6196
- workspace: workspace || void 0
6197
- });
6406
+ } catch {
6407
+ continue;
6408
+ }
6409
+ sleepBlocking(SAVED_HISTORY_INDEX_LOCK_POLL_MS);
6410
+ }
6411
+ }
6412
+ return null;
6413
+ }
6414
+ function withLockedPersistedSavedHistoryIndex(dir, callback) {
6415
+ const release2 = acquireSavedHistoryIndexLock(dir);
6416
+ if (!release2) return null;
6417
+ try {
6418
+ const entries = loadPersistedSavedHistoryIndexFromFile(dir);
6419
+ const result = callback(entries);
6420
+ writePersistedSavedHistoryIndexFile(dir, entries);
6421
+ return result;
6422
+ } catch {
6423
+ return null;
6424
+ } finally {
6425
+ release2();
6426
+ }
6427
+ }
6428
+ function loadPersistedSavedHistoryIndex(dir) {
6429
+ return loadPersistedSavedHistoryIndexFromFile(dir);
6430
+ }
6431
+ function savePersistedSavedHistoryIndex(dir, entries) {
6432
+ withLockedPersistedSavedHistoryIndex(dir, (currentEntries) => {
6433
+ const incomingFiles = new Set(Array.from(entries.keys()));
6434
+ for (const [file, entry] of Array.from(entries.entries())) {
6435
+ const liveSignature = buildSavedHistoryFileSignature(dir, file);
6436
+ const existingEntry = currentEntries.get(file);
6437
+ if (existingEntry && existingEntry.signature !== liveSignature && entry.signature !== liveSignature) {
6438
+ continue;
6439
+ }
6440
+ if (entry.signature !== liveSignature && (!existingEntry || existingEntry.signature !== liveSignature)) {
6441
+ continue;
6442
+ }
6443
+ currentEntries.set(file, entry.signature === liveSignature ? entry : {
6444
+ signature: liveSignature,
6445
+ summary: existingEntry?.summary || entry.summary
6446
+ });
6447
+ }
6448
+ for (const file of Array.from(currentEntries.keys())) {
6449
+ if (incomingFiles.has(file)) continue;
6450
+ if (!fs3.existsSync(path7.join(dir, file))) {
6451
+ currentEntries.delete(file);
6452
+ }
6453
+ }
6454
+ });
6455
+ }
6456
+ function invalidatePersistedSavedHistoryIndex(agentType, dir) {
6457
+ try {
6458
+ fs3.rmSync(getSavedHistoryIndexFilePath(dir), { force: true });
6459
+ } catch {
6460
+ }
6461
+ savedHistorySessionCache.delete(agentType.replace(/[^a-zA-Z0-9_-]/g, "_"));
6462
+ }
6463
+ function buildSavedHistoryIndexFileSignature(dir) {
6464
+ try {
6465
+ const stat = fs3.statSync(getSavedHistoryIndexFilePath(dir));
6466
+ return `index:${stat.size}:${Math.trunc(stat.mtimeMs)}`;
6467
+ } catch {
6468
+ return "index:missing";
6469
+ }
6470
+ }
6471
+ function historyDirectoryHasFilesNewerThanIndex(dir) {
6472
+ try {
6473
+ const indexStat = fs3.statSync(getSavedHistoryIndexFilePath(dir));
6474
+ const files = listHistoryFiles(dir);
6475
+ for (const file of files) {
6476
+ const stat = fs3.statSync(path7.join(dir, file));
6477
+ if (stat.mtimeMs > indexStat.mtimeMs) return true;
6478
+ }
6479
+ return false;
6480
+ } catch {
6481
+ return true;
6482
+ }
6483
+ }
6484
+ function buildSavedHistoryFileSignature(dir, file) {
6485
+ try {
6486
+ const stat = fs3.statSync(path7.join(dir, file));
6487
+ return `${file}:${stat.size}:${Math.trunc(stat.mtimeMs)}`;
6488
+ } catch {
6489
+ return `${file}:missing`;
6490
+ }
6491
+ }
6492
+ function persistSavedHistoryFileSummaryEntry(agentType, dir, file, updater) {
6493
+ const filePath = path7.join(dir, file);
6494
+ const result = withLockedPersistedSavedHistoryIndex(dir, (entries) => {
6495
+ const currentEntry = entries.get(file) || null;
6496
+ const nextSummary = updater(currentEntry?.summary || null);
6497
+ const nextEntry = {
6498
+ signature: buildSavedHistoryFileSignature(dir, file),
6499
+ summary: nextSummary
6500
+ };
6501
+ entries.set(file, nextEntry);
6502
+ savedHistoryFileSummaryCache.set(filePath, nextEntry);
6503
+ return nextEntry;
6504
+ });
6505
+ if (!result) return;
6506
+ if (result.summary?.historySessionId && shouldScheduleSavedHistoryRollupForSignature(result.signature)) {
6507
+ scheduleSavedHistoryRollup(agentType, result.summary.historySessionId);
6508
+ }
6509
+ }
6510
+ function updateSavedHistoryIndexForSessionStart(agentType, dir, file, historySessionId, workspace) {
6511
+ const normalizedSessionId = normalizeSavedHistorySessionId(agentType, historySessionId);
6512
+ const normalizedWorkspace = String(workspace || "").trim();
6513
+ if (!normalizedSessionId || !normalizedWorkspace) return;
6514
+ persistSavedHistoryFileSummaryEntry(agentType, dir, file, (currentSummary) => ({
6515
+ file,
6516
+ historySessionId: normalizedSessionId,
6517
+ messageCount: currentSummary?.messageCount || 0,
6518
+ firstMessageAt: currentSummary?.firstMessageAt || 0,
6519
+ lastMessageAt: currentSummary?.lastMessageAt || 0,
6520
+ sessionTitle: currentSummary?.sessionTitle,
6521
+ preview: currentSummary?.preview,
6522
+ workspace: normalizedWorkspace
6523
+ }));
6524
+ }
6525
+ function updateSavedHistoryIndexForAppendedMessages(agentType, dir, file, historySessionId, messages) {
6526
+ const normalizedSessionId = normalizeSavedHistorySessionId(agentType, historySessionId || "");
6527
+ if (!normalizedSessionId || messages.length === 0) return;
6528
+ persistSavedHistoryFileSummaryEntry(agentType, dir, file, (currentSummary) => {
6529
+ const nextSummary = {
6530
+ file,
6531
+ historySessionId: normalizedSessionId,
6532
+ messageCount: currentSummary?.messageCount || 0,
6533
+ firstMessageAt: currentSummary?.firstMessageAt || 0,
6534
+ lastMessageAt: currentSummary?.lastMessageAt || 0,
6535
+ sessionTitle: currentSummary?.sessionTitle,
6536
+ preview: currentSummary?.preview,
6537
+ workspace: currentSummary?.workspace
6538
+ };
6539
+ for (const message of messages) {
6540
+ if (!message || message.historySessionId !== historySessionId) continue;
6541
+ if (message.kind === "session_start") {
6542
+ if (message.workspace) nextSummary.workspace = message.workspace;
6543
+ continue;
6544
+ }
6545
+ nextSummary.messageCount += 1;
6546
+ if (!nextSummary.firstMessageAt || message.receivedAt < nextSummary.firstMessageAt) {
6547
+ nextSummary.firstMessageAt = message.receivedAt;
6548
+ }
6549
+ if (!nextSummary.lastMessageAt || message.receivedAt >= nextSummary.lastMessageAt) {
6550
+ nextSummary.lastMessageAt = message.receivedAt;
6551
+ if (message.sessionTitle) nextSummary.sessionTitle = message.sessionTitle;
6552
+ if (message.role !== "system" && message.content.trim()) nextSummary.preview = message.content.trim();
6553
+ } else if (message.sessionTitle) {
6554
+ nextSummary.sessionTitle = message.sessionTitle;
6555
+ }
6556
+ if (!nextSummary.preview && message.role !== "system" && message.content.trim()) {
6557
+ nextSummary.preview = message.content.trim();
6558
+ }
6559
+ }
6560
+ return nextSummary;
6561
+ });
6562
+ }
6563
+ function computeSavedHistoryFileSummary(agentType, dir, file) {
6564
+ const historySessionId = extractSavedHistorySessionIdFromFile(agentType, file);
6565
+ if (!historySessionId) return null;
6566
+ const filePath = path7.join(dir, file);
6567
+ const content = fs3.readFileSync(filePath, "utf-8");
6568
+ const lines = content.split("\n").filter(Boolean);
6569
+ let messageCount = 0;
6570
+ let firstMessageAt = 0;
6571
+ let lastMessageAt = 0;
6572
+ let sessionTitle = "";
6573
+ let preview = "";
6574
+ let workspace = "";
6575
+ for (const line of lines) {
6576
+ let parsed = null;
6577
+ try {
6578
+ parsed = JSON.parse(line);
6579
+ } catch {
6580
+ parsed = null;
6581
+ }
6582
+ if (!parsed || parsed.historySessionId !== historySessionId) continue;
6583
+ if (parsed.kind === "session_start") {
6584
+ if (!workspace && parsed.workspace) workspace = parsed.workspace;
6585
+ continue;
6586
+ }
6587
+ messageCount += 1;
6588
+ if (!firstMessageAt || parsed.receivedAt < firstMessageAt) firstMessageAt = parsed.receivedAt;
6589
+ if (!lastMessageAt || parsed.receivedAt > lastMessageAt) lastMessageAt = parsed.receivedAt;
6590
+ if (parsed.sessionTitle) sessionTitle = parsed.sessionTitle;
6591
+ if (parsed.role !== "system" && parsed.content.trim()) preview = parsed.content.trim();
6592
+ }
6593
+ if (messageCount === 0 || !lastMessageAt) return null;
6594
+ return {
6595
+ file,
6596
+ historySessionId,
6597
+ messageCount,
6598
+ firstMessageAt,
6599
+ lastMessageAt,
6600
+ sessionTitle: sessionTitle || void 0,
6601
+ preview: preview || void 0,
6602
+ workspace: workspace || void 0
6603
+ };
6604
+ }
6605
+ function shouldScheduleSavedHistoryRollupForSignature(signature) {
6606
+ const parts = String(signature || "").split(":");
6607
+ const size = Number(parts[1] || 0);
6608
+ return shouldScheduleSavedHistoryRollup(size);
6609
+ }
6610
+ function scheduleSavedHistoryRollup(agentType, historySessionId) {
6611
+ const key = `${agentType}:${historySessionId}`;
6612
+ if (!historySessionId || savedHistoryRollupInFlight.has(key)) return;
6613
+ savedHistoryRollupInFlight.add(key);
6614
+ setTimeout(() => {
6615
+ try {
6616
+ new ChatHistoryWriter().compactHistorySession(agentType, historySessionId);
6617
+ } finally {
6618
+ savedHistoryRollupInFlight.delete(key);
6619
+ }
6620
+ }, 0);
6621
+ }
6622
+ function scheduleSavedHistoryBackgroundRefresh(agentType, dir) {
6623
+ const key = `${agentType}:${dir}`;
6624
+ if (savedHistoryBackgroundRefresh.has(key)) return;
6625
+ savedHistoryBackgroundRefresh.add(key);
6626
+ setTimeout(() => {
6627
+ try {
6628
+ if (!fs3.existsSync(dir)) return;
6629
+ const files = listHistoryFiles(dir);
6630
+ const fileSignatures = buildSavedHistoryFileSignatureMap(dir, files);
6631
+ const persistedEntries = loadPersistedSavedHistoryIndex(dir);
6632
+ const computed = computeSavedHistorySessionSummaries(agentType, dir, files, fileSignatures, persistedEntries);
6633
+ savePersistedSavedHistoryIndex(dir, computed.persistedEntries || /* @__PURE__ */ new Map());
6634
+ const refreshedIndexSignature = buildSavedHistoryIndexFileSignature(dir);
6635
+ savedHistorySessionCache.set(agentType.replace(/[^a-zA-Z0-9_-]/g, "_"), {
6636
+ signature: refreshedIndexSignature,
6637
+ summaries: computed.summaries || []
6638
+ });
6639
+ for (const [file, entry] of Array.from(computed.persistedEntries.entries())) {
6640
+ if (!entry?.summary || !shouldScheduleSavedHistoryRollupForSignature(entry.signature)) continue;
6641
+ scheduleSavedHistoryRollup(agentType, entry.summary.historySessionId);
6642
+ }
6643
+ } catch {
6644
+ } finally {
6645
+ savedHistoryBackgroundRefresh.delete(key);
6646
+ }
6647
+ }, 0);
6648
+ }
6649
+ function computeSavedHistorySessionSummaries(agentType, dir, files, fileSignatures, persistedEntries) {
6650
+ const summaryBySessionId = /* @__PURE__ */ new Map();
6651
+ const nextPersistedEntries = /* @__PURE__ */ new Map();
6652
+ for (const file of files.slice().sort()) {
6653
+ const filePath = path7.join(dir, file);
6654
+ const signature = fileSignatures.get(file) || `${file}:missing`;
6655
+ const cached = savedHistoryFileSummaryCache.get(filePath);
6656
+ const persisted = persistedEntries.get(file);
6657
+ const reusableEntry = cached?.signature === signature ? cached : persisted?.signature === signature ? persisted : null;
6658
+ const fileSummary = reusableEntry?.summary || computeSavedHistoryFileSummary(agentType, dir, file);
6659
+ const nextEntry = reusableEntry || {
6660
+ signature,
6661
+ summary: fileSummary
6662
+ };
6663
+ if (!reusableEntry) {
6664
+ nextEntry.signature = signature;
6665
+ nextEntry.summary = fileSummary;
6666
+ }
6667
+ savedHistoryFileSummaryCache.set(filePath, nextEntry);
6668
+ nextPersistedEntries.set(file, nextEntry);
6669
+ if (!fileSummary) continue;
6670
+ const existing = summaryBySessionId.get(fileSummary.historySessionId);
6671
+ if (fileSummary.messageCount <= 0 || !fileSummary.lastMessageAt) {
6672
+ continue;
6673
+ }
6674
+ if (!existing) {
6675
+ summaryBySessionId.set(fileSummary.historySessionId, {
6676
+ historySessionId: fileSummary.historySessionId,
6677
+ sessionTitle: fileSummary.sessionTitle,
6678
+ messageCount: fileSummary.messageCount,
6679
+ firstMessageAt: fileSummary.firstMessageAt,
6680
+ lastMessageAt: fileSummary.lastMessageAt,
6681
+ preview: fileSummary.preview,
6682
+ workspace: fileSummary.workspace
6683
+ });
6684
+ continue;
6685
+ }
6686
+ existing.messageCount += fileSummary.messageCount;
6687
+ if (!existing.firstMessageAt || fileSummary.firstMessageAt < existing.firstMessageAt) {
6688
+ existing.firstMessageAt = fileSummary.firstMessageAt;
6689
+ }
6690
+ if (fileSummary.lastMessageAt >= existing.lastMessageAt) {
6691
+ existing.lastMessageAt = fileSummary.lastMessageAt;
6692
+ if (fileSummary.sessionTitle) existing.sessionTitle = fileSummary.sessionTitle;
6693
+ if (fileSummary.preview) existing.preview = fileSummary.preview;
6694
+ }
6695
+ if (!existing.workspace && fileSummary.workspace) {
6696
+ existing.workspace = fileSummary.workspace;
6697
+ }
6198
6698
  }
6199
- summaries.sort((a, b) => b.lastMessageAt - a.lastMessageAt);
6200
- return summaries;
6699
+ return {
6700
+ summaries: Array.from(summaryBySessionId.values()).sort((a, b) => b.lastMessageAt - a.lastMessageAt),
6701
+ persistedEntries: nextPersistedEntries
6702
+ };
6201
6703
  }
6202
6704
  var ChatHistoryWriter = class {
6203
6705
  /** Last seen message count per agent (deduplication) */
@@ -6272,9 +6774,11 @@ var ChatHistoryWriter = class {
6272
6774
  fs3.mkdirSync(dir, { recursive: true });
6273
6775
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
6274
6776
  const filePrefix = effectiveHistoryKey ? `${this.sanitize(effectiveHistoryKey)}_` : "";
6275
- const filePath = path7.join(dir, `${filePrefix}${date}.jsonl`);
6777
+ const fileName = `${filePrefix}${date}.jsonl`;
6778
+ const filePath = path7.join(dir, fileName);
6276
6779
  const lines = newMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
6277
6780
  fs3.appendFileSync(filePath, lines, "utf-8");
6781
+ updateSavedHistoryIndexForAppendedMessages(agentType, dir, fileName, effectiveHistoryKey, newMessages);
6278
6782
  const prevCount = this.lastSeenCounts.get(dedupKey) || 0;
6279
6783
  if (!historySessionId && messages.length < prevCount * 0.5 && prevCount > 3) {
6280
6784
  seenHashes.clear();
@@ -6365,7 +6869,8 @@ var ChatHistoryWriter = class {
6365
6869
  const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
6366
6870
  fs3.mkdirSync(dir, { recursive: true });
6367
6871
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
6368
- const filePath = path7.join(dir, `${this.sanitize(id)}_${date}.jsonl`);
6872
+ const fileName = `${this.sanitize(id)}_${date}.jsonl`;
6873
+ const filePath = path7.join(dir, fileName);
6369
6874
  const record = {
6370
6875
  ts: (/* @__PURE__ */ new Date()).toISOString(),
6371
6876
  receivedAt: Date.now(),
@@ -6378,6 +6883,7 @@ var ChatHistoryWriter = class {
6378
6883
  workspace: ws
6379
6884
  };
6380
6885
  fs3.appendFileSync(filePath, JSON.stringify(record) + "\n", "utf-8");
6886
+ updateSavedHistoryIndexForSessionStart(agentType, dir, fileName, id, ws);
6381
6887
  } catch {
6382
6888
  }
6383
6889
  }
@@ -6443,6 +6949,7 @@ var ChatHistoryWriter = class {
6443
6949
  }
6444
6950
  fs3.unlinkSync(sourcePath);
6445
6951
  }
6952
+ invalidatePersistedSavedHistoryIndex(agentType, dir);
6446
6953
  } catch {
6447
6954
  }
6448
6955
  }
@@ -6492,6 +6999,7 @@ var ChatHistoryWriter = class {
6492
6999
  fs3.writeFileSync(filePath, `${collapsed.map((entry) => JSON.stringify(entry)).join("\n")}
6493
7000
  `, "utf-8");
6494
7001
  }
7002
+ invalidatePersistedSavedHistoryIndex(agentType, dir);
6495
7003
  } catch {
6496
7004
  }
6497
7005
  }
@@ -6511,13 +7019,18 @@ var ChatHistoryWriter = class {
6511
7019
  for (const dir of agentDirs) {
6512
7020
  const dirPath = path7.join(HISTORY_DIR, dir.name);
6513
7021
  const files = fs3.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl") || f.endsWith(".terminal.log"));
7022
+ let removedAny = false;
6514
7023
  for (const file of files) {
6515
7024
  const filePath = path7.join(dirPath, file);
6516
7025
  const stat = fs3.statSync(filePath);
6517
7026
  if (stat.mtimeMs < cutoff) {
6518
7027
  fs3.unlinkSync(filePath);
7028
+ removedAny = true;
6519
7029
  }
6520
7030
  }
7031
+ if (removedAny) {
7032
+ invalidatePersistedSavedHistoryIndex(dir.name, dirPath);
7033
+ }
6521
7034
  }
6522
7035
  } catch {
6523
7036
  }
@@ -6583,18 +7096,51 @@ function listSavedHistorySessions(agentType, options = {}) {
6583
7096
  savedHistorySessionCache.delete(sanitized);
6584
7097
  return { sessions: [], hasMore: false };
6585
7098
  }
6586
- const files = listHistoryFiles(dir);
6587
- const signature = buildSavedHistoryCacheSignature(dir, files);
6588
7099
  const cached = savedHistorySessionCache.get(sanitized);
6589
- const summaries = cached?.signature === signature ? cached.summaries : computeSavedHistorySessionSummaries(agentType, dir, files);
6590
- if (!cached || cached.signature !== signature) {
7100
+ const offset = Math.max(0, options.offset || 0);
7101
+ const limit = Math.max(1, options.limit || 30);
7102
+ const indexSignature = buildSavedHistoryIndexFileSignature(dir);
7103
+ let cacheWasInvalidated = false;
7104
+ if (cached) {
7105
+ const cacheLooksPersisted = cached.signature.startsWith("index:");
7106
+ const cacheStillValid = cacheLooksPersisted ? cached.signature === indexSignature : (() => {
7107
+ const files2 = listHistoryFiles(dir);
7108
+ const fileSignatures2 = buildSavedHistoryFileSignatureMap(dir, files2);
7109
+ return cached.signature === buildSavedHistoryCacheSignature(files2, fileSignatures2);
7110
+ })();
7111
+ if (cacheStillValid) {
7112
+ const sliced2 = cached.summaries.slice(offset, offset + limit);
7113
+ return {
7114
+ sessions: sliced2,
7115
+ hasMore: cached.summaries.length > offset + limit
7116
+ };
7117
+ }
7118
+ cacheWasInvalidated = true;
7119
+ }
7120
+ const persistedSessions = readPersistedSavedHistorySessionSummaries(dir);
7121
+ if (!cacheWasInvalidated && persistedSessions?.length && !historyDirectoryHasFilesNewerThanIndex(dir)) {
6591
7122
  savedHistorySessionCache.set(sanitized, {
6592
- signature,
6593
- summaries
7123
+ signature: indexSignature,
7124
+ summaries: persistedSessions
6594
7125
  });
7126
+ scheduleSavedHistoryBackgroundRefresh(agentType, dir);
7127
+ const sliced2 = persistedSessions.slice(offset, offset + limit);
7128
+ return {
7129
+ sessions: sliced2,
7130
+ hasMore: persistedSessions.length > offset + limit
7131
+ };
6595
7132
  }
6596
- const offset = Math.max(0, options.offset || 0);
6597
- const limit = Math.max(1, options.limit || 30);
7133
+ const files = listHistoryFiles(dir);
7134
+ const fileSignatures = buildSavedHistoryFileSignatureMap(dir, files);
7135
+ const signature = buildSavedHistoryCacheSignature(files, fileSignatures);
7136
+ const persistedEntries = loadPersistedSavedHistoryIndex(dir);
7137
+ const computed = computeSavedHistorySessionSummaries(agentType, dir, files, fileSignatures, persistedEntries);
7138
+ const summaries = computed.summaries || [];
7139
+ savePersistedSavedHistoryIndex(dir, computed.persistedEntries || /* @__PURE__ */ new Map());
7140
+ savedHistorySessionCache.set(sanitized, {
7141
+ signature,
7142
+ summaries
7143
+ });
6598
7144
  const sliced = summaries.slice(offset, offset + limit);
6599
7145
  return {
6600
7146
  sessions: sliced,
@@ -8232,7 +8778,7 @@ function shouldIncludeSessionMetadata(profile) {
8232
8778
  return profile !== "live";
8233
8779
  }
8234
8780
  function shouldIncludeRuntimeMetadata(profile) {
8235
- return profile !== "live";
8781
+ return true;
8236
8782
  }
8237
8783
  function findCdpManager(cdpManagers, key) {
8238
8784
  const exact = cdpManagers.get(key);
@@ -8347,6 +8893,21 @@ function buildExtensionAgentSession(parent, ext, options) {
8347
8893
  lastUpdated: ext.lastUpdated
8348
8894
  };
8349
8895
  }
8896
+ function shouldIncludeExtensionSession(ext) {
8897
+ const status = String(ext.status || "").trim().toLowerCase();
8898
+ const hasActiveChat = !!ext.activeChat;
8899
+ const hasMessages = Array.isArray(ext.activeChat?.messages) && ext.activeChat.messages.length > 0;
8900
+ const hasModal = !!ext.activeChat?.activeModal;
8901
+ const hasStreams = Array.isArray(ext.agentStreams) && ext.agentStreams.length > 0;
8902
+ const hasProviderSessionId = typeof ext.providerSessionId === "string" && ext.providerSessionId.trim().length > 0;
8903
+ const hasControlValues = !!(ext.controlValues && Object.keys(ext.controlValues).length > 0);
8904
+ const hasProviderControls = Array.isArray(ext.providerControls) && ext.providerControls.length > 0;
8905
+ const hasOpenPanelCapability = Array.isArray(ext.sessionCapabilities) && ext.sessionCapabilities.includes("open_panel");
8906
+ const hasSummaryMetadata = !!ext.summaryMetadata;
8907
+ const hasError = typeof ext.errorMessage === "string" && ext.errorMessage.trim().length > 0;
8908
+ const hasInterestingStatus = !!status && !["idle", "panel_hidden", "disconnected", "not_monitored"].includes(status);
8909
+ return hasActiveChat || hasMessages || hasModal || hasStreams || hasProviderSessionId || hasControlValues || hasProviderControls || hasOpenPanelCapability || hasSummaryMetadata || hasError || hasInterestingStatus;
8910
+ }
8350
8911
  function buildCliSession(state, options) {
8351
8912
  const profile = options.profile || "full";
8352
8913
  const activeChat = normalizeActiveChatData(state.activeChat, getActiveChatOptions(profile));
@@ -8372,8 +8933,12 @@ function buildCliSession(state, options) {
8372
8933
  runtimeKey: state.runtime?.runtimeKey,
8373
8934
  runtimeDisplayName: state.runtime?.displayName,
8374
8935
  runtimeWorkspaceLabel: state.runtime?.workspaceLabel,
8936
+ runtimeLifecycle: state.runtime?.lifecycle ?? null,
8937
+ runtimeSurfaceKind: state.runtime?.surfaceKind,
8375
8938
  runtimeWriteOwner: state.runtime?.writeOwner || null,
8376
- runtimeAttachedClients: state.runtime?.attachedClients || []
8939
+ runtimeAttachedClients: state.runtime?.attachedClients || [],
8940
+ runtimeRestoredFromStorage: state.runtime?.restoredFromStorage === true,
8941
+ runtimeRecoveryState: state.runtime?.recoveryState ?? null
8377
8942
  },
8378
8943
  mode: state.mode,
8379
8944
  resume: state.resume,
@@ -8430,6 +8995,7 @@ function buildSessionEntries(allStates, cdpManagers, options = {}) {
8430
8995
  for (const state of ideStates) {
8431
8996
  sessions.push(buildIdeWorkspaceSession(state, cdpManagers, options));
8432
8997
  for (const ext of state.extensions) {
8998
+ if (!shouldIncludeExtensionSession(ext)) continue;
8433
8999
  sessions.push(buildExtensionAgentSession(state, ext, options));
8434
9000
  }
8435
9001
  }
@@ -10600,7 +11166,9 @@ function applyProviderPatch(h, args, payload) {
10600
11166
  });
10601
11167
  }
10602
11168
  async function executeProviderScript(h, args, scriptName) {
10603
- const resolvedProviderType = h.currentSession?.providerType || h.currentProviderType || args?.agentType || args?.providerType;
11169
+ const explicitTargetSessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : "";
11170
+ const targetSession = explicitTargetSessionId ? h.ctx.sessionRegistry?.get(explicitTargetSessionId) : void 0;
11171
+ const resolvedProviderType = targetSession?.providerType || h.currentSession?.providerType || h.currentProviderType || args?.agentType || args?.providerType;
10604
11172
  if (!resolvedProviderType) return { success: false, error: "targetSessionId or providerType is required" };
10605
11173
  const loader = h.ctx.providerLoader;
10606
11174
  if (!loader) return { success: false, error: "ProviderLoader not initialized" };
@@ -10643,16 +11211,16 @@ async function executeProviderScript(h, args, scriptName) {
10643
11211
  const scriptFn = provider.scripts[actualScriptName];
10644
11212
  const scriptCode = scriptFn(normalizedArgs);
10645
11213
  if (!scriptCode) return { success: false, error: `Script '${actualScriptName}' returned null` };
10646
- const cdpKey = provider.category === "ide" ? h.currentSession?.cdpManagerKey || h.currentManagerKey || resolvedProviderType : h.currentSession?.cdpManagerKey || h.currentManagerKey;
11214
+ const cdpKey = provider.category === "ide" ? targetSession?.cdpManagerKey || h.currentSession?.cdpManagerKey || h.currentManagerKey || resolvedProviderType : targetSession?.cdpManagerKey || h.currentSession?.cdpManagerKey || h.currentManagerKey;
10647
11215
  LOG.info("Command", `[ExtScript] provider=${provider.type} category=${provider.category} cdpKey=${cdpKey}`);
10648
11216
  const cdp = h.getCdp(cdpKey);
10649
11217
  if (!cdp?.isConnected) return { success: false, error: `No CDP connection for ${cdpKey || "any"}` };
10650
11218
  try {
10651
11219
  let result;
10652
11220
  if (provider.category === "extension") {
10653
- const runtimeSessionId = h.currentSession?.sessionId || args?.targetSessionId;
11221
+ const runtimeSessionId = explicitTargetSessionId || h.currentSession?.sessionId;
10654
11222
  if (!runtimeSessionId) return { success: false, error: `No target session found for ${resolvedProviderType}` };
10655
- const parentSessionId = h.currentSession?.parentSessionId;
11223
+ const parentSessionId = targetSession?.parentSessionId || h.currentSession?.parentSessionId;
10656
11224
  if (parentSessionId) {
10657
11225
  await h.agentStream?.setActiveSession(cdp, parentSessionId, runtimeSessionId);
10658
11226
  await h.agentStream?.syncActiveSession(cdp, parentSessionId);
@@ -11655,8 +12223,12 @@ var CliProviderInstance = class {
11655
12223
  runtimeKey: runtime.runtimeKey,
11656
12224
  displayName: runtime.displayName,
11657
12225
  workspaceLabel: runtime.workspaceLabel,
12226
+ lifecycle: runtime.lifecycle ?? null,
12227
+ surfaceKind: runtime.surfaceKind,
11658
12228
  writeOwner: runtime.writeOwner || null,
11659
- attachedClients: runtime.attachedClients || []
12229
+ attachedClients: runtime.attachedClients || [],
12230
+ restoredFromStorage: runtime.restoredFromStorage === true,
12231
+ recoveryState: runtime.recoveryState ?? null
11660
12232
  } : void 0,
11661
12233
  resume: this.provider.resume,
11662
12234
  controlValues: surface.controlValues,
@@ -15963,61 +16535,6 @@ cleanOldFiles();
15963
16535
  // src/commands/router.ts
15964
16536
  init_logger();
15965
16537
 
15966
- // src/session-host/runtime-surface.ts
15967
- var LIVE_LIFECYCLES = /* @__PURE__ */ new Set(["starting", "running", "stopping", "interrupted"]);
15968
- function isSessionHostLiveRuntime(record) {
15969
- const lifecycle = String(record?.lifecycle || "").trim();
15970
- return LIVE_LIFECYCLES.has(lifecycle);
15971
- }
15972
- function getSessionHostRecoveryLabel(meta) {
15973
- const recoveryState = typeof meta?.runtimeRecoveryState === "string" ? String(meta.runtimeRecoveryState).trim() : "";
15974
- if (!recoveryState) return null;
15975
- if (recoveryState === "auto_resumed") return "restored after restart";
15976
- if (recoveryState === "resume_failed") return "restore failed";
15977
- if (recoveryState === "host_restart_interrupted") return "host restart interrupted";
15978
- if (recoveryState === "orphan_snapshot") return "snapshot recovered";
15979
- return recoveryState.replace(/_/g, " ");
15980
- }
15981
- function isSessionHostRecoverySnapshot(record) {
15982
- if (!record) return false;
15983
- if (isSessionHostLiveRuntime(record)) return false;
15984
- const lifecycle = String(record.lifecycle || "").trim();
15985
- if (lifecycle && lifecycle !== "stopped" && lifecycle !== "failed") {
15986
- return false;
15987
- }
15988
- const meta = record.meta || void 0;
15989
- if (meta?.restoredFromStorage === true) return true;
15990
- return getSessionHostRecoveryLabel(meta) !== null;
15991
- }
15992
- function getSessionHostSurfaceKind(record) {
15993
- if (isSessionHostLiveRuntime(record)) return "live_runtime";
15994
- if (isSessionHostRecoverySnapshot(record)) return "recovery_snapshot";
15995
- return "inactive_record";
15996
- }
15997
- function partitionSessionHostRecords(records) {
15998
- const liveRuntimes = [];
15999
- const recoverySnapshots = [];
16000
- const inactiveRecords = [];
16001
- for (const record of records) {
16002
- const kind = getSessionHostSurfaceKind(record);
16003
- if (kind === "live_runtime") {
16004
- liveRuntimes.push(record);
16005
- } else if (kind === "recovery_snapshot") {
16006
- recoverySnapshots.push(record);
16007
- } else {
16008
- inactiveRecords.push(record);
16009
- }
16010
- }
16011
- return {
16012
- liveRuntimes,
16013
- recoverySnapshots,
16014
- inactiveRecords
16015
- };
16016
- }
16017
- function partitionSessionHostDiagnosticsSessions(records) {
16018
- return partitionSessionHostRecords(records || []);
16019
- }
16020
-
16021
16538
  // src/status/snapshot.ts
16022
16539
  var os16 = __toESM(require("os"));
16023
16540
  init_config();
@@ -17575,6 +18092,23 @@ function prepareSessionModalUpdate(input) {
17575
18092
  };
17576
18093
  }
17577
18094
 
18095
+ // src/chat/async-batch.ts
18096
+ async function runAsyncBatch(items, worker, options = {}) {
18097
+ const list = Array.from(items);
18098
+ if (list.length === 0) return;
18099
+ const concurrency = Math.max(1, Math.min(list.length, Math.floor(options.concurrency || 1)));
18100
+ let nextIndex = 0;
18101
+ const runners = Array.from({ length: concurrency }, async () => {
18102
+ while (true) {
18103
+ const currentIndex = nextIndex;
18104
+ nextIndex += 1;
18105
+ if (currentIndex >= list.length) return;
18106
+ await worker(list[currentIndex], currentIndex);
18107
+ }
18108
+ });
18109
+ await Promise.all(runners);
18110
+ }
18111
+
17578
18112
  // src/agent-stream/provider-adapter.ts
17579
18113
  init_read_chat_contract();
17580
18114
  init_chat_message_normalization();
@@ -18043,10 +18577,12 @@ var DaemonAgentStreamManager = class {
18043
18577
  }
18044
18578
  }
18045
18579
  /** Collect active extension session state */
18046
- async collectActiveSession(cdp, parentSessionId) {
18580
+ async collectActiveSession(cdp, parentSessionId, attemptedSessionIds = /* @__PURE__ */ new Set(), originSessionId) {
18047
18581
  if (!this.enabled) return null;
18048
18582
  const activeSessionId = this.getActiveSessionId(parentSessionId);
18049
18583
  if (!activeSessionId) return null;
18584
+ const resolvedOriginSessionId = originSessionId || activeSessionId;
18585
+ attemptedSessionIds.add(activeSessionId);
18050
18586
  let agent = this.managedBySessionId.get(activeSessionId);
18051
18587
  if (!agent) {
18052
18588
  agent = await this.connectManagedSession(cdp, parentSessionId, activeSessionId) || void 0;
@@ -18059,18 +18595,44 @@ var DaemonAgentStreamManager = class {
18059
18595
  try {
18060
18596
  const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
18061
18597
  const state = await agent.adapter.readChat(evaluate);
18062
- const stateError = this.getStateError(state);
18063
- const selectedModelValue = typeof state.controlValues?.model === "string" ? state.controlValues.model : "";
18064
- LOG.debug("AgentStream", `[AgentStream] readChat(${type}) result: status=${state.status} msgs=${state.messages?.length || 0} model=${selectedModelValue}${state.status === "error" ? " error=" + JSON.stringify(stateError) : ""}`);
18065
- if (state.status === "error" && this.isRecoverableSessionError(stateError)) {
18598
+ 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;
18599
+ const normalizedState = {
18600
+ ...state,
18601
+ sessionId: agent.runtimeSessionId,
18602
+ ...resolvedProviderSessionId ? { providerSessionId: resolvedProviderSessionId } : {}
18603
+ };
18604
+ const stateError = this.getStateError(normalizedState);
18605
+ const selectedModelValue = typeof normalizedState.controlValues?.model === "string" ? normalizedState.controlValues.model : "";
18606
+ LOG.debug("AgentStream", `[AgentStream] readChat(${type}) result: status=${normalizedState.status} msgs=${normalizedState.messages?.length || 0} model=${selectedModelValue}${normalizedState.status === "error" ? " error=" + JSON.stringify(stateError) : ""}`);
18607
+ if (normalizedState.status === "error" && this.isRecoverableSessionError(stateError)) {
18066
18608
  throw new Error(stateError);
18067
18609
  }
18068
- agent.lastState = state;
18610
+ agent.lastState = normalizedState;
18069
18611
  agent.lastError = null;
18070
- if (state.status === "panel_hidden") {
18612
+ if (normalizedState.status === "panel_hidden") {
18613
+ const discovered = await cdp.discoverAgentWebviews().catch(() => []);
18614
+ const fallbackTarget = discovered.find((entry) => {
18615
+ if (entry.agentType === type) return false;
18616
+ const fallbackSessionId = this.resolveSessionIdForTarget(parentSessionId, entry.agentType);
18617
+ return !!fallbackSessionId && fallbackSessionId !== activeSessionId && !attemptedSessionIds.has(fallbackSessionId);
18618
+ });
18619
+ if (fallbackTarget) {
18620
+ const fallbackSessionId = this.resolveSessionIdForTarget(parentSessionId, fallbackTarget.agentType);
18621
+ if (fallbackSessionId && fallbackSessionId !== activeSessionId && !attemptedSessionIds.has(fallbackSessionId)) {
18622
+ this.logFn(`[AgentStream] Active session ${type} is hidden; switching to visible agent ${fallbackTarget.agentType} (${parentSessionId})`);
18623
+ await this.setActiveSession(cdp, parentSessionId, fallbackSessionId);
18624
+ await this.syncActiveSession(cdp, parentSessionId);
18625
+ const fallbackState = await this.collectActiveSession(cdp, parentSessionId, attemptedSessionIds, resolvedOriginSessionId);
18626
+ if (fallbackState?.status === "panel_hidden" && resolvedOriginSessionId !== fallbackSessionId) {
18627
+ await this.setActiveSession(cdp, parentSessionId, resolvedOriginSessionId);
18628
+ await this.syncActiveSession(cdp, parentSessionId);
18629
+ }
18630
+ return fallbackState;
18631
+ }
18632
+ }
18071
18633
  agent.lastHiddenCheckTime = Date.now();
18072
18634
  }
18073
- return state;
18635
+ return normalizedState;
18074
18636
  } catch (e) {
18075
18637
  const errorMsg = e?.message || String(e);
18076
18638
  this.logFn(`[AgentStream] readChat(${type}) error: ${errorMsg.slice(0, 200)}`);
@@ -18366,6 +18928,7 @@ var AgentStreamPoller = class {
18366
18928
  try {
18367
18929
  await agentStreamManager.syncActiveSession(cdp, parentSessionId);
18368
18930
  let stream = await agentStreamManager.collectActiveSession(cdp, parentSessionId);
18931
+ resolvedActiveSessionId = stream?.sessionId || agentStreamManager.getActiveSessionId(parentSessionId) || resolvedActiveSessionId;
18369
18932
  if (stream?.status === "waiting_approval") {
18370
18933
  const autoApprove = providerLoader.getSettings(stream.agentType).autoApprove !== false;
18371
18934
  if (autoApprove && resolvedActiveSessionId) {
@@ -24346,6 +24909,8 @@ var SessionHostRuntimeTransport = class {
24346
24909
  runtimeKey: record.runtimeKey,
24347
24910
  displayName: record.displayName,
24348
24911
  workspaceLabel: record.workspaceLabel,
24912
+ lifecycle: typeof record.lifecycle === "string" ? record.lifecycle : null,
24913
+ surfaceKind: record.surfaceKind,
24349
24914
  writeOwner: record.writeOwner ? {
24350
24915
  clientId: record.writeOwner.clientId,
24351
24916
  ownerType: record.writeOwner.ownerType
@@ -25071,6 +25636,7 @@ async function shutdownDaemonComponents(components) {
25071
25636
  resolveChatMessageKind,
25072
25637
  resolveDebugRuntimeConfig,
25073
25638
  resolveSessionHostAppName,
25639
+ runAsyncBatch,
25074
25640
  saveConfig,
25075
25641
  saveState,
25076
25642
  setDebugRuntimeConfig,