adhdev 0.8.74 → 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/cli/index.js CHANGED
@@ -1807,6 +1807,32 @@ var init_logger = __esm({
1807
1807
  });
1808
1808
 
1809
1809
  // ../../oss/packages/daemon-core/src/cdp/manager.ts
1810
+ function normalizeTitle(value) {
1811
+ return String(value || "").trim().replace(/\s+/g, " ").toLowerCase();
1812
+ }
1813
+ function titlesMatch(lhs, rhs) {
1814
+ const a = normalizeTitle(lhs);
1815
+ const b = normalizeTitle(rhs);
1816
+ if (!a || !b) return false;
1817
+ return a === b || a.includes(b) || b.includes(a);
1818
+ }
1819
+ function resolveCdpPageTarget(params) {
1820
+ const { pages, pinnedTargetId, previousPageTitle } = params;
1821
+ if (pages.length === 0) return { target: null, retargeted: false };
1822
+ if (!pinnedTargetId) {
1823
+ return { target: pages[0] || null, retargeted: false };
1824
+ }
1825
+ const exact = pages.find((page) => page.id === pinnedTargetId);
1826
+ if (exact) return { target: exact, retargeted: false };
1827
+ const titleMatchesList = pages.filter((page) => titlesMatch(page.title, previousPageTitle));
1828
+ if (titleMatchesList.length === 1) {
1829
+ return { target: titleMatchesList[0], retargeted: true };
1830
+ }
1831
+ if (pages.length === 1) {
1832
+ return { target: pages[0], retargeted: true };
1833
+ }
1834
+ return { target: null, retargeted: false };
1835
+ }
1810
1836
  var import_ws, http, DaemonCdpManager;
1811
1837
  var init_manager = __esm({
1812
1838
  "../../oss/packages/daemon-core/src/cdp/manager.ts"() {
@@ -1974,18 +2000,28 @@ var init_manager = __esm({
1974
2000
  resolve17(targets.find((t) => t.webSocketDebuggerUrl) || null);
1975
2001
  return;
1976
2002
  }
1977
- const mainPages = pages.filter((t) => !this.isNonMainTitle(t.title || ""));
1978
- const list = mainPages.length > 0 ? mainPages : pages;
2003
+ const titleFilteredPages = pages.filter((t) => !this.isNonMainTitle(t.title || ""));
2004
+ const mainPages = titleFilteredPages.filter((t) => this.isMainPageUrl(t.url));
2005
+ const list = mainPages.length > 0 ? mainPages : titleFilteredPages.length > 0 ? titleFilteredPages : pages;
1979
2006
  this.log(`[CDP] pages(${list.length}): ${list.map((t) => `"${t.title}"`).join(", ")}`);
1980
- if (this._targetId) {
1981
- const specific = list.find((t) => t.id === this._targetId);
1982
- if (specific) {
1983
- this._pageTitle = specific.title || "";
1984
- resolve17(specific);
1985
- } else {
1986
- this.log(`[CDP] Target ${this._targetId} not found in page list`);
1987
- resolve17(null);
2007
+ const previousTargetId = this._targetId;
2008
+ const selected = resolveCdpPageTarget({
2009
+ pages: list,
2010
+ pinnedTargetId: previousTargetId,
2011
+ previousPageTitle: this._pageTitle
2012
+ });
2013
+ if (selected.target) {
2014
+ if (selected.retargeted && previousTargetId && previousTargetId !== selected.target.id) {
2015
+ this.log(`[CDP] Target ${previousTargetId} rekeyed to ${selected.target.id}`);
2016
+ this._targetId = selected.target.id;
1988
2017
  }
2018
+ this._pageTitle = selected.target.title || "";
2019
+ resolve17(selected.target);
2020
+ return;
2021
+ }
2022
+ if (previousTargetId) {
2023
+ this.log(`[CDP] Target ${previousTargetId} not found in page list`);
2024
+ resolve17(null);
1989
2025
  return;
1990
2026
  }
1991
2027
  this._pageTitle = list[0]?.title || "";
@@ -3862,6 +3898,68 @@ function sanitizeHistoryMessage(agentType, message) {
3862
3898
  content
3863
3899
  };
3864
3900
  }
3901
+ function sortSavedHistorySessionSummaries(summaries) {
3902
+ return summaries.slice().sort((a, b) => b.lastMessageAt - a.lastMessageAt);
3903
+ }
3904
+ function buildSavedHistorySessionSummaryMapFromEntries(entries) {
3905
+ const summaries = /* @__PURE__ */ new Map();
3906
+ for (const entry of Array.from(entries.values())) {
3907
+ const fileSummary = entry.summary;
3908
+ if (!fileSummary || fileSummary.messageCount <= 0 || !fileSummary.lastMessageAt) continue;
3909
+ const existing = summaries.get(fileSummary.historySessionId);
3910
+ if (!existing) {
3911
+ summaries.set(fileSummary.historySessionId, {
3912
+ historySessionId: fileSummary.historySessionId,
3913
+ sessionTitle: fileSummary.sessionTitle,
3914
+ messageCount: fileSummary.messageCount,
3915
+ firstMessageAt: fileSummary.firstMessageAt,
3916
+ lastMessageAt: fileSummary.lastMessageAt,
3917
+ preview: fileSummary.preview,
3918
+ workspace: fileSummary.workspace
3919
+ });
3920
+ continue;
3921
+ }
3922
+ existing.messageCount += fileSummary.messageCount;
3923
+ if (!existing.firstMessageAt || fileSummary.firstMessageAt < existing.firstMessageAt) {
3924
+ existing.firstMessageAt = fileSummary.firstMessageAt;
3925
+ }
3926
+ if (fileSummary.lastMessageAt >= existing.lastMessageAt) {
3927
+ existing.lastMessageAt = fileSummary.lastMessageAt;
3928
+ if (fileSummary.sessionTitle) existing.sessionTitle = fileSummary.sessionTitle;
3929
+ if (fileSummary.preview) existing.preview = fileSummary.preview;
3930
+ }
3931
+ if (!existing.workspace && fileSummary.workspace) {
3932
+ existing.workspace = fileSummary.workspace;
3933
+ }
3934
+ }
3935
+ return Object.fromEntries(sortSavedHistorySessionSummaries(Array.from(summaries.values())).map((summary) => [summary.historySessionId, summary]));
3936
+ }
3937
+ function readPersistedSavedHistorySessionSummaries(dir) {
3938
+ try {
3939
+ const filePath = getSavedHistoryIndexFilePath(dir);
3940
+ if (!fs3.existsSync(filePath)) return null;
3941
+ const raw = JSON.parse(fs3.readFileSync(filePath, "utf-8"));
3942
+ if (!raw || raw.version !== SAVED_HISTORY_INDEX_VERSION || !raw.sessions || typeof raw.sessions !== "object") {
3943
+ return null;
3944
+ }
3945
+ return sortSavedHistorySessionSummaries(
3946
+ Object.values(raw.sessions).filter((summary) => !!summary && typeof summary.historySessionId === "string" && summary.messageCount > 0 && summary.lastMessageAt > 0).map((summary) => ({
3947
+ historySessionId: summary.historySessionId,
3948
+ sessionTitle: summary.sessionTitle,
3949
+ messageCount: summary.messageCount,
3950
+ firstMessageAt: summary.firstMessageAt,
3951
+ lastMessageAt: summary.lastMessageAt,
3952
+ preview: summary.preview,
3953
+ workspace: summary.workspace
3954
+ }))
3955
+ );
3956
+ } catch {
3957
+ return null;
3958
+ }
3959
+ }
3960
+ function shouldScheduleSavedHistoryRollup(totalBytes) {
3961
+ return Number.isFinite(totalBytes) && totalBytes >= SAVED_HISTORY_ROLLUP_THRESHOLD_BYTES;
3962
+ }
3865
3963
  function sanitizeHistoryFileSegment(value) {
3866
3964
  return String(value || "").replace(/[^a-zA-Z0-9_-]/g, "_");
3867
3965
  }
@@ -3875,71 +3973,386 @@ function listHistoryFiles(dir, historySessionId) {
3875
3973
  return true;
3876
3974
  }).sort().reverse();
3877
3975
  }
3878
- function buildSavedHistoryCacheSignature(dir, files) {
3879
- return files.map((file2) => {
3976
+ function normalizeSavedHistorySessionId(agentType, historySessionId) {
3977
+ const normalizedId = String(historySessionId || "").trim();
3978
+ if (!normalizedId) return "";
3979
+ const strictProviderId = normalizeProviderSessionId(agentType, normalizedId);
3980
+ if (strictProviderId) return strictProviderId;
3981
+ return agentType === "hermes-cli" ? "" : normalizedId;
3982
+ }
3983
+ function extractSavedHistorySessionIdFromFile(agentType, file2) {
3984
+ const match = file2.match(/^([A-Za-z0-9_-]+)_\d{4}-\d{2}-\d{2}\.jsonl$/);
3985
+ return normalizeSavedHistorySessionId(agentType, match?.[1] || "");
3986
+ }
3987
+ function buildSavedHistoryFileSignatureMap(dir, files) {
3988
+ return new Map(files.map((file2) => {
3880
3989
  try {
3881
3990
  const stat4 = fs3.statSync(path7.join(dir, file2));
3882
- return `${file2}:${stat4.size}:${Math.trunc(stat4.mtimeMs)}`;
3991
+ return [file2, `${file2}:${stat4.size}:${Math.trunc(stat4.mtimeMs)}`];
3883
3992
  } catch {
3884
- return `${file2}:missing`;
3885
- }
3886
- }).join("|");
3887
- }
3888
- function computeSavedHistorySessionSummaries(agentType, dir, files) {
3889
- const groupedFiles = /* @__PURE__ */ new Map();
3890
- const filePattern = /^([A-Za-z0-9_-]+)_\d{4}-\d{2}-\d{2}\.jsonl$/;
3891
- for (const file2 of files) {
3892
- const match = file2.match(filePattern);
3893
- if (!match?.[1]) continue;
3894
- const historySessionId = match[1];
3895
- const grouped = groupedFiles.get(historySessionId) || [];
3896
- grouped.push(file2);
3897
- groupedFiles.set(historySessionId, grouped);
3898
- }
3899
- const summaries = [];
3900
- for (const [historySessionId, grouped] of groupedFiles.entries()) {
3901
- let messageCount = 0;
3902
- let firstMessageAt = 0;
3903
- let lastMessageAt = 0;
3904
- let sessionTitle = "";
3905
- let preview = "";
3906
- let workspace = "";
3907
- for (const file2 of grouped.sort()) {
3908
- const filePath = path7.join(dir, file2);
3909
- const content = fs3.readFileSync(filePath, "utf-8");
3910
- const lines = content.split("\n").filter(Boolean);
3911
- for (const line of lines) {
3912
- let parsed = null;
3993
+ return [file2, `${file2}:missing`];
3994
+ }
3995
+ }));
3996
+ }
3997
+ function buildSavedHistoryCacheSignature(files, fileSignatures) {
3998
+ return files.map((file2) => fileSignatures.get(file2) || `${file2}:missing`).join("|");
3999
+ }
4000
+ function getSavedHistoryIndexFilePath(dir) {
4001
+ return path7.join(dir, SAVED_HISTORY_INDEX_FILE);
4002
+ }
4003
+ function getSavedHistoryIndexLockPath(dir) {
4004
+ return `${getSavedHistoryIndexFilePath(dir)}${SAVED_HISTORY_INDEX_LOCK_SUFFIX}`;
4005
+ }
4006
+ function sleepBlocking(ms) {
4007
+ if (ms <= 0) return;
4008
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
4009
+ }
4010
+ function loadPersistedSavedHistoryIndexFromFile(dir) {
4011
+ try {
4012
+ const filePath = getSavedHistoryIndexFilePath(dir);
4013
+ if (!fs3.existsSync(filePath)) return /* @__PURE__ */ new Map();
4014
+ const raw = JSON.parse(fs3.readFileSync(filePath, "utf-8"));
4015
+ if (!raw || raw.version !== SAVED_HISTORY_INDEX_VERSION || !raw.files || typeof raw.files !== "object") {
4016
+ return /* @__PURE__ */ new Map();
4017
+ }
4018
+ return new Map(
4019
+ Object.entries(raw.files).filter(([file2, entry]) => !!file2 && !!entry && typeof entry.signature === "string").map(([file2, entry]) => [file2, {
4020
+ signature: entry.signature,
4021
+ summary: entry.summary || null
4022
+ }])
4023
+ );
4024
+ } catch {
4025
+ return /* @__PURE__ */ new Map();
4026
+ }
4027
+ }
4028
+ function writePersistedSavedHistoryIndexFile(dir, entries) {
4029
+ const filePath = getSavedHistoryIndexFilePath(dir);
4030
+ const tempPath = `${filePath}.tmp`;
4031
+ const payload = {
4032
+ version: SAVED_HISTORY_INDEX_VERSION,
4033
+ files: Object.fromEntries(entries.entries()),
4034
+ sessions: buildSavedHistorySessionSummaryMapFromEntries(entries)
4035
+ };
4036
+ fs3.writeFileSync(tempPath, JSON.stringify(payload), "utf-8");
4037
+ fs3.renameSync(tempPath, filePath);
4038
+ }
4039
+ function acquireSavedHistoryIndexLock(dir) {
4040
+ const lockPath = getSavedHistoryIndexLockPath(dir);
4041
+ const deadline = Date.now() + SAVED_HISTORY_INDEX_LOCK_WAIT_MS;
4042
+ while (Date.now() <= deadline) {
4043
+ try {
4044
+ fs3.mkdirSync(lockPath);
4045
+ return () => {
3913
4046
  try {
3914
- parsed = JSON.parse(line);
4047
+ fs3.rmSync(lockPath, { recursive: true, force: true });
3915
4048
  } catch {
3916
- parsed = null;
3917
4049
  }
3918
- if (!parsed || parsed.historySessionId !== historySessionId) continue;
3919
- if (parsed.kind === "session_start") {
3920
- if (!workspace && parsed.workspace) workspace = parsed.workspace;
4050
+ };
4051
+ } catch (error48) {
4052
+ if (error48?.code !== "EEXIST") return null;
4053
+ try {
4054
+ const stat4 = fs3.statSync(lockPath);
4055
+ if (Date.now() - stat4.mtimeMs > SAVED_HISTORY_INDEX_LOCK_STALE_MS) {
4056
+ fs3.rmSync(lockPath, { recursive: true, force: true });
3921
4057
  continue;
3922
4058
  }
3923
- messageCount += 1;
3924
- if (!firstMessageAt || parsed.receivedAt < firstMessageAt) firstMessageAt = parsed.receivedAt;
3925
- if (!lastMessageAt || parsed.receivedAt > lastMessageAt) lastMessageAt = parsed.receivedAt;
3926
- if (parsed.sessionTitle) sessionTitle = parsed.sessionTitle;
3927
- if (parsed.role !== "system" && parsed.content.trim()) preview = parsed.content.trim();
3928
- }
3929
- }
3930
- if (messageCount === 0 || !lastMessageAt) continue;
3931
- summaries.push({
3932
- historySessionId,
3933
- sessionTitle: sessionTitle || void 0,
3934
- messageCount,
3935
- firstMessageAt,
3936
- lastMessageAt,
3937
- preview: preview || void 0,
3938
- workspace: workspace || void 0
3939
- });
4059
+ } catch {
4060
+ continue;
4061
+ }
4062
+ sleepBlocking(SAVED_HISTORY_INDEX_LOCK_POLL_MS);
4063
+ }
4064
+ }
4065
+ return null;
4066
+ }
4067
+ function withLockedPersistedSavedHistoryIndex(dir, callback) {
4068
+ const release2 = acquireSavedHistoryIndexLock(dir);
4069
+ if (!release2) return null;
4070
+ try {
4071
+ const entries = loadPersistedSavedHistoryIndexFromFile(dir);
4072
+ const result = callback(entries);
4073
+ writePersistedSavedHistoryIndexFile(dir, entries);
4074
+ return result;
4075
+ } catch {
4076
+ return null;
4077
+ } finally {
4078
+ release2();
4079
+ }
4080
+ }
4081
+ function loadPersistedSavedHistoryIndex(dir) {
4082
+ return loadPersistedSavedHistoryIndexFromFile(dir);
4083
+ }
4084
+ function savePersistedSavedHistoryIndex(dir, entries) {
4085
+ withLockedPersistedSavedHistoryIndex(dir, (currentEntries) => {
4086
+ const incomingFiles = new Set(Array.from(entries.keys()));
4087
+ for (const [file2, entry] of Array.from(entries.entries())) {
4088
+ const liveSignature = buildSavedHistoryFileSignature(dir, file2);
4089
+ const existingEntry = currentEntries.get(file2);
4090
+ if (existingEntry && existingEntry.signature !== liveSignature && entry.signature !== liveSignature) {
4091
+ continue;
4092
+ }
4093
+ if (entry.signature !== liveSignature && (!existingEntry || existingEntry.signature !== liveSignature)) {
4094
+ continue;
4095
+ }
4096
+ currentEntries.set(file2, entry.signature === liveSignature ? entry : {
4097
+ signature: liveSignature,
4098
+ summary: existingEntry?.summary || entry.summary
4099
+ });
4100
+ }
4101
+ for (const file2 of Array.from(currentEntries.keys())) {
4102
+ if (incomingFiles.has(file2)) continue;
4103
+ if (!fs3.existsSync(path7.join(dir, file2))) {
4104
+ currentEntries.delete(file2);
4105
+ }
4106
+ }
4107
+ });
4108
+ }
4109
+ function invalidatePersistedSavedHistoryIndex(agentType, dir) {
4110
+ try {
4111
+ fs3.rmSync(getSavedHistoryIndexFilePath(dir), { force: true });
4112
+ } catch {
4113
+ }
4114
+ savedHistorySessionCache.delete(agentType.replace(/[^a-zA-Z0-9_-]/g, "_"));
4115
+ }
4116
+ function buildSavedHistoryIndexFileSignature(dir) {
4117
+ try {
4118
+ const stat4 = fs3.statSync(getSavedHistoryIndexFilePath(dir));
4119
+ return `index:${stat4.size}:${Math.trunc(stat4.mtimeMs)}`;
4120
+ } catch {
4121
+ return "index:missing";
4122
+ }
4123
+ }
4124
+ function historyDirectoryHasFilesNewerThanIndex(dir) {
4125
+ try {
4126
+ const indexStat = fs3.statSync(getSavedHistoryIndexFilePath(dir));
4127
+ const files = listHistoryFiles(dir);
4128
+ for (const file2 of files) {
4129
+ const stat4 = fs3.statSync(path7.join(dir, file2));
4130
+ if (stat4.mtimeMs > indexStat.mtimeMs) return true;
4131
+ }
4132
+ return false;
4133
+ } catch {
4134
+ return true;
4135
+ }
4136
+ }
4137
+ function buildSavedHistoryFileSignature(dir, file2) {
4138
+ try {
4139
+ const stat4 = fs3.statSync(path7.join(dir, file2));
4140
+ return `${file2}:${stat4.size}:${Math.trunc(stat4.mtimeMs)}`;
4141
+ } catch {
4142
+ return `${file2}:missing`;
3940
4143
  }
3941
- summaries.sort((a, b) => b.lastMessageAt - a.lastMessageAt);
3942
- return summaries;
4144
+ }
4145
+ function persistSavedHistoryFileSummaryEntry(agentType, dir, file2, updater) {
4146
+ const filePath = path7.join(dir, file2);
4147
+ const result = withLockedPersistedSavedHistoryIndex(dir, (entries) => {
4148
+ const currentEntry = entries.get(file2) || null;
4149
+ const nextSummary = updater(currentEntry?.summary || null);
4150
+ const nextEntry = {
4151
+ signature: buildSavedHistoryFileSignature(dir, file2),
4152
+ summary: nextSummary
4153
+ };
4154
+ entries.set(file2, nextEntry);
4155
+ savedHistoryFileSummaryCache.set(filePath, nextEntry);
4156
+ return nextEntry;
4157
+ });
4158
+ if (!result) return;
4159
+ if (result.summary?.historySessionId && shouldScheduleSavedHistoryRollupForSignature(result.signature)) {
4160
+ scheduleSavedHistoryRollup(agentType, result.summary.historySessionId);
4161
+ }
4162
+ }
4163
+ function updateSavedHistoryIndexForSessionStart(agentType, dir, file2, historySessionId, workspace) {
4164
+ const normalizedSessionId = normalizeSavedHistorySessionId(agentType, historySessionId);
4165
+ const normalizedWorkspace = String(workspace || "").trim();
4166
+ if (!normalizedSessionId || !normalizedWorkspace) return;
4167
+ persistSavedHistoryFileSummaryEntry(agentType, dir, file2, (currentSummary) => ({
4168
+ file: file2,
4169
+ historySessionId: normalizedSessionId,
4170
+ messageCount: currentSummary?.messageCount || 0,
4171
+ firstMessageAt: currentSummary?.firstMessageAt || 0,
4172
+ lastMessageAt: currentSummary?.lastMessageAt || 0,
4173
+ sessionTitle: currentSummary?.sessionTitle,
4174
+ preview: currentSummary?.preview,
4175
+ workspace: normalizedWorkspace
4176
+ }));
4177
+ }
4178
+ function updateSavedHistoryIndexForAppendedMessages(agentType, dir, file2, historySessionId, messages) {
4179
+ const normalizedSessionId = normalizeSavedHistorySessionId(agentType, historySessionId || "");
4180
+ if (!normalizedSessionId || messages.length === 0) return;
4181
+ persistSavedHistoryFileSummaryEntry(agentType, dir, file2, (currentSummary) => {
4182
+ const nextSummary = {
4183
+ file: file2,
4184
+ historySessionId: normalizedSessionId,
4185
+ messageCount: currentSummary?.messageCount || 0,
4186
+ firstMessageAt: currentSummary?.firstMessageAt || 0,
4187
+ lastMessageAt: currentSummary?.lastMessageAt || 0,
4188
+ sessionTitle: currentSummary?.sessionTitle,
4189
+ preview: currentSummary?.preview,
4190
+ workspace: currentSummary?.workspace
4191
+ };
4192
+ for (const message of messages) {
4193
+ if (!message || message.historySessionId !== historySessionId) continue;
4194
+ if (message.kind === "session_start") {
4195
+ if (message.workspace) nextSummary.workspace = message.workspace;
4196
+ continue;
4197
+ }
4198
+ nextSummary.messageCount += 1;
4199
+ if (!nextSummary.firstMessageAt || message.receivedAt < nextSummary.firstMessageAt) {
4200
+ nextSummary.firstMessageAt = message.receivedAt;
4201
+ }
4202
+ if (!nextSummary.lastMessageAt || message.receivedAt >= nextSummary.lastMessageAt) {
4203
+ nextSummary.lastMessageAt = message.receivedAt;
4204
+ if (message.sessionTitle) nextSummary.sessionTitle = message.sessionTitle;
4205
+ if (message.role !== "system" && message.content.trim()) nextSummary.preview = message.content.trim();
4206
+ } else if (message.sessionTitle) {
4207
+ nextSummary.sessionTitle = message.sessionTitle;
4208
+ }
4209
+ if (!nextSummary.preview && message.role !== "system" && message.content.trim()) {
4210
+ nextSummary.preview = message.content.trim();
4211
+ }
4212
+ }
4213
+ return nextSummary;
4214
+ });
4215
+ }
4216
+ function computeSavedHistoryFileSummary(agentType, dir, file2) {
4217
+ const historySessionId = extractSavedHistorySessionIdFromFile(agentType, file2);
4218
+ if (!historySessionId) return null;
4219
+ const filePath = path7.join(dir, file2);
4220
+ const content = fs3.readFileSync(filePath, "utf-8");
4221
+ const lines = content.split("\n").filter(Boolean);
4222
+ let messageCount = 0;
4223
+ let firstMessageAt = 0;
4224
+ let lastMessageAt = 0;
4225
+ let sessionTitle = "";
4226
+ let preview = "";
4227
+ let workspace = "";
4228
+ for (const line of lines) {
4229
+ let parsed = null;
4230
+ try {
4231
+ parsed = JSON.parse(line);
4232
+ } catch {
4233
+ parsed = null;
4234
+ }
4235
+ if (!parsed || parsed.historySessionId !== historySessionId) continue;
4236
+ if (parsed.kind === "session_start") {
4237
+ if (!workspace && parsed.workspace) workspace = parsed.workspace;
4238
+ continue;
4239
+ }
4240
+ messageCount += 1;
4241
+ if (!firstMessageAt || parsed.receivedAt < firstMessageAt) firstMessageAt = parsed.receivedAt;
4242
+ if (!lastMessageAt || parsed.receivedAt > lastMessageAt) lastMessageAt = parsed.receivedAt;
4243
+ if (parsed.sessionTitle) sessionTitle = parsed.sessionTitle;
4244
+ if (parsed.role !== "system" && parsed.content.trim()) preview = parsed.content.trim();
4245
+ }
4246
+ if (messageCount === 0 || !lastMessageAt) return null;
4247
+ return {
4248
+ file: file2,
4249
+ historySessionId,
4250
+ messageCount,
4251
+ firstMessageAt,
4252
+ lastMessageAt,
4253
+ sessionTitle: sessionTitle || void 0,
4254
+ preview: preview || void 0,
4255
+ workspace: workspace || void 0
4256
+ };
4257
+ }
4258
+ function shouldScheduleSavedHistoryRollupForSignature(signature) {
4259
+ const parts = String(signature || "").split(":");
4260
+ const size = Number(parts[1] || 0);
4261
+ return shouldScheduleSavedHistoryRollup(size);
4262
+ }
4263
+ function scheduleSavedHistoryRollup(agentType, historySessionId) {
4264
+ const key = `${agentType}:${historySessionId}`;
4265
+ if (!historySessionId || savedHistoryRollupInFlight.has(key)) return;
4266
+ savedHistoryRollupInFlight.add(key);
4267
+ setTimeout(() => {
4268
+ try {
4269
+ new ChatHistoryWriter().compactHistorySession(agentType, historySessionId);
4270
+ } finally {
4271
+ savedHistoryRollupInFlight.delete(key);
4272
+ }
4273
+ }, 0);
4274
+ }
4275
+ function scheduleSavedHistoryBackgroundRefresh(agentType, dir) {
4276
+ const key = `${agentType}:${dir}`;
4277
+ if (savedHistoryBackgroundRefresh.has(key)) return;
4278
+ savedHistoryBackgroundRefresh.add(key);
4279
+ setTimeout(() => {
4280
+ try {
4281
+ if (!fs3.existsSync(dir)) return;
4282
+ const files = listHistoryFiles(dir);
4283
+ const fileSignatures = buildSavedHistoryFileSignatureMap(dir, files);
4284
+ const persistedEntries = loadPersistedSavedHistoryIndex(dir);
4285
+ const computed = computeSavedHistorySessionSummaries(agentType, dir, files, fileSignatures, persistedEntries);
4286
+ savePersistedSavedHistoryIndex(dir, computed.persistedEntries || /* @__PURE__ */ new Map());
4287
+ const refreshedIndexSignature = buildSavedHistoryIndexFileSignature(dir);
4288
+ savedHistorySessionCache.set(agentType.replace(/[^a-zA-Z0-9_-]/g, "_"), {
4289
+ signature: refreshedIndexSignature,
4290
+ summaries: computed.summaries || []
4291
+ });
4292
+ for (const [file2, entry] of Array.from(computed.persistedEntries.entries())) {
4293
+ if (!entry?.summary || !shouldScheduleSavedHistoryRollupForSignature(entry.signature)) continue;
4294
+ scheduleSavedHistoryRollup(agentType, entry.summary.historySessionId);
4295
+ }
4296
+ } catch {
4297
+ } finally {
4298
+ savedHistoryBackgroundRefresh.delete(key);
4299
+ }
4300
+ }, 0);
4301
+ }
4302
+ function computeSavedHistorySessionSummaries(agentType, dir, files, fileSignatures, persistedEntries) {
4303
+ const summaryBySessionId = /* @__PURE__ */ new Map();
4304
+ const nextPersistedEntries = /* @__PURE__ */ new Map();
4305
+ for (const file2 of files.slice().sort()) {
4306
+ const filePath = path7.join(dir, file2);
4307
+ const signature = fileSignatures.get(file2) || `${file2}:missing`;
4308
+ const cached2 = savedHistoryFileSummaryCache.get(filePath);
4309
+ const persisted = persistedEntries.get(file2);
4310
+ const reusableEntry = cached2?.signature === signature ? cached2 : persisted?.signature === signature ? persisted : null;
4311
+ const fileSummary = reusableEntry?.summary || computeSavedHistoryFileSummary(agentType, dir, file2);
4312
+ const nextEntry = reusableEntry || {
4313
+ signature,
4314
+ summary: fileSummary
4315
+ };
4316
+ if (!reusableEntry) {
4317
+ nextEntry.signature = signature;
4318
+ nextEntry.summary = fileSummary;
4319
+ }
4320
+ savedHistoryFileSummaryCache.set(filePath, nextEntry);
4321
+ nextPersistedEntries.set(file2, nextEntry);
4322
+ if (!fileSummary) continue;
4323
+ const existing = summaryBySessionId.get(fileSummary.historySessionId);
4324
+ if (fileSummary.messageCount <= 0 || !fileSummary.lastMessageAt) {
4325
+ continue;
4326
+ }
4327
+ if (!existing) {
4328
+ summaryBySessionId.set(fileSummary.historySessionId, {
4329
+ historySessionId: fileSummary.historySessionId,
4330
+ sessionTitle: fileSummary.sessionTitle,
4331
+ messageCount: fileSummary.messageCount,
4332
+ firstMessageAt: fileSummary.firstMessageAt,
4333
+ lastMessageAt: fileSummary.lastMessageAt,
4334
+ preview: fileSummary.preview,
4335
+ workspace: fileSummary.workspace
4336
+ });
4337
+ continue;
4338
+ }
4339
+ existing.messageCount += fileSummary.messageCount;
4340
+ if (!existing.firstMessageAt || fileSummary.firstMessageAt < existing.firstMessageAt) {
4341
+ existing.firstMessageAt = fileSummary.firstMessageAt;
4342
+ }
4343
+ if (fileSummary.lastMessageAt >= existing.lastMessageAt) {
4344
+ existing.lastMessageAt = fileSummary.lastMessageAt;
4345
+ if (fileSummary.sessionTitle) existing.sessionTitle = fileSummary.sessionTitle;
4346
+ if (fileSummary.preview) existing.preview = fileSummary.preview;
4347
+ }
4348
+ if (!existing.workspace && fileSummary.workspace) {
4349
+ existing.workspace = fileSummary.workspace;
4350
+ }
4351
+ }
4352
+ return {
4353
+ summaries: Array.from(summaryBySessionId.values()).sort((a, b) => b.lastMessageAt - a.lastMessageAt),
4354
+ persistedEntries: nextPersistedEntries
4355
+ };
3943
4356
  }
3944
4357
  function readChatHistory(agentType, offset = 0, limit = 30, historySessionId, excludeRecentCount = 0) {
3945
4358
  try {
@@ -3997,18 +4410,51 @@ function listSavedHistorySessions(agentType, options = {}) {
3997
4410
  savedHistorySessionCache.delete(sanitized);
3998
4411
  return { sessions: [], hasMore: false };
3999
4412
  }
4000
- const files = listHistoryFiles(dir);
4001
- const signature = buildSavedHistoryCacheSignature(dir, files);
4002
4413
  const cached2 = savedHistorySessionCache.get(sanitized);
4003
- const summaries = cached2?.signature === signature ? cached2.summaries : computeSavedHistorySessionSummaries(agentType, dir, files);
4004
- if (!cached2 || cached2.signature !== signature) {
4414
+ const offset = Math.max(0, options.offset || 0);
4415
+ const limit = Math.max(1, options.limit || 30);
4416
+ const indexSignature = buildSavedHistoryIndexFileSignature(dir);
4417
+ let cacheWasInvalidated = false;
4418
+ if (cached2) {
4419
+ const cacheLooksPersisted = cached2.signature.startsWith("index:");
4420
+ const cacheStillValid = cacheLooksPersisted ? cached2.signature === indexSignature : (() => {
4421
+ const files2 = listHistoryFiles(dir);
4422
+ const fileSignatures2 = buildSavedHistoryFileSignatureMap(dir, files2);
4423
+ return cached2.signature === buildSavedHistoryCacheSignature(files2, fileSignatures2);
4424
+ })();
4425
+ if (cacheStillValid) {
4426
+ const sliced2 = cached2.summaries.slice(offset, offset + limit);
4427
+ return {
4428
+ sessions: sliced2,
4429
+ hasMore: cached2.summaries.length > offset + limit
4430
+ };
4431
+ }
4432
+ cacheWasInvalidated = true;
4433
+ }
4434
+ const persistedSessions = readPersistedSavedHistorySessionSummaries(dir);
4435
+ if (!cacheWasInvalidated && persistedSessions?.length && !historyDirectoryHasFilesNewerThanIndex(dir)) {
4005
4436
  savedHistorySessionCache.set(sanitized, {
4006
- signature,
4007
- summaries
4437
+ signature: indexSignature,
4438
+ summaries: persistedSessions
4008
4439
  });
4440
+ scheduleSavedHistoryBackgroundRefresh(agentType, dir);
4441
+ const sliced2 = persistedSessions.slice(offset, offset + limit);
4442
+ return {
4443
+ sessions: sliced2,
4444
+ hasMore: persistedSessions.length > offset + limit
4445
+ };
4009
4446
  }
4010
- const offset = Math.max(0, options.offset || 0);
4011
- const limit = Math.max(1, options.limit || 30);
4447
+ const files = listHistoryFiles(dir);
4448
+ const fileSignatures = buildSavedHistoryFileSignatureMap(dir, files);
4449
+ const signature = buildSavedHistoryCacheSignature(files, fileSignatures);
4450
+ const persistedEntries = loadPersistedSavedHistoryIndex(dir);
4451
+ const computed = computeSavedHistorySessionSummaries(agentType, dir, files, fileSignatures, persistedEntries);
4452
+ const summaries = computed.summaries || [];
4453
+ savePersistedSavedHistoryIndex(dir, computed.persistedEntries || /* @__PURE__ */ new Map());
4454
+ savedHistorySessionCache.set(sanitized, {
4455
+ signature,
4456
+ summaries
4457
+ });
4012
4458
  const sliced = summaries.slice(offset, offset + limit);
4013
4459
  return {
4014
4460
  sessions: sliced,
@@ -4018,7 +4464,7 @@ function listSavedHistorySessions(agentType, options = {}) {
4018
4464
  return { sessions: [], hasMore: false };
4019
4465
  }
4020
4466
  }
4021
- var fs3, path7, os6, HISTORY_DIR, RETAIN_DAYS, savedHistorySessionCache, CODEX_STARTER_PROMPT_RE, ChatHistoryWriter;
4467
+ var fs3, path7, os6, HISTORY_DIR, RETAIN_DAYS, SAVED_HISTORY_INDEX_VERSION, SAVED_HISTORY_INDEX_FILE, SAVED_HISTORY_INDEX_LOCK_SUFFIX, SAVED_HISTORY_INDEX_LOCK_WAIT_MS, SAVED_HISTORY_INDEX_LOCK_STALE_MS, SAVED_HISTORY_INDEX_LOCK_POLL_MS, SAVED_HISTORY_ROLLUP_THRESHOLD_BYTES, savedHistorySessionCache, savedHistoryFileSummaryCache, savedHistoryBackgroundRefresh, savedHistoryRollupInFlight, CODEX_STARTER_PROMPT_RE, ChatHistoryWriter;
4022
4468
  var init_chat_history = __esm({
4023
4469
  "../../oss/packages/daemon-core/src/config/chat-history.ts"() {
4024
4470
  "use strict";
@@ -4026,9 +4472,20 @@ var init_chat_history = __esm({
4026
4472
  path7 = __toESM(require("path"));
4027
4473
  os6 = __toESM(require("os"));
4028
4474
  init_chat_message_normalization();
4475
+ init_provider_session_id();
4029
4476
  HISTORY_DIR = path7.join(os6.homedir(), ".adhdev", "history");
4030
4477
  RETAIN_DAYS = 30;
4478
+ SAVED_HISTORY_INDEX_VERSION = 1;
4479
+ SAVED_HISTORY_INDEX_FILE = ".saved-history-index.json";
4480
+ SAVED_HISTORY_INDEX_LOCK_SUFFIX = ".lock";
4481
+ SAVED_HISTORY_INDEX_LOCK_WAIT_MS = 1500;
4482
+ SAVED_HISTORY_INDEX_LOCK_STALE_MS = 15e3;
4483
+ SAVED_HISTORY_INDEX_LOCK_POLL_MS = 25;
4484
+ SAVED_HISTORY_ROLLUP_THRESHOLD_BYTES = 16 * 1024 * 1024;
4031
4485
  savedHistorySessionCache = /* @__PURE__ */ new Map();
4486
+ savedHistoryFileSummaryCache = /* @__PURE__ */ new Map();
4487
+ savedHistoryBackgroundRefresh = /* @__PURE__ */ new Set();
4488
+ savedHistoryRollupInFlight = /* @__PURE__ */ new Set();
4032
4489
  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;
4033
4490
  ChatHistoryWriter = class {
4034
4491
  /** Last seen message count per agent (deduplication) */
@@ -4103,9 +4560,11 @@ var init_chat_history = __esm({
4103
4560
  fs3.mkdirSync(dir, { recursive: true });
4104
4561
  const date5 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4105
4562
  const filePrefix = effectiveHistoryKey ? `${this.sanitize(effectiveHistoryKey)}_` : "";
4106
- const filePath = path7.join(dir, `${filePrefix}${date5}.jsonl`);
4563
+ const fileName = `${filePrefix}${date5}.jsonl`;
4564
+ const filePath = path7.join(dir, fileName);
4107
4565
  const lines = newMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
4108
4566
  fs3.appendFileSync(filePath, lines, "utf-8");
4567
+ updateSavedHistoryIndexForAppendedMessages(agentType, dir, fileName, effectiveHistoryKey, newMessages);
4109
4568
  const prevCount = this.lastSeenCounts.get(dedupKey) || 0;
4110
4569
  if (!historySessionId && messages.length < prevCount * 0.5 && prevCount > 3) {
4111
4570
  seenHashes.clear();
@@ -4196,7 +4655,8 @@ var init_chat_history = __esm({
4196
4655
  const dir = path7.join(HISTORY_DIR, this.sanitize(agentType));
4197
4656
  fs3.mkdirSync(dir, { recursive: true });
4198
4657
  const date5 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4199
- const filePath = path7.join(dir, `${this.sanitize(id)}_${date5}.jsonl`);
4658
+ const fileName = `${this.sanitize(id)}_${date5}.jsonl`;
4659
+ const filePath = path7.join(dir, fileName);
4200
4660
  const record2 = {
4201
4661
  ts: (/* @__PURE__ */ new Date()).toISOString(),
4202
4662
  receivedAt: Date.now(),
@@ -4209,6 +4669,7 @@ var init_chat_history = __esm({
4209
4669
  workspace: ws
4210
4670
  };
4211
4671
  fs3.appendFileSync(filePath, JSON.stringify(record2) + "\n", "utf-8");
4672
+ updateSavedHistoryIndexForSessionStart(agentType, dir, fileName, id, ws);
4212
4673
  } catch {
4213
4674
  }
4214
4675
  }
@@ -4274,6 +4735,7 @@ var init_chat_history = __esm({
4274
4735
  }
4275
4736
  fs3.unlinkSync(sourcePath);
4276
4737
  }
4738
+ invalidatePersistedSavedHistoryIndex(agentType, dir);
4277
4739
  } catch {
4278
4740
  }
4279
4741
  }
@@ -4323,6 +4785,7 @@ var init_chat_history = __esm({
4323
4785
  fs3.writeFileSync(filePath, `${collapsed.map((entry) => JSON.stringify(entry)).join("\n")}
4324
4786
  `, "utf-8");
4325
4787
  }
4788
+ invalidatePersistedSavedHistoryIndex(agentType, dir);
4326
4789
  } catch {
4327
4790
  }
4328
4791
  }
@@ -4342,13 +4805,18 @@ var init_chat_history = __esm({
4342
4805
  for (const dir of agentDirs) {
4343
4806
  const dirPath = path7.join(HISTORY_DIR, dir.name);
4344
4807
  const files = fs3.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl") || f.endsWith(".terminal.log"));
4808
+ let removedAny = false;
4345
4809
  for (const file2 of files) {
4346
4810
  const filePath = path7.join(dirPath, file2);
4347
4811
  const stat4 = fs3.statSync(filePath);
4348
4812
  if (stat4.mtimeMs < cutoff) {
4349
4813
  fs3.unlinkSync(filePath);
4814
+ removedAny = true;
4350
4815
  }
4351
4816
  }
4817
+ if (removedAny) {
4818
+ invalidatePersistedSavedHistoryIndex(dir.name, dirPath);
4819
+ }
4352
4820
  }
4353
4821
  } catch {
4354
4822
  }
@@ -6275,6 +6743,21 @@ function buildExtensionAgentSession(parent, ext, options) {
6275
6743
  lastUpdated: ext.lastUpdated
6276
6744
  };
6277
6745
  }
6746
+ function shouldIncludeExtensionSession(ext) {
6747
+ const status = String(ext.status || "").trim().toLowerCase();
6748
+ const hasActiveChat = !!ext.activeChat;
6749
+ const hasMessages = Array.isArray(ext.activeChat?.messages) && ext.activeChat.messages.length > 0;
6750
+ const hasModal = !!ext.activeChat?.activeModal;
6751
+ const hasStreams = Array.isArray(ext.agentStreams) && ext.agentStreams.length > 0;
6752
+ const hasProviderSessionId = typeof ext.providerSessionId === "string" && ext.providerSessionId.trim().length > 0;
6753
+ const hasControlValues = !!(ext.controlValues && Object.keys(ext.controlValues).length > 0);
6754
+ const hasProviderControls = Array.isArray(ext.providerControls) && ext.providerControls.length > 0;
6755
+ const hasOpenPanelCapability = Array.isArray(ext.sessionCapabilities) && ext.sessionCapabilities.includes("open_panel");
6756
+ const hasSummaryMetadata = !!ext.summaryMetadata;
6757
+ const hasError = typeof ext.errorMessage === "string" && ext.errorMessage.trim().length > 0;
6758
+ const hasInterestingStatus = !!status && !["idle", "panel_hidden", "disconnected", "not_monitored"].includes(status);
6759
+ return hasActiveChat || hasMessages || hasModal || hasStreams || hasProviderSessionId || hasControlValues || hasProviderControls || hasOpenPanelCapability || hasSummaryMetadata || hasError || hasInterestingStatus;
6760
+ }
6278
6761
  function buildCliSession(state, options) {
6279
6762
  const profile = options.profile || "full";
6280
6763
  const activeChat = normalizeActiveChatData(state.activeChat, getActiveChatOptions(profile));
@@ -6358,6 +6841,7 @@ function buildSessionEntries(allStates, cdpManagers, options = {}) {
6358
6841
  for (const state of ideStates) {
6359
6842
  sessions.push(buildIdeWorkspaceSession(state, cdpManagers, options));
6360
6843
  for (const ext of state.extensions) {
6844
+ if (!shouldIncludeExtensionSession(ext)) continue;
6361
6845
  sessions.push(buildExtensionAgentSession(state, ext, options));
6362
6846
  }
6363
6847
  }
@@ -8610,7 +9094,9 @@ function applyProviderPatch(h, args, payload) {
8610
9094
  });
8611
9095
  }
8612
9096
  async function executeProviderScript(h, args, scriptName) {
8613
- const resolvedProviderType = h.currentSession?.providerType || h.currentProviderType || args?.agentType || args?.providerType;
9097
+ const explicitTargetSessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : "";
9098
+ const targetSession = explicitTargetSessionId ? h.ctx.sessionRegistry?.get(explicitTargetSessionId) : void 0;
9099
+ const resolvedProviderType = targetSession?.providerType || h.currentSession?.providerType || h.currentProviderType || args?.agentType || args?.providerType;
8614
9100
  if (!resolvedProviderType) return { success: false, error: "targetSessionId or providerType is required" };
8615
9101
  const loader = h.ctx.providerLoader;
8616
9102
  if (!loader) return { success: false, error: "ProviderLoader not initialized" };
@@ -8653,16 +9139,16 @@ async function executeProviderScript(h, args, scriptName) {
8653
9139
  const scriptFn = provider.scripts[actualScriptName];
8654
9140
  const scriptCode = scriptFn(normalizedArgs);
8655
9141
  if (!scriptCode) return { success: false, error: `Script '${actualScriptName}' returned null` };
8656
- const cdpKey = provider.category === "ide" ? h.currentSession?.cdpManagerKey || h.currentManagerKey || resolvedProviderType : h.currentSession?.cdpManagerKey || h.currentManagerKey;
9142
+ const cdpKey = provider.category === "ide" ? targetSession?.cdpManagerKey || h.currentSession?.cdpManagerKey || h.currentManagerKey || resolvedProviderType : targetSession?.cdpManagerKey || h.currentSession?.cdpManagerKey || h.currentManagerKey;
8657
9143
  LOG.info("Command", `[ExtScript] provider=${provider.type} category=${provider.category} cdpKey=${cdpKey}`);
8658
9144
  const cdp = h.getCdp(cdpKey);
8659
9145
  if (!cdp?.isConnected) return { success: false, error: `No CDP connection for ${cdpKey || "any"}` };
8660
9146
  try {
8661
9147
  let result;
8662
9148
  if (provider.category === "extension") {
8663
- const runtimeSessionId = h.currentSession?.sessionId || args?.targetSessionId;
9149
+ const runtimeSessionId = explicitTargetSessionId || h.currentSession?.sessionId;
8664
9150
  if (!runtimeSessionId) return { success: false, error: `No target session found for ${resolvedProviderType}` };
8665
- const parentSessionId = h.currentSession?.parentSessionId;
9151
+ const parentSessionId = targetSession?.parentSessionId || h.currentSession?.parentSessionId;
8666
9152
  if (parentSessionId) {
8667
9153
  await h.agentStream?.setActiveSession(cdp, parentSessionId, runtimeSessionId);
8668
9154
  await h.agentStream?.syncActiveSession(cdp, parentSessionId);
@@ -37523,6 +38009,28 @@ var init_subscription_updates = __esm({
37523
38009
  }
37524
38010
  });
37525
38011
 
38012
+ // ../../oss/packages/daemon-core/src/chat/async-batch.ts
38013
+ async function runAsyncBatch(items, worker, options = {}) {
38014
+ const list = Array.from(items);
38015
+ if (list.length === 0) return;
38016
+ const concurrency = Math.max(1, Math.min(list.length, Math.floor(options.concurrency || 1)));
38017
+ let nextIndex = 0;
38018
+ const runners = Array.from({ length: concurrency }, async () => {
38019
+ while (true) {
38020
+ const currentIndex = nextIndex;
38021
+ nextIndex += 1;
38022
+ if (currentIndex >= list.length) return;
38023
+ await worker(list[currentIndex], currentIndex);
38024
+ }
38025
+ });
38026
+ await Promise.all(runners);
38027
+ }
38028
+ var init_async_batch = __esm({
38029
+ "../../oss/packages/daemon-core/src/chat/async-batch.ts"() {
38030
+ "use strict";
38031
+ }
38032
+ });
38033
+
37526
38034
  // ../../oss/packages/daemon-core/src/agent-stream/provider-adapter.ts
37527
38035
  var ProviderStreamAdapter;
37528
38036
  var init_provider_adapter = __esm({
@@ -38004,10 +38512,12 @@ var init_manager2 = __esm({
38004
38512
  }
38005
38513
  }
38006
38514
  /** Collect active extension session state */
38007
- async collectActiveSession(cdp, parentSessionId) {
38515
+ async collectActiveSession(cdp, parentSessionId, attemptedSessionIds = /* @__PURE__ */ new Set(), originSessionId) {
38008
38516
  if (!this.enabled) return null;
38009
38517
  const activeSessionId = this.getActiveSessionId(parentSessionId);
38010
38518
  if (!activeSessionId) return null;
38519
+ const resolvedOriginSessionId = originSessionId || activeSessionId;
38520
+ attemptedSessionIds.add(activeSessionId);
38011
38521
  let agent = this.managedBySessionId.get(activeSessionId);
38012
38522
  if (!agent) {
38013
38523
  agent = await this.connectManagedSession(cdp, parentSessionId, activeSessionId) || void 0;
@@ -38020,18 +38530,44 @@ var init_manager2 = __esm({
38020
38530
  try {
38021
38531
  const evaluate = (expr, timeout) => cdp.evaluateInSessionFrame(agent.cdpSessionId, expr, timeout);
38022
38532
  const state = await agent.adapter.readChat(evaluate);
38023
- const stateError = this.getStateError(state);
38024
- const selectedModelValue = typeof state.controlValues?.model === "string" ? state.controlValues.model : "";
38025
- LOG.debug("AgentStream", `[AgentStream] readChat(${type}) result: status=${state.status} msgs=${state.messages?.length || 0} model=${selectedModelValue}${state.status === "error" ? " error=" + JSON.stringify(stateError) : ""}`);
38026
- if (state.status === "error" && this.isRecoverableSessionError(stateError)) {
38533
+ 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;
38534
+ const normalizedState = {
38535
+ ...state,
38536
+ sessionId: agent.runtimeSessionId,
38537
+ ...resolvedProviderSessionId ? { providerSessionId: resolvedProviderSessionId } : {}
38538
+ };
38539
+ const stateError = this.getStateError(normalizedState);
38540
+ const selectedModelValue = typeof normalizedState.controlValues?.model === "string" ? normalizedState.controlValues.model : "";
38541
+ LOG.debug("AgentStream", `[AgentStream] readChat(${type}) result: status=${normalizedState.status} msgs=${normalizedState.messages?.length || 0} model=${selectedModelValue}${normalizedState.status === "error" ? " error=" + JSON.stringify(stateError) : ""}`);
38542
+ if (normalizedState.status === "error" && this.isRecoverableSessionError(stateError)) {
38027
38543
  throw new Error(stateError);
38028
38544
  }
38029
- agent.lastState = state;
38545
+ agent.lastState = normalizedState;
38030
38546
  agent.lastError = null;
38031
- if (state.status === "panel_hidden") {
38547
+ if (normalizedState.status === "panel_hidden") {
38548
+ const discovered = await cdp.discoverAgentWebviews().catch(() => []);
38549
+ const fallbackTarget = discovered.find((entry) => {
38550
+ if (entry.agentType === type) return false;
38551
+ const fallbackSessionId = this.resolveSessionIdForTarget(parentSessionId, entry.agentType);
38552
+ return !!fallbackSessionId && fallbackSessionId !== activeSessionId && !attemptedSessionIds.has(fallbackSessionId);
38553
+ });
38554
+ if (fallbackTarget) {
38555
+ const fallbackSessionId = this.resolveSessionIdForTarget(parentSessionId, fallbackTarget.agentType);
38556
+ if (fallbackSessionId && fallbackSessionId !== activeSessionId && !attemptedSessionIds.has(fallbackSessionId)) {
38557
+ this.logFn(`[AgentStream] Active session ${type} is hidden; switching to visible agent ${fallbackTarget.agentType} (${parentSessionId})`);
38558
+ await this.setActiveSession(cdp, parentSessionId, fallbackSessionId);
38559
+ await this.syncActiveSession(cdp, parentSessionId);
38560
+ const fallbackState = await this.collectActiveSession(cdp, parentSessionId, attemptedSessionIds, resolvedOriginSessionId);
38561
+ if (fallbackState?.status === "panel_hidden" && resolvedOriginSessionId !== fallbackSessionId) {
38562
+ await this.setActiveSession(cdp, parentSessionId, resolvedOriginSessionId);
38563
+ await this.syncActiveSession(cdp, parentSessionId);
38564
+ }
38565
+ return fallbackState;
38566
+ }
38567
+ }
38032
38568
  agent.lastHiddenCheckTime = Date.now();
38033
38569
  }
38034
- return state;
38570
+ return normalizedState;
38035
38571
  } catch (e) {
38036
38572
  const errorMsg = e?.message || String(e);
38037
38573
  this.logFn(`[AgentStream] readChat(${type}) error: ${errorMsg.slice(0, 200)}`);
@@ -38343,6 +38879,7 @@ var init_poller = __esm({
38343
38879
  try {
38344
38880
  await agentStreamManager.syncActiveSession(cdp, parentSessionId);
38345
38881
  let stream = await agentStreamManager.collectActiveSession(cdp, parentSessionId);
38882
+ resolvedActiveSessionId = stream?.sessionId || agentStreamManager.getActiveSessionId(parentSessionId) || resolvedActiveSessionId;
38346
38883
  if (stream?.status === "waiting_approval") {
38347
38884
  const autoApprove = providerLoader.getSettings(stream.agentType).autoApprove !== false;
38348
38885
  if (autoApprove && resolvedActiveSessionId) {
@@ -45162,6 +45699,7 @@ __export(src_exports, {
45162
45699
  resolveChatMessageKind: () => resolveChatMessageKind,
45163
45700
  resolveDebugRuntimeConfig: () => resolveDebugRuntimeConfig,
45164
45701
  resolveSessionHostAppName: () => resolveSessionHostAppName,
45702
+ runAsyncBatch: () => runAsyncBatch,
45165
45703
  saveConfig: () => saveConfig,
45166
45704
  saveState: () => saveState,
45167
45705
  setDebugRuntimeConfig: () => setDebugRuntimeConfig,
@@ -45209,6 +45747,7 @@ var init_src = __esm({
45209
45747
  init_chat_history();
45210
45748
  init_chat_signatures();
45211
45749
  init_subscription_updates();
45750
+ init_async_batch();
45212
45751
  init_agent_stream();
45213
45752
  init_agent_stream();
45214
45753
  init_forward();
@@ -77527,6 +78066,7 @@ var init_daemon_p2p = __esm({
77527
78066
  fs20 = __toESM(require("fs"));
77528
78067
  path26 = __toESM(require("path"));
77529
78068
  import_node_module2 = require("module");
78069
+ init_src();
77530
78070
  init_data_channel_router();
77531
78071
  init_screenshot_sender();
77532
78072
  init_peer_connection_manager();
@@ -77776,14 +78316,22 @@ ${e?.stack || ""}`);
77776
78316
  return false;
77777
78317
  }
77778
78318
  async flushChatSubscriptions(builder) {
78319
+ const tasks = [];
77779
78320
  for (const peer of this.peers.values()) {
77780
78321
  if (peer.state !== "connected" || !peer.chatSubscriptions || peer.chatSubscriptions.size === 0) continue;
77781
78322
  for (const subscription of peer.chatSubscriptions.values()) {
78323
+ tasks.push({ peer, subscription });
78324
+ }
78325
+ }
78326
+ await runAsyncBatch(tasks, async ({ peer, subscription }) => {
78327
+ try {
77782
78328
  const update = await builder(subscription);
77783
- if (!update) continue;
78329
+ if (!update) return;
77784
78330
  this.screenshotSender.sendTopicUpdateToPeer(peer, update);
78331
+ } catch (error48) {
78332
+ log(`chat_tail flush skipped: peer=${peer.peerId} session=${subscription.params.targetSessionId} error=${error48?.message || error48}`);
77785
78333
  }
77786
- }
78334
+ }, { concurrency: 4 });
77787
78335
  }
77788
78336
  async flushMachineRuntimeSubscriptions(builder) {
77789
78337
  for (const peer of this.peers.values()) {
@@ -85267,7 +85815,7 @@ var init_adhdev_daemon = __esm({
85267
85815
  init_source();
85268
85816
  init_version();
85269
85817
  init_src();
85270
- pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.74" });
85818
+ pkgVersion = resolvePackageVersion({ injectedVersion: "0.8.76" });
85271
85819
  AdhdevDaemon = class _AdhdevDaemon {
85272
85820
  localHttpServer = null;
85273
85821
  localWss = null;
@@ -85681,7 +86229,7 @@ ${err?.stack || ""}`);
85681
86229
  }),
85682
86230
  onStatusChange: () => {
85683
86231
  this.statusReporter?.onStatusChange();
85684
- void this.flushP2PChatSubscriptions({ onlyActive: false });
86232
+ void this.flushP2PChatSubscriptions({ onlyActive: true });
85685
86233
  },
85686
86234
  removeAgentTracking: (key) => this.statusReporter?.removeAgentTracking(key),
85687
86235
  hostedRuntimeManagerTag: "adhdev-cloud",
@@ -85710,7 +86258,7 @@ ${err?.stack || ""}`);
85710
86258
  statusVersion: pkgVersion,
85711
86259
  onStatusChange: () => {
85712
86260
  this.statusReporter?.onStatusChange();
85713
- void this.flushP2PChatSubscriptions({ onlyActive: false });
86261
+ void this.flushP2PChatSubscriptions({ onlyActive: true });
85714
86262
  },
85715
86263
  onPostChatCommand: () => {
85716
86264
  setTimeout(() => this.statusReporter?.throttledReport(), 1e3);