@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.mjs CHANGED
@@ -4271,6 +4271,61 @@ function getHostMemorySnapshot() {
4271
4271
  return { totalMem, freeMem, availableMem };
4272
4272
  }
4273
4273
 
4274
+ // src/session-host/runtime-surface.ts
4275
+ var LIVE_LIFECYCLES = /* @__PURE__ */ new Set(["starting", "running", "stopping", "interrupted"]);
4276
+ function isSessionHostLiveRuntime(record) {
4277
+ const lifecycle = String(record?.lifecycle || "").trim();
4278
+ return LIVE_LIFECYCLES.has(lifecycle);
4279
+ }
4280
+ function getSessionHostRecoveryLabel(meta) {
4281
+ const recoveryState = typeof meta?.runtimeRecoveryState === "string" ? String(meta.runtimeRecoveryState).trim() : "";
4282
+ if (!recoveryState) return null;
4283
+ if (recoveryState === "auto_resumed") return "restored after restart";
4284
+ if (recoveryState === "resume_failed") return "restore failed";
4285
+ if (recoveryState === "host_restart_interrupted") return "host restart interrupted";
4286
+ if (recoveryState === "orphan_snapshot") return "snapshot recovered";
4287
+ return recoveryState.replace(/_/g, " ");
4288
+ }
4289
+ function isSessionHostRecoverySnapshot(record) {
4290
+ if (!record) return false;
4291
+ if (isSessionHostLiveRuntime(record)) return false;
4292
+ const lifecycle = String(record.lifecycle || "").trim();
4293
+ if (lifecycle && lifecycle !== "stopped" && lifecycle !== "failed") {
4294
+ return false;
4295
+ }
4296
+ const meta = record.meta || void 0;
4297
+ if (meta?.restoredFromStorage === true) return true;
4298
+ return getSessionHostRecoveryLabel(meta) !== null;
4299
+ }
4300
+ function getSessionHostSurfaceKind(record) {
4301
+ if (isSessionHostLiveRuntime(record)) return "live_runtime";
4302
+ if (isSessionHostRecoverySnapshot(record)) return "recovery_snapshot";
4303
+ return "inactive_record";
4304
+ }
4305
+ function partitionSessionHostRecords(records) {
4306
+ const liveRuntimes = [];
4307
+ const recoverySnapshots = [];
4308
+ const inactiveRecords = [];
4309
+ for (const record of records) {
4310
+ const kind = getSessionHostSurfaceKind(record);
4311
+ if (kind === "live_runtime") {
4312
+ liveRuntimes.push(record);
4313
+ } else if (kind === "recovery_snapshot") {
4314
+ recoverySnapshots.push(record);
4315
+ } else {
4316
+ inactiveRecords.push(record);
4317
+ }
4318
+ }
4319
+ return {
4320
+ liveRuntimes,
4321
+ recoverySnapshots,
4322
+ inactiveRecords
4323
+ };
4324
+ }
4325
+ function partitionSessionHostDiagnosticsSessions(records) {
4326
+ return partitionSessionHostRecords(records || []);
4327
+ }
4328
+
4274
4329
  // src/status/chat-tail-hot-sessions.ts
4275
4330
  var DEFAULT_ACTIVE_CHAT_POLL_STATUSES = /* @__PURE__ */ new Set([
4276
4331
  "generating",
@@ -4278,6 +4333,7 @@ var DEFAULT_ACTIVE_CHAT_POLL_STATUSES = /* @__PURE__ */ new Set([
4278
4333
  "starting"
4279
4334
  ]);
4280
4335
  var DEFAULT_CHAT_TAIL_RECENT_MESSAGE_GRACE_MS = 8e3;
4336
+ var LIVE_RUNTIME_LIFECYCLES = /* @__PURE__ */ new Set(["starting", "running", "stopping", "interrupted"]);
4281
4337
  function parseMessageTimestamp(value) {
4282
4338
  if (typeof value === "number" && Number.isFinite(value)) return value;
4283
4339
  if (typeof value === "string") {
@@ -4286,6 +4342,23 @@ function parseMessageTimestamp(value) {
4286
4342
  }
4287
4343
  return 0;
4288
4344
  }
4345
+ function isDefinitelyNonLiveRuntimeSession(session) {
4346
+ const surfaceKind = String(session?.runtimeSurfaceKind || "").trim();
4347
+ if (surfaceKind === "live_runtime") return false;
4348
+ if (surfaceKind === "recovery_snapshot") return true;
4349
+ if (surfaceKind === "inactive_record") return false;
4350
+ const lifecycle = String(session?.runtimeLifecycle || "").trim();
4351
+ if (lifecycle && LIVE_RUNTIME_LIFECYCLES.has(lifecycle)) return false;
4352
+ const inferredSurfaceKind = getSessionHostSurfaceKind({
4353
+ lifecycle: lifecycle || null,
4354
+ meta: {
4355
+ restoredFromStorage: session?.runtimeRestoredFromStorage === true,
4356
+ ...session?.runtimeRecoveryState ? { runtimeRecoveryState: session.runtimeRecoveryState } : {}
4357
+ }
4358
+ });
4359
+ if (inferredSurfaceKind === "recovery_snapshot") return true;
4360
+ return false;
4361
+ }
4289
4362
  function classifyHotChatSessionsForSubscriptionFlush(sessions, previousHotSessionIds, options = {}) {
4290
4363
  const now = options.now ?? Date.now();
4291
4364
  const recentMessageGraceMs = Math.max(
@@ -4294,9 +4367,14 @@ function classifyHotChatSessionsForSubscriptionFlush(sessions, previousHotSessio
4294
4367
  );
4295
4368
  const activeStatuses = options.activeStatuses ?? DEFAULT_ACTIVE_CHAT_POLL_STATUSES;
4296
4369
  const active = /* @__PURE__ */ new Set();
4370
+ const excluded = /* @__PURE__ */ new Set();
4297
4371
  for (const session of sessions) {
4298
4372
  const sessionId = typeof session?.id === "string" ? session.id : "";
4299
4373
  if (!sessionId) continue;
4374
+ if (isDefinitelyNonLiveRuntimeSession(session)) {
4375
+ excluded.add(sessionId);
4376
+ continue;
4377
+ }
4300
4378
  const status = String(session?.status || "").toLowerCase();
4301
4379
  const lastMessageAt = parseMessageTimestamp(session?.lastMessageAt);
4302
4380
  const recentlyUpdated = lastMessageAt > 0 && now - lastMessageAt <= recentMessageGraceMs;
@@ -4305,7 +4383,7 @@ function classifyHotChatSessionsForSubscriptionFlush(sessions, previousHotSessio
4305
4383
  }
4306
4384
  }
4307
4385
  const finalizing = new Set(
4308
- Array.from(previousHotSessionIds).filter((sessionId) => !active.has(sessionId))
4386
+ Array.from(previousHotSessionIds).filter((sessionId) => !active.has(sessionId) && !excluded.has(sessionId))
4309
4387
  );
4310
4388
  return { active, finalizing };
4311
4389
  }
@@ -4314,6 +4392,32 @@ function classifyHotChatSessionsForSubscriptionFlush(sessions, previousHotSessio
4314
4392
  init_logger();
4315
4393
  import WebSocket from "ws";
4316
4394
  import * as http from "http";
4395
+ function normalizeTitle(value) {
4396
+ return String(value || "").trim().replace(/\s+/g, " ").toLowerCase();
4397
+ }
4398
+ function titlesMatch(lhs, rhs) {
4399
+ const a = normalizeTitle(lhs);
4400
+ const b = normalizeTitle(rhs);
4401
+ if (!a || !b) return false;
4402
+ return a === b || a.includes(b) || b.includes(a);
4403
+ }
4404
+ function resolveCdpPageTarget(params) {
4405
+ const { pages, pinnedTargetId, previousPageTitle } = params;
4406
+ if (pages.length === 0) return { target: null, retargeted: false };
4407
+ if (!pinnedTargetId) {
4408
+ return { target: pages[0] || null, retargeted: false };
4409
+ }
4410
+ const exact = pages.find((page) => page.id === pinnedTargetId);
4411
+ if (exact) return { target: exact, retargeted: false };
4412
+ const titleMatchesList = pages.filter((page) => titlesMatch(page.title, previousPageTitle));
4413
+ if (titleMatchesList.length === 1) {
4414
+ return { target: titleMatchesList[0], retargeted: true };
4415
+ }
4416
+ if (pages.length === 1) {
4417
+ return { target: pages[0], retargeted: true };
4418
+ }
4419
+ return { target: null, retargeted: false };
4420
+ }
4317
4421
  var DaemonCdpManager = class {
4318
4422
  ws = null;
4319
4423
  browserWs = null;
@@ -4474,18 +4578,28 @@ var DaemonCdpManager = class {
4474
4578
  resolve11(targets.find((t) => t.webSocketDebuggerUrl) || null);
4475
4579
  return;
4476
4580
  }
4477
- const mainPages = pages.filter((t) => !this.isNonMainTitle(t.title || ""));
4478
- const list = mainPages.length > 0 ? mainPages : pages;
4581
+ const titleFilteredPages = pages.filter((t) => !this.isNonMainTitle(t.title || ""));
4582
+ const mainPages = titleFilteredPages.filter((t) => this.isMainPageUrl(t.url));
4583
+ const list = mainPages.length > 0 ? mainPages : titleFilteredPages.length > 0 ? titleFilteredPages : pages;
4479
4584
  this.log(`[CDP] pages(${list.length}): ${list.map((t) => `"${t.title}"`).join(", ")}`);
4480
- if (this._targetId) {
4481
- const specific = list.find((t) => t.id === this._targetId);
4482
- if (specific) {
4483
- this._pageTitle = specific.title || "";
4484
- resolve11(specific);
4485
- } else {
4486
- this.log(`[CDP] Target ${this._targetId} not found in page list`);
4487
- resolve11(null);
4585
+ const previousTargetId = this._targetId;
4586
+ const selected = resolveCdpPageTarget({
4587
+ pages: list,
4588
+ pinnedTargetId: previousTargetId,
4589
+ previousPageTitle: this._pageTitle
4590
+ });
4591
+ if (selected.target) {
4592
+ if (selected.retargeted && previousTargetId && previousTargetId !== selected.target.id) {
4593
+ this.log(`[CDP] Target ${previousTargetId} rekeyed to ${selected.target.id}`);
4594
+ this._targetId = selected.target.id;
4488
4595
  }
4596
+ this._pageTitle = selected.target.title || "";
4597
+ resolve11(selected.target);
4598
+ return;
4599
+ }
4600
+ if (previousTargetId) {
4601
+ this.log(`[CDP] Target ${previousTargetId} not found in page list`);
4602
+ resolve11(null);
4489
4603
  return;
4490
4604
  }
4491
4605
  this._pageTitle = list[0]?.title || "";
@@ -5925,7 +6039,17 @@ import * as path7 from "path";
5925
6039
  import * as os5 from "os";
5926
6040
  var HISTORY_DIR = path7.join(os5.homedir(), ".adhdev", "history");
5927
6041
  var RETAIN_DAYS = 30;
6042
+ var SAVED_HISTORY_INDEX_VERSION = 1;
6043
+ var SAVED_HISTORY_INDEX_FILE = ".saved-history-index.json";
6044
+ var SAVED_HISTORY_INDEX_LOCK_SUFFIX = ".lock";
6045
+ var SAVED_HISTORY_INDEX_LOCK_WAIT_MS = 1500;
6046
+ var SAVED_HISTORY_INDEX_LOCK_STALE_MS = 15e3;
6047
+ var SAVED_HISTORY_INDEX_LOCK_POLL_MS = 25;
6048
+ var SAVED_HISTORY_ROLLUP_THRESHOLD_BYTES = 16 * 1024 * 1024;
5928
6049
  var savedHistorySessionCache = /* @__PURE__ */ new Map();
6050
+ var savedHistoryFileSummaryCache = /* @__PURE__ */ new Map();
6051
+ var savedHistoryBackgroundRefresh = /* @__PURE__ */ new Set();
6052
+ var savedHistoryRollupInFlight = /* @__PURE__ */ new Set();
5929
6053
  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;
5930
6054
  function normalizeHistoryComparable(text) {
5931
6055
  return String(text || "").replace(/\s+/g, " ").trim();
@@ -5983,6 +6107,68 @@ function sanitizeHistoryMessage(agentType, message) {
5983
6107
  content
5984
6108
  };
5985
6109
  }
6110
+ function sortSavedHistorySessionSummaries(summaries) {
6111
+ return summaries.slice().sort((a, b) => b.lastMessageAt - a.lastMessageAt);
6112
+ }
6113
+ function buildSavedHistorySessionSummaryMapFromEntries(entries) {
6114
+ const summaries = /* @__PURE__ */ new Map();
6115
+ for (const entry of Array.from(entries.values())) {
6116
+ const fileSummary = entry.summary;
6117
+ if (!fileSummary || fileSummary.messageCount <= 0 || !fileSummary.lastMessageAt) continue;
6118
+ const existing = summaries.get(fileSummary.historySessionId);
6119
+ if (!existing) {
6120
+ summaries.set(fileSummary.historySessionId, {
6121
+ historySessionId: fileSummary.historySessionId,
6122
+ sessionTitle: fileSummary.sessionTitle,
6123
+ messageCount: fileSummary.messageCount,
6124
+ firstMessageAt: fileSummary.firstMessageAt,
6125
+ lastMessageAt: fileSummary.lastMessageAt,
6126
+ preview: fileSummary.preview,
6127
+ workspace: fileSummary.workspace
6128
+ });
6129
+ continue;
6130
+ }
6131
+ existing.messageCount += fileSummary.messageCount;
6132
+ if (!existing.firstMessageAt || fileSummary.firstMessageAt < existing.firstMessageAt) {
6133
+ existing.firstMessageAt = fileSummary.firstMessageAt;
6134
+ }
6135
+ if (fileSummary.lastMessageAt >= existing.lastMessageAt) {
6136
+ existing.lastMessageAt = fileSummary.lastMessageAt;
6137
+ if (fileSummary.sessionTitle) existing.sessionTitle = fileSummary.sessionTitle;
6138
+ if (fileSummary.preview) existing.preview = fileSummary.preview;
6139
+ }
6140
+ if (!existing.workspace && fileSummary.workspace) {
6141
+ existing.workspace = fileSummary.workspace;
6142
+ }
6143
+ }
6144
+ return Object.fromEntries(sortSavedHistorySessionSummaries(Array.from(summaries.values())).map((summary) => [summary.historySessionId, summary]));
6145
+ }
6146
+ function readPersistedSavedHistorySessionSummaries(dir) {
6147
+ try {
6148
+ const filePath = getSavedHistoryIndexFilePath(dir);
6149
+ if (!fs3.existsSync(filePath)) return null;
6150
+ const raw = JSON.parse(fs3.readFileSync(filePath, "utf-8"));
6151
+ if (!raw || raw.version !== SAVED_HISTORY_INDEX_VERSION || !raw.sessions || typeof raw.sessions !== "object") {
6152
+ return null;
6153
+ }
6154
+ return sortSavedHistorySessionSummaries(
6155
+ Object.values(raw.sessions).filter((summary) => !!summary && typeof summary.historySessionId === "string" && summary.messageCount > 0 && summary.lastMessageAt > 0).map((summary) => ({
6156
+ historySessionId: summary.historySessionId,
6157
+ sessionTitle: summary.sessionTitle,
6158
+ messageCount: summary.messageCount,
6159
+ firstMessageAt: summary.firstMessageAt,
6160
+ lastMessageAt: summary.lastMessageAt,
6161
+ preview: summary.preview,
6162
+ workspace: summary.workspace
6163
+ }))
6164
+ );
6165
+ } catch {
6166
+ return null;
6167
+ }
6168
+ }
6169
+ function shouldScheduleSavedHistoryRollup(totalBytes) {
6170
+ return Number.isFinite(totalBytes) && totalBytes >= SAVED_HISTORY_ROLLUP_THRESHOLD_BYTES;
6171
+ }
5986
6172
  function sanitizeHistoryFileSegment(value) {
5987
6173
  return String(value || "").replace(/[^a-zA-Z0-9_-]/g, "_");
5988
6174
  }
@@ -5996,71 +6182,386 @@ function listHistoryFiles(dir, historySessionId) {
5996
6182
  return true;
5997
6183
  }).sort().reverse();
5998
6184
  }
5999
- function buildSavedHistoryCacheSignature(dir, files) {
6000
- return files.map((file) => {
6185
+ function normalizeSavedHistorySessionId(agentType, historySessionId) {
6186
+ const normalizedId = String(historySessionId || "").trim();
6187
+ if (!normalizedId) return "";
6188
+ const strictProviderId = normalizeProviderSessionId(agentType, normalizedId);
6189
+ if (strictProviderId) return strictProviderId;
6190
+ return agentType === "hermes-cli" ? "" : normalizedId;
6191
+ }
6192
+ function extractSavedHistorySessionIdFromFile(agentType, file) {
6193
+ const match = file.match(/^([A-Za-z0-9_-]+)_\d{4}-\d{2}-\d{2}\.jsonl$/);
6194
+ return normalizeSavedHistorySessionId(agentType, match?.[1] || "");
6195
+ }
6196
+ function buildSavedHistoryFileSignatureMap(dir, files) {
6197
+ return new Map(files.map((file) => {
6001
6198
  try {
6002
6199
  const stat = fs3.statSync(path7.join(dir, file));
6003
- return `${file}:${stat.size}:${Math.trunc(stat.mtimeMs)}`;
6200
+ return [file, `${file}:${stat.size}:${Math.trunc(stat.mtimeMs)}`];
6004
6201
  } catch {
6005
- return `${file}:missing`;
6006
- }
6007
- }).join("|");
6008
- }
6009
- function computeSavedHistorySessionSummaries(agentType, dir, files) {
6010
- const groupedFiles = /* @__PURE__ */ new Map();
6011
- const filePattern = /^([A-Za-z0-9_-]+)_\d{4}-\d{2}-\d{2}\.jsonl$/;
6012
- for (const file of files) {
6013
- const match = file.match(filePattern);
6014
- if (!match?.[1]) continue;
6015
- const historySessionId = match[1];
6016
- const grouped = groupedFiles.get(historySessionId) || [];
6017
- grouped.push(file);
6018
- groupedFiles.set(historySessionId, grouped);
6019
- }
6020
- const summaries = [];
6021
- for (const [historySessionId, grouped] of groupedFiles.entries()) {
6022
- let messageCount = 0;
6023
- let firstMessageAt = 0;
6024
- let lastMessageAt = 0;
6025
- let sessionTitle = "";
6026
- let preview = "";
6027
- let workspace = "";
6028
- for (const file of grouped.sort()) {
6029
- const filePath = path7.join(dir, file);
6030
- const content = fs3.readFileSync(filePath, "utf-8");
6031
- const lines = content.split("\n").filter(Boolean);
6032
- for (const line of lines) {
6033
- let parsed = null;
6202
+ return [file, `${file}:missing`];
6203
+ }
6204
+ }));
6205
+ }
6206
+ function buildSavedHistoryCacheSignature(files, fileSignatures) {
6207
+ return files.map((file) => fileSignatures.get(file) || `${file}:missing`).join("|");
6208
+ }
6209
+ function getSavedHistoryIndexFilePath(dir) {
6210
+ return path7.join(dir, SAVED_HISTORY_INDEX_FILE);
6211
+ }
6212
+ function getSavedHistoryIndexLockPath(dir) {
6213
+ return `${getSavedHistoryIndexFilePath(dir)}${SAVED_HISTORY_INDEX_LOCK_SUFFIX}`;
6214
+ }
6215
+ function sleepBlocking(ms) {
6216
+ if (ms <= 0) return;
6217
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
6218
+ }
6219
+ function loadPersistedSavedHistoryIndexFromFile(dir) {
6220
+ try {
6221
+ const filePath = getSavedHistoryIndexFilePath(dir);
6222
+ if (!fs3.existsSync(filePath)) return /* @__PURE__ */ new Map();
6223
+ const raw = JSON.parse(fs3.readFileSync(filePath, "utf-8"));
6224
+ if (!raw || raw.version !== SAVED_HISTORY_INDEX_VERSION || !raw.files || typeof raw.files !== "object") {
6225
+ return /* @__PURE__ */ new Map();
6226
+ }
6227
+ return new Map(
6228
+ Object.entries(raw.files).filter(([file, entry]) => !!file && !!entry && typeof entry.signature === "string").map(([file, entry]) => [file, {
6229
+ signature: entry.signature,
6230
+ summary: entry.summary || null
6231
+ }])
6232
+ );
6233
+ } catch {
6234
+ return /* @__PURE__ */ new Map();
6235
+ }
6236
+ }
6237
+ function writePersistedSavedHistoryIndexFile(dir, entries) {
6238
+ const filePath = getSavedHistoryIndexFilePath(dir);
6239
+ const tempPath = `${filePath}.tmp`;
6240
+ const payload = {
6241
+ version: SAVED_HISTORY_INDEX_VERSION,
6242
+ files: Object.fromEntries(entries.entries()),
6243
+ sessions: buildSavedHistorySessionSummaryMapFromEntries(entries)
6244
+ };
6245
+ fs3.writeFileSync(tempPath, JSON.stringify(payload), "utf-8");
6246
+ fs3.renameSync(tempPath, filePath);
6247
+ }
6248
+ function acquireSavedHistoryIndexLock(dir) {
6249
+ const lockPath = getSavedHistoryIndexLockPath(dir);
6250
+ const deadline = Date.now() + SAVED_HISTORY_INDEX_LOCK_WAIT_MS;
6251
+ while (Date.now() <= deadline) {
6252
+ try {
6253
+ fs3.mkdirSync(lockPath);
6254
+ return () => {
6034
6255
  try {
6035
- parsed = JSON.parse(line);
6256
+ fs3.rmSync(lockPath, { recursive: true, force: true });
6036
6257
  } catch {
6037
- parsed = null;
6038
6258
  }
6039
- if (!parsed || parsed.historySessionId !== historySessionId) continue;
6040
- if (parsed.kind === "session_start") {
6041
- if (!workspace && parsed.workspace) workspace = parsed.workspace;
6259
+ };
6260
+ } catch (error) {
6261
+ if (error?.code !== "EEXIST") return null;
6262
+ try {
6263
+ const stat = fs3.statSync(lockPath);
6264
+ if (Date.now() - stat.mtimeMs > SAVED_HISTORY_INDEX_LOCK_STALE_MS) {
6265
+ fs3.rmSync(lockPath, { recursive: true, force: true });
6042
6266
  continue;
6043
6267
  }
6044
- messageCount += 1;
6045
- if (!firstMessageAt || parsed.receivedAt < firstMessageAt) firstMessageAt = parsed.receivedAt;
6046
- if (!lastMessageAt || parsed.receivedAt > lastMessageAt) lastMessageAt = parsed.receivedAt;
6047
- if (parsed.sessionTitle) sessionTitle = parsed.sessionTitle;
6048
- if (parsed.role !== "system" && parsed.content.trim()) preview = parsed.content.trim();
6049
- }
6050
- }
6051
- if (messageCount === 0 || !lastMessageAt) continue;
6052
- summaries.push({
6053
- historySessionId,
6054
- sessionTitle: sessionTitle || void 0,
6055
- messageCount,
6056
- firstMessageAt,
6057
- lastMessageAt,
6058
- preview: preview || void 0,
6059
- workspace: workspace || void 0
6060
- });
6268
+ } catch {
6269
+ continue;
6270
+ }
6271
+ sleepBlocking(SAVED_HISTORY_INDEX_LOCK_POLL_MS);
6272
+ }
6273
+ }
6274
+ return null;
6275
+ }
6276
+ function withLockedPersistedSavedHistoryIndex(dir, callback) {
6277
+ const release2 = acquireSavedHistoryIndexLock(dir);
6278
+ if (!release2) return null;
6279
+ try {
6280
+ const entries = loadPersistedSavedHistoryIndexFromFile(dir);
6281
+ const result = callback(entries);
6282
+ writePersistedSavedHistoryIndexFile(dir, entries);
6283
+ return result;
6284
+ } catch {
6285
+ return null;
6286
+ } finally {
6287
+ release2();
6288
+ }
6289
+ }
6290
+ function loadPersistedSavedHistoryIndex(dir) {
6291
+ return loadPersistedSavedHistoryIndexFromFile(dir);
6292
+ }
6293
+ function savePersistedSavedHistoryIndex(dir, entries) {
6294
+ withLockedPersistedSavedHistoryIndex(dir, (currentEntries) => {
6295
+ const incomingFiles = new Set(Array.from(entries.keys()));
6296
+ for (const [file, entry] of Array.from(entries.entries())) {
6297
+ const liveSignature = buildSavedHistoryFileSignature(dir, file);
6298
+ const existingEntry = currentEntries.get(file);
6299
+ if (existingEntry && existingEntry.signature !== liveSignature && entry.signature !== liveSignature) {
6300
+ continue;
6301
+ }
6302
+ if (entry.signature !== liveSignature && (!existingEntry || existingEntry.signature !== liveSignature)) {
6303
+ continue;
6304
+ }
6305
+ currentEntries.set(file, entry.signature === liveSignature ? entry : {
6306
+ signature: liveSignature,
6307
+ summary: existingEntry?.summary || entry.summary
6308
+ });
6309
+ }
6310
+ for (const file of Array.from(currentEntries.keys())) {
6311
+ if (incomingFiles.has(file)) continue;
6312
+ if (!fs3.existsSync(path7.join(dir, file))) {
6313
+ currentEntries.delete(file);
6314
+ }
6315
+ }
6316
+ });
6317
+ }
6318
+ function invalidatePersistedSavedHistoryIndex(agentType, dir) {
6319
+ try {
6320
+ fs3.rmSync(getSavedHistoryIndexFilePath(dir), { force: true });
6321
+ } catch {
6322
+ }
6323
+ savedHistorySessionCache.delete(agentType.replace(/[^a-zA-Z0-9_-]/g, "_"));
6324
+ }
6325
+ function buildSavedHistoryIndexFileSignature(dir) {
6326
+ try {
6327
+ const stat = fs3.statSync(getSavedHistoryIndexFilePath(dir));
6328
+ return `index:${stat.size}:${Math.trunc(stat.mtimeMs)}`;
6329
+ } catch {
6330
+ return "index:missing";
6331
+ }
6332
+ }
6333
+ function historyDirectoryHasFilesNewerThanIndex(dir) {
6334
+ try {
6335
+ const indexStat = fs3.statSync(getSavedHistoryIndexFilePath(dir));
6336
+ const files = listHistoryFiles(dir);
6337
+ for (const file of files) {
6338
+ const stat = fs3.statSync(path7.join(dir, file));
6339
+ if (stat.mtimeMs > indexStat.mtimeMs) return true;
6340
+ }
6341
+ return false;
6342
+ } catch {
6343
+ return true;
6344
+ }
6345
+ }
6346
+ function buildSavedHistoryFileSignature(dir, file) {
6347
+ try {
6348
+ const stat = fs3.statSync(path7.join(dir, file));
6349
+ return `${file}:${stat.size}:${Math.trunc(stat.mtimeMs)}`;
6350
+ } catch {
6351
+ return `${file}:missing`;
6352
+ }
6353
+ }
6354
+ function persistSavedHistoryFileSummaryEntry(agentType, dir, file, updater) {
6355
+ const filePath = path7.join(dir, file);
6356
+ const result = withLockedPersistedSavedHistoryIndex(dir, (entries) => {
6357
+ const currentEntry = entries.get(file) || null;
6358
+ const nextSummary = updater(currentEntry?.summary || null);
6359
+ const nextEntry = {
6360
+ signature: buildSavedHistoryFileSignature(dir, file),
6361
+ summary: nextSummary
6362
+ };
6363
+ entries.set(file, nextEntry);
6364
+ savedHistoryFileSummaryCache.set(filePath, nextEntry);
6365
+ return nextEntry;
6366
+ });
6367
+ if (!result) return;
6368
+ if (result.summary?.historySessionId && shouldScheduleSavedHistoryRollupForSignature(result.signature)) {
6369
+ scheduleSavedHistoryRollup(agentType, result.summary.historySessionId);
6370
+ }
6371
+ }
6372
+ function updateSavedHistoryIndexForSessionStart(agentType, dir, file, historySessionId, workspace) {
6373
+ const normalizedSessionId = normalizeSavedHistorySessionId(agentType, historySessionId);
6374
+ const normalizedWorkspace = String(workspace || "").trim();
6375
+ if (!normalizedSessionId || !normalizedWorkspace) return;
6376
+ persistSavedHistoryFileSummaryEntry(agentType, dir, file, (currentSummary) => ({
6377
+ file,
6378
+ historySessionId: normalizedSessionId,
6379
+ messageCount: currentSummary?.messageCount || 0,
6380
+ firstMessageAt: currentSummary?.firstMessageAt || 0,
6381
+ lastMessageAt: currentSummary?.lastMessageAt || 0,
6382
+ sessionTitle: currentSummary?.sessionTitle,
6383
+ preview: currentSummary?.preview,
6384
+ workspace: normalizedWorkspace
6385
+ }));
6386
+ }
6387
+ function updateSavedHistoryIndexForAppendedMessages(agentType, dir, file, historySessionId, messages) {
6388
+ const normalizedSessionId = normalizeSavedHistorySessionId(agentType, historySessionId || "");
6389
+ if (!normalizedSessionId || messages.length === 0) return;
6390
+ persistSavedHistoryFileSummaryEntry(agentType, dir, file, (currentSummary) => {
6391
+ const nextSummary = {
6392
+ file,
6393
+ historySessionId: normalizedSessionId,
6394
+ messageCount: currentSummary?.messageCount || 0,
6395
+ firstMessageAt: currentSummary?.firstMessageAt || 0,
6396
+ lastMessageAt: currentSummary?.lastMessageAt || 0,
6397
+ sessionTitle: currentSummary?.sessionTitle,
6398
+ preview: currentSummary?.preview,
6399
+ workspace: currentSummary?.workspace
6400
+ };
6401
+ for (const message of messages) {
6402
+ if (!message || message.historySessionId !== historySessionId) continue;
6403
+ if (message.kind === "session_start") {
6404
+ if (message.workspace) nextSummary.workspace = message.workspace;
6405
+ continue;
6406
+ }
6407
+ nextSummary.messageCount += 1;
6408
+ if (!nextSummary.firstMessageAt || message.receivedAt < nextSummary.firstMessageAt) {
6409
+ nextSummary.firstMessageAt = message.receivedAt;
6410
+ }
6411
+ if (!nextSummary.lastMessageAt || message.receivedAt >= nextSummary.lastMessageAt) {
6412
+ nextSummary.lastMessageAt = message.receivedAt;
6413
+ if (message.sessionTitle) nextSummary.sessionTitle = message.sessionTitle;
6414
+ if (message.role !== "system" && message.content.trim()) nextSummary.preview = message.content.trim();
6415
+ } else if (message.sessionTitle) {
6416
+ nextSummary.sessionTitle = message.sessionTitle;
6417
+ }
6418
+ if (!nextSummary.preview && message.role !== "system" && message.content.trim()) {
6419
+ nextSummary.preview = message.content.trim();
6420
+ }
6421
+ }
6422
+ return nextSummary;
6423
+ });
6424
+ }
6425
+ function computeSavedHistoryFileSummary(agentType, dir, file) {
6426
+ const historySessionId = extractSavedHistorySessionIdFromFile(agentType, file);
6427
+ if (!historySessionId) return null;
6428
+ const filePath = path7.join(dir, file);
6429
+ const content = fs3.readFileSync(filePath, "utf-8");
6430
+ const lines = content.split("\n").filter(Boolean);
6431
+ let messageCount = 0;
6432
+ let firstMessageAt = 0;
6433
+ let lastMessageAt = 0;
6434
+ let sessionTitle = "";
6435
+ let preview = "";
6436
+ let workspace = "";
6437
+ for (const line of lines) {
6438
+ let parsed = null;
6439
+ try {
6440
+ parsed = JSON.parse(line);
6441
+ } catch {
6442
+ parsed = null;
6443
+ }
6444
+ if (!parsed || parsed.historySessionId !== historySessionId) continue;
6445
+ if (parsed.kind === "session_start") {
6446
+ if (!workspace && parsed.workspace) workspace = parsed.workspace;
6447
+ continue;
6448
+ }
6449
+ messageCount += 1;
6450
+ if (!firstMessageAt || parsed.receivedAt < firstMessageAt) firstMessageAt = parsed.receivedAt;
6451
+ if (!lastMessageAt || parsed.receivedAt > lastMessageAt) lastMessageAt = parsed.receivedAt;
6452
+ if (parsed.sessionTitle) sessionTitle = parsed.sessionTitle;
6453
+ if (parsed.role !== "system" && parsed.content.trim()) preview = parsed.content.trim();
6454
+ }
6455
+ if (messageCount === 0 || !lastMessageAt) return null;
6456
+ return {
6457
+ file,
6458
+ historySessionId,
6459
+ messageCount,
6460
+ firstMessageAt,
6461
+ lastMessageAt,
6462
+ sessionTitle: sessionTitle || void 0,
6463
+ preview: preview || void 0,
6464
+ workspace: workspace || void 0
6465
+ };
6466
+ }
6467
+ function shouldScheduleSavedHistoryRollupForSignature(signature) {
6468
+ const parts = String(signature || "").split(":");
6469
+ const size = Number(parts[1] || 0);
6470
+ return shouldScheduleSavedHistoryRollup(size);
6471
+ }
6472
+ function scheduleSavedHistoryRollup(agentType, historySessionId) {
6473
+ const key = `${agentType}:${historySessionId}`;
6474
+ if (!historySessionId || savedHistoryRollupInFlight.has(key)) return;
6475
+ savedHistoryRollupInFlight.add(key);
6476
+ setTimeout(() => {
6477
+ try {
6478
+ new ChatHistoryWriter().compactHistorySession(agentType, historySessionId);
6479
+ } finally {
6480
+ savedHistoryRollupInFlight.delete(key);
6481
+ }
6482
+ }, 0);
6483
+ }
6484
+ function scheduleSavedHistoryBackgroundRefresh(agentType, dir) {
6485
+ const key = `${agentType}:${dir}`;
6486
+ if (savedHistoryBackgroundRefresh.has(key)) return;
6487
+ savedHistoryBackgroundRefresh.add(key);
6488
+ setTimeout(() => {
6489
+ try {
6490
+ if (!fs3.existsSync(dir)) return;
6491
+ const files = listHistoryFiles(dir);
6492
+ const fileSignatures = buildSavedHistoryFileSignatureMap(dir, files);
6493
+ const persistedEntries = loadPersistedSavedHistoryIndex(dir);
6494
+ const computed = computeSavedHistorySessionSummaries(agentType, dir, files, fileSignatures, persistedEntries);
6495
+ savePersistedSavedHistoryIndex(dir, computed.persistedEntries || /* @__PURE__ */ new Map());
6496
+ const refreshedIndexSignature = buildSavedHistoryIndexFileSignature(dir);
6497
+ savedHistorySessionCache.set(agentType.replace(/[^a-zA-Z0-9_-]/g, "_"), {
6498
+ signature: refreshedIndexSignature,
6499
+ summaries: computed.summaries || []
6500
+ });
6501
+ for (const [file, entry] of Array.from(computed.persistedEntries.entries())) {
6502
+ if (!entry?.summary || !shouldScheduleSavedHistoryRollupForSignature(entry.signature)) continue;
6503
+ scheduleSavedHistoryRollup(agentType, entry.summary.historySessionId);
6504
+ }
6505
+ } catch {
6506
+ } finally {
6507
+ savedHistoryBackgroundRefresh.delete(key);
6508
+ }
6509
+ }, 0);
6510
+ }
6511
+ function computeSavedHistorySessionSummaries(agentType, dir, files, fileSignatures, persistedEntries) {
6512
+ const summaryBySessionId = /* @__PURE__ */ new Map();
6513
+ const nextPersistedEntries = /* @__PURE__ */ new Map();
6514
+ for (const file of files.slice().sort()) {
6515
+ const filePath = path7.join(dir, file);
6516
+ const signature = fileSignatures.get(file) || `${file}:missing`;
6517
+ const cached = savedHistoryFileSummaryCache.get(filePath);
6518
+ const persisted = persistedEntries.get(file);
6519
+ const reusableEntry = cached?.signature === signature ? cached : persisted?.signature === signature ? persisted : null;
6520
+ const fileSummary = reusableEntry?.summary || computeSavedHistoryFileSummary(agentType, dir, file);
6521
+ const nextEntry = reusableEntry || {
6522
+ signature,
6523
+ summary: fileSummary
6524
+ };
6525
+ if (!reusableEntry) {
6526
+ nextEntry.signature = signature;
6527
+ nextEntry.summary = fileSummary;
6528
+ }
6529
+ savedHistoryFileSummaryCache.set(filePath, nextEntry);
6530
+ nextPersistedEntries.set(file, nextEntry);
6531
+ if (!fileSummary) continue;
6532
+ const existing = summaryBySessionId.get(fileSummary.historySessionId);
6533
+ if (fileSummary.messageCount <= 0 || !fileSummary.lastMessageAt) {
6534
+ continue;
6535
+ }
6536
+ if (!existing) {
6537
+ summaryBySessionId.set(fileSummary.historySessionId, {
6538
+ historySessionId: fileSummary.historySessionId,
6539
+ sessionTitle: fileSummary.sessionTitle,
6540
+ messageCount: fileSummary.messageCount,
6541
+ firstMessageAt: fileSummary.firstMessageAt,
6542
+ lastMessageAt: fileSummary.lastMessageAt,
6543
+ preview: fileSummary.preview,
6544
+ workspace: fileSummary.workspace
6545
+ });
6546
+ continue;
6547
+ }
6548
+ existing.messageCount += fileSummary.messageCount;
6549
+ if (!existing.firstMessageAt || fileSummary.firstMessageAt < existing.firstMessageAt) {
6550
+ existing.firstMessageAt = fileSummary.firstMessageAt;
6551
+ }
6552
+ if (fileSummary.lastMessageAt >= existing.lastMessageAt) {
6553
+ existing.lastMessageAt = fileSummary.lastMessageAt;
6554
+ if (fileSummary.sessionTitle) existing.sessionTitle = fileSummary.sessionTitle;
6555
+ if (fileSummary.preview) existing.preview = fileSummary.preview;
6556
+ }
6557
+ if (!existing.workspace && fileSummary.workspace) {
6558
+ existing.workspace = fileSummary.workspace;
6559
+ }
6061
6560
  }
6062
- summaries.sort((a, b) => b.lastMessageAt - a.lastMessageAt);
6063
- return summaries;
6561
+ return {
6562
+ summaries: Array.from(summaryBySessionId.values()).sort((a, b) => b.lastMessageAt - a.lastMessageAt),
6563
+ persistedEntries: nextPersistedEntries
6564
+ };
6064
6565
  }
6065
6566
  var ChatHistoryWriter = class {
6066
6567
  /** Last seen message count per agent (deduplication) */
@@ -6135,9 +6636,11 @@ var ChatHistoryWriter = class {
6135
6636
  fs3.mkdirSync(dir, { recursive: true });
6136
6637
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
6137
6638
  const filePrefix = effectiveHistoryKey ? `${this.sanitize(effectiveHistoryKey)}_` : "";
6138
- const filePath = path7.join(dir, `${filePrefix}${date}.jsonl`);
6639
+ const fileName = `${filePrefix}${date}.jsonl`;
6640
+ const filePath = path7.join(dir, fileName);
6139
6641
  const lines = newMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
6140
6642
  fs3.appendFileSync(filePath, lines, "utf-8");
6643
+ updateSavedHistoryIndexForAppendedMessages(agentType, dir, fileName, effectiveHistoryKey, newMessages);
6141
6644
  const prevCount = this.lastSeenCounts.get(dedupKey) || 0;
6142
6645
  if (!historySessionId && messages.length < prevCount * 0.5 && prevCount > 3) {
6143
6646
  seenHashes.clear();
@@ -6228,7 +6731,8 @@ var ChatHistoryWriter = class {
6228
6731
  const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
6229
6732
  fs3.mkdirSync(dir, { recursive: true });
6230
6733
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
6231
- const filePath = path7.join(dir, `${this.sanitize(id)}_${date}.jsonl`);
6734
+ const fileName = `${this.sanitize(id)}_${date}.jsonl`;
6735
+ const filePath = path7.join(dir, fileName);
6232
6736
  const record = {
6233
6737
  ts: (/* @__PURE__ */ new Date()).toISOString(),
6234
6738
  receivedAt: Date.now(),
@@ -6241,6 +6745,7 @@ var ChatHistoryWriter = class {
6241
6745
  workspace: ws
6242
6746
  };
6243
6747
  fs3.appendFileSync(filePath, JSON.stringify(record) + "\n", "utf-8");
6748
+ updateSavedHistoryIndexForSessionStart(agentType, dir, fileName, id, ws);
6244
6749
  } catch {
6245
6750
  }
6246
6751
  }
@@ -6306,6 +6811,7 @@ var ChatHistoryWriter = class {
6306
6811
  }
6307
6812
  fs3.unlinkSync(sourcePath);
6308
6813
  }
6814
+ invalidatePersistedSavedHistoryIndex(agentType, dir);
6309
6815
  } catch {
6310
6816
  }
6311
6817
  }
@@ -6355,6 +6861,7 @@ var ChatHistoryWriter = class {
6355
6861
  fs3.writeFileSync(filePath, `${collapsed.map((entry) => JSON.stringify(entry)).join("\n")}
6356
6862
  `, "utf-8");
6357
6863
  }
6864
+ invalidatePersistedSavedHistoryIndex(agentType, dir);
6358
6865
  } catch {
6359
6866
  }
6360
6867
  }
@@ -6374,13 +6881,18 @@ var ChatHistoryWriter = class {
6374
6881
  for (const dir of agentDirs) {
6375
6882
  const dirPath = path7.join(HISTORY_DIR, dir.name);
6376
6883
  const files = fs3.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl") || f.endsWith(".terminal.log"));
6884
+ let removedAny = false;
6377
6885
  for (const file of files) {
6378
6886
  const filePath = path7.join(dirPath, file);
6379
6887
  const stat = fs3.statSync(filePath);
6380
6888
  if (stat.mtimeMs < cutoff) {
6381
6889
  fs3.unlinkSync(filePath);
6890
+ removedAny = true;
6382
6891
  }
6383
6892
  }
6893
+ if (removedAny) {
6894
+ invalidatePersistedSavedHistoryIndex(dir.name, dirPath);
6895
+ }
6384
6896
  }
6385
6897
  } catch {
6386
6898
  }
@@ -6446,18 +6958,51 @@ function listSavedHistorySessions(agentType, options = {}) {
6446
6958
  savedHistorySessionCache.delete(sanitized);
6447
6959
  return { sessions: [], hasMore: false };
6448
6960
  }
6449
- const files = listHistoryFiles(dir);
6450
- const signature = buildSavedHistoryCacheSignature(dir, files);
6451
6961
  const cached = savedHistorySessionCache.get(sanitized);
6452
- const summaries = cached?.signature === signature ? cached.summaries : computeSavedHistorySessionSummaries(agentType, dir, files);
6453
- if (!cached || cached.signature !== signature) {
6962
+ const offset = Math.max(0, options.offset || 0);
6963
+ const limit = Math.max(1, options.limit || 30);
6964
+ const indexSignature = buildSavedHistoryIndexFileSignature(dir);
6965
+ let cacheWasInvalidated = false;
6966
+ if (cached) {
6967
+ const cacheLooksPersisted = cached.signature.startsWith("index:");
6968
+ const cacheStillValid = cacheLooksPersisted ? cached.signature === indexSignature : (() => {
6969
+ const files2 = listHistoryFiles(dir);
6970
+ const fileSignatures2 = buildSavedHistoryFileSignatureMap(dir, files2);
6971
+ return cached.signature === buildSavedHistoryCacheSignature(files2, fileSignatures2);
6972
+ })();
6973
+ if (cacheStillValid) {
6974
+ const sliced2 = cached.summaries.slice(offset, offset + limit);
6975
+ return {
6976
+ sessions: sliced2,
6977
+ hasMore: cached.summaries.length > offset + limit
6978
+ };
6979
+ }
6980
+ cacheWasInvalidated = true;
6981
+ }
6982
+ const persistedSessions = readPersistedSavedHistorySessionSummaries(dir);
6983
+ if (!cacheWasInvalidated && persistedSessions?.length && !historyDirectoryHasFilesNewerThanIndex(dir)) {
6454
6984
  savedHistorySessionCache.set(sanitized, {
6455
- signature,
6456
- summaries
6985
+ signature: indexSignature,
6986
+ summaries: persistedSessions
6457
6987
  });
6988
+ scheduleSavedHistoryBackgroundRefresh(agentType, dir);
6989
+ const sliced2 = persistedSessions.slice(offset, offset + limit);
6990
+ return {
6991
+ sessions: sliced2,
6992
+ hasMore: persistedSessions.length > offset + limit
6993
+ };
6458
6994
  }
6459
- const offset = Math.max(0, options.offset || 0);
6460
- const limit = Math.max(1, options.limit || 30);
6995
+ const files = listHistoryFiles(dir);
6996
+ const fileSignatures = buildSavedHistoryFileSignatureMap(dir, files);
6997
+ const signature = buildSavedHistoryCacheSignature(files, fileSignatures);
6998
+ const persistedEntries = loadPersistedSavedHistoryIndex(dir);
6999
+ const computed = computeSavedHistorySessionSummaries(agentType, dir, files, fileSignatures, persistedEntries);
7000
+ const summaries = computed.summaries || [];
7001
+ savePersistedSavedHistoryIndex(dir, computed.persistedEntries || /* @__PURE__ */ new Map());
7002
+ savedHistorySessionCache.set(sanitized, {
7003
+ signature,
7004
+ summaries
7005
+ });
6461
7006
  const sliced = summaries.slice(offset, offset + limit);
6462
7007
  return {
6463
7008
  sessions: sliced,
@@ -8095,7 +8640,7 @@ function shouldIncludeSessionMetadata(profile) {
8095
8640
  return profile !== "live";
8096
8641
  }
8097
8642
  function shouldIncludeRuntimeMetadata(profile) {
8098
- return profile !== "live";
8643
+ return true;
8099
8644
  }
8100
8645
  function findCdpManager(cdpManagers, key) {
8101
8646
  const exact = cdpManagers.get(key);
@@ -8210,6 +8755,21 @@ function buildExtensionAgentSession(parent, ext, options) {
8210
8755
  lastUpdated: ext.lastUpdated
8211
8756
  };
8212
8757
  }
8758
+ function shouldIncludeExtensionSession(ext) {
8759
+ const status = String(ext.status || "").trim().toLowerCase();
8760
+ const hasActiveChat = !!ext.activeChat;
8761
+ const hasMessages = Array.isArray(ext.activeChat?.messages) && ext.activeChat.messages.length > 0;
8762
+ const hasModal = !!ext.activeChat?.activeModal;
8763
+ const hasStreams = Array.isArray(ext.agentStreams) && ext.agentStreams.length > 0;
8764
+ const hasProviderSessionId = typeof ext.providerSessionId === "string" && ext.providerSessionId.trim().length > 0;
8765
+ const hasControlValues = !!(ext.controlValues && Object.keys(ext.controlValues).length > 0);
8766
+ const hasProviderControls = Array.isArray(ext.providerControls) && ext.providerControls.length > 0;
8767
+ const hasOpenPanelCapability = Array.isArray(ext.sessionCapabilities) && ext.sessionCapabilities.includes("open_panel");
8768
+ const hasSummaryMetadata = !!ext.summaryMetadata;
8769
+ const hasError = typeof ext.errorMessage === "string" && ext.errorMessage.trim().length > 0;
8770
+ const hasInterestingStatus = !!status && !["idle", "panel_hidden", "disconnected", "not_monitored"].includes(status);
8771
+ return hasActiveChat || hasMessages || hasModal || hasStreams || hasProviderSessionId || hasControlValues || hasProviderControls || hasOpenPanelCapability || hasSummaryMetadata || hasError || hasInterestingStatus;
8772
+ }
8213
8773
  function buildCliSession(state, options) {
8214
8774
  const profile = options.profile || "full";
8215
8775
  const activeChat = normalizeActiveChatData(state.activeChat, getActiveChatOptions(profile));
@@ -8235,8 +8795,12 @@ function buildCliSession(state, options) {
8235
8795
  runtimeKey: state.runtime?.runtimeKey,
8236
8796
  runtimeDisplayName: state.runtime?.displayName,
8237
8797
  runtimeWorkspaceLabel: state.runtime?.workspaceLabel,
8798
+ runtimeLifecycle: state.runtime?.lifecycle ?? null,
8799
+ runtimeSurfaceKind: state.runtime?.surfaceKind,
8238
8800
  runtimeWriteOwner: state.runtime?.writeOwner || null,
8239
- runtimeAttachedClients: state.runtime?.attachedClients || []
8801
+ runtimeAttachedClients: state.runtime?.attachedClients || [],
8802
+ runtimeRestoredFromStorage: state.runtime?.restoredFromStorage === true,
8803
+ runtimeRecoveryState: state.runtime?.recoveryState ?? null
8240
8804
  },
8241
8805
  mode: state.mode,
8242
8806
  resume: state.resume,
@@ -8293,6 +8857,7 @@ function buildSessionEntries(allStates, cdpManagers, options = {}) {
8293
8857
  for (const state of ideStates) {
8294
8858
  sessions.push(buildIdeWorkspaceSession(state, cdpManagers, options));
8295
8859
  for (const ext of state.extensions) {
8860
+ if (!shouldIncludeExtensionSession(ext)) continue;
8296
8861
  sessions.push(buildExtensionAgentSession(state, ext, options));
8297
8862
  }
8298
8863
  }
@@ -10463,7 +11028,9 @@ function applyProviderPatch(h, args, payload) {
10463
11028
  });
10464
11029
  }
10465
11030
  async function executeProviderScript(h, args, scriptName) {
10466
- const resolvedProviderType = h.currentSession?.providerType || h.currentProviderType || args?.agentType || args?.providerType;
11031
+ const explicitTargetSessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : "";
11032
+ const targetSession = explicitTargetSessionId ? h.ctx.sessionRegistry?.get(explicitTargetSessionId) : void 0;
11033
+ const resolvedProviderType = targetSession?.providerType || h.currentSession?.providerType || h.currentProviderType || args?.agentType || args?.providerType;
10467
11034
  if (!resolvedProviderType) return { success: false, error: "targetSessionId or providerType is required" };
10468
11035
  const loader = h.ctx.providerLoader;
10469
11036
  if (!loader) return { success: false, error: "ProviderLoader not initialized" };
@@ -10506,16 +11073,16 @@ async function executeProviderScript(h, args, scriptName) {
10506
11073
  const scriptFn = provider.scripts[actualScriptName];
10507
11074
  const scriptCode = scriptFn(normalizedArgs);
10508
11075
  if (!scriptCode) return { success: false, error: `Script '${actualScriptName}' returned null` };
10509
- const cdpKey = provider.category === "ide" ? h.currentSession?.cdpManagerKey || h.currentManagerKey || resolvedProviderType : h.currentSession?.cdpManagerKey || h.currentManagerKey;
11076
+ const cdpKey = provider.category === "ide" ? targetSession?.cdpManagerKey || h.currentSession?.cdpManagerKey || h.currentManagerKey || resolvedProviderType : targetSession?.cdpManagerKey || h.currentSession?.cdpManagerKey || h.currentManagerKey;
10510
11077
  LOG.info("Command", `[ExtScript] provider=${provider.type} category=${provider.category} cdpKey=${cdpKey}`);
10511
11078
  const cdp = h.getCdp(cdpKey);
10512
11079
  if (!cdp?.isConnected) return { success: false, error: `No CDP connection for ${cdpKey || "any"}` };
10513
11080
  try {
10514
11081
  let result;
10515
11082
  if (provider.category === "extension") {
10516
- const runtimeSessionId = h.currentSession?.sessionId || args?.targetSessionId;
11083
+ const runtimeSessionId = explicitTargetSessionId || h.currentSession?.sessionId;
10517
11084
  if (!runtimeSessionId) return { success: false, error: `No target session found for ${resolvedProviderType}` };
10518
- const parentSessionId = h.currentSession?.parentSessionId;
11085
+ const parentSessionId = targetSession?.parentSessionId || h.currentSession?.parentSessionId;
10519
11086
  if (parentSessionId) {
10520
11087
  await h.agentStream?.setActiveSession(cdp, parentSessionId, runtimeSessionId);
10521
11088
  await h.agentStream?.syncActiveSession(cdp, parentSessionId);
@@ -11518,8 +12085,12 @@ var CliProviderInstance = class {
11518
12085
  runtimeKey: runtime.runtimeKey,
11519
12086
  displayName: runtime.displayName,
11520
12087
  workspaceLabel: runtime.workspaceLabel,
12088
+ lifecycle: runtime.lifecycle ?? null,
12089
+ surfaceKind: runtime.surfaceKind,
11521
12090
  writeOwner: runtime.writeOwner || null,
11522
- attachedClients: runtime.attachedClients || []
12091
+ attachedClients: runtime.attachedClients || [],
12092
+ restoredFromStorage: runtime.restoredFromStorage === true,
12093
+ recoveryState: runtime.recoveryState ?? null
11523
12094
  } : void 0,
11524
12095
  resume: this.provider.resume,
11525
12096
  controlValues: surface.controlValues,
@@ -15831,61 +16402,6 @@ cleanOldFiles();
15831
16402
  // src/commands/router.ts
15832
16403
  init_logger();
15833
16404
 
15834
- // src/session-host/runtime-surface.ts
15835
- var LIVE_LIFECYCLES = /* @__PURE__ */ new Set(["starting", "running", "stopping", "interrupted"]);
15836
- function isSessionHostLiveRuntime(record) {
15837
- const lifecycle = String(record?.lifecycle || "").trim();
15838
- return LIVE_LIFECYCLES.has(lifecycle);
15839
- }
15840
- function getSessionHostRecoveryLabel(meta) {
15841
- const recoveryState = typeof meta?.runtimeRecoveryState === "string" ? String(meta.runtimeRecoveryState).trim() : "";
15842
- if (!recoveryState) return null;
15843
- if (recoveryState === "auto_resumed") return "restored after restart";
15844
- if (recoveryState === "resume_failed") return "restore failed";
15845
- if (recoveryState === "host_restart_interrupted") return "host restart interrupted";
15846
- if (recoveryState === "orphan_snapshot") return "snapshot recovered";
15847
- return recoveryState.replace(/_/g, " ");
15848
- }
15849
- function isSessionHostRecoverySnapshot(record) {
15850
- if (!record) return false;
15851
- if (isSessionHostLiveRuntime(record)) return false;
15852
- const lifecycle = String(record.lifecycle || "").trim();
15853
- if (lifecycle && lifecycle !== "stopped" && lifecycle !== "failed") {
15854
- return false;
15855
- }
15856
- const meta = record.meta || void 0;
15857
- if (meta?.restoredFromStorage === true) return true;
15858
- return getSessionHostRecoveryLabel(meta) !== null;
15859
- }
15860
- function getSessionHostSurfaceKind(record) {
15861
- if (isSessionHostLiveRuntime(record)) return "live_runtime";
15862
- if (isSessionHostRecoverySnapshot(record)) return "recovery_snapshot";
15863
- return "inactive_record";
15864
- }
15865
- function partitionSessionHostRecords(records) {
15866
- const liveRuntimes = [];
15867
- const recoverySnapshots = [];
15868
- const inactiveRecords = [];
15869
- for (const record of records) {
15870
- const kind = getSessionHostSurfaceKind(record);
15871
- if (kind === "live_runtime") {
15872
- liveRuntimes.push(record);
15873
- } else if (kind === "recovery_snapshot") {
15874
- recoverySnapshots.push(record);
15875
- } else {
15876
- inactiveRecords.push(record);
15877
- }
15878
- }
15879
- return {
15880
- liveRuntimes,
15881
- recoverySnapshots,
15882
- inactiveRecords
15883
- };
15884
- }
15885
- function partitionSessionHostDiagnosticsSessions(records) {
15886
- return partitionSessionHostRecords(records || []);
15887
- }
15888
-
15889
16405
  // src/status/snapshot.ts
15890
16406
  init_config();
15891
16407
  import * as os16 from "os";
@@ -17443,6 +17959,23 @@ function prepareSessionModalUpdate(input) {
17443
17959
  };
17444
17960
  }
17445
17961
 
17962
+ // src/chat/async-batch.ts
17963
+ async function runAsyncBatch(items, worker, options = {}) {
17964
+ const list = Array.from(items);
17965
+ if (list.length === 0) return;
17966
+ const concurrency = Math.max(1, Math.min(list.length, Math.floor(options.concurrency || 1)));
17967
+ let nextIndex = 0;
17968
+ const runners = Array.from({ length: concurrency }, async () => {
17969
+ while (true) {
17970
+ const currentIndex = nextIndex;
17971
+ nextIndex += 1;
17972
+ if (currentIndex >= list.length) return;
17973
+ await worker(list[currentIndex], currentIndex);
17974
+ }
17975
+ });
17976
+ await Promise.all(runners);
17977
+ }
17978
+
17446
17979
  // src/agent-stream/provider-adapter.ts
17447
17980
  init_read_chat_contract();
17448
17981
  init_chat_message_normalization();
@@ -17911,10 +18444,12 @@ var DaemonAgentStreamManager = class {
17911
18444
  }
17912
18445
  }
17913
18446
  /** Collect active extension session state */
17914
- async collectActiveSession(cdp, parentSessionId) {
18447
+ async collectActiveSession(cdp, parentSessionId, attemptedSessionIds = /* @__PURE__ */ new Set(), originSessionId) {
17915
18448
  if (!this.enabled) return null;
17916
18449
  const activeSessionId = this.getActiveSessionId(parentSessionId);
17917
18450
  if (!activeSessionId) return null;
18451
+ const resolvedOriginSessionId = originSessionId || activeSessionId;
18452
+ attemptedSessionIds.add(activeSessionId);
17918
18453
  let agent = this.managedBySessionId.get(activeSessionId);
17919
18454
  if (!agent) {
17920
18455
  agent = await this.connectManagedSession(cdp, parentSessionId, activeSessionId) || void 0;
@@ -17927,18 +18462,44 @@ var DaemonAgentStreamManager = class {
17927
18462
  try {
17928
18463
  const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
17929
18464
  const state = await agent.adapter.readChat(evaluate);
17930
- const stateError = this.getStateError(state);
17931
- const selectedModelValue = typeof state.controlValues?.model === "string" ? state.controlValues.model : "";
17932
- LOG.debug("AgentStream", `[AgentStream] readChat(${type}) result: status=${state.status} msgs=${state.messages?.length || 0} model=${selectedModelValue}${state.status === "error" ? " error=" + JSON.stringify(stateError) : ""}`);
17933
- if (state.status === "error" && this.isRecoverableSessionError(stateError)) {
18465
+ 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;
18466
+ const normalizedState = {
18467
+ ...state,
18468
+ sessionId: agent.runtimeSessionId,
18469
+ ...resolvedProviderSessionId ? { providerSessionId: resolvedProviderSessionId } : {}
18470
+ };
18471
+ const stateError = this.getStateError(normalizedState);
18472
+ const selectedModelValue = typeof normalizedState.controlValues?.model === "string" ? normalizedState.controlValues.model : "";
18473
+ LOG.debug("AgentStream", `[AgentStream] readChat(${type}) result: status=${normalizedState.status} msgs=${normalizedState.messages?.length || 0} model=${selectedModelValue}${normalizedState.status === "error" ? " error=" + JSON.stringify(stateError) : ""}`);
18474
+ if (normalizedState.status === "error" && this.isRecoverableSessionError(stateError)) {
17934
18475
  throw new Error(stateError);
17935
18476
  }
17936
- agent.lastState = state;
18477
+ agent.lastState = normalizedState;
17937
18478
  agent.lastError = null;
17938
- if (state.status === "panel_hidden") {
18479
+ if (normalizedState.status === "panel_hidden") {
18480
+ const discovered = await cdp.discoverAgentWebviews().catch(() => []);
18481
+ const fallbackTarget = discovered.find((entry) => {
18482
+ if (entry.agentType === type) return false;
18483
+ const fallbackSessionId = this.resolveSessionIdForTarget(parentSessionId, entry.agentType);
18484
+ return !!fallbackSessionId && fallbackSessionId !== activeSessionId && !attemptedSessionIds.has(fallbackSessionId);
18485
+ });
18486
+ if (fallbackTarget) {
18487
+ const fallbackSessionId = this.resolveSessionIdForTarget(parentSessionId, fallbackTarget.agentType);
18488
+ if (fallbackSessionId && fallbackSessionId !== activeSessionId && !attemptedSessionIds.has(fallbackSessionId)) {
18489
+ this.logFn(`[AgentStream] Active session ${type} is hidden; switching to visible agent ${fallbackTarget.agentType} (${parentSessionId})`);
18490
+ await this.setActiveSession(cdp, parentSessionId, fallbackSessionId);
18491
+ await this.syncActiveSession(cdp, parentSessionId);
18492
+ const fallbackState = await this.collectActiveSession(cdp, parentSessionId, attemptedSessionIds, resolvedOriginSessionId);
18493
+ if (fallbackState?.status === "panel_hidden" && resolvedOriginSessionId !== fallbackSessionId) {
18494
+ await this.setActiveSession(cdp, parentSessionId, resolvedOriginSessionId);
18495
+ await this.syncActiveSession(cdp, parentSessionId);
18496
+ }
18497
+ return fallbackState;
18498
+ }
18499
+ }
17939
18500
  agent.lastHiddenCheckTime = Date.now();
17940
18501
  }
17941
- return state;
18502
+ return normalizedState;
17942
18503
  } catch (e) {
17943
18504
  const errorMsg = e?.message || String(e);
17944
18505
  this.logFn(`[AgentStream] readChat(${type}) error: ${errorMsg.slice(0, 200)}`);
@@ -18234,6 +18795,7 @@ var AgentStreamPoller = class {
18234
18795
  try {
18235
18796
  await agentStreamManager.syncActiveSession(cdp, parentSessionId);
18236
18797
  let stream = await agentStreamManager.collectActiveSession(cdp, parentSessionId);
18798
+ resolvedActiveSessionId = stream?.sessionId || agentStreamManager.getActiveSessionId(parentSessionId) || resolvedActiveSessionId;
18237
18799
  if (stream?.status === "waiting_approval") {
18238
18800
  const autoApprove = providerLoader.getSettings(stream.agentType).autoApprove !== false;
18239
18801
  if (autoApprove && resolvedActiveSessionId) {
@@ -24216,6 +24778,8 @@ var SessionHostRuntimeTransport = class {
24216
24778
  runtimeKey: record.runtimeKey,
24217
24779
  displayName: record.displayName,
24218
24780
  workspaceLabel: record.workspaceLabel,
24781
+ lifecycle: typeof record.lifecycle === "string" ? record.lifecycle : null,
24782
+ surfaceKind: record.surfaceKind,
24219
24783
  writeOwner: record.writeOwner ? {
24220
24784
  clientId: record.writeOwner.clientId,
24221
24785
  ownerType: record.writeOwner.ownerType
@@ -24943,6 +25507,7 @@ export {
24943
25507
  resolveChatMessageKind,
24944
25508
  resolveDebugRuntimeConfig,
24945
25509
  resolveSessionHostAppName,
25510
+ runAsyncBatch,
24946
25511
  saveConfig,
24947
25512
  saveState,
24948
25513
  setDebugRuntimeConfig,