@adhdev/daemon-standalone 0.8.75 → 0.8.76

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
@@ -31510,6 +31510,7 @@ ${data.message || ""}`.trim();
31510
31510
  resolveChatMessageKind: () => resolveChatMessageKind,
31511
31511
  resolveDebugRuntimeConfig: () => resolveDebugRuntimeConfig,
31512
31512
  resolveSessionHostAppName: () => resolveSessionHostAppName2,
31513
+ runAsyncBatch: () => runAsyncBatch2,
31513
31514
  saveConfig: () => saveConfig,
31514
31515
  saveState: () => saveState,
31515
31516
  setDebugRuntimeConfig: () => setDebugRuntimeConfig,
@@ -32268,6 +32269,32 @@ ${data.message || ""}`.trim();
32268
32269
  var import_ws2 = __toESM2(require("ws"));
32269
32270
  var http = __toESM2(require("http"));
32270
32271
  init_logger();
32272
+ function normalizeTitle(value) {
32273
+ return String(value || "").trim().replace(/\s+/g, " ").toLowerCase();
32274
+ }
32275
+ function titlesMatch(lhs, rhs) {
32276
+ const a = normalizeTitle(lhs);
32277
+ const b2 = normalizeTitle(rhs);
32278
+ if (!a || !b2) return false;
32279
+ return a === b2 || a.includes(b2) || b2.includes(a);
32280
+ }
32281
+ function resolveCdpPageTarget(params) {
32282
+ const { pages, pinnedTargetId, previousPageTitle } = params;
32283
+ if (pages.length === 0) return { target: null, retargeted: false };
32284
+ if (!pinnedTargetId) {
32285
+ return { target: pages[0] || null, retargeted: false };
32286
+ }
32287
+ const exact = pages.find((page) => page.id === pinnedTargetId);
32288
+ if (exact) return { target: exact, retargeted: false };
32289
+ const titleMatchesList = pages.filter((page) => titlesMatch(page.title, previousPageTitle));
32290
+ if (titleMatchesList.length === 1) {
32291
+ return { target: titleMatchesList[0], retargeted: true };
32292
+ }
32293
+ if (pages.length === 1) {
32294
+ return { target: pages[0], retargeted: true };
32295
+ }
32296
+ return { target: null, retargeted: false };
32297
+ }
32271
32298
  var DaemonCdpManager = class {
32272
32299
  ws = null;
32273
32300
  browserWs = null;
@@ -32428,18 +32455,28 @@ ${data.message || ""}`.trim();
32428
32455
  resolve11(targets.find((t) => t.webSocketDebuggerUrl) || null);
32429
32456
  return;
32430
32457
  }
32431
- const mainPages = pages.filter((t) => !this.isNonMainTitle(t.title || ""));
32432
- const list = mainPages.length > 0 ? mainPages : pages;
32458
+ const titleFilteredPages = pages.filter((t) => !this.isNonMainTitle(t.title || ""));
32459
+ const mainPages = titleFilteredPages.filter((t) => this.isMainPageUrl(t.url));
32460
+ const list = mainPages.length > 0 ? mainPages : titleFilteredPages.length > 0 ? titleFilteredPages : pages;
32433
32461
  this.log(`[CDP] pages(${list.length}): ${list.map((t) => `"${t.title}"`).join(", ")}`);
32434
- if (this._targetId) {
32435
- const specific = list.find((t) => t.id === this._targetId);
32436
- if (specific) {
32437
- this._pageTitle = specific.title || "";
32438
- resolve11(specific);
32439
- } else {
32440
- this.log(`[CDP] Target ${this._targetId} not found in page list`);
32441
- resolve11(null);
32462
+ const previousTargetId = this._targetId;
32463
+ const selected = resolveCdpPageTarget({
32464
+ pages: list,
32465
+ pinnedTargetId: previousTargetId,
32466
+ previousPageTitle: this._pageTitle
32467
+ });
32468
+ if (selected.target) {
32469
+ if (selected.retargeted && previousTargetId && previousTargetId !== selected.target.id) {
32470
+ this.log(`[CDP] Target ${previousTargetId} rekeyed to ${selected.target.id}`);
32471
+ this._targetId = selected.target.id;
32442
32472
  }
32473
+ this._pageTitle = selected.target.title || "";
32474
+ resolve11(selected.target);
32475
+ return;
32476
+ }
32477
+ if (previousTargetId) {
32478
+ this.log(`[CDP] Target ${previousTargetId} not found in page list`);
32479
+ resolve11(null);
32443
32480
  return;
32444
32481
  }
32445
32482
  this._pageTitle = list[0]?.title || "";
@@ -33867,7 +33904,17 @@ ${cleanBody}`;
33867
33904
  init_chat_message_normalization();
33868
33905
  var HISTORY_DIR = path7.join(os52.homedir(), ".adhdev", "history");
33869
33906
  var RETAIN_DAYS = 30;
33907
+ var SAVED_HISTORY_INDEX_VERSION = 1;
33908
+ var SAVED_HISTORY_INDEX_FILE = ".saved-history-index.json";
33909
+ var SAVED_HISTORY_INDEX_LOCK_SUFFIX = ".lock";
33910
+ var SAVED_HISTORY_INDEX_LOCK_WAIT_MS = 1500;
33911
+ var SAVED_HISTORY_INDEX_LOCK_STALE_MS = 15e3;
33912
+ var SAVED_HISTORY_INDEX_LOCK_POLL_MS = 25;
33913
+ var SAVED_HISTORY_ROLLUP_THRESHOLD_BYTES = 16 * 1024 * 1024;
33870
33914
  var savedHistorySessionCache = /* @__PURE__ */ new Map();
33915
+ var savedHistoryFileSummaryCache = /* @__PURE__ */ new Map();
33916
+ var savedHistoryBackgroundRefresh = /* @__PURE__ */ new Set();
33917
+ var savedHistoryRollupInFlight = /* @__PURE__ */ new Set();
33871
33918
  var CODEX_STARTER_PROMPT_RE = /^(?:[›❯]\s*)?(?:Find and fix a bug in @filename|Improve documentation in @filename|Write tests for @filename|Explain this codebase|Summarize recent commits|Implement \{feature\}|Use \/skills(?: to list available skills)?|Run \/review on my current changes)$/i;
33872
33919
  function normalizeHistoryComparable(text) {
33873
33920
  return String(text || "").replace(/\s+/g, " ").trim();
@@ -33925,6 +33972,68 @@ ${cleanBody}`;
33925
33972
  content
33926
33973
  };
33927
33974
  }
33975
+ function sortSavedHistorySessionSummaries(summaries) {
33976
+ return summaries.slice().sort((a, b2) => b2.lastMessageAt - a.lastMessageAt);
33977
+ }
33978
+ function buildSavedHistorySessionSummaryMapFromEntries(entries) {
33979
+ const summaries = /* @__PURE__ */ new Map();
33980
+ for (const entry of Array.from(entries.values())) {
33981
+ const fileSummary = entry.summary;
33982
+ if (!fileSummary || fileSummary.messageCount <= 0 || !fileSummary.lastMessageAt) continue;
33983
+ const existing = summaries.get(fileSummary.historySessionId);
33984
+ if (!existing) {
33985
+ summaries.set(fileSummary.historySessionId, {
33986
+ historySessionId: fileSummary.historySessionId,
33987
+ sessionTitle: fileSummary.sessionTitle,
33988
+ messageCount: fileSummary.messageCount,
33989
+ firstMessageAt: fileSummary.firstMessageAt,
33990
+ lastMessageAt: fileSummary.lastMessageAt,
33991
+ preview: fileSummary.preview,
33992
+ workspace: fileSummary.workspace
33993
+ });
33994
+ continue;
33995
+ }
33996
+ existing.messageCount += fileSummary.messageCount;
33997
+ if (!existing.firstMessageAt || fileSummary.firstMessageAt < existing.firstMessageAt) {
33998
+ existing.firstMessageAt = fileSummary.firstMessageAt;
33999
+ }
34000
+ if (fileSummary.lastMessageAt >= existing.lastMessageAt) {
34001
+ existing.lastMessageAt = fileSummary.lastMessageAt;
34002
+ if (fileSummary.sessionTitle) existing.sessionTitle = fileSummary.sessionTitle;
34003
+ if (fileSummary.preview) existing.preview = fileSummary.preview;
34004
+ }
34005
+ if (!existing.workspace && fileSummary.workspace) {
34006
+ existing.workspace = fileSummary.workspace;
34007
+ }
34008
+ }
34009
+ return Object.fromEntries(sortSavedHistorySessionSummaries(Array.from(summaries.values())).map((summary) => [summary.historySessionId, summary]));
34010
+ }
34011
+ function readPersistedSavedHistorySessionSummaries(dir) {
34012
+ try {
34013
+ const filePath = getSavedHistoryIndexFilePath(dir);
34014
+ if (!fs32.existsSync(filePath)) return null;
34015
+ const raw = JSON.parse(fs32.readFileSync(filePath, "utf-8"));
34016
+ if (!raw || raw.version !== SAVED_HISTORY_INDEX_VERSION || !raw.sessions || typeof raw.sessions !== "object") {
34017
+ return null;
34018
+ }
34019
+ return sortSavedHistorySessionSummaries(
34020
+ Object.values(raw.sessions).filter((summary) => !!summary && typeof summary.historySessionId === "string" && summary.messageCount > 0 && summary.lastMessageAt > 0).map((summary) => ({
34021
+ historySessionId: summary.historySessionId,
34022
+ sessionTitle: summary.sessionTitle,
34023
+ messageCount: summary.messageCount,
34024
+ firstMessageAt: summary.firstMessageAt,
34025
+ lastMessageAt: summary.lastMessageAt,
34026
+ preview: summary.preview,
34027
+ workspace: summary.workspace
34028
+ }))
34029
+ );
34030
+ } catch {
34031
+ return null;
34032
+ }
34033
+ }
34034
+ function shouldScheduleSavedHistoryRollup(totalBytes) {
34035
+ return Number.isFinite(totalBytes) && totalBytes >= SAVED_HISTORY_ROLLUP_THRESHOLD_BYTES;
34036
+ }
33928
34037
  function sanitizeHistoryFileSegment(value) {
33929
34038
  return String(value || "").replace(/[^a-zA-Z0-9_-]/g, "_");
33930
34039
  }
@@ -33938,71 +34047,386 @@ ${cleanBody}`;
33938
34047
  return true;
33939
34048
  }).sort().reverse();
33940
34049
  }
33941
- function buildSavedHistoryCacheSignature(dir, files) {
33942
- return files.map((file2) => {
34050
+ function normalizeSavedHistorySessionId(agentType, historySessionId) {
34051
+ const normalizedId = String(historySessionId || "").trim();
34052
+ if (!normalizedId) return "";
34053
+ const strictProviderId = normalizeProviderSessionId(agentType, normalizedId);
34054
+ if (strictProviderId) return strictProviderId;
34055
+ return agentType === "hermes-cli" ? "" : normalizedId;
34056
+ }
34057
+ function extractSavedHistorySessionIdFromFile(agentType, file2) {
34058
+ const match = file2.match(/^([A-Za-z0-9_-]+)_\d{4}-\d{2}-\d{2}\.jsonl$/);
34059
+ return normalizeSavedHistorySessionId(agentType, match?.[1] || "");
34060
+ }
34061
+ function buildSavedHistoryFileSignatureMap(dir, files) {
34062
+ return new Map(files.map((file2) => {
33943
34063
  try {
33944
34064
  const stat4 = fs32.statSync(path7.join(dir, file2));
33945
- return `${file2}:${stat4.size}:${Math.trunc(stat4.mtimeMs)}`;
34065
+ return [file2, `${file2}:${stat4.size}:${Math.trunc(stat4.mtimeMs)}`];
33946
34066
  } catch {
33947
- return `${file2}:missing`;
33948
- }
33949
- }).join("|");
33950
- }
33951
- function computeSavedHistorySessionSummaries(agentType, dir, files) {
33952
- const groupedFiles = /* @__PURE__ */ new Map();
33953
- const filePattern = /^([A-Za-z0-9_-]+)_\d{4}-\d{2}-\d{2}\.jsonl$/;
33954
- for (const file2 of files) {
33955
- const match = file2.match(filePattern);
33956
- if (!match?.[1]) continue;
33957
- const historySessionId = match[1];
33958
- const grouped = groupedFiles.get(historySessionId) || [];
33959
- grouped.push(file2);
33960
- groupedFiles.set(historySessionId, grouped);
33961
- }
33962
- const summaries = [];
33963
- for (const [historySessionId, grouped] of groupedFiles.entries()) {
33964
- let messageCount = 0;
33965
- let firstMessageAt = 0;
33966
- let lastMessageAt = 0;
33967
- let sessionTitle = "";
33968
- let preview = "";
33969
- let workspace = "";
33970
- for (const file2 of grouped.sort()) {
33971
- const filePath = path7.join(dir, file2);
33972
- const content = fs32.readFileSync(filePath, "utf-8");
33973
- const lines = content.split("\n").filter(Boolean);
33974
- for (const line of lines) {
33975
- let parsed = null;
34067
+ return [file2, `${file2}:missing`];
34068
+ }
34069
+ }));
34070
+ }
34071
+ function buildSavedHistoryCacheSignature(files, fileSignatures) {
34072
+ return files.map((file2) => fileSignatures.get(file2) || `${file2}:missing`).join("|");
34073
+ }
34074
+ function getSavedHistoryIndexFilePath(dir) {
34075
+ return path7.join(dir, SAVED_HISTORY_INDEX_FILE);
34076
+ }
34077
+ function getSavedHistoryIndexLockPath(dir) {
34078
+ return `${getSavedHistoryIndexFilePath(dir)}${SAVED_HISTORY_INDEX_LOCK_SUFFIX}`;
34079
+ }
34080
+ function sleepBlocking(ms2) {
34081
+ if (ms2 <= 0) return;
34082
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms2);
34083
+ }
34084
+ function loadPersistedSavedHistoryIndexFromFile(dir) {
34085
+ try {
34086
+ const filePath = getSavedHistoryIndexFilePath(dir);
34087
+ if (!fs32.existsSync(filePath)) return /* @__PURE__ */ new Map();
34088
+ const raw = JSON.parse(fs32.readFileSync(filePath, "utf-8"));
34089
+ if (!raw || raw.version !== SAVED_HISTORY_INDEX_VERSION || !raw.files || typeof raw.files !== "object") {
34090
+ return /* @__PURE__ */ new Map();
34091
+ }
34092
+ return new Map(
34093
+ Object.entries(raw.files).filter(([file2, entry]) => !!file2 && !!entry && typeof entry.signature === "string").map(([file2, entry]) => [file2, {
34094
+ signature: entry.signature,
34095
+ summary: entry.summary || null
34096
+ }])
34097
+ );
34098
+ } catch {
34099
+ return /* @__PURE__ */ new Map();
34100
+ }
34101
+ }
34102
+ function writePersistedSavedHistoryIndexFile(dir, entries) {
34103
+ const filePath = getSavedHistoryIndexFilePath(dir);
34104
+ const tempPath = `${filePath}.tmp`;
34105
+ const payload = {
34106
+ version: SAVED_HISTORY_INDEX_VERSION,
34107
+ files: Object.fromEntries(entries.entries()),
34108
+ sessions: buildSavedHistorySessionSummaryMapFromEntries(entries)
34109
+ };
34110
+ fs32.writeFileSync(tempPath, JSON.stringify(payload), "utf-8");
34111
+ fs32.renameSync(tempPath, filePath);
34112
+ }
34113
+ function acquireSavedHistoryIndexLock(dir) {
34114
+ const lockPath = getSavedHistoryIndexLockPath(dir);
34115
+ const deadline = Date.now() + SAVED_HISTORY_INDEX_LOCK_WAIT_MS;
34116
+ while (Date.now() <= deadline) {
34117
+ try {
34118
+ fs32.mkdirSync(lockPath);
34119
+ return () => {
33976
34120
  try {
33977
- parsed = JSON.parse(line);
34121
+ fs32.rmSync(lockPath, { recursive: true, force: true });
33978
34122
  } catch {
33979
- parsed = null;
33980
34123
  }
33981
- if (!parsed || parsed.historySessionId !== historySessionId) continue;
33982
- if (parsed.kind === "session_start") {
33983
- if (!workspace && parsed.workspace) workspace = parsed.workspace;
34124
+ };
34125
+ } catch (error48) {
34126
+ if (error48?.code !== "EEXIST") return null;
34127
+ try {
34128
+ const stat4 = fs32.statSync(lockPath);
34129
+ if (Date.now() - stat4.mtimeMs > SAVED_HISTORY_INDEX_LOCK_STALE_MS) {
34130
+ fs32.rmSync(lockPath, { recursive: true, force: true });
33984
34131
  continue;
33985
34132
  }
33986
- messageCount += 1;
33987
- if (!firstMessageAt || parsed.receivedAt < firstMessageAt) firstMessageAt = parsed.receivedAt;
33988
- if (!lastMessageAt || parsed.receivedAt > lastMessageAt) lastMessageAt = parsed.receivedAt;
33989
- if (parsed.sessionTitle) sessionTitle = parsed.sessionTitle;
33990
- if (parsed.role !== "system" && parsed.content.trim()) preview = parsed.content.trim();
33991
- }
33992
- }
33993
- if (messageCount === 0 || !lastMessageAt) continue;
33994
- summaries.push({
33995
- historySessionId,
33996
- sessionTitle: sessionTitle || void 0,
33997
- messageCount,
33998
- firstMessageAt,
33999
- lastMessageAt,
34000
- preview: preview || void 0,
34001
- workspace: workspace || void 0
34002
- });
34133
+ } catch {
34134
+ continue;
34135
+ }
34136
+ sleepBlocking(SAVED_HISTORY_INDEX_LOCK_POLL_MS);
34137
+ }
34003
34138
  }
34004
- summaries.sort((a, b2) => b2.lastMessageAt - a.lastMessageAt);
34005
- return summaries;
34139
+ return null;
34140
+ }
34141
+ function withLockedPersistedSavedHistoryIndex(dir, callback) {
34142
+ const release2 = acquireSavedHistoryIndexLock(dir);
34143
+ if (!release2) return null;
34144
+ try {
34145
+ const entries = loadPersistedSavedHistoryIndexFromFile(dir);
34146
+ const result = callback(entries);
34147
+ writePersistedSavedHistoryIndexFile(dir, entries);
34148
+ return result;
34149
+ } catch {
34150
+ return null;
34151
+ } finally {
34152
+ release2();
34153
+ }
34154
+ }
34155
+ function loadPersistedSavedHistoryIndex(dir) {
34156
+ return loadPersistedSavedHistoryIndexFromFile(dir);
34157
+ }
34158
+ function savePersistedSavedHistoryIndex(dir, entries) {
34159
+ withLockedPersistedSavedHistoryIndex(dir, (currentEntries) => {
34160
+ const incomingFiles = new Set(Array.from(entries.keys()));
34161
+ for (const [file2, entry] of Array.from(entries.entries())) {
34162
+ const liveSignature = buildSavedHistoryFileSignature(dir, file2);
34163
+ const existingEntry = currentEntries.get(file2);
34164
+ if (existingEntry && existingEntry.signature !== liveSignature && entry.signature !== liveSignature) {
34165
+ continue;
34166
+ }
34167
+ if (entry.signature !== liveSignature && (!existingEntry || existingEntry.signature !== liveSignature)) {
34168
+ continue;
34169
+ }
34170
+ currentEntries.set(file2, entry.signature === liveSignature ? entry : {
34171
+ signature: liveSignature,
34172
+ summary: existingEntry?.summary || entry.summary
34173
+ });
34174
+ }
34175
+ for (const file2 of Array.from(currentEntries.keys())) {
34176
+ if (incomingFiles.has(file2)) continue;
34177
+ if (!fs32.existsSync(path7.join(dir, file2))) {
34178
+ currentEntries.delete(file2);
34179
+ }
34180
+ }
34181
+ });
34182
+ }
34183
+ function invalidatePersistedSavedHistoryIndex(agentType, dir) {
34184
+ try {
34185
+ fs32.rmSync(getSavedHistoryIndexFilePath(dir), { force: true });
34186
+ } catch {
34187
+ }
34188
+ savedHistorySessionCache.delete(agentType.replace(/[^a-zA-Z0-9_-]/g, "_"));
34189
+ }
34190
+ function buildSavedHistoryIndexFileSignature(dir) {
34191
+ try {
34192
+ const stat4 = fs32.statSync(getSavedHistoryIndexFilePath(dir));
34193
+ return `index:${stat4.size}:${Math.trunc(stat4.mtimeMs)}`;
34194
+ } catch {
34195
+ return "index:missing";
34196
+ }
34197
+ }
34198
+ function historyDirectoryHasFilesNewerThanIndex(dir) {
34199
+ try {
34200
+ const indexStat = fs32.statSync(getSavedHistoryIndexFilePath(dir));
34201
+ const files = listHistoryFiles(dir);
34202
+ for (const file2 of files) {
34203
+ const stat4 = fs32.statSync(path7.join(dir, file2));
34204
+ if (stat4.mtimeMs > indexStat.mtimeMs) return true;
34205
+ }
34206
+ return false;
34207
+ } catch {
34208
+ return true;
34209
+ }
34210
+ }
34211
+ function buildSavedHistoryFileSignature(dir, file2) {
34212
+ try {
34213
+ const stat4 = fs32.statSync(path7.join(dir, file2));
34214
+ return `${file2}:${stat4.size}:${Math.trunc(stat4.mtimeMs)}`;
34215
+ } catch {
34216
+ return `${file2}:missing`;
34217
+ }
34218
+ }
34219
+ function persistSavedHistoryFileSummaryEntry(agentType, dir, file2, updater) {
34220
+ const filePath = path7.join(dir, file2);
34221
+ const result = withLockedPersistedSavedHistoryIndex(dir, (entries) => {
34222
+ const currentEntry = entries.get(file2) || null;
34223
+ const nextSummary = updater(currentEntry?.summary || null);
34224
+ const nextEntry = {
34225
+ signature: buildSavedHistoryFileSignature(dir, file2),
34226
+ summary: nextSummary
34227
+ };
34228
+ entries.set(file2, nextEntry);
34229
+ savedHistoryFileSummaryCache.set(filePath, nextEntry);
34230
+ return nextEntry;
34231
+ });
34232
+ if (!result) return;
34233
+ if (result.summary?.historySessionId && shouldScheduleSavedHistoryRollupForSignature(result.signature)) {
34234
+ scheduleSavedHistoryRollup(agentType, result.summary.historySessionId);
34235
+ }
34236
+ }
34237
+ function updateSavedHistoryIndexForSessionStart(agentType, dir, file2, historySessionId, workspace) {
34238
+ const normalizedSessionId = normalizeSavedHistorySessionId(agentType, historySessionId);
34239
+ const normalizedWorkspace = String(workspace || "").trim();
34240
+ if (!normalizedSessionId || !normalizedWorkspace) return;
34241
+ persistSavedHistoryFileSummaryEntry(agentType, dir, file2, (currentSummary) => ({
34242
+ file: file2,
34243
+ historySessionId: normalizedSessionId,
34244
+ messageCount: currentSummary?.messageCount || 0,
34245
+ firstMessageAt: currentSummary?.firstMessageAt || 0,
34246
+ lastMessageAt: currentSummary?.lastMessageAt || 0,
34247
+ sessionTitle: currentSummary?.sessionTitle,
34248
+ preview: currentSummary?.preview,
34249
+ workspace: normalizedWorkspace
34250
+ }));
34251
+ }
34252
+ function updateSavedHistoryIndexForAppendedMessages(agentType, dir, file2, historySessionId, messages) {
34253
+ const normalizedSessionId = normalizeSavedHistorySessionId(agentType, historySessionId || "");
34254
+ if (!normalizedSessionId || messages.length === 0) return;
34255
+ persistSavedHistoryFileSummaryEntry(agentType, dir, file2, (currentSummary) => {
34256
+ const nextSummary = {
34257
+ file: file2,
34258
+ historySessionId: normalizedSessionId,
34259
+ messageCount: currentSummary?.messageCount || 0,
34260
+ firstMessageAt: currentSummary?.firstMessageAt || 0,
34261
+ lastMessageAt: currentSummary?.lastMessageAt || 0,
34262
+ sessionTitle: currentSummary?.sessionTitle,
34263
+ preview: currentSummary?.preview,
34264
+ workspace: currentSummary?.workspace
34265
+ };
34266
+ for (const message of messages) {
34267
+ if (!message || message.historySessionId !== historySessionId) continue;
34268
+ if (message.kind === "session_start") {
34269
+ if (message.workspace) nextSummary.workspace = message.workspace;
34270
+ continue;
34271
+ }
34272
+ nextSummary.messageCount += 1;
34273
+ if (!nextSummary.firstMessageAt || message.receivedAt < nextSummary.firstMessageAt) {
34274
+ nextSummary.firstMessageAt = message.receivedAt;
34275
+ }
34276
+ if (!nextSummary.lastMessageAt || message.receivedAt >= nextSummary.lastMessageAt) {
34277
+ nextSummary.lastMessageAt = message.receivedAt;
34278
+ if (message.sessionTitle) nextSummary.sessionTitle = message.sessionTitle;
34279
+ if (message.role !== "system" && message.content.trim()) nextSummary.preview = message.content.trim();
34280
+ } else if (message.sessionTitle) {
34281
+ nextSummary.sessionTitle = message.sessionTitle;
34282
+ }
34283
+ if (!nextSummary.preview && message.role !== "system" && message.content.trim()) {
34284
+ nextSummary.preview = message.content.trim();
34285
+ }
34286
+ }
34287
+ return nextSummary;
34288
+ });
34289
+ }
34290
+ function computeSavedHistoryFileSummary(agentType, dir, file2) {
34291
+ const historySessionId = extractSavedHistorySessionIdFromFile(agentType, file2);
34292
+ if (!historySessionId) return null;
34293
+ const filePath = path7.join(dir, file2);
34294
+ const content = fs32.readFileSync(filePath, "utf-8");
34295
+ const lines = content.split("\n").filter(Boolean);
34296
+ let messageCount = 0;
34297
+ let firstMessageAt = 0;
34298
+ let lastMessageAt = 0;
34299
+ let sessionTitle = "";
34300
+ let preview = "";
34301
+ let workspace = "";
34302
+ for (const line of lines) {
34303
+ let parsed = null;
34304
+ try {
34305
+ parsed = JSON.parse(line);
34306
+ } catch {
34307
+ parsed = null;
34308
+ }
34309
+ if (!parsed || parsed.historySessionId !== historySessionId) continue;
34310
+ if (parsed.kind === "session_start") {
34311
+ if (!workspace && parsed.workspace) workspace = parsed.workspace;
34312
+ continue;
34313
+ }
34314
+ messageCount += 1;
34315
+ if (!firstMessageAt || parsed.receivedAt < firstMessageAt) firstMessageAt = parsed.receivedAt;
34316
+ if (!lastMessageAt || parsed.receivedAt > lastMessageAt) lastMessageAt = parsed.receivedAt;
34317
+ if (parsed.sessionTitle) sessionTitle = parsed.sessionTitle;
34318
+ if (parsed.role !== "system" && parsed.content.trim()) preview = parsed.content.trim();
34319
+ }
34320
+ if (messageCount === 0 || !lastMessageAt) return null;
34321
+ return {
34322
+ file: file2,
34323
+ historySessionId,
34324
+ messageCount,
34325
+ firstMessageAt,
34326
+ lastMessageAt,
34327
+ sessionTitle: sessionTitle || void 0,
34328
+ preview: preview || void 0,
34329
+ workspace: workspace || void 0
34330
+ };
34331
+ }
34332
+ function shouldScheduleSavedHistoryRollupForSignature(signature) {
34333
+ const parts = String(signature || "").split(":");
34334
+ const size = Number(parts[1] || 0);
34335
+ return shouldScheduleSavedHistoryRollup(size);
34336
+ }
34337
+ function scheduleSavedHistoryRollup(agentType, historySessionId) {
34338
+ const key = `${agentType}:${historySessionId}`;
34339
+ if (!historySessionId || savedHistoryRollupInFlight.has(key)) return;
34340
+ savedHistoryRollupInFlight.add(key);
34341
+ setTimeout(() => {
34342
+ try {
34343
+ new ChatHistoryWriter().compactHistorySession(agentType, historySessionId);
34344
+ } finally {
34345
+ savedHistoryRollupInFlight.delete(key);
34346
+ }
34347
+ }, 0);
34348
+ }
34349
+ function scheduleSavedHistoryBackgroundRefresh(agentType, dir) {
34350
+ const key = `${agentType}:${dir}`;
34351
+ if (savedHistoryBackgroundRefresh.has(key)) return;
34352
+ savedHistoryBackgroundRefresh.add(key);
34353
+ setTimeout(() => {
34354
+ try {
34355
+ if (!fs32.existsSync(dir)) return;
34356
+ const files = listHistoryFiles(dir);
34357
+ const fileSignatures = buildSavedHistoryFileSignatureMap(dir, files);
34358
+ const persistedEntries = loadPersistedSavedHistoryIndex(dir);
34359
+ const computed = computeSavedHistorySessionSummaries(agentType, dir, files, fileSignatures, persistedEntries);
34360
+ savePersistedSavedHistoryIndex(dir, computed.persistedEntries || /* @__PURE__ */ new Map());
34361
+ const refreshedIndexSignature = buildSavedHistoryIndexFileSignature(dir);
34362
+ savedHistorySessionCache.set(agentType.replace(/[^a-zA-Z0-9_-]/g, "_"), {
34363
+ signature: refreshedIndexSignature,
34364
+ summaries: computed.summaries || []
34365
+ });
34366
+ for (const [file2, entry] of Array.from(computed.persistedEntries.entries())) {
34367
+ if (!entry?.summary || !shouldScheduleSavedHistoryRollupForSignature(entry.signature)) continue;
34368
+ scheduleSavedHistoryRollup(agentType, entry.summary.historySessionId);
34369
+ }
34370
+ } catch {
34371
+ } finally {
34372
+ savedHistoryBackgroundRefresh.delete(key);
34373
+ }
34374
+ }, 0);
34375
+ }
34376
+ function computeSavedHistorySessionSummaries(agentType, dir, files, fileSignatures, persistedEntries) {
34377
+ const summaryBySessionId = /* @__PURE__ */ new Map();
34378
+ const nextPersistedEntries = /* @__PURE__ */ new Map();
34379
+ for (const file2 of files.slice().sort()) {
34380
+ const filePath = path7.join(dir, file2);
34381
+ const signature = fileSignatures.get(file2) || `${file2}:missing`;
34382
+ const cached2 = savedHistoryFileSummaryCache.get(filePath);
34383
+ const persisted = persistedEntries.get(file2);
34384
+ const reusableEntry = cached2?.signature === signature ? cached2 : persisted?.signature === signature ? persisted : null;
34385
+ const fileSummary = reusableEntry?.summary || computeSavedHistoryFileSummary(agentType, dir, file2);
34386
+ const nextEntry = reusableEntry || {
34387
+ signature,
34388
+ summary: fileSummary
34389
+ };
34390
+ if (!reusableEntry) {
34391
+ nextEntry.signature = signature;
34392
+ nextEntry.summary = fileSummary;
34393
+ }
34394
+ savedHistoryFileSummaryCache.set(filePath, nextEntry);
34395
+ nextPersistedEntries.set(file2, nextEntry);
34396
+ if (!fileSummary) continue;
34397
+ const existing = summaryBySessionId.get(fileSummary.historySessionId);
34398
+ if (fileSummary.messageCount <= 0 || !fileSummary.lastMessageAt) {
34399
+ continue;
34400
+ }
34401
+ if (!existing) {
34402
+ summaryBySessionId.set(fileSummary.historySessionId, {
34403
+ historySessionId: fileSummary.historySessionId,
34404
+ sessionTitle: fileSummary.sessionTitle,
34405
+ messageCount: fileSummary.messageCount,
34406
+ firstMessageAt: fileSummary.firstMessageAt,
34407
+ lastMessageAt: fileSummary.lastMessageAt,
34408
+ preview: fileSummary.preview,
34409
+ workspace: fileSummary.workspace
34410
+ });
34411
+ continue;
34412
+ }
34413
+ existing.messageCount += fileSummary.messageCount;
34414
+ if (!existing.firstMessageAt || fileSummary.firstMessageAt < existing.firstMessageAt) {
34415
+ existing.firstMessageAt = fileSummary.firstMessageAt;
34416
+ }
34417
+ if (fileSummary.lastMessageAt >= existing.lastMessageAt) {
34418
+ existing.lastMessageAt = fileSummary.lastMessageAt;
34419
+ if (fileSummary.sessionTitle) existing.sessionTitle = fileSummary.sessionTitle;
34420
+ if (fileSummary.preview) existing.preview = fileSummary.preview;
34421
+ }
34422
+ if (!existing.workspace && fileSummary.workspace) {
34423
+ existing.workspace = fileSummary.workspace;
34424
+ }
34425
+ }
34426
+ return {
34427
+ summaries: Array.from(summaryBySessionId.values()).sort((a, b2) => b2.lastMessageAt - a.lastMessageAt),
34428
+ persistedEntries: nextPersistedEntries
34429
+ };
34006
34430
  }
34007
34431
  var ChatHistoryWriter = class {
34008
34432
  /** Last seen message count per agent (deduplication) */
@@ -34077,9 +34501,11 @@ ${cleanBody}`;
34077
34501
  fs32.mkdirSync(dir, { recursive: true });
34078
34502
  const date5 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
34079
34503
  const filePrefix = effectiveHistoryKey ? `${this.sanitize(effectiveHistoryKey)}_` : "";
34080
- const filePath = path7.join(dir, `${filePrefix}${date5}.jsonl`);
34504
+ const fileName = `${filePrefix}${date5}.jsonl`;
34505
+ const filePath = path7.join(dir, fileName);
34081
34506
  const lines = newMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
34082
34507
  fs32.appendFileSync(filePath, lines, "utf-8");
34508
+ updateSavedHistoryIndexForAppendedMessages(agentType, dir, fileName, effectiveHistoryKey, newMessages);
34083
34509
  const prevCount = this.lastSeenCounts.get(dedupKey) || 0;
34084
34510
  if (!historySessionId && messages.length < prevCount * 0.5 && prevCount > 3) {
34085
34511
  seenHashes.clear();
@@ -34170,7 +34596,8 @@ ${cleanBody}`;
34170
34596
  const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
34171
34597
  fs32.mkdirSync(dir, { recursive: true });
34172
34598
  const date5 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
34173
- const filePath = path7.join(dir, `${this.sanitize(id)}_${date5}.jsonl`);
34599
+ const fileName = `${this.sanitize(id)}_${date5}.jsonl`;
34600
+ const filePath = path7.join(dir, fileName);
34174
34601
  const record2 = {
34175
34602
  ts: (/* @__PURE__ */ new Date()).toISOString(),
34176
34603
  receivedAt: Date.now(),
@@ -34183,6 +34610,7 @@ ${cleanBody}`;
34183
34610
  workspace: ws2
34184
34611
  };
34185
34612
  fs32.appendFileSync(filePath, JSON.stringify(record2) + "\n", "utf-8");
34613
+ updateSavedHistoryIndexForSessionStart(agentType, dir, fileName, id, ws2);
34186
34614
  } catch {
34187
34615
  }
34188
34616
  }
@@ -34248,6 +34676,7 @@ ${cleanBody}`;
34248
34676
  }
34249
34677
  fs32.unlinkSync(sourcePath);
34250
34678
  }
34679
+ invalidatePersistedSavedHistoryIndex(agentType, dir);
34251
34680
  } catch {
34252
34681
  }
34253
34682
  }
@@ -34297,6 +34726,7 @@ ${cleanBody}`;
34297
34726
  fs32.writeFileSync(filePath, `${collapsed.map((entry) => JSON.stringify(entry)).join("\n")}
34298
34727
  `, "utf-8");
34299
34728
  }
34729
+ invalidatePersistedSavedHistoryIndex(agentType, dir);
34300
34730
  } catch {
34301
34731
  }
34302
34732
  }
@@ -34316,13 +34746,18 @@ ${cleanBody}`;
34316
34746
  for (const dir of agentDirs) {
34317
34747
  const dirPath = path7.join(HISTORY_DIR, dir.name);
34318
34748
  const files = fs32.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl") || f.endsWith(".terminal.log"));
34749
+ let removedAny = false;
34319
34750
  for (const file2 of files) {
34320
34751
  const filePath = path7.join(dirPath, file2);
34321
34752
  const stat4 = fs32.statSync(filePath);
34322
34753
  if (stat4.mtimeMs < cutoff) {
34323
34754
  fs32.unlinkSync(filePath);
34755
+ removedAny = true;
34324
34756
  }
34325
34757
  }
34758
+ if (removedAny) {
34759
+ invalidatePersistedSavedHistoryIndex(dir.name, dirPath);
34760
+ }
34326
34761
  }
34327
34762
  } catch {
34328
34763
  }
@@ -34388,18 +34823,51 @@ ${cleanBody}`;
34388
34823
  savedHistorySessionCache.delete(sanitized);
34389
34824
  return { sessions: [], hasMore: false };
34390
34825
  }
34391
- const files = listHistoryFiles(dir);
34392
- const signature = buildSavedHistoryCacheSignature(dir, files);
34393
34826
  const cached2 = savedHistorySessionCache.get(sanitized);
34394
- const summaries = cached2?.signature === signature ? cached2.summaries : computeSavedHistorySessionSummaries(agentType, dir, files);
34395
- if (!cached2 || cached2.signature !== signature) {
34827
+ const offset = Math.max(0, options.offset || 0);
34828
+ const limit = Math.max(1, options.limit || 30);
34829
+ const indexSignature = buildSavedHistoryIndexFileSignature(dir);
34830
+ let cacheWasInvalidated = false;
34831
+ if (cached2) {
34832
+ const cacheLooksPersisted = cached2.signature.startsWith("index:");
34833
+ const cacheStillValid = cacheLooksPersisted ? cached2.signature === indexSignature : (() => {
34834
+ const files2 = listHistoryFiles(dir);
34835
+ const fileSignatures2 = buildSavedHistoryFileSignatureMap(dir, files2);
34836
+ return cached2.signature === buildSavedHistoryCacheSignature(files2, fileSignatures2);
34837
+ })();
34838
+ if (cacheStillValid) {
34839
+ const sliced2 = cached2.summaries.slice(offset, offset + limit);
34840
+ return {
34841
+ sessions: sliced2,
34842
+ hasMore: cached2.summaries.length > offset + limit
34843
+ };
34844
+ }
34845
+ cacheWasInvalidated = true;
34846
+ }
34847
+ const persistedSessions = readPersistedSavedHistorySessionSummaries(dir);
34848
+ if (!cacheWasInvalidated && persistedSessions?.length && !historyDirectoryHasFilesNewerThanIndex(dir)) {
34396
34849
  savedHistorySessionCache.set(sanitized, {
34397
- signature,
34398
- summaries
34850
+ signature: indexSignature,
34851
+ summaries: persistedSessions
34399
34852
  });
34853
+ scheduleSavedHistoryBackgroundRefresh(agentType, dir);
34854
+ const sliced2 = persistedSessions.slice(offset, offset + limit);
34855
+ return {
34856
+ sessions: sliced2,
34857
+ hasMore: persistedSessions.length > offset + limit
34858
+ };
34400
34859
  }
34401
- const offset = Math.max(0, options.offset || 0);
34402
- const limit = Math.max(1, options.limit || 30);
34860
+ const files = listHistoryFiles(dir);
34861
+ const fileSignatures = buildSavedHistoryFileSignatureMap(dir, files);
34862
+ const signature = buildSavedHistoryCacheSignature(files, fileSignatures);
34863
+ const persistedEntries = loadPersistedSavedHistoryIndex(dir);
34864
+ const computed = computeSavedHistorySessionSummaries(agentType, dir, files, fileSignatures, persistedEntries);
34865
+ const summaries = computed.summaries || [];
34866
+ savePersistedSavedHistoryIndex(dir, computed.persistedEntries || /* @__PURE__ */ new Map());
34867
+ savedHistorySessionCache.set(sanitized, {
34868
+ signature,
34869
+ summaries
34870
+ });
34403
34871
  const sliced = summaries.slice(offset, offset + limit);
34404
34872
  return {
34405
34873
  sessions: sliced,
@@ -36128,6 +36596,21 @@ ${effect.notification.body || ""}`.trim();
36128
36596
  lastUpdated: ext.lastUpdated
36129
36597
  };
36130
36598
  }
36599
+ function shouldIncludeExtensionSession(ext) {
36600
+ const status = String(ext.status || "").trim().toLowerCase();
36601
+ const hasActiveChat = !!ext.activeChat;
36602
+ const hasMessages = Array.isArray(ext.activeChat?.messages) && ext.activeChat.messages.length > 0;
36603
+ const hasModal = !!ext.activeChat?.activeModal;
36604
+ const hasStreams = Array.isArray(ext.agentStreams) && ext.agentStreams.length > 0;
36605
+ const hasProviderSessionId = typeof ext.providerSessionId === "string" && ext.providerSessionId.trim().length > 0;
36606
+ const hasControlValues = !!(ext.controlValues && Object.keys(ext.controlValues).length > 0);
36607
+ const hasProviderControls = Array.isArray(ext.providerControls) && ext.providerControls.length > 0;
36608
+ const hasOpenPanelCapability = Array.isArray(ext.sessionCapabilities) && ext.sessionCapabilities.includes("open_panel");
36609
+ const hasSummaryMetadata = !!ext.summaryMetadata;
36610
+ const hasError = typeof ext.errorMessage === "string" && ext.errorMessage.trim().length > 0;
36611
+ const hasInterestingStatus = !!status && !["idle", "panel_hidden", "disconnected", "not_monitored"].includes(status);
36612
+ return hasActiveChat || hasMessages || hasModal || hasStreams || hasProviderSessionId || hasControlValues || hasProviderControls || hasOpenPanelCapability || hasSummaryMetadata || hasError || hasInterestingStatus;
36613
+ }
36131
36614
  function buildCliSession(state, options) {
36132
36615
  const profile = options.profile || "full";
36133
36616
  const activeChat = normalizeActiveChatData(state.activeChat, getActiveChatOptions(profile));
@@ -36211,6 +36694,7 @@ ${effect.notification.body || ""}`.trim();
36211
36694
  for (const state of ideStates) {
36212
36695
  sessions.push(buildIdeWorkspaceSession(state, cdpManagers, options));
36213
36696
  for (const ext of state.extensions) {
36697
+ if (!shouldIncludeExtensionSession(ext)) continue;
36214
36698
  sessions.push(buildExtensionAgentSession(state, ext, options));
36215
36699
  }
36216
36700
  }
@@ -38351,7 +38835,9 @@ ${effect.notification.body || ""}`.trim();
38351
38835
  });
38352
38836
  }
38353
38837
  async function executeProviderScript(h, args, scriptName) {
38354
- const resolvedProviderType = h.currentSession?.providerType || h.currentProviderType || args?.agentType || args?.providerType;
38838
+ const explicitTargetSessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : "";
38839
+ const targetSession = explicitTargetSessionId ? h.ctx.sessionRegistry?.get(explicitTargetSessionId) : void 0;
38840
+ const resolvedProviderType = targetSession?.providerType || h.currentSession?.providerType || h.currentProviderType || args?.agentType || args?.providerType;
38355
38841
  if (!resolvedProviderType) return { success: false, error: "targetSessionId or providerType is required" };
38356
38842
  const loader = h.ctx.providerLoader;
38357
38843
  if (!loader) return { success: false, error: "ProviderLoader not initialized" };
@@ -38394,16 +38880,16 @@ ${effect.notification.body || ""}`.trim();
38394
38880
  const scriptFn = provider.scripts[actualScriptName];
38395
38881
  const scriptCode = scriptFn(normalizedArgs);
38396
38882
  if (!scriptCode) return { success: false, error: `Script '${actualScriptName}' returned null` };
38397
- const cdpKey = provider.category === "ide" ? h.currentSession?.cdpManagerKey || h.currentManagerKey || resolvedProviderType : h.currentSession?.cdpManagerKey || h.currentManagerKey;
38883
+ const cdpKey = provider.category === "ide" ? targetSession?.cdpManagerKey || h.currentSession?.cdpManagerKey || h.currentManagerKey || resolvedProviderType : targetSession?.cdpManagerKey || h.currentSession?.cdpManagerKey || h.currentManagerKey;
38398
38884
  LOG2.info("Command", `[ExtScript] provider=${provider.type} category=${provider.category} cdpKey=${cdpKey}`);
38399
38885
  const cdp = h.getCdp(cdpKey);
38400
38886
  if (!cdp?.isConnected) return { success: false, error: `No CDP connection for ${cdpKey || "any"}` };
38401
38887
  try {
38402
38888
  let result;
38403
38889
  if (provider.category === "extension") {
38404
- const runtimeSessionId = h.currentSession?.sessionId || args?.targetSessionId;
38890
+ const runtimeSessionId = explicitTargetSessionId || h.currentSession?.sessionId;
38405
38891
  if (!runtimeSessionId) return { success: false, error: `No target session found for ${resolvedProviderType}` };
38406
- const parentSessionId = h.currentSession?.parentSessionId;
38892
+ const parentSessionId = targetSession?.parentSessionId || h.currentSession?.parentSessionId;
38407
38893
  if (parentSessionId) {
38408
38894
  await h.agentStream?.setActiveSession(cdp, parentSessionId, runtimeSessionId);
38409
38895
  await h.agentStream?.syncActiveSession(cdp, parentSessionId);
@@ -45277,6 +45763,21 @@ Run 'adhdev doctor' for detailed diagnostics.`
45277
45763
  }
45278
45764
  };
45279
45765
  }
45766
+ async function runAsyncBatch2(items, worker, options = {}) {
45767
+ const list = Array.from(items);
45768
+ if (list.length === 0) return;
45769
+ const concurrency = Math.max(1, Math.min(list.length, Math.floor(options.concurrency || 1)));
45770
+ let nextIndex = 0;
45771
+ const runners = Array.from({ length: concurrency }, async () => {
45772
+ while (true) {
45773
+ const currentIndex = nextIndex;
45774
+ nextIndex += 1;
45775
+ if (currentIndex >= list.length) return;
45776
+ await worker(list[currentIndex], currentIndex);
45777
+ }
45778
+ });
45779
+ await Promise.all(runners);
45780
+ }
45280
45781
  init_read_chat_contract();
45281
45782
  init_chat_message_normalization();
45282
45783
  var ProviderStreamAdapter = class {
@@ -45742,10 +46243,12 @@ Run 'adhdev doctor' for detailed diagnostics.`
45742
46243
  }
45743
46244
  }
45744
46245
  /** Collect active extension session state */
45745
- async collectActiveSession(cdp, parentSessionId) {
46246
+ async collectActiveSession(cdp, parentSessionId, attemptedSessionIds = /* @__PURE__ */ new Set(), originSessionId) {
45746
46247
  if (!this.enabled) return null;
45747
46248
  const activeSessionId = this.getActiveSessionId(parentSessionId);
45748
46249
  if (!activeSessionId) return null;
46250
+ const resolvedOriginSessionId = originSessionId || activeSessionId;
46251
+ attemptedSessionIds.add(activeSessionId);
45749
46252
  let agent = this.managedBySessionId.get(activeSessionId);
45750
46253
  if (!agent) {
45751
46254
  agent = await this.connectManagedSession(cdp, parentSessionId, activeSessionId) || void 0;
@@ -45758,18 +46261,44 @@ Run 'adhdev doctor' for detailed diagnostics.`
45758
46261
  try {
45759
46262
  const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
45760
46263
  const state = await agent.adapter.readChat(evaluate);
45761
- const stateError = this.getStateError(state);
45762
- const selectedModelValue = typeof state.controlValues?.model === "string" ? state.controlValues.model : "";
45763
- LOG2.debug("AgentStream", `[AgentStream] readChat(${type}) result: status=${state.status} msgs=${state.messages?.length || 0} model=${selectedModelValue}${state.status === "error" ? " error=" + JSON.stringify(stateError) : ""}`);
45764
- if (state.status === "error" && this.isRecoverableSessionError(stateError)) {
46264
+ 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;
46265
+ const normalizedState = {
46266
+ ...state,
46267
+ sessionId: agent.runtimeSessionId,
46268
+ ...resolvedProviderSessionId ? { providerSessionId: resolvedProviderSessionId } : {}
46269
+ };
46270
+ const stateError = this.getStateError(normalizedState);
46271
+ const selectedModelValue = typeof normalizedState.controlValues?.model === "string" ? normalizedState.controlValues.model : "";
46272
+ LOG2.debug("AgentStream", `[AgentStream] readChat(${type}) result: status=${normalizedState.status} msgs=${normalizedState.messages?.length || 0} model=${selectedModelValue}${normalizedState.status === "error" ? " error=" + JSON.stringify(stateError) : ""}`);
46273
+ if (normalizedState.status === "error" && this.isRecoverableSessionError(stateError)) {
45765
46274
  throw new Error(stateError);
45766
46275
  }
45767
- agent.lastState = state;
46276
+ agent.lastState = normalizedState;
45768
46277
  agent.lastError = null;
45769
- if (state.status === "panel_hidden") {
46278
+ if (normalizedState.status === "panel_hidden") {
46279
+ const discovered = await cdp.discoverAgentWebviews().catch(() => []);
46280
+ const fallbackTarget = discovered.find((entry) => {
46281
+ if (entry.agentType === type) return false;
46282
+ const fallbackSessionId = this.resolveSessionIdForTarget(parentSessionId, entry.agentType);
46283
+ return !!fallbackSessionId && fallbackSessionId !== activeSessionId && !attemptedSessionIds.has(fallbackSessionId);
46284
+ });
46285
+ if (fallbackTarget) {
46286
+ const fallbackSessionId = this.resolveSessionIdForTarget(parentSessionId, fallbackTarget.agentType);
46287
+ if (fallbackSessionId && fallbackSessionId !== activeSessionId && !attemptedSessionIds.has(fallbackSessionId)) {
46288
+ this.logFn(`[AgentStream] Active session ${type} is hidden; switching to visible agent ${fallbackTarget.agentType} (${parentSessionId})`);
46289
+ await this.setActiveSession(cdp, parentSessionId, fallbackSessionId);
46290
+ await this.syncActiveSession(cdp, parentSessionId);
46291
+ const fallbackState = await this.collectActiveSession(cdp, parentSessionId, attemptedSessionIds, resolvedOriginSessionId);
46292
+ if (fallbackState?.status === "panel_hidden" && resolvedOriginSessionId !== fallbackSessionId) {
46293
+ await this.setActiveSession(cdp, parentSessionId, resolvedOriginSessionId);
46294
+ await this.syncActiveSession(cdp, parentSessionId);
46295
+ }
46296
+ return fallbackState;
46297
+ }
46298
+ }
45770
46299
  agent.lastHiddenCheckTime = Date.now();
45771
46300
  }
45772
- return state;
46301
+ return normalizedState;
45773
46302
  } catch (e) {
45774
46303
  const errorMsg = e?.message || String(e);
45775
46304
  this.logFn(`[AgentStream] readChat(${type}) error: ${errorMsg.slice(0, 200)}`);
@@ -46063,6 +46592,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
46063
46592
  try {
46064
46593
  await agentStreamManager.syncActiveSession(cdp, parentSessionId);
46065
46594
  let stream = await agentStreamManager.collectActiveSession(cdp, parentSessionId);
46595
+ resolvedActiveSessionId = stream?.sessionId || agentStreamManager.getActiveSessionId(parentSessionId) || resolvedActiveSessionId;
46066
46596
  if (stream?.status === "waiting_approval") {
46067
46597
  const autoApprove = providerLoader.getSettings(stream.agentType).autoApprove !== false;
46068
46598
  if (autoApprove && resolvedActiveSessionId) {
@@ -53256,7 +53786,7 @@ var StandaloneServer = class {
53256
53786
  }),
53257
53787
  onStatusChange: () => {
53258
53788
  this.scheduleBroadcastStatus();
53259
- void this.flushWsChatSubscriptions(void 0, { onlyActive: false });
53789
+ void this.flushWsChatSubscriptions(void 0, { onlyActive: true });
53260
53790
  },
53261
53791
  removeAgentTracking: () => {
53262
53792
  },
@@ -53285,7 +53815,7 @@ var StandaloneServer = class {
53285
53815
  statusDaemonMode: false,
53286
53816
  onStatusChange: () => {
53287
53817
  this.scheduleBroadcastStatus();
53288
- void this.flushWsChatSubscriptions(void 0, { onlyActive: false });
53818
+ void this.flushWsChatSubscriptions(void 0, { onlyActive: true });
53289
53819
  },
53290
53820
  sessionHostControl,
53291
53821
  onStreamsUpdated: (ideType, streams) => {
@@ -53885,6 +54415,7 @@ var StandaloneServer = class {
53885
54415
  try {
53886
54416
  const targets = targetWs ? [targetWs] : Array.from(this.clients);
53887
54417
  const hotSessionIds = options.onlyActive ? this.getHotChatSessionIdsForWsFlush() : null;
54418
+ const tasks = [];
53888
54419
  for (const ws2 of targets) {
53889
54420
  if (ws2.readyState !== import_ws.WebSocket.OPEN) continue;
53890
54421
  const subs = this.wsSubscriptions.get(ws2);
@@ -53894,11 +54425,18 @@ var StandaloneServer = class {
53894
54425
  if (hotSessionIds && !hotSessionIds.active.has(targetSessionId) && !hotSessionIds.finalizing.has(targetSessionId)) {
53895
54426
  continue;
53896
54427
  }
54428
+ tasks.push({ ws: ws2, key, sub });
54429
+ }
54430
+ }
54431
+ await (0, import_daemon_core2.runAsyncBatch)(tasks, async ({ ws: ws2, key, sub }) => {
54432
+ try {
53897
54433
  const update = await this.buildChatTailUpdate(sub.request.params, sub, key);
53898
- if (!update || ws2.readyState !== import_ws.WebSocket.OPEN) continue;
54434
+ if (!update || ws2.readyState !== import_ws.WebSocket.OPEN) return;
53899
54435
  ws2.send(JSON.stringify({ type: "topic_update", update }));
54436
+ } catch (error48) {
54437
+ import_daemon_core2.LOG.warn("Standalone", `[chat_tail] skipped session=${sub.request.params.targetSessionId} key=${key} error=${error48?.message || error48}`);
53900
54438
  }
53901
- }
54439
+ }, { concurrency: 4 });
53902
54440
  } finally {
53903
54441
  this.wsChatFlushInFlight = false;
53904
54442
  if (this.pendingWsChatFlush) {