adhdev 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
@@ -1026,6 +1026,67 @@ var init_host_memory = __esm({
1026
1026
  }
1027
1027
  });
1028
1028
 
1029
+ // ../../oss/packages/daemon-core/src/session-host/runtime-surface.ts
1030
+ function isSessionHostLiveRuntime(record2) {
1031
+ const lifecycle = String(record2?.lifecycle || "").trim();
1032
+ return LIVE_LIFECYCLES.has(lifecycle);
1033
+ }
1034
+ function getSessionHostRecoveryLabel(meta3) {
1035
+ const recoveryState = typeof meta3?.runtimeRecoveryState === "string" ? String(meta3.runtimeRecoveryState).trim() : "";
1036
+ if (!recoveryState) return null;
1037
+ if (recoveryState === "auto_resumed") return "restored after restart";
1038
+ if (recoveryState === "resume_failed") return "restore failed";
1039
+ if (recoveryState === "host_restart_interrupted") return "host restart interrupted";
1040
+ if (recoveryState === "orphan_snapshot") return "snapshot recovered";
1041
+ return recoveryState.replace(/_/g, " ");
1042
+ }
1043
+ function isSessionHostRecoverySnapshot(record2) {
1044
+ if (!record2) return false;
1045
+ if (isSessionHostLiveRuntime(record2)) return false;
1046
+ const lifecycle = String(record2.lifecycle || "").trim();
1047
+ if (lifecycle && lifecycle !== "stopped" && lifecycle !== "failed") {
1048
+ return false;
1049
+ }
1050
+ const meta3 = record2.meta || void 0;
1051
+ if (meta3?.restoredFromStorage === true) return true;
1052
+ return getSessionHostRecoveryLabel(meta3) !== null;
1053
+ }
1054
+ function getSessionHostSurfaceKind(record2) {
1055
+ if (isSessionHostLiveRuntime(record2)) return "live_runtime";
1056
+ if (isSessionHostRecoverySnapshot(record2)) return "recovery_snapshot";
1057
+ return "inactive_record";
1058
+ }
1059
+ function partitionSessionHostRecords(records) {
1060
+ const liveRuntimes = [];
1061
+ const recoverySnapshots = [];
1062
+ const inactiveRecords = [];
1063
+ for (const record2 of records) {
1064
+ const kind = getSessionHostSurfaceKind(record2);
1065
+ if (kind === "live_runtime") {
1066
+ liveRuntimes.push(record2);
1067
+ } else if (kind === "recovery_snapshot") {
1068
+ recoverySnapshots.push(record2);
1069
+ } else {
1070
+ inactiveRecords.push(record2);
1071
+ }
1072
+ }
1073
+ return {
1074
+ liveRuntimes,
1075
+ recoverySnapshots,
1076
+ inactiveRecords
1077
+ };
1078
+ }
1079
+ function partitionSessionHostDiagnosticsSessions(records) {
1080
+ return partitionSessionHostRecords(records || []);
1081
+ }
1082
+ var LIVE_LIFECYCLES;
1083
+ var init_runtime_surface = __esm({
1084
+ "../../oss/packages/daemon-core/src/session-host/runtime-surface.ts"() {
1085
+ "use strict";
1086
+ LIVE_LIFECYCLES = /* @__PURE__ */ new Set(["starting", "running", "stopping", "interrupted"]);
1087
+ }
1088
+ });
1089
+
1029
1090
  // ../../oss/packages/daemon-core/src/status/chat-tail-hot-sessions.ts
1030
1091
  function parseMessageTimestamp(value) {
1031
1092
  if (typeof value === "number" && Number.isFinite(value)) return value;
@@ -1035,6 +1096,23 @@ function parseMessageTimestamp(value) {
1035
1096
  }
1036
1097
  return 0;
1037
1098
  }
1099
+ function isDefinitelyNonLiveRuntimeSession(session) {
1100
+ const surfaceKind = String(session?.runtimeSurfaceKind || "").trim();
1101
+ if (surfaceKind === "live_runtime") return false;
1102
+ if (surfaceKind === "recovery_snapshot") return true;
1103
+ if (surfaceKind === "inactive_record") return false;
1104
+ const lifecycle = String(session?.runtimeLifecycle || "").trim();
1105
+ if (lifecycle && LIVE_RUNTIME_LIFECYCLES.has(lifecycle)) return false;
1106
+ const inferredSurfaceKind = getSessionHostSurfaceKind({
1107
+ lifecycle: lifecycle || null,
1108
+ meta: {
1109
+ restoredFromStorage: session?.runtimeRestoredFromStorage === true,
1110
+ ...session?.runtimeRecoveryState ? { runtimeRecoveryState: session.runtimeRecoveryState } : {}
1111
+ }
1112
+ });
1113
+ if (inferredSurfaceKind === "recovery_snapshot") return true;
1114
+ return false;
1115
+ }
1038
1116
  function classifyHotChatSessionsForSubscriptionFlush(sessions, previousHotSessionIds, options = {}) {
1039
1117
  const now = options.now ?? Date.now();
1040
1118
  const recentMessageGraceMs = Math.max(
@@ -1043,9 +1121,14 @@ function classifyHotChatSessionsForSubscriptionFlush(sessions, previousHotSessio
1043
1121
  );
1044
1122
  const activeStatuses = options.activeStatuses ?? DEFAULT_ACTIVE_CHAT_POLL_STATUSES;
1045
1123
  const active = /* @__PURE__ */ new Set();
1124
+ const excluded = /* @__PURE__ */ new Set();
1046
1125
  for (const session of sessions) {
1047
1126
  const sessionId = typeof session?.id === "string" ? session.id : "";
1048
1127
  if (!sessionId) continue;
1128
+ if (isDefinitelyNonLiveRuntimeSession(session)) {
1129
+ excluded.add(sessionId);
1130
+ continue;
1131
+ }
1049
1132
  const status = String(session?.status || "").toLowerCase();
1050
1133
  const lastMessageAt = parseMessageTimestamp(session?.lastMessageAt);
1051
1134
  const recentlyUpdated = lastMessageAt > 0 && now - lastMessageAt <= recentMessageGraceMs;
@@ -1054,20 +1137,22 @@ function classifyHotChatSessionsForSubscriptionFlush(sessions, previousHotSessio
1054
1137
  }
1055
1138
  }
1056
1139
  const finalizing = new Set(
1057
- Array.from(previousHotSessionIds).filter((sessionId) => !active.has(sessionId))
1140
+ Array.from(previousHotSessionIds).filter((sessionId) => !active.has(sessionId) && !excluded.has(sessionId))
1058
1141
  );
1059
1142
  return { active, finalizing };
1060
1143
  }
1061
- var DEFAULT_ACTIVE_CHAT_POLL_STATUSES, DEFAULT_CHAT_TAIL_RECENT_MESSAGE_GRACE_MS;
1144
+ var DEFAULT_ACTIVE_CHAT_POLL_STATUSES, DEFAULT_CHAT_TAIL_RECENT_MESSAGE_GRACE_MS, LIVE_RUNTIME_LIFECYCLES;
1062
1145
  var init_chat_tail_hot_sessions = __esm({
1063
1146
  "../../oss/packages/daemon-core/src/status/chat-tail-hot-sessions.ts"() {
1064
1147
  "use strict";
1148
+ init_runtime_surface();
1065
1149
  DEFAULT_ACTIVE_CHAT_POLL_STATUSES = /* @__PURE__ */ new Set([
1066
1150
  "generating",
1067
1151
  "waiting_approval",
1068
1152
  "starting"
1069
1153
  ]);
1070
1154
  DEFAULT_CHAT_TAIL_RECENT_MESSAGE_GRACE_MS = 8e3;
1155
+ LIVE_RUNTIME_LIFECYCLES = /* @__PURE__ */ new Set(["starting", "running", "stopping", "interrupted"]);
1071
1156
  }
1072
1157
  });
1073
1158
 
@@ -1287,6 +1372,32 @@ var init_logger = __esm({
1287
1372
  });
1288
1373
 
1289
1374
  // ../../oss/packages/daemon-core/src/cdp/manager.ts
1375
+ function normalizeTitle(value) {
1376
+ return String(value || "").trim().replace(/\s+/g, " ").toLowerCase();
1377
+ }
1378
+ function titlesMatch(lhs, rhs) {
1379
+ const a = normalizeTitle(lhs);
1380
+ const b = normalizeTitle(rhs);
1381
+ if (!a || !b) return false;
1382
+ return a === b || a.includes(b) || b.includes(a);
1383
+ }
1384
+ function resolveCdpPageTarget(params) {
1385
+ const { pages, pinnedTargetId, previousPageTitle } = params;
1386
+ if (pages.length === 0) return { target: null, retargeted: false };
1387
+ if (!pinnedTargetId) {
1388
+ return { target: pages[0] || null, retargeted: false };
1389
+ }
1390
+ const exact = pages.find((page) => page.id === pinnedTargetId);
1391
+ if (exact) return { target: exact, retargeted: false };
1392
+ const titleMatchesList = pages.filter((page) => titlesMatch(page.title, previousPageTitle));
1393
+ if (titleMatchesList.length === 1) {
1394
+ return { target: titleMatchesList[0], retargeted: true };
1395
+ }
1396
+ if (pages.length === 1) {
1397
+ return { target: pages[0], retargeted: true };
1398
+ }
1399
+ return { target: null, retargeted: false };
1400
+ }
1290
1401
  var import_ws, http, DaemonCdpManager;
1291
1402
  var init_manager = __esm({
1292
1403
  "../../oss/packages/daemon-core/src/cdp/manager.ts"() {
@@ -1454,18 +1565,28 @@ var init_manager = __esm({
1454
1565
  resolve15(targets.find((t) => t.webSocketDebuggerUrl) || null);
1455
1566
  return;
1456
1567
  }
1457
- const mainPages = pages.filter((t) => !this.isNonMainTitle(t.title || ""));
1458
- const list = mainPages.length > 0 ? mainPages : pages;
1568
+ const titleFilteredPages = pages.filter((t) => !this.isNonMainTitle(t.title || ""));
1569
+ const mainPages = titleFilteredPages.filter((t) => this.isMainPageUrl(t.url));
1570
+ const list = mainPages.length > 0 ? mainPages : titleFilteredPages.length > 0 ? titleFilteredPages : pages;
1459
1571
  this.log(`[CDP] pages(${list.length}): ${list.map((t) => `"${t.title}"`).join(", ")}`);
1460
- if (this._targetId) {
1461
- const specific = list.find((t) => t.id === this._targetId);
1462
- if (specific) {
1463
- this._pageTitle = specific.title || "";
1464
- resolve15(specific);
1465
- } else {
1466
- this.log(`[CDP] Target ${this._targetId} not found in page list`);
1467
- resolve15(null);
1572
+ const previousTargetId = this._targetId;
1573
+ const selected = resolveCdpPageTarget({
1574
+ pages: list,
1575
+ pinnedTargetId: previousTargetId,
1576
+ previousPageTitle: this._pageTitle
1577
+ });
1578
+ if (selected.target) {
1579
+ if (selected.retargeted && previousTargetId && previousTargetId !== selected.target.id) {
1580
+ this.log(`[CDP] Target ${previousTargetId} rekeyed to ${selected.target.id}`);
1581
+ this._targetId = selected.target.id;
1468
1582
  }
1583
+ this._pageTitle = selected.target.title || "";
1584
+ resolve15(selected.target);
1585
+ return;
1586
+ }
1587
+ if (previousTargetId) {
1588
+ this.log(`[CDP] Target ${previousTargetId} not found in page list`);
1589
+ resolve15(null);
1469
1590
  return;
1470
1591
  }
1471
1592
  this._pageTitle = list[0]?.title || "";
@@ -3342,6 +3463,68 @@ function sanitizeHistoryMessage(agentType, message) {
3342
3463
  content
3343
3464
  };
3344
3465
  }
3466
+ function sortSavedHistorySessionSummaries(summaries) {
3467
+ return summaries.slice().sort((a, b) => b.lastMessageAt - a.lastMessageAt);
3468
+ }
3469
+ function buildSavedHistorySessionSummaryMapFromEntries(entries) {
3470
+ const summaries = /* @__PURE__ */ new Map();
3471
+ for (const entry of Array.from(entries.values())) {
3472
+ const fileSummary = entry.summary;
3473
+ if (!fileSummary || fileSummary.messageCount <= 0 || !fileSummary.lastMessageAt) continue;
3474
+ const existing = summaries.get(fileSummary.historySessionId);
3475
+ if (!existing) {
3476
+ summaries.set(fileSummary.historySessionId, {
3477
+ historySessionId: fileSummary.historySessionId,
3478
+ sessionTitle: fileSummary.sessionTitle,
3479
+ messageCount: fileSummary.messageCount,
3480
+ firstMessageAt: fileSummary.firstMessageAt,
3481
+ lastMessageAt: fileSummary.lastMessageAt,
3482
+ preview: fileSummary.preview,
3483
+ workspace: fileSummary.workspace
3484
+ });
3485
+ continue;
3486
+ }
3487
+ existing.messageCount += fileSummary.messageCount;
3488
+ if (!existing.firstMessageAt || fileSummary.firstMessageAt < existing.firstMessageAt) {
3489
+ existing.firstMessageAt = fileSummary.firstMessageAt;
3490
+ }
3491
+ if (fileSummary.lastMessageAt >= existing.lastMessageAt) {
3492
+ existing.lastMessageAt = fileSummary.lastMessageAt;
3493
+ if (fileSummary.sessionTitle) existing.sessionTitle = fileSummary.sessionTitle;
3494
+ if (fileSummary.preview) existing.preview = fileSummary.preview;
3495
+ }
3496
+ if (!existing.workspace && fileSummary.workspace) {
3497
+ existing.workspace = fileSummary.workspace;
3498
+ }
3499
+ }
3500
+ return Object.fromEntries(sortSavedHistorySessionSummaries(Array.from(summaries.values())).map((summary) => [summary.historySessionId, summary]));
3501
+ }
3502
+ function readPersistedSavedHistorySessionSummaries(dir) {
3503
+ try {
3504
+ const filePath = getSavedHistoryIndexFilePath(dir);
3505
+ if (!fs3.existsSync(filePath)) return null;
3506
+ const raw = JSON.parse(fs3.readFileSync(filePath, "utf-8"));
3507
+ if (!raw || raw.version !== SAVED_HISTORY_INDEX_VERSION || !raw.sessions || typeof raw.sessions !== "object") {
3508
+ return null;
3509
+ }
3510
+ return sortSavedHistorySessionSummaries(
3511
+ Object.values(raw.sessions).filter((summary) => !!summary && typeof summary.historySessionId === "string" && summary.messageCount > 0 && summary.lastMessageAt > 0).map((summary) => ({
3512
+ historySessionId: summary.historySessionId,
3513
+ sessionTitle: summary.sessionTitle,
3514
+ messageCount: summary.messageCount,
3515
+ firstMessageAt: summary.firstMessageAt,
3516
+ lastMessageAt: summary.lastMessageAt,
3517
+ preview: summary.preview,
3518
+ workspace: summary.workspace
3519
+ }))
3520
+ );
3521
+ } catch {
3522
+ return null;
3523
+ }
3524
+ }
3525
+ function shouldScheduleSavedHistoryRollup(totalBytes) {
3526
+ return Number.isFinite(totalBytes) && totalBytes >= SAVED_HISTORY_ROLLUP_THRESHOLD_BYTES;
3527
+ }
3345
3528
  function sanitizeHistoryFileSegment(value) {
3346
3529
  return String(value || "").replace(/[^a-zA-Z0-9_-]/g, "_");
3347
3530
  }
@@ -3355,71 +3538,386 @@ function listHistoryFiles(dir, historySessionId) {
3355
3538
  return true;
3356
3539
  }).sort().reverse();
3357
3540
  }
3358
- function buildSavedHistoryCacheSignature(dir, files) {
3359
- return files.map((file2) => {
3541
+ function normalizeSavedHistorySessionId(agentType, historySessionId) {
3542
+ const normalizedId = String(historySessionId || "").trim();
3543
+ if (!normalizedId) return "";
3544
+ const strictProviderId = normalizeProviderSessionId(agentType, normalizedId);
3545
+ if (strictProviderId) return strictProviderId;
3546
+ return agentType === "hermes-cli" ? "" : normalizedId;
3547
+ }
3548
+ function extractSavedHistorySessionIdFromFile(agentType, file2) {
3549
+ const match = file2.match(/^([A-Za-z0-9_-]+)_\d{4}-\d{2}-\d{2}\.jsonl$/);
3550
+ return normalizeSavedHistorySessionId(agentType, match?.[1] || "");
3551
+ }
3552
+ function buildSavedHistoryFileSignatureMap(dir, files) {
3553
+ return new Map(files.map((file2) => {
3360
3554
  try {
3361
3555
  const stat4 = fs3.statSync(path7.join(dir, file2));
3362
- return `${file2}:${stat4.size}:${Math.trunc(stat4.mtimeMs)}`;
3556
+ return [file2, `${file2}:${stat4.size}:${Math.trunc(stat4.mtimeMs)}`];
3363
3557
  } catch {
3364
- return `${file2}:missing`;
3365
- }
3366
- }).join("|");
3367
- }
3368
- function computeSavedHistorySessionSummaries(agentType, dir, files) {
3369
- const groupedFiles = /* @__PURE__ */ new Map();
3370
- const filePattern = /^([A-Za-z0-9_-]+)_\d{4}-\d{2}-\d{2}\.jsonl$/;
3371
- for (const file2 of files) {
3372
- const match = file2.match(filePattern);
3373
- if (!match?.[1]) continue;
3374
- const historySessionId = match[1];
3375
- const grouped = groupedFiles.get(historySessionId) || [];
3376
- grouped.push(file2);
3377
- groupedFiles.set(historySessionId, grouped);
3378
- }
3379
- const summaries = [];
3380
- for (const [historySessionId, grouped] of groupedFiles.entries()) {
3381
- let messageCount = 0;
3382
- let firstMessageAt = 0;
3383
- let lastMessageAt = 0;
3384
- let sessionTitle = "";
3385
- let preview = "";
3386
- let workspace = "";
3387
- for (const file2 of grouped.sort()) {
3388
- const filePath = path7.join(dir, file2);
3389
- const content = fs3.readFileSync(filePath, "utf-8");
3390
- const lines = content.split("\n").filter(Boolean);
3391
- for (const line of lines) {
3392
- let parsed = null;
3558
+ return [file2, `${file2}:missing`];
3559
+ }
3560
+ }));
3561
+ }
3562
+ function buildSavedHistoryCacheSignature(files, fileSignatures) {
3563
+ return files.map((file2) => fileSignatures.get(file2) || `${file2}:missing`).join("|");
3564
+ }
3565
+ function getSavedHistoryIndexFilePath(dir) {
3566
+ return path7.join(dir, SAVED_HISTORY_INDEX_FILE);
3567
+ }
3568
+ function getSavedHistoryIndexLockPath(dir) {
3569
+ return `${getSavedHistoryIndexFilePath(dir)}${SAVED_HISTORY_INDEX_LOCK_SUFFIX}`;
3570
+ }
3571
+ function sleepBlocking(ms) {
3572
+ if (ms <= 0) return;
3573
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
3574
+ }
3575
+ function loadPersistedSavedHistoryIndexFromFile(dir) {
3576
+ try {
3577
+ const filePath = getSavedHistoryIndexFilePath(dir);
3578
+ if (!fs3.existsSync(filePath)) return /* @__PURE__ */ new Map();
3579
+ const raw = JSON.parse(fs3.readFileSync(filePath, "utf-8"));
3580
+ if (!raw || raw.version !== SAVED_HISTORY_INDEX_VERSION || !raw.files || typeof raw.files !== "object") {
3581
+ return /* @__PURE__ */ new Map();
3582
+ }
3583
+ return new Map(
3584
+ Object.entries(raw.files).filter(([file2, entry]) => !!file2 && !!entry && typeof entry.signature === "string").map(([file2, entry]) => [file2, {
3585
+ signature: entry.signature,
3586
+ summary: entry.summary || null
3587
+ }])
3588
+ );
3589
+ } catch {
3590
+ return /* @__PURE__ */ new Map();
3591
+ }
3592
+ }
3593
+ function writePersistedSavedHistoryIndexFile(dir, entries) {
3594
+ const filePath = getSavedHistoryIndexFilePath(dir);
3595
+ const tempPath = `${filePath}.tmp`;
3596
+ const payload = {
3597
+ version: SAVED_HISTORY_INDEX_VERSION,
3598
+ files: Object.fromEntries(entries.entries()),
3599
+ sessions: buildSavedHistorySessionSummaryMapFromEntries(entries)
3600
+ };
3601
+ fs3.writeFileSync(tempPath, JSON.stringify(payload), "utf-8");
3602
+ fs3.renameSync(tempPath, filePath);
3603
+ }
3604
+ function acquireSavedHistoryIndexLock(dir) {
3605
+ const lockPath = getSavedHistoryIndexLockPath(dir);
3606
+ const deadline = Date.now() + SAVED_HISTORY_INDEX_LOCK_WAIT_MS;
3607
+ while (Date.now() <= deadline) {
3608
+ try {
3609
+ fs3.mkdirSync(lockPath);
3610
+ return () => {
3393
3611
  try {
3394
- parsed = JSON.parse(line);
3612
+ fs3.rmSync(lockPath, { recursive: true, force: true });
3395
3613
  } catch {
3396
- parsed = null;
3397
3614
  }
3398
- if (!parsed || parsed.historySessionId !== historySessionId) continue;
3399
- if (parsed.kind === "session_start") {
3400
- if (!workspace && parsed.workspace) workspace = parsed.workspace;
3615
+ };
3616
+ } catch (error48) {
3617
+ if (error48?.code !== "EEXIST") return null;
3618
+ try {
3619
+ const stat4 = fs3.statSync(lockPath);
3620
+ if (Date.now() - stat4.mtimeMs > SAVED_HISTORY_INDEX_LOCK_STALE_MS) {
3621
+ fs3.rmSync(lockPath, { recursive: true, force: true });
3401
3622
  continue;
3402
3623
  }
3403
- messageCount += 1;
3404
- if (!firstMessageAt || parsed.receivedAt < firstMessageAt) firstMessageAt = parsed.receivedAt;
3405
- if (!lastMessageAt || parsed.receivedAt > lastMessageAt) lastMessageAt = parsed.receivedAt;
3406
- if (parsed.sessionTitle) sessionTitle = parsed.sessionTitle;
3407
- if (parsed.role !== "system" && parsed.content.trim()) preview = parsed.content.trim();
3408
- }
3409
- }
3410
- if (messageCount === 0 || !lastMessageAt) continue;
3411
- summaries.push({
3412
- historySessionId,
3413
- sessionTitle: sessionTitle || void 0,
3414
- messageCount,
3415
- firstMessageAt,
3416
- lastMessageAt,
3417
- preview: preview || void 0,
3418
- workspace: workspace || void 0
3419
- });
3624
+ } catch {
3625
+ continue;
3626
+ }
3627
+ sleepBlocking(SAVED_HISTORY_INDEX_LOCK_POLL_MS);
3628
+ }
3629
+ }
3630
+ return null;
3631
+ }
3632
+ function withLockedPersistedSavedHistoryIndex(dir, callback) {
3633
+ const release2 = acquireSavedHistoryIndexLock(dir);
3634
+ if (!release2) return null;
3635
+ try {
3636
+ const entries = loadPersistedSavedHistoryIndexFromFile(dir);
3637
+ const result = callback(entries);
3638
+ writePersistedSavedHistoryIndexFile(dir, entries);
3639
+ return result;
3640
+ } catch {
3641
+ return null;
3642
+ } finally {
3643
+ release2();
3420
3644
  }
3421
- summaries.sort((a, b) => b.lastMessageAt - a.lastMessageAt);
3422
- return summaries;
3645
+ }
3646
+ function loadPersistedSavedHistoryIndex(dir) {
3647
+ return loadPersistedSavedHistoryIndexFromFile(dir);
3648
+ }
3649
+ function savePersistedSavedHistoryIndex(dir, entries) {
3650
+ withLockedPersistedSavedHistoryIndex(dir, (currentEntries) => {
3651
+ const incomingFiles = new Set(Array.from(entries.keys()));
3652
+ for (const [file2, entry] of Array.from(entries.entries())) {
3653
+ const liveSignature = buildSavedHistoryFileSignature(dir, file2);
3654
+ const existingEntry = currentEntries.get(file2);
3655
+ if (existingEntry && existingEntry.signature !== liveSignature && entry.signature !== liveSignature) {
3656
+ continue;
3657
+ }
3658
+ if (entry.signature !== liveSignature && (!existingEntry || existingEntry.signature !== liveSignature)) {
3659
+ continue;
3660
+ }
3661
+ currentEntries.set(file2, entry.signature === liveSignature ? entry : {
3662
+ signature: liveSignature,
3663
+ summary: existingEntry?.summary || entry.summary
3664
+ });
3665
+ }
3666
+ for (const file2 of Array.from(currentEntries.keys())) {
3667
+ if (incomingFiles.has(file2)) continue;
3668
+ if (!fs3.existsSync(path7.join(dir, file2))) {
3669
+ currentEntries.delete(file2);
3670
+ }
3671
+ }
3672
+ });
3673
+ }
3674
+ function invalidatePersistedSavedHistoryIndex(agentType, dir) {
3675
+ try {
3676
+ fs3.rmSync(getSavedHistoryIndexFilePath(dir), { force: true });
3677
+ } catch {
3678
+ }
3679
+ savedHistorySessionCache.delete(agentType.replace(/[^a-zA-Z0-9_-]/g, "_"));
3680
+ }
3681
+ function buildSavedHistoryIndexFileSignature(dir) {
3682
+ try {
3683
+ const stat4 = fs3.statSync(getSavedHistoryIndexFilePath(dir));
3684
+ return `index:${stat4.size}:${Math.trunc(stat4.mtimeMs)}`;
3685
+ } catch {
3686
+ return "index:missing";
3687
+ }
3688
+ }
3689
+ function historyDirectoryHasFilesNewerThanIndex(dir) {
3690
+ try {
3691
+ const indexStat = fs3.statSync(getSavedHistoryIndexFilePath(dir));
3692
+ const files = listHistoryFiles(dir);
3693
+ for (const file2 of files) {
3694
+ const stat4 = fs3.statSync(path7.join(dir, file2));
3695
+ if (stat4.mtimeMs > indexStat.mtimeMs) return true;
3696
+ }
3697
+ return false;
3698
+ } catch {
3699
+ return true;
3700
+ }
3701
+ }
3702
+ function buildSavedHistoryFileSignature(dir, file2) {
3703
+ try {
3704
+ const stat4 = fs3.statSync(path7.join(dir, file2));
3705
+ return `${file2}:${stat4.size}:${Math.trunc(stat4.mtimeMs)}`;
3706
+ } catch {
3707
+ return `${file2}:missing`;
3708
+ }
3709
+ }
3710
+ function persistSavedHistoryFileSummaryEntry(agentType, dir, file2, updater) {
3711
+ const filePath = path7.join(dir, file2);
3712
+ const result = withLockedPersistedSavedHistoryIndex(dir, (entries) => {
3713
+ const currentEntry = entries.get(file2) || null;
3714
+ const nextSummary = updater(currentEntry?.summary || null);
3715
+ const nextEntry = {
3716
+ signature: buildSavedHistoryFileSignature(dir, file2),
3717
+ summary: nextSummary
3718
+ };
3719
+ entries.set(file2, nextEntry);
3720
+ savedHistoryFileSummaryCache.set(filePath, nextEntry);
3721
+ return nextEntry;
3722
+ });
3723
+ if (!result) return;
3724
+ if (result.summary?.historySessionId && shouldScheduleSavedHistoryRollupForSignature(result.signature)) {
3725
+ scheduleSavedHistoryRollup(agentType, result.summary.historySessionId);
3726
+ }
3727
+ }
3728
+ function updateSavedHistoryIndexForSessionStart(agentType, dir, file2, historySessionId, workspace) {
3729
+ const normalizedSessionId = normalizeSavedHistorySessionId(agentType, historySessionId);
3730
+ const normalizedWorkspace = String(workspace || "").trim();
3731
+ if (!normalizedSessionId || !normalizedWorkspace) return;
3732
+ persistSavedHistoryFileSummaryEntry(agentType, dir, file2, (currentSummary) => ({
3733
+ file: file2,
3734
+ historySessionId: normalizedSessionId,
3735
+ messageCount: currentSummary?.messageCount || 0,
3736
+ firstMessageAt: currentSummary?.firstMessageAt || 0,
3737
+ lastMessageAt: currentSummary?.lastMessageAt || 0,
3738
+ sessionTitle: currentSummary?.sessionTitle,
3739
+ preview: currentSummary?.preview,
3740
+ workspace: normalizedWorkspace
3741
+ }));
3742
+ }
3743
+ function updateSavedHistoryIndexForAppendedMessages(agentType, dir, file2, historySessionId, messages) {
3744
+ const normalizedSessionId = normalizeSavedHistorySessionId(agentType, historySessionId || "");
3745
+ if (!normalizedSessionId || messages.length === 0) return;
3746
+ persistSavedHistoryFileSummaryEntry(agentType, dir, file2, (currentSummary) => {
3747
+ const nextSummary = {
3748
+ file: file2,
3749
+ historySessionId: normalizedSessionId,
3750
+ messageCount: currentSummary?.messageCount || 0,
3751
+ firstMessageAt: currentSummary?.firstMessageAt || 0,
3752
+ lastMessageAt: currentSummary?.lastMessageAt || 0,
3753
+ sessionTitle: currentSummary?.sessionTitle,
3754
+ preview: currentSummary?.preview,
3755
+ workspace: currentSummary?.workspace
3756
+ };
3757
+ for (const message of messages) {
3758
+ if (!message || message.historySessionId !== historySessionId) continue;
3759
+ if (message.kind === "session_start") {
3760
+ if (message.workspace) nextSummary.workspace = message.workspace;
3761
+ continue;
3762
+ }
3763
+ nextSummary.messageCount += 1;
3764
+ if (!nextSummary.firstMessageAt || message.receivedAt < nextSummary.firstMessageAt) {
3765
+ nextSummary.firstMessageAt = message.receivedAt;
3766
+ }
3767
+ if (!nextSummary.lastMessageAt || message.receivedAt >= nextSummary.lastMessageAt) {
3768
+ nextSummary.lastMessageAt = message.receivedAt;
3769
+ if (message.sessionTitle) nextSummary.sessionTitle = message.sessionTitle;
3770
+ if (message.role !== "system" && message.content.trim()) nextSummary.preview = message.content.trim();
3771
+ } else if (message.sessionTitle) {
3772
+ nextSummary.sessionTitle = message.sessionTitle;
3773
+ }
3774
+ if (!nextSummary.preview && message.role !== "system" && message.content.trim()) {
3775
+ nextSummary.preview = message.content.trim();
3776
+ }
3777
+ }
3778
+ return nextSummary;
3779
+ });
3780
+ }
3781
+ function computeSavedHistoryFileSummary(agentType, dir, file2) {
3782
+ const historySessionId = extractSavedHistorySessionIdFromFile(agentType, file2);
3783
+ if (!historySessionId) return null;
3784
+ const filePath = path7.join(dir, file2);
3785
+ const content = fs3.readFileSync(filePath, "utf-8");
3786
+ const lines = content.split("\n").filter(Boolean);
3787
+ let messageCount = 0;
3788
+ let firstMessageAt = 0;
3789
+ let lastMessageAt = 0;
3790
+ let sessionTitle = "";
3791
+ let preview = "";
3792
+ let workspace = "";
3793
+ for (const line of lines) {
3794
+ let parsed = null;
3795
+ try {
3796
+ parsed = JSON.parse(line);
3797
+ } catch {
3798
+ parsed = null;
3799
+ }
3800
+ if (!parsed || parsed.historySessionId !== historySessionId) continue;
3801
+ if (parsed.kind === "session_start") {
3802
+ if (!workspace && parsed.workspace) workspace = parsed.workspace;
3803
+ continue;
3804
+ }
3805
+ messageCount += 1;
3806
+ if (!firstMessageAt || parsed.receivedAt < firstMessageAt) firstMessageAt = parsed.receivedAt;
3807
+ if (!lastMessageAt || parsed.receivedAt > lastMessageAt) lastMessageAt = parsed.receivedAt;
3808
+ if (parsed.sessionTitle) sessionTitle = parsed.sessionTitle;
3809
+ if (parsed.role !== "system" && parsed.content.trim()) preview = parsed.content.trim();
3810
+ }
3811
+ if (messageCount === 0 || !lastMessageAt) return null;
3812
+ return {
3813
+ file: file2,
3814
+ historySessionId,
3815
+ messageCount,
3816
+ firstMessageAt,
3817
+ lastMessageAt,
3818
+ sessionTitle: sessionTitle || void 0,
3819
+ preview: preview || void 0,
3820
+ workspace: workspace || void 0
3821
+ };
3822
+ }
3823
+ function shouldScheduleSavedHistoryRollupForSignature(signature) {
3824
+ const parts = String(signature || "").split(":");
3825
+ const size = Number(parts[1] || 0);
3826
+ return shouldScheduleSavedHistoryRollup(size);
3827
+ }
3828
+ function scheduleSavedHistoryRollup(agentType, historySessionId) {
3829
+ const key = `${agentType}:${historySessionId}`;
3830
+ if (!historySessionId || savedHistoryRollupInFlight.has(key)) return;
3831
+ savedHistoryRollupInFlight.add(key);
3832
+ setTimeout(() => {
3833
+ try {
3834
+ new ChatHistoryWriter().compactHistorySession(agentType, historySessionId);
3835
+ } finally {
3836
+ savedHistoryRollupInFlight.delete(key);
3837
+ }
3838
+ }, 0);
3839
+ }
3840
+ function scheduleSavedHistoryBackgroundRefresh(agentType, dir) {
3841
+ const key = `${agentType}:${dir}`;
3842
+ if (savedHistoryBackgroundRefresh.has(key)) return;
3843
+ savedHistoryBackgroundRefresh.add(key);
3844
+ setTimeout(() => {
3845
+ try {
3846
+ if (!fs3.existsSync(dir)) return;
3847
+ const files = listHistoryFiles(dir);
3848
+ const fileSignatures = buildSavedHistoryFileSignatureMap(dir, files);
3849
+ const persistedEntries = loadPersistedSavedHistoryIndex(dir);
3850
+ const computed = computeSavedHistorySessionSummaries(agentType, dir, files, fileSignatures, persistedEntries);
3851
+ savePersistedSavedHistoryIndex(dir, computed.persistedEntries || /* @__PURE__ */ new Map());
3852
+ const refreshedIndexSignature = buildSavedHistoryIndexFileSignature(dir);
3853
+ savedHistorySessionCache.set(agentType.replace(/[^a-zA-Z0-9_-]/g, "_"), {
3854
+ signature: refreshedIndexSignature,
3855
+ summaries: computed.summaries || []
3856
+ });
3857
+ for (const [file2, entry] of Array.from(computed.persistedEntries.entries())) {
3858
+ if (!entry?.summary || !shouldScheduleSavedHistoryRollupForSignature(entry.signature)) continue;
3859
+ scheduleSavedHistoryRollup(agentType, entry.summary.historySessionId);
3860
+ }
3861
+ } catch {
3862
+ } finally {
3863
+ savedHistoryBackgroundRefresh.delete(key);
3864
+ }
3865
+ }, 0);
3866
+ }
3867
+ function computeSavedHistorySessionSummaries(agentType, dir, files, fileSignatures, persistedEntries) {
3868
+ const summaryBySessionId = /* @__PURE__ */ new Map();
3869
+ const nextPersistedEntries = /* @__PURE__ */ new Map();
3870
+ for (const file2 of files.slice().sort()) {
3871
+ const filePath = path7.join(dir, file2);
3872
+ const signature = fileSignatures.get(file2) || `${file2}:missing`;
3873
+ const cached2 = savedHistoryFileSummaryCache.get(filePath);
3874
+ const persisted = persistedEntries.get(file2);
3875
+ const reusableEntry = cached2?.signature === signature ? cached2 : persisted?.signature === signature ? persisted : null;
3876
+ const fileSummary = reusableEntry?.summary || computeSavedHistoryFileSummary(agentType, dir, file2);
3877
+ const nextEntry = reusableEntry || {
3878
+ signature,
3879
+ summary: fileSummary
3880
+ };
3881
+ if (!reusableEntry) {
3882
+ nextEntry.signature = signature;
3883
+ nextEntry.summary = fileSummary;
3884
+ }
3885
+ savedHistoryFileSummaryCache.set(filePath, nextEntry);
3886
+ nextPersistedEntries.set(file2, nextEntry);
3887
+ if (!fileSummary) continue;
3888
+ const existing = summaryBySessionId.get(fileSummary.historySessionId);
3889
+ if (fileSummary.messageCount <= 0 || !fileSummary.lastMessageAt) {
3890
+ continue;
3891
+ }
3892
+ if (!existing) {
3893
+ summaryBySessionId.set(fileSummary.historySessionId, {
3894
+ historySessionId: fileSummary.historySessionId,
3895
+ sessionTitle: fileSummary.sessionTitle,
3896
+ messageCount: fileSummary.messageCount,
3897
+ firstMessageAt: fileSummary.firstMessageAt,
3898
+ lastMessageAt: fileSummary.lastMessageAt,
3899
+ preview: fileSummary.preview,
3900
+ workspace: fileSummary.workspace
3901
+ });
3902
+ continue;
3903
+ }
3904
+ existing.messageCount += fileSummary.messageCount;
3905
+ if (!existing.firstMessageAt || fileSummary.firstMessageAt < existing.firstMessageAt) {
3906
+ existing.firstMessageAt = fileSummary.firstMessageAt;
3907
+ }
3908
+ if (fileSummary.lastMessageAt >= existing.lastMessageAt) {
3909
+ existing.lastMessageAt = fileSummary.lastMessageAt;
3910
+ if (fileSummary.sessionTitle) existing.sessionTitle = fileSummary.sessionTitle;
3911
+ if (fileSummary.preview) existing.preview = fileSummary.preview;
3912
+ }
3913
+ if (!existing.workspace && fileSummary.workspace) {
3914
+ existing.workspace = fileSummary.workspace;
3915
+ }
3916
+ }
3917
+ return {
3918
+ summaries: Array.from(summaryBySessionId.values()).sort((a, b) => b.lastMessageAt - a.lastMessageAt),
3919
+ persistedEntries: nextPersistedEntries
3920
+ };
3423
3921
  }
3424
3922
  function readChatHistory(agentType, offset = 0, limit = 30, historySessionId, excludeRecentCount = 0) {
3425
3923
  try {
@@ -3477,18 +3975,51 @@ function listSavedHistorySessions(agentType, options = {}) {
3477
3975
  savedHistorySessionCache.delete(sanitized);
3478
3976
  return { sessions: [], hasMore: false };
3479
3977
  }
3480
- const files = listHistoryFiles(dir);
3481
- const signature = buildSavedHistoryCacheSignature(dir, files);
3482
3978
  const cached2 = savedHistorySessionCache.get(sanitized);
3483
- const summaries = cached2?.signature === signature ? cached2.summaries : computeSavedHistorySessionSummaries(agentType, dir, files);
3484
- if (!cached2 || cached2.signature !== signature) {
3979
+ const offset = Math.max(0, options.offset || 0);
3980
+ const limit = Math.max(1, options.limit || 30);
3981
+ const indexSignature = buildSavedHistoryIndexFileSignature(dir);
3982
+ let cacheWasInvalidated = false;
3983
+ if (cached2) {
3984
+ const cacheLooksPersisted = cached2.signature.startsWith("index:");
3985
+ const cacheStillValid = cacheLooksPersisted ? cached2.signature === indexSignature : (() => {
3986
+ const files2 = listHistoryFiles(dir);
3987
+ const fileSignatures2 = buildSavedHistoryFileSignatureMap(dir, files2);
3988
+ return cached2.signature === buildSavedHistoryCacheSignature(files2, fileSignatures2);
3989
+ })();
3990
+ if (cacheStillValid) {
3991
+ const sliced2 = cached2.summaries.slice(offset, offset + limit);
3992
+ return {
3993
+ sessions: sliced2,
3994
+ hasMore: cached2.summaries.length > offset + limit
3995
+ };
3996
+ }
3997
+ cacheWasInvalidated = true;
3998
+ }
3999
+ const persistedSessions = readPersistedSavedHistorySessionSummaries(dir);
4000
+ if (!cacheWasInvalidated && persistedSessions?.length && !historyDirectoryHasFilesNewerThanIndex(dir)) {
3485
4001
  savedHistorySessionCache.set(sanitized, {
3486
- signature,
3487
- summaries
4002
+ signature: indexSignature,
4003
+ summaries: persistedSessions
3488
4004
  });
4005
+ scheduleSavedHistoryBackgroundRefresh(agentType, dir);
4006
+ const sliced2 = persistedSessions.slice(offset, offset + limit);
4007
+ return {
4008
+ sessions: sliced2,
4009
+ hasMore: persistedSessions.length > offset + limit
4010
+ };
3489
4011
  }
3490
- const offset = Math.max(0, options.offset || 0);
3491
- const limit = Math.max(1, options.limit || 30);
4012
+ const files = listHistoryFiles(dir);
4013
+ const fileSignatures = buildSavedHistoryFileSignatureMap(dir, files);
4014
+ const signature = buildSavedHistoryCacheSignature(files, fileSignatures);
4015
+ const persistedEntries = loadPersistedSavedHistoryIndex(dir);
4016
+ const computed = computeSavedHistorySessionSummaries(agentType, dir, files, fileSignatures, persistedEntries);
4017
+ const summaries = computed.summaries || [];
4018
+ savePersistedSavedHistoryIndex(dir, computed.persistedEntries || /* @__PURE__ */ new Map());
4019
+ savedHistorySessionCache.set(sanitized, {
4020
+ signature,
4021
+ summaries
4022
+ });
3492
4023
  const sliced = summaries.slice(offset, offset + limit);
3493
4024
  return {
3494
4025
  sessions: sliced,
@@ -3498,7 +4029,7 @@ function listSavedHistorySessions(agentType, options = {}) {
3498
4029
  return { sessions: [], hasMore: false };
3499
4030
  }
3500
4031
  }
3501
- var fs3, path7, os5, HISTORY_DIR, RETAIN_DAYS, savedHistorySessionCache, CODEX_STARTER_PROMPT_RE, ChatHistoryWriter;
4032
+ var fs3, path7, os5, HISTORY_DIR, RETAIN_DAYS, SAVED_HISTORY_INDEX_VERSION, SAVED_HISTORY_INDEX_FILE, SAVED_HISTORY_INDEX_LOCK_SUFFIX, SAVED_HISTORY_INDEX_LOCK_WAIT_MS, SAVED_HISTORY_INDEX_LOCK_STALE_MS, SAVED_HISTORY_INDEX_LOCK_POLL_MS, SAVED_HISTORY_ROLLUP_THRESHOLD_BYTES, savedHistorySessionCache, savedHistoryFileSummaryCache, savedHistoryBackgroundRefresh, savedHistoryRollupInFlight, CODEX_STARTER_PROMPT_RE, ChatHistoryWriter;
3502
4033
  var init_chat_history = __esm({
3503
4034
  "../../oss/packages/daemon-core/src/config/chat-history.ts"() {
3504
4035
  "use strict";
@@ -3506,9 +4037,20 @@ var init_chat_history = __esm({
3506
4037
  path7 = __toESM(require("path"));
3507
4038
  os5 = __toESM(require("os"));
3508
4039
  init_chat_message_normalization();
4040
+ init_provider_session_id();
3509
4041
  HISTORY_DIR = path7.join(os5.homedir(), ".adhdev", "history");
3510
4042
  RETAIN_DAYS = 30;
4043
+ SAVED_HISTORY_INDEX_VERSION = 1;
4044
+ SAVED_HISTORY_INDEX_FILE = ".saved-history-index.json";
4045
+ SAVED_HISTORY_INDEX_LOCK_SUFFIX = ".lock";
4046
+ SAVED_HISTORY_INDEX_LOCK_WAIT_MS = 1500;
4047
+ SAVED_HISTORY_INDEX_LOCK_STALE_MS = 15e3;
4048
+ SAVED_HISTORY_INDEX_LOCK_POLL_MS = 25;
4049
+ SAVED_HISTORY_ROLLUP_THRESHOLD_BYTES = 16 * 1024 * 1024;
3511
4050
  savedHistorySessionCache = /* @__PURE__ */ new Map();
4051
+ savedHistoryFileSummaryCache = /* @__PURE__ */ new Map();
4052
+ savedHistoryBackgroundRefresh = /* @__PURE__ */ new Set();
4053
+ savedHistoryRollupInFlight = /* @__PURE__ */ new Set();
3512
4054
  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;
3513
4055
  ChatHistoryWriter = class {
3514
4056
  /** Last seen message count per agent (deduplication) */
@@ -3583,9 +4125,11 @@ var init_chat_history = __esm({
3583
4125
  fs3.mkdirSync(dir, { recursive: true });
3584
4126
  const date5 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
3585
4127
  const filePrefix = effectiveHistoryKey ? `${this.sanitize(effectiveHistoryKey)}_` : "";
3586
- const filePath = path7.join(dir, `${filePrefix}${date5}.jsonl`);
4128
+ const fileName = `${filePrefix}${date5}.jsonl`;
4129
+ const filePath = path7.join(dir, fileName);
3587
4130
  const lines = newMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
3588
4131
  fs3.appendFileSync(filePath, lines, "utf-8");
4132
+ updateSavedHistoryIndexForAppendedMessages(agentType, dir, fileName, effectiveHistoryKey, newMessages);
3589
4133
  const prevCount = this.lastSeenCounts.get(dedupKey) || 0;
3590
4134
  if (!historySessionId && messages.length < prevCount * 0.5 && prevCount > 3) {
3591
4135
  seenHashes.clear();
@@ -3676,7 +4220,8 @@ var init_chat_history = __esm({
3676
4220
  const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
3677
4221
  fs3.mkdirSync(dir, { recursive: true });
3678
4222
  const date5 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
3679
- const filePath = path7.join(dir, `${this.sanitize(id)}_${date5}.jsonl`);
4223
+ const fileName = `${this.sanitize(id)}_${date5}.jsonl`;
4224
+ const filePath = path7.join(dir, fileName);
3680
4225
  const record2 = {
3681
4226
  ts: (/* @__PURE__ */ new Date()).toISOString(),
3682
4227
  receivedAt: Date.now(),
@@ -3689,6 +4234,7 @@ var init_chat_history = __esm({
3689
4234
  workspace: ws
3690
4235
  };
3691
4236
  fs3.appendFileSync(filePath, JSON.stringify(record2) + "\n", "utf-8");
4237
+ updateSavedHistoryIndexForSessionStart(agentType, dir, fileName, id, ws);
3692
4238
  } catch {
3693
4239
  }
3694
4240
  }
@@ -3754,6 +4300,7 @@ var init_chat_history = __esm({
3754
4300
  }
3755
4301
  fs3.unlinkSync(sourcePath);
3756
4302
  }
4303
+ invalidatePersistedSavedHistoryIndex(agentType, dir);
3757
4304
  } catch {
3758
4305
  }
3759
4306
  }
@@ -3803,6 +4350,7 @@ var init_chat_history = __esm({
3803
4350
  fs3.writeFileSync(filePath, `${collapsed.map((entry) => JSON.stringify(entry)).join("\n")}
3804
4351
  `, "utf-8");
3805
4352
  }
4353
+ invalidatePersistedSavedHistoryIndex(agentType, dir);
3806
4354
  } catch {
3807
4355
  }
3808
4356
  }
@@ -3822,13 +4370,18 @@ var init_chat_history = __esm({
3822
4370
  for (const dir of agentDirs) {
3823
4371
  const dirPath = path7.join(HISTORY_DIR, dir.name);
3824
4372
  const files = fs3.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl") || f.endsWith(".terminal.log"));
4373
+ let removedAny = false;
3825
4374
  for (const file2 of files) {
3826
4375
  const filePath = path7.join(dirPath, file2);
3827
4376
  const stat4 = fs3.statSync(filePath);
3828
4377
  if (stat4.mtimeMs < cutoff) {
3829
4378
  fs3.unlinkSync(filePath);
4379
+ removedAny = true;
3830
4380
  }
3831
4381
  }
4382
+ if (removedAny) {
4383
+ invalidatePersistedSavedHistoryIndex(dir.name, dirPath);
4384
+ }
3832
4385
  }
3833
4386
  } catch {
3834
4387
  }
@@ -5663,7 +6216,7 @@ function shouldIncludeSessionMetadata(profile) {
5663
6216
  return profile !== "live";
5664
6217
  }
5665
6218
  function shouldIncludeRuntimeMetadata(profile) {
5666
- return profile !== "live";
6219
+ return true;
5667
6220
  }
5668
6221
  function findCdpManager(cdpManagers, key) {
5669
6222
  const exact = cdpManagers.get(key);
@@ -5755,6 +6308,21 @@ function buildExtensionAgentSession(parent, ext, options) {
5755
6308
  lastUpdated: ext.lastUpdated
5756
6309
  };
5757
6310
  }
6311
+ function shouldIncludeExtensionSession(ext) {
6312
+ const status = String(ext.status || "").trim().toLowerCase();
6313
+ const hasActiveChat = !!ext.activeChat;
6314
+ const hasMessages = Array.isArray(ext.activeChat?.messages) && ext.activeChat.messages.length > 0;
6315
+ const hasModal = !!ext.activeChat?.activeModal;
6316
+ const hasStreams = Array.isArray(ext.agentStreams) && ext.agentStreams.length > 0;
6317
+ const hasProviderSessionId = typeof ext.providerSessionId === "string" && ext.providerSessionId.trim().length > 0;
6318
+ const hasControlValues = !!(ext.controlValues && Object.keys(ext.controlValues).length > 0);
6319
+ const hasProviderControls = Array.isArray(ext.providerControls) && ext.providerControls.length > 0;
6320
+ const hasOpenPanelCapability = Array.isArray(ext.sessionCapabilities) && ext.sessionCapabilities.includes("open_panel");
6321
+ const hasSummaryMetadata = !!ext.summaryMetadata;
6322
+ const hasError = typeof ext.errorMessage === "string" && ext.errorMessage.trim().length > 0;
6323
+ const hasInterestingStatus = !!status && !["idle", "panel_hidden", "disconnected", "not_monitored"].includes(status);
6324
+ return hasActiveChat || hasMessages || hasModal || hasStreams || hasProviderSessionId || hasControlValues || hasProviderControls || hasOpenPanelCapability || hasSummaryMetadata || hasError || hasInterestingStatus;
6325
+ }
5758
6326
  function buildCliSession(state, options) {
5759
6327
  const profile = options.profile || "full";
5760
6328
  const activeChat = normalizeActiveChatData(state.activeChat, getActiveChatOptions(profile));
@@ -5780,8 +6348,12 @@ function buildCliSession(state, options) {
5780
6348
  runtimeKey: state.runtime?.runtimeKey,
5781
6349
  runtimeDisplayName: state.runtime?.displayName,
5782
6350
  runtimeWorkspaceLabel: state.runtime?.workspaceLabel,
6351
+ runtimeLifecycle: state.runtime?.lifecycle ?? null,
6352
+ runtimeSurfaceKind: state.runtime?.surfaceKind,
5783
6353
  runtimeWriteOwner: state.runtime?.writeOwner || null,
5784
- runtimeAttachedClients: state.runtime?.attachedClients || []
6354
+ runtimeAttachedClients: state.runtime?.attachedClients || [],
6355
+ runtimeRestoredFromStorage: state.runtime?.restoredFromStorage === true,
6356
+ runtimeRecoveryState: state.runtime?.recoveryState ?? null
5785
6357
  },
5786
6358
  mode: state.mode,
5787
6359
  resume: state.resume,
@@ -5838,6 +6410,7 @@ function buildSessionEntries(allStates, cdpManagers, options = {}) {
5838
6410
  for (const state of ideStates) {
5839
6411
  sessions.push(buildIdeWorkspaceSession(state, cdpManagers, options));
5840
6412
  for (const ext of state.extensions) {
6413
+ if (!shouldIncludeExtensionSession(ext)) continue;
5841
6414
  sessions.push(buildExtensionAgentSession(state, ext, options));
5842
6415
  }
5843
6416
  }
@@ -8090,7 +8663,9 @@ function applyProviderPatch(h, args, payload) {
8090
8663
  });
8091
8664
  }
8092
8665
  async function executeProviderScript(h, args, scriptName) {
8093
- const resolvedProviderType = h.currentSession?.providerType || h.currentProviderType || args?.agentType || args?.providerType;
8666
+ const explicitTargetSessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : "";
8667
+ const targetSession = explicitTargetSessionId ? h.ctx.sessionRegistry?.get(explicitTargetSessionId) : void 0;
8668
+ const resolvedProviderType = targetSession?.providerType || h.currentSession?.providerType || h.currentProviderType || args?.agentType || args?.providerType;
8094
8669
  if (!resolvedProviderType) return { success: false, error: "targetSessionId or providerType is required" };
8095
8670
  const loader = h.ctx.providerLoader;
8096
8671
  if (!loader) return { success: false, error: "ProviderLoader not initialized" };
@@ -8133,16 +8708,16 @@ async function executeProviderScript(h, args, scriptName) {
8133
8708
  const scriptFn = provider.scripts[actualScriptName];
8134
8709
  const scriptCode = scriptFn(normalizedArgs);
8135
8710
  if (!scriptCode) return { success: false, error: `Script '${actualScriptName}' returned null` };
8136
- const cdpKey = provider.category === "ide" ? h.currentSession?.cdpManagerKey || h.currentManagerKey || resolvedProviderType : h.currentSession?.cdpManagerKey || h.currentManagerKey;
8711
+ const cdpKey = provider.category === "ide" ? targetSession?.cdpManagerKey || h.currentSession?.cdpManagerKey || h.currentManagerKey || resolvedProviderType : targetSession?.cdpManagerKey || h.currentSession?.cdpManagerKey || h.currentManagerKey;
8137
8712
  LOG.info("Command", `[ExtScript] provider=${provider.type} category=${provider.category} cdpKey=${cdpKey}`);
8138
8713
  const cdp = h.getCdp(cdpKey);
8139
8714
  if (!cdp?.isConnected) return { success: false, error: `No CDP connection for ${cdpKey || "any"}` };
8140
8715
  try {
8141
8716
  let result;
8142
8717
  if (provider.category === "extension") {
8143
- const runtimeSessionId = h.currentSession?.sessionId || args?.targetSessionId;
8718
+ const runtimeSessionId = explicitTargetSessionId || h.currentSession?.sessionId;
8144
8719
  if (!runtimeSessionId) return { success: false, error: `No target session found for ${resolvedProviderType}` };
8145
- const parentSessionId = h.currentSession?.parentSessionId;
8720
+ const parentSessionId = targetSession?.parentSessionId || h.currentSession?.parentSessionId;
8146
8721
  if (parentSessionId) {
8147
8722
  await h.agentStream?.setActiveSession(cdp, parentSessionId, runtimeSessionId);
8148
8723
  await h.agentStream?.syncActiveSession(cdp, parentSessionId);
@@ -12492,8 +13067,12 @@ var init_cli_provider_instance = __esm({
12492
13067
  runtimeKey: runtime.runtimeKey,
12493
13068
  displayName: runtime.displayName,
12494
13069
  workspaceLabel: runtime.workspaceLabel,
13070
+ lifecycle: runtime.lifecycle ?? null,
13071
+ surfaceKind: runtime.surfaceKind,
12495
13072
  writeOwner: runtime.writeOwner || null,
12496
- attachedClients: runtime.attachedClients || []
13073
+ attachedClients: runtime.attachedClients || [],
13074
+ restoredFromStorage: runtime.restoredFromStorage === true,
13075
+ recoveryState: runtime.recoveryState ?? null
12497
13076
  } : void 0,
12498
13077
  resume: this.provider.resume,
12499
13078
  controlValues: surface.controlValues,
@@ -34904,67 +35483,6 @@ var init_command_log = __esm({
34904
35483
  }
34905
35484
  });
34906
35485
 
34907
- // ../../oss/packages/daemon-core/src/session-host/runtime-surface.ts
34908
- function isSessionHostLiveRuntime(record2) {
34909
- const lifecycle = String(record2?.lifecycle || "").trim();
34910
- return LIVE_LIFECYCLES.has(lifecycle);
34911
- }
34912
- function getSessionHostRecoveryLabel(meta3) {
34913
- const recoveryState = typeof meta3?.runtimeRecoveryState === "string" ? String(meta3.runtimeRecoveryState).trim() : "";
34914
- if (!recoveryState) return null;
34915
- if (recoveryState === "auto_resumed") return "restored after restart";
34916
- if (recoveryState === "resume_failed") return "restore failed";
34917
- if (recoveryState === "host_restart_interrupted") return "host restart interrupted";
34918
- if (recoveryState === "orphan_snapshot") return "snapshot recovered";
34919
- return recoveryState.replace(/_/g, " ");
34920
- }
34921
- function isSessionHostRecoverySnapshot(record2) {
34922
- if (!record2) return false;
34923
- if (isSessionHostLiveRuntime(record2)) return false;
34924
- const lifecycle = String(record2.lifecycle || "").trim();
34925
- if (lifecycle && lifecycle !== "stopped" && lifecycle !== "failed") {
34926
- return false;
34927
- }
34928
- const meta3 = record2.meta || void 0;
34929
- if (meta3?.restoredFromStorage === true) return true;
34930
- return getSessionHostRecoveryLabel(meta3) !== null;
34931
- }
34932
- function getSessionHostSurfaceKind(record2) {
34933
- if (isSessionHostLiveRuntime(record2)) return "live_runtime";
34934
- if (isSessionHostRecoverySnapshot(record2)) return "recovery_snapshot";
34935
- return "inactive_record";
34936
- }
34937
- function partitionSessionHostRecords(records) {
34938
- const liveRuntimes = [];
34939
- const recoverySnapshots = [];
34940
- const inactiveRecords = [];
34941
- for (const record2 of records) {
34942
- const kind = getSessionHostSurfaceKind(record2);
34943
- if (kind === "live_runtime") {
34944
- liveRuntimes.push(record2);
34945
- } else if (kind === "recovery_snapshot") {
34946
- recoverySnapshots.push(record2);
34947
- } else {
34948
- inactiveRecords.push(record2);
34949
- }
34950
- }
34951
- return {
34952
- liveRuntimes,
34953
- recoverySnapshots,
34954
- inactiveRecords
34955
- };
34956
- }
34957
- function partitionSessionHostDiagnosticsSessions(records) {
34958
- return partitionSessionHostRecords(records || []);
34959
- }
34960
- var LIVE_LIFECYCLES;
34961
- var init_runtime_surface = __esm({
34962
- "../../oss/packages/daemon-core/src/session-host/runtime-surface.ts"() {
34963
- "use strict";
34964
- LIVE_LIFECYCLES = /* @__PURE__ */ new Set(["starting", "running", "stopping", "interrupted"]);
34965
- }
34966
- });
34967
-
34968
35486
  // ../../oss/packages/daemon-core/src/status/snapshot.ts
34969
35487
  function buildRecentReadDebugSignature(snapshot) {
34970
35488
  return [
@@ -36582,6 +37100,28 @@ var init_subscription_updates = __esm({
36582
37100
  }
36583
37101
  });
36584
37102
 
37103
+ // ../../oss/packages/daemon-core/src/chat/async-batch.ts
37104
+ async function runAsyncBatch(items, worker, options = {}) {
37105
+ const list = Array.from(items);
37106
+ if (list.length === 0) return;
37107
+ const concurrency = Math.max(1, Math.min(list.length, Math.floor(options.concurrency || 1)));
37108
+ let nextIndex = 0;
37109
+ const runners = Array.from({ length: concurrency }, async () => {
37110
+ while (true) {
37111
+ const currentIndex = nextIndex;
37112
+ nextIndex += 1;
37113
+ if (currentIndex >= list.length) return;
37114
+ await worker(list[currentIndex], currentIndex);
37115
+ }
37116
+ });
37117
+ await Promise.all(runners);
37118
+ }
37119
+ var init_async_batch = __esm({
37120
+ "../../oss/packages/daemon-core/src/chat/async-batch.ts"() {
37121
+ "use strict";
37122
+ }
37123
+ });
37124
+
36585
37125
  // ../../oss/packages/daemon-core/src/agent-stream/provider-adapter.ts
36586
37126
  var ProviderStreamAdapter;
36587
37127
  var init_provider_adapter = __esm({
@@ -37063,10 +37603,12 @@ var init_manager2 = __esm({
37063
37603
  }
37064
37604
  }
37065
37605
  /** Collect active extension session state */
37066
- async collectActiveSession(cdp, parentSessionId) {
37606
+ async collectActiveSession(cdp, parentSessionId, attemptedSessionIds = /* @__PURE__ */ new Set(), originSessionId) {
37067
37607
  if (!this.enabled) return null;
37068
37608
  const activeSessionId = this.getActiveSessionId(parentSessionId);
37069
37609
  if (!activeSessionId) return null;
37610
+ const resolvedOriginSessionId = originSessionId || activeSessionId;
37611
+ attemptedSessionIds.add(activeSessionId);
37070
37612
  let agent = this.managedBySessionId.get(activeSessionId);
37071
37613
  if (!agent) {
37072
37614
  agent = await this.connectManagedSession(cdp, parentSessionId, activeSessionId) || void 0;
@@ -37079,18 +37621,44 @@ var init_manager2 = __esm({
37079
37621
  try {
37080
37622
  const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
37081
37623
  const state = await agent.adapter.readChat(evaluate);
37082
- const stateError = this.getStateError(state);
37083
- const selectedModelValue = typeof state.controlValues?.model === "string" ? state.controlValues.model : "";
37084
- LOG.debug("AgentStream", `[AgentStream] readChat(${type}) result: status=${state.status} msgs=${state.messages?.length || 0} model=${selectedModelValue}${state.status === "error" ? " error=" + JSON.stringify(stateError) : ""}`);
37085
- if (state.status === "error" && this.isRecoverableSessionError(stateError)) {
37624
+ 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;
37625
+ const normalizedState = {
37626
+ ...state,
37627
+ sessionId: agent.runtimeSessionId,
37628
+ ...resolvedProviderSessionId ? { providerSessionId: resolvedProviderSessionId } : {}
37629
+ };
37630
+ const stateError = this.getStateError(normalizedState);
37631
+ const selectedModelValue = typeof normalizedState.controlValues?.model === "string" ? normalizedState.controlValues.model : "";
37632
+ LOG.debug("AgentStream", `[AgentStream] readChat(${type}) result: status=${normalizedState.status} msgs=${normalizedState.messages?.length || 0} model=${selectedModelValue}${normalizedState.status === "error" ? " error=" + JSON.stringify(stateError) : ""}`);
37633
+ if (normalizedState.status === "error" && this.isRecoverableSessionError(stateError)) {
37086
37634
  throw new Error(stateError);
37087
37635
  }
37088
- agent.lastState = state;
37636
+ agent.lastState = normalizedState;
37089
37637
  agent.lastError = null;
37090
- if (state.status === "panel_hidden") {
37638
+ if (normalizedState.status === "panel_hidden") {
37639
+ const discovered = await cdp.discoverAgentWebviews().catch(() => []);
37640
+ const fallbackTarget = discovered.find((entry) => {
37641
+ if (entry.agentType === type) return false;
37642
+ const fallbackSessionId = this.resolveSessionIdForTarget(parentSessionId, entry.agentType);
37643
+ return !!fallbackSessionId && fallbackSessionId !== activeSessionId && !attemptedSessionIds.has(fallbackSessionId);
37644
+ });
37645
+ if (fallbackTarget) {
37646
+ const fallbackSessionId = this.resolveSessionIdForTarget(parentSessionId, fallbackTarget.agentType);
37647
+ if (fallbackSessionId && fallbackSessionId !== activeSessionId && !attemptedSessionIds.has(fallbackSessionId)) {
37648
+ this.logFn(`[AgentStream] Active session ${type} is hidden; switching to visible agent ${fallbackTarget.agentType} (${parentSessionId})`);
37649
+ await this.setActiveSession(cdp, parentSessionId, fallbackSessionId);
37650
+ await this.syncActiveSession(cdp, parentSessionId);
37651
+ const fallbackState = await this.collectActiveSession(cdp, parentSessionId, attemptedSessionIds, resolvedOriginSessionId);
37652
+ if (fallbackState?.status === "panel_hidden" && resolvedOriginSessionId !== fallbackSessionId) {
37653
+ await this.setActiveSession(cdp, parentSessionId, resolvedOriginSessionId);
37654
+ await this.syncActiveSession(cdp, parentSessionId);
37655
+ }
37656
+ return fallbackState;
37657
+ }
37658
+ }
37091
37659
  agent.lastHiddenCheckTime = Date.now();
37092
37660
  }
37093
- return state;
37661
+ return normalizedState;
37094
37662
  } catch (e) {
37095
37663
  const errorMsg = e?.message || String(e);
37096
37664
  this.logFn(`[AgentStream] readChat(${type}) error: ${errorMsg.slice(0, 200)}`);
@@ -37402,6 +37970,7 @@ var init_poller = __esm({
37402
37970
  try {
37403
37971
  await agentStreamManager.syncActiveSession(cdp, parentSessionId);
37404
37972
  let stream = await agentStreamManager.collectActiveSession(cdp, parentSessionId);
37973
+ resolvedActiveSessionId = stream?.sessionId || agentStreamManager.getActiveSessionId(parentSessionId) || resolvedActiveSessionId;
37405
37974
  if (stream?.status === "waiting_approval") {
37406
37975
  const autoApprove = providerLoader.getSettings(stream.agentType).autoApprove !== false;
37407
37976
  if (autoApprove && resolvedActiveSessionId) {
@@ -43444,6 +44013,8 @@ var init_session_host_transport = __esm({
43444
44013
  runtimeKey: record2.runtimeKey,
43445
44014
  displayName: record2.displayName,
43446
44015
  workspaceLabel: record2.workspaceLabel,
44016
+ lifecycle: typeof record2.lifecycle === "string" ? record2.lifecycle : null,
44017
+ surfaceKind: record2.surfaceKind,
43447
44018
  writeOwner: record2.writeOwner ? {
43448
44019
  clientId: record2.writeOwner.clientId,
43449
44020
  ownerType: record2.writeOwner.ownerType
@@ -44221,6 +44792,7 @@ __export(src_exports, {
44221
44792
  resolveChatMessageKind: () => resolveChatMessageKind,
44222
44793
  resolveDebugRuntimeConfig: () => resolveDebugRuntimeConfig,
44223
44794
  resolveSessionHostAppName: () => resolveSessionHostAppName,
44795
+ runAsyncBatch: () => runAsyncBatch,
44224
44796
  saveConfig: () => saveConfig,
44225
44797
  saveState: () => saveState,
44226
44798
  setDebugRuntimeConfig: () => setDebugRuntimeConfig,
@@ -44268,6 +44840,7 @@ var init_src = __esm({
44268
44840
  init_chat_history();
44269
44841
  init_chat_signatures();
44270
44842
  init_subscription_updates();
44843
+ init_async_batch();
44271
44844
  init_agent_stream();
44272
44845
  init_agent_stream();
44273
44846
  init_forward();
@@ -45356,6 +45929,7 @@ var init_daemon_p2p = __esm({
45356
45929
  fs15 = __toESM(require("fs"));
45357
45930
  path23 = __toESM(require("path"));
45358
45931
  import_node_module2 = require("module");
45932
+ init_src();
45359
45933
  init_data_channel_router();
45360
45934
  init_screenshot_sender();
45361
45935
  init_peer_connection_manager();
@@ -45605,14 +46179,22 @@ ${e?.stack || ""}`);
45605
46179
  return false;
45606
46180
  }
45607
46181
  async flushChatSubscriptions(builder) {
46182
+ const tasks = [];
45608
46183
  for (const peer of this.peers.values()) {
45609
46184
  if (peer.state !== "connected" || !peer.chatSubscriptions || peer.chatSubscriptions.size === 0) continue;
45610
46185
  for (const subscription of peer.chatSubscriptions.values()) {
46186
+ tasks.push({ peer, subscription });
46187
+ }
46188
+ }
46189
+ await runAsyncBatch(tasks, async ({ peer, subscription }) => {
46190
+ try {
45611
46191
  const update = await builder(subscription);
45612
- if (!update) continue;
46192
+ if (!update) return;
45613
46193
  this.screenshotSender.sendTopicUpdateToPeer(peer, update);
46194
+ } catch (error48) {
46195
+ log(`chat_tail flush skipped: peer=${peer.peerId} session=${subscription.params.targetSessionId} error=${error48?.message || error48}`);
45614
46196
  }
45615
- }
46197
+ }, { concurrency: 4 });
45616
46198
  }
45617
46199
  async flushMachineRuntimeSubscriptions(builder) {
45618
46200
  for (const peer of this.peers.values()) {
@@ -53563,7 +54145,7 @@ var init_adhdev_daemon = __esm({
53563
54145
  init_source2();
53564
54146
  init_version();
53565
54147
  init_src();
53566
- pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.75" });
54148
+ pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.77" });
53567
54149
  AdhdevDaemon = class _AdhdevDaemon {
53568
54150
  localHttpServer = null;
53569
54151
  localWss = null;
@@ -53977,7 +54559,7 @@ ${err?.stack || ""}`);
53977
54559
  }),
53978
54560
  onStatusChange: () => {
53979
54561
  this.statusReporter?.onStatusChange();
53980
- void this.flushP2PChatSubscriptions({ onlyActive: false });
54562
+ void this.flushP2PChatSubscriptions({ onlyActive: true });
53981
54563
  },
53982
54564
  removeAgentTracking: (key) => this.statusReporter?.removeAgentTracking(key),
53983
54565
  hostedRuntimeManagerTag: "adhdev-cloud",
@@ -54006,7 +54588,7 @@ ${err?.stack || ""}`);
54006
54588
  statusVersion: pkgVersion,
54007
54589
  onStatusChange: () => {
54008
54590
  this.statusReporter?.onStatusChange();
54009
- void this.flushP2PChatSubscriptions({ onlyActive: false });
54591
+ void this.flushP2PChatSubscriptions({ onlyActive: true });
54010
54592
  },
54011
54593
  onPostChatCommand: () => {
54012
54594
  setTimeout(() => this.statusReporter?.throttledReport(), 1e3);